diff --git a/internal/cmd/reload/reload.go b/internal/cmd/reload/reload.go index 9b16e2c..2fc2cb2 100644 --- a/internal/cmd/reload/reload.go +++ b/internal/cmd/reload/reload.go @@ -1,9 +1,8 @@ package reload import ( - "github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/safety" - "strings" + "sync" ) var calls []func() @@ -12,37 +11,118 @@ var str = safety.NewMap[string, string]() var anyMap = safety.NewMap[string, any]() -func GetAnyValBy[T any](k string, fn func() T) T { - v, ok := anyMap.Load(k) - if ok { - return v.(T) - } - vv := fn() - anyMap.Store(k, vv) - return vv +type safetyVar[T, A any] struct { + Val *safety.Var[val[T]] + mutex sync.Mutex } -func GetAnyValBys[T, A any](k string, a A, fn func(A) T) T { - v, ok := anyMap.Load(k) - if ok { - return v.(T) - } - vv := fn(a) - anyMap.Store(k, vv) - return vv +type val[T any] struct { + v T + ok bool +} +type safetyMap[K comparable, V, A any] struct { + val *safety.Map[K, V] + mutex sync.Mutex } -func GetStrBy[T any](key, delimiter string, t T, fn ...func(T) string) string { - v, ok := str.Load(key) +var safetyMaps = safety.NewMap[string, any]() +var safetyMapLock = sync.Mutex{} + +func SafetyMapByFn[K comparable, V, A any](namespace string, fn func(A) V) func(key K, args A) V { + m := safetyMapFn[K, V, A](namespace) + return func(key K, a A) V { + v, ok := m.val.Load(key) + if ok { + return v + } + m.mutex.Lock() + v, ok = m.val.Load(key) + if ok { + m.mutex.Unlock() + return v + } + v = fn(a) + m.val.Store(key, v) + m.mutex.Unlock() + return v + } +} + +func safetyMapFn[K comparable, V, A any](namespace string) *safetyMap[K, V, A] { + vv, ok := safetyMaps.Load(namespace) + var m *safetyMap[K, V, A] + if ok { + m = vv.(*safetyMap[K, V, A]) + } else { + safetyMapLock.Lock() + vv, ok = safetyMaps.Load(namespace) + if ok { + m = vv.(*safetyMap[K, V, A]) + } else { + m = &safetyMap[K, V, A]{safety.NewMap[K, V](), sync.Mutex{}} + Push(func() { + m.val.Flush() + }) + safetyMaps.Store(namespace, m) + } + safetyMapLock.Unlock() + } + return m +} + +func SafetyMapBy[K comparable, V, A any](namespace string, key K, a A, fn func(A) V) V { + m := safetyMapFn[K, V, A](namespace) + v, ok := m.val.Load(key) if ok { return v } - v = strings.Join(slice.Map(fn, func(vv func(T) string) string { - return vv(t) - }), delimiter) - str.Store(key, v) + m.mutex.Lock() + v, ok = m.val.Load(key) + if ok { + m.mutex.Unlock() + return v + } + v = fn(a) + m.val.Store(key, v) + m.mutex.Unlock() return v } +func GetAnyValBys[T, A any](namespace string, a A, fn func(A) T) T { + var vv *safetyVar[T, A] + vvv, ok := safetyMaps.Load(namespace) + if ok { + vv = vvv.(*safetyVar[T, A]) + } else { + safetyMapLock.Lock() + vvv, ok = safetyMaps.Load(namespace) + if ok { + vv = vvv.(*safetyVar[T, A]) + } else { + vv = &safetyVar[T, A]{safety.NewVar(val[T]{}), sync.Mutex{}} + Push(func() { + vv.Val.Flush() + }) + safetyMaps.Store(namespace, vv) + } + safetyMapLock.Unlock() + } + v := vv.Val.Load() + if v.ok { + return v.v + } + vv.mutex.Lock() + v = vv.Val.Load() + if v.ok { + vv.mutex.Unlock() + return v.v + } + v.v = fn(a) + v.ok = true + vv.Val.Store(v) + vv.mutex.Unlock() + return v.v +} + func Vars[T any](defaults T) *safety.Var[T] { ss := safety.NewVar(defaults) calls = append(calls, func() { @@ -68,4 +148,5 @@ func Reload() { } anyMap.Flush() str.Flush() + safetyMaps.Flush() } diff --git a/internal/theme/twentyfifteen/twentyfifteen.go b/internal/theme/twentyfifteen/twentyfifteen.go index 5fb4f7c..3d43ea8 100644 --- a/internal/theme/twentyfifteen/twentyfifteen.go +++ b/internal/theme/twentyfifteen/twentyfifteen.go @@ -34,23 +34,38 @@ func Init(fs embed.FS) { logs.ErrPrintln(err, "解析colorscheme失败") } -var pipe = wp.HandlePipe(wp.Render, widget.MiddleWare(ready)...) +var pipe = wp.HandlePipe(wp.ExecuteHandleFn, widget.MiddleWare(ready, data)...) func Hook(h *wp.Handle) { pipe(h) } func ready(next wp.HandleFn[*wp.Handle], h *wp.Handle) { + wp.InitThemeArgAndConfig(configs, h) h.GetPassword() + next(h) +} + +func data(next wp.HandleFn[*wp.Handle], h *wp.Handle) { + if h.Scene() == constraints.Detail { + wp.Details(h) + } else { + wp.Indexs(h) + } + h.DetermineHandleFns() + next(h) +} + +func configs(h *wp.Handle) *wp.Handle { h.PushComponentFilterFn(widgets.Search, func(h *wp.Handle, s string, args ...any) string { return strings.ReplaceAll(s, `class="search-submit"`, `class="search-submit screen-reader-text"`) }) wphandle.RegisterPlugins(h, config.GetConfig().Plugins...) - h.PushCacheGroupHeadScript("CalCustomBackGround", 10, CalCustomBackGround, colorSchemeCss) + h.CommonComponents() h.PushHandleFn(constraints.Ok, wp.NewHandleFn(components.WidgetArea, 20)) h.PushHandleFn(constraints.AllStats, wp.NewHandleFn(customHeader, 10)) - h.PushHandleFn(constraints.AllStats, wp.NewHandleFn(wp.Indexs, 100)) - h.PushHandleFn(constraints.Detail, wp.NewHandleFn(wp.Details, 100)) - next(h) + h.PushHandleFn(constraints.AllStats, wp.NewHandleFn(wp.IndexRender, 50)) + h.PushHandleFn(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 50)) + return h } diff --git a/internal/theme/twentyseventeen/script.go b/internal/theme/twentyseventeen/script.go index 29cd568..f62e538 100644 --- a/internal/theme/twentyseventeen/script.go +++ b/internal/theme/twentyseventeen/script.go @@ -7,7 +7,7 @@ import ( ) func pushScripts(h *wp.Handle) { - h.PushCacheGroupFooterScript("head", 30, func(h *wp.Handle) string { + h.PushCacheGroupHeadScript("head", 30, func(h *wp.Handle) string { head := headScript if "dark" == wpconfig.GetThemeModsVal(ThemeName, "colorscheme", "light") { head = fmt.Sprintf("%s\n%s", headScript, ` `) diff --git a/internal/theme/twentyseventeen/twentyseventeen.go b/internal/theme/twentyseventeen/twentyseventeen.go index 1231bbf..842e182 100644 --- a/internal/theme/twentyseventeen/twentyseventeen.go +++ b/internal/theme/twentyseventeen/twentyseventeen.go @@ -43,29 +43,35 @@ var paginate = func() plugins.PageEle { return p }() -var pipe = wp.HandlePipe(wp.Render, widget.MiddleWare(ready)...) +var pipe = wp.HandlePipe(wp.ExecuteHandleFn, widget.MiddleWare(ready, data)...) func Hook(h *wp.Handle) { pipe(h) } -func ready(next wp.HandleFn[*wp.Handle], h *wp.Handle) { - h.GetPassword() +func configs(h *wp.Handle) *wp.Handle { wphandle.RegisterPlugins(h, config.GetConfig().Plugins...) h.PushHandleFn(constraints.AllStats, wp.NewHandleFn(calClass, 20)) - h.PushHandleFn(constraints.AllStats, wp.NewHandleFn(index, 100)) - h.PushHandleFn(constraints.Detail, wp.NewHandleFn(detail, 100)) - h.PushGroupHandleFn(constraints.AllStats, 90, wp.PreCodeAndStats, wp.PreTemplate, errorsHandle) h.PushCacheGroupHeadScript("colorScheme-customHeader", 10, colorScheme, customHeader) - h.PushHandleFn(constraints.Ok, wp.NewHandleFn(components.WidgetArea, 20)) + components.WidgetArea(h) pushScripts(h) - h.SetData("HeaderImage", getHeaderImage(h)) + h.PushHandleFn(constraints.AllStats, wp.NewHandleFn(func(h *wp.Handle) { + h.SetData("HeaderImage", getHeaderImage(h)) + }, 10)) h.SetComponentsArgs(widgets.Widget, map[string]string{ "{$before_widget}": `
`, "{$after_widget}": `
`, }) + h.PushGroupHandleFn(constraints.AllStats, 90, wp.PreTemplate, errorsHandle) + h.CommonComponents() wp.SetComponentsArgsForMap(h, widgets.Search, "{$form}", searchForm) - + h.PushHandleFn(constraints.AllStats, wp.NewHandleFn(wp.IndexRender, 10)) + h.PushHandleFn(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 10)) + return h +} +func ready(next wp.HandleFn[*wp.Handle], h *wp.Handle) { + wp.InitThemeArgAndConfig(configs, h) + h.GetPassword() next(h) } @@ -94,6 +100,17 @@ func errorsHandle(h *wp.Handle) { } } +func data(next wp.HandleFn[*wp.Handle], h *wp.Handle) { + if h.Scene() == constraints.Detail { + detail(h) + } else { + index(h) + } + wp.PreCodeAndStats(h) + h.DetermineHandleFns() + next(h) +} + func index(h *wp.Handle) { if h.Scene() == constraints.Detail { return diff --git a/internal/theme/wp/components/widget/recentcomments.go b/internal/theme/wp/components/widget/recentcomments.go index 67cb0ef..ba7daba 100644 --- a/internal/theme/wp/components/widget/recentcomments.go +++ b/internal/theme/wp/components/widget/recentcomments.go @@ -68,7 +68,7 @@ func RecentComments(h *wp.Handle, id string) string { %s发表在《 %s 》 - `, t.CommentAuthor, t.CommentId, t.CommentPostId, t.PostTitle) + `, t.CommentAuthor, t.CommentPostId, t.CommentId, t.PostTitle) }) s := strings.ReplaceAll(recentCommentsTemplate, "{$li}", strings.Join(comments, "\n")) return h.ComponentFilterFnHook(widgets.RecentComments, str.Replace(s, args)) diff --git a/internal/theme/wp/customheader.go b/internal/theme/wp/customheader.go index 1c5ef01..c049f33 100644 --- a/internal/theme/wp/customheader.go +++ b/internal/theme/wp/customheader.go @@ -18,7 +18,7 @@ func (h *Handle) DisplayHeaderText() bool { func (h *Handle) GetCustomHeader() (r models.PostThumbnail, isRand bool) { var err error - hss := reload.GetAnyValBys("headerImages", h.theme, func(theme string) []models.PostThumbnail { + img := reload.GetAnyValBys("headerImages", h.theme, func(theme string) []models.PostThumbnail { hs, er := h.GetHeaderImages(h.theme) if er != nil { err = er @@ -30,7 +30,7 @@ func (h *Handle) GetCustomHeader() (r models.PostThumbnail, isRand bool) { logs.ErrPrintln(err, "获取页眉背景图失败") return } - hs := slice.Copy(hss) + hs := slice.Copy(img) if len(hs) < 1 { return diff --git a/internal/theme/wp/customlogo.go b/internal/theme/wp/customlogo.go index 573bcd9..1708910 100644 --- a/internal/theme/wp/customlogo.go +++ b/internal/theme/wp/customlogo.go @@ -6,11 +6,8 @@ import ( str "github.com/fthvgb1/wp-go/helper/strings" "github.com/fthvgb1/wp-go/internal/pkg/cache" "github.com/fthvgb1/wp-go/internal/wpconfig" - "sync" ) -var logoLock = sync.Mutex{} - func CalCustomLogo(h *Handle) (r string) { id := uint64(h.themeMods.CustomLogo) if id < 1 { @@ -33,9 +30,7 @@ func CalCustomLogo(h *Handle) (r string) { "decoding": "async", //"loading":"lazy", } - logoLock.Lock() img := wpconfig.Thumbnail(logo.AttachmentMetadata, siz, "", "") - logoLock.Unlock() imgx["srcset"] = img.Srcset imgx["sizes"] = img.Sizes imgx["src"] = img.Path diff --git a/internal/theme/wp/detail.go b/internal/theme/wp/detail.go index 653ec86..2df6ec3 100644 --- a/internal/theme/wp/detail.go +++ b/internal/theme/wp/detail.go @@ -101,19 +101,33 @@ func (d *DetailHandle) ContextPost() { } func (d *DetailHandle) Render() { + d.PushHandleFn(constraints.Ok, NewHandleFn(func(h *Handle) { + d.PasswordProject() + d.RenderComment() + d.ginH["post"] = d.Post + reply := "" + if d.Post.CommentStatus == "open" && wpconfig.GetOption("thread_comments") == "1" { + reply = `` + } + d.PushGroupFooterScript(10, reply) + }, 10)) + + d.Handle.Render() +} + +func DetailRender(h *Handle) { + if h.Stats != constraints.Ok { + return + } + d := h.Detail + d.PasswordProject() + d.RenderComment() + d.ginH["post"] = d.Post reply := "" if d.Post.CommentStatus == "open" && wpconfig.GetOption("thread_comments") == "1" { reply = `` } d.PushGroupFooterScript(10, reply) - - d.PushHandleFn(constraints.Ok, NewHandleFn(func(h *Handle) { - d.PasswordProject() - d.RenderComment() - d.ginH["post"] = d.Post - }, 10)) - - d.Handle.Render() } func Details(h *Handle) { diff --git a/internal/theme/wp/index.go b/internal/theme/wp/index.go index 3a4133c..d7fabf1 100644 --- a/internal/theme/wp/index.go +++ b/internal/theme/wp/index.go @@ -156,6 +156,16 @@ func (i *IndexHandle) Render() { i.Handle.Render() } +func IndexRender(h *Handle) { + if h.scene == constraints.Detail || h.Stats != constraints.Ok { + return + } + i := h.Index + i.ExecPostsPlugin() + i.Pagination() + i.ginH["posts"] = i.Posts +} + func Indexs(h *Handle) { if h.Scene() == constraints.Detail { return @@ -171,7 +181,9 @@ func (i *IndexHandle) MarkSticky(posts *[]models.Posts) { if len(a) < 1 { return } + m := i.StickMapPosts() *posts = append(a, slice.Filter(*posts, func(post models.Posts, _ int) bool { - return !i.IsStick(post.Id) + _, ok := m[post.Id] + return !ok })...) } diff --git a/internal/theme/wp/stickyposts.go b/internal/theme/wp/stickyposts.go index e0476a5..d88c8a8 100644 --- a/internal/theme/wp/stickyposts.go +++ b/internal/theme/wp/stickyposts.go @@ -14,7 +14,7 @@ import ( ) func (h *Handle) StickPosts() []models.Posts { - return reload.GetAnyValBys("stickPosts", h, func(h *Handle) (r []models.Posts) { + return reload.GetAnyValBys("stickPostsSlice", h, func(h *Handle) (r []models.Posts) { v := wpconfig.GetOption("sticky_posts") if v == "" { return diff --git a/internal/theme/wp/wp.go b/internal/theme/wp/wp.go index b1e3a4c..f1e7dde 100644 --- a/internal/theme/wp/wp.go +++ b/internal/theme/wp/wp.go @@ -1,6 +1,7 @@ package wp import ( + "fmt" "github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/slice" str "github.com/fthvgb1/wp-go/helper/strings" @@ -12,7 +13,6 @@ import ( "github.com/gin-gonic/gin" "net/http" "strings" - "sync" ) type Handle struct { @@ -35,10 +35,16 @@ type Handle struct { abort bool componentsArgs map[string]any componentFilterFn map[string][]func(*Handle, string, ...any) string + handleCall []HandleCall } -var configHandle = reload.Vars(&Handle{}) -var configHandleMux = sync.Mutex{} +func (h *Handle) HandleCall() []HandleCall { + return h.handleCall +} + +func (h *Handle) SetHandleCall(handleCall []HandleCall) { + h.handleCall = handleCall +} type HandlePlugins map[string]HandleFn[*Handle] @@ -60,17 +66,21 @@ type HandleCall struct { } func InitThemeArgAndConfig(fn func(*Handle) *Handle, h *Handle) { - hh := configHandle.Load() - if len(hh.handleFns) < 1 { - configHandleMux.Lock() - hh = configHandle.Load() - if len(hh.handleFns) < 1 { - hh = fn(h) - configHandle.Store(hh) - } - configHandleMux.Unlock() + hh := reload.GetAnyValBys("themeArgAndConfig", h, func(h *Handle) Handle { + h.components = make(map[string][]Components[string]) + h.handleFns = make(map[int][]HandleCall) + h.componentsArgs = make(map[string]any) + h.componentFilterFn = make(map[string][]func(*Handle, string, ...any) string) + hh := fn(h) + return *hh + }) + m := make(map[string][]Components[string]) + for k, v := range hh.components { + vv := make([]Components[string], len(v)) + copy(vv, v) + m[k] = vv // slice.Copy(v) } - h.components = hh.components + h.components = m // maps.Copy(hh.components) h.handleFns = hh.handleFns h.componentsArgs = hh.componentsArgs h.componentFilterFn = hh.componentFilterFn @@ -193,17 +203,13 @@ func NewHandle(c *gin.Context, scene int, theme string) *Handle { mods, err := wpconfig.GetThemeMods(theme) logs.ErrPrintln(err, "获取mods失败") return &Handle{ - C: c, - theme: theme, - Session: sessions.Default(c), - ginH: gin.H{}, - scene: scene, - Stats: constraints.Ok, - themeMods: mods, - components: make(map[string][]Components[string]), - handleFns: make(map[int][]HandleCall), - componentsArgs: make(map[string]any), - componentFilterFn: make(map[string][]func(*Handle, string, ...any) string), + C: c, + theme: theme, + Session: sessions.Default(c), + ginH: gin.H{}, + scene: scene, + Stats: constraints.Ok, + themeMods: mods, } } @@ -249,8 +255,11 @@ func (h *Handle) PushCacheGroupFooterScript(key string, order int, fns ...func(* h.PushGroupCacheComponentFn(constraints.FooterScript, key, order, fns...) } func (h *Handle) PushGroupCacheComponentFn(name, key string, order int, fns ...func(*Handle) string) { - v := reload.GetStrBy(key, "\n", h, fns...) - h.PushGroupComponentStrs(name, order, v) + h.PushComponents(name, h.NewCacheComponent(key, order, func(h *Handle) string { + return strings.Join(slice.Map(fns, func(t func(*Handle) string) string { + return t(h) + }), "\n") + })) } func (h *Handle) GetPassword() { @@ -360,7 +369,7 @@ func CalComponents(h *Handle) { if component.Fn != nil { v := "" if component.CacheKey != "" { - v = reload.GetAnyValBys(component.CacheKey, h, component.Fn) + v = reload.SafetyMapBy("calComponent", component.CacheKey, h, component.Fn) } else { v = component.Fn(h) } @@ -386,6 +395,42 @@ func HandlePipe[T any](initial func(T), fns ...HandlePipeFn[T]) HandleFn[T] { }, initial) } +func DetermineHandleFn(h *Handle) []HandleCall { + calls, ok := h.handleFns[h.Stats] + var fns []HandleCall + if ok { + fns = append(fns, calls...) + } + calls, ok = h.handleFns[h.scene] + if ok { + fns = append(fns, calls...) + } + calls, ok = h.handleFns[constraints.AllStats] + if ok { + fns = append(fns, calls...) + } + slice.Sort(fns, func(i, j HandleCall) bool { + return i.Order > j.Order + }) + return fns +} + +func (h *Handle) DetermineHandleFns() { + key := fmt.Sprintf("%d-%d", h.scene, h.Stats) + h.handleCall = reload.SafetyMapBy("handleCalls", key, h, func(h *Handle) []HandleCall { + return DetermineHandleFn(h) + }) +} + +func ExecuteHandleFn(h *Handle) { + for _, fn := range h.handleCall { + fn.Fn(h) + if h.abort { + break + } + } +} + func Render(h *Handle) { switch h.scene { case constraints.Detail: