@@ -51,23 +51,33 @@ type OutlineDoc struct {
5151
5252var recentDocLock = sync.Mutex {}
5353
54- // 三种类型各保留 32 条记录,并清空 Title 和 Icon
55- func trimRecentDocs (recentDocs []* RecentDoc ) []* RecentDoc {
56- if len (recentDocs ) <= 32 {
54+ // normalizeRecentDocs 规范化最近文档列表:去重、清空 Title/Icon、按类型截取 32 条记录
55+ func normalizeRecentDocs (recentDocs []* RecentDoc ) []* RecentDoc {
56+ // 去重
57+ seen := make (map [string ]struct {}, len (recentDocs ))
58+ deduplicated := make ([]* RecentDoc , 0 , len (recentDocs ))
59+ for _ , doc := range recentDocs {
60+ if _ , ok := seen [doc .RootID ]; ! ok {
61+ seen [doc .RootID ] = struct {}{}
62+ deduplicated = append (deduplicated , doc )
63+ }
64+ }
65+
66+ if len (deduplicated ) <= 32 {
5767 // 清空 Title 和 Icon
58- for _ , doc := range recentDocs {
68+ for _ , doc := range deduplicated {
5969 doc .Title = ""
6070 doc .Icon = ""
6171 }
62- return recentDocs
72+ return deduplicated
6373 }
6474
6575 // 分别统计三种类型的记录
6676 var viewedDocs []* RecentDoc
6777 var openedDocs []* RecentDoc
6878 var closedDocs []* RecentDoc
6979
70- for _ , doc := range recentDocs {
80+ for _ , doc := range deduplicated {
7181 if doc .ViewedAt > 0 {
7282 viewedDocs = append (viewedDocs , doc )
7383 }
@@ -131,7 +141,7 @@ func UpdateRecentDocOpenTime(rootID string) (err error) {
131141 recentDocLock .Lock ()
132142 defer recentDocLock .Unlock ()
133143
134- recentDocs , err := getRecentDocs ( "" )
144+ recentDocs , err := loadRecentDocsRaw ( )
135145 if err != nil {
136146 return
137147 }
@@ -168,7 +178,7 @@ func UpdateRecentDocViewTime(rootID string) (err error) {
168178 recentDocLock .Lock ()
169179 defer recentDocLock .Unlock ()
170180
171- recentDocs , err := getRecentDocs ( "" )
181+ recentDocs , err := loadRecentDocsRaw ( )
172182 if err != nil {
173183 return
174184 }
@@ -214,7 +224,7 @@ func BatchUpdateRecentDocCloseTime(rootIDs []string) (err error) {
214224 recentDocLock .Lock ()
215225 defer recentDocLock .Unlock ()
216226
217- recentDocs , err := getRecentDocs ( "" )
227+ recentDocs , err := loadRecentDocsRaw ( )
218228 if err != nil {
219229 return
220230 }
@@ -266,7 +276,7 @@ func GetRecentDocs(sortBy string) (ret []*RecentDoc, err error) {
266276}
267277
268278func setRecentDocs (recentDocs []* RecentDoc ) (err error ) {
269- recentDocs = trimRecentDocs (recentDocs )
279+ recentDocs = normalizeRecentDocs (recentDocs )
270280
271281 dirPath := filepath .Join (util .DataDir , "storage" )
272282 if err = os .MkdirAll (dirPath , 0755 ); err != nil {
@@ -289,8 +299,7 @@ func setRecentDocs(recentDocs []*RecentDoc) (err error) {
289299 return
290300}
291301
292- func getRecentDocs (sortBy string ) (ret []* RecentDoc , err error ) {
293- var tmp []* RecentDoc
302+ func loadRecentDocsRaw () (ret []* RecentDoc , err error ) {
294303 dataPath := filepath .Join (util .DataDir , "storage/recent-doc.json" )
295304 if ! filelock .IsExist (dataPath ) {
296305 return
@@ -302,22 +311,41 @@ func getRecentDocs(sortBy string) (ret []*RecentDoc, err error) {
302311 return
303312 }
304313
305- if err = gulu .JSON .UnmarshalJSON (data , & tmp ); err != nil {
314+ if err = gulu .JSON .UnmarshalJSON (data , & ret ); err != nil {
306315 logging .LogErrorf ("unmarshal storage [recent-doc] failed: %s" , err )
307316 if err = setRecentDocs ([]* RecentDoc {}); err != nil {
308317 logging .LogErrorf ("reset storage [recent-doc] failed: %s" , err )
309318 }
310319 ret = []* RecentDoc {}
311320 return
312321 }
322+ return
323+ }
324+
325+ func getRecentDocs (sortBy string ) (ret []* RecentDoc , err error ) {
326+ ret = []* RecentDoc {} // 初始化为空切片,确保 API 始终返回非 nil
327+ recentDocs , err := loadRecentDocsRaw ()
328+ if err != nil {
329+ return
330+ }
331+
332+ // 去重
333+ seen := make (map [string ]struct {}, len (recentDocs ))
334+ var deduplicated []* RecentDoc
335+ for _ , doc := range recentDocs {
336+ if _ , ok := seen [doc .RootID ]; ! ok {
337+ seen [doc .RootID ] = struct {}{}
338+ deduplicated = append (deduplicated , doc )
339+ }
340+ }
313341
314342 var rootIDs []string
315- for _ , doc := range tmp {
343+ for _ , doc := range deduplicated {
316344 rootIDs = append (rootIDs , doc .RootID )
317345 }
318346 bts := treenode .GetBlockTrees (rootIDs )
319347 var notExists []string
320- for _ , doc := range tmp {
348+ for _ , doc := range deduplicated {
321349 if bt := bts [doc .RootID ]; nil != bt {
322350 // 获取最新的文档标题和图标
323351 doc .Title = path .Base (bt .HPath ) // Recent docs not updated after renaming https://github.com/siyuan-note/siyuan/issues/7827
@@ -332,9 +360,9 @@ func getRecentDocs(sortBy string) (ret []*RecentDoc, err error) {
332360 }
333361
334362 if 0 < len (notExists ) {
335- err : = setRecentDocs (ret )
363+ err = setRecentDocs (ret )
336364 if err != nil {
337- return nil , err
365+ return
338366 }
339367 }
340368
@@ -391,38 +419,45 @@ func getRecentDocs(sortBy string) (ret []*RecentDoc, err error) {
391419 ret = append (ret , doc )
392420 }
393421 case "closedAt" : // 按关闭时间排序
394- var filtered []* RecentDoc
422+ filtered := []* RecentDoc {} // 初始化为空切片,确保 API 始终返回非 nil
395423 for _ , doc := range ret {
396424 if doc .ClosedAt > 0 {
397425 filtered = append (filtered , doc )
398426 }
399427 }
400428 ret = filtered
401- sort .Slice (ret , func (i , j int ) bool {
402- return ret [i ].ClosedAt > ret [j ].ClosedAt
403- })
429+ if 0 < len (ret ) {
430+ sort .Slice (ret , func (i , j int ) bool {
431+ return ret [i ].ClosedAt > ret [j ].ClosedAt
432+ })
433+ }
404434 case "openAt" : // 按打开时间排序
405- var filtered []* RecentDoc
435+ filtered := []* RecentDoc {} // 初始化为空切片,确保 API 始终返回非 nil
406436 for _ , doc := range ret {
407437 if doc .OpenAt > 0 {
408438 filtered = append (filtered , doc )
409439 }
410440 }
411441 ret = filtered
412- sort .Slice (ret , func (i , j int ) bool {
413- return ret [i ].OpenAt > ret [j ].OpenAt
414- })
415- default : // 默认按浏览时间排序
416- var filtered []* RecentDoc
442+ if 0 < len (ret ) {
443+ sort .Slice (ret , func (i , j int ) bool {
444+ return ret [i ].OpenAt > ret [j ].OpenAt
445+ })
446+ }
447+ case "viewedAt" : // 按浏览时间排序
448+ default :
449+ filtered := []* RecentDoc {} // 初始化为空切片,确保 API 始终返回非 nil
417450 for _ , doc := range ret {
418451 if doc .ViewedAt > 0 {
419452 filtered = append (filtered , doc )
420453 }
421454 }
422455 ret = filtered
423- sort .Slice (ret , func (i , j int ) bool {
424- return ret [i ].ViewedAt > ret [j ].ViewedAt
425- })
456+ if 0 < len (ret ) {
457+ sort .Slice (ret , func (i , j int ) bool {
458+ return ret [i ].ViewedAt > ret [j ].ViewedAt
459+ })
460+ }
426461 }
427462 return
428463}
0 commit comments