@@ -16,12 +16,55 @@ interface DisabledMenuItems {
16
16
playlists : boolean ;
17
17
}
18
18
19
+ // Cache episode lookups to avoid repeated searches
20
+ const episodeLookupCache = new Map < string , {
21
+ isPlayed : boolean ;
22
+ isFavorite : boolean ;
23
+ isInQueue : boolean ;
24
+ playlists : Set < string > ;
25
+ } > ( ) ;
26
+
27
+ function getCacheKey ( episode : Episode ) : string {
28
+ return `${ episode . title } -${ episode . podcastName } ` ;
29
+ }
30
+
31
+ function getEpisodeCachedState ( episode : Episode ) {
32
+ const cacheKey = getCacheKey ( episode ) ;
33
+ let cached = episodeLookupCache . get ( cacheKey ) ;
34
+
35
+ if ( ! cached ) {
36
+ const playedEps = get ( playedEpisodes ) ;
37
+ const favs = get ( favorites ) ;
38
+ const q = get ( queue ) ;
39
+ const pls = get ( playlists ) ;
40
+
41
+ cached = {
42
+ isPlayed : Object . values ( playedEps ) . some ( e => e . title === episode . title && e . finished ) ,
43
+ isFavorite : favs . episodes . some ( e => e . title === episode . title ) ,
44
+ isInQueue : q . episodes . some ( e => e . title === episode . title ) ,
45
+ playlists : new Set (
46
+ Object . entries ( pls )
47
+ . filter ( ( [ _ , playlist ] ) => playlist . episodes . some ( e => e . title === episode . title ) )
48
+ . map ( ( [ name ] ) => name )
49
+ )
50
+ } ;
51
+
52
+ episodeLookupCache . set ( cacheKey , cached ) ;
53
+
54
+ // Clear cache after a short delay to handle rapid updates
55
+ setTimeout ( ( ) => episodeLookupCache . delete ( cacheKey ) , 5000 ) ;
56
+ }
57
+
58
+ return cached ;
59
+ }
60
+
19
61
export default function spawnEpisodeContextMenu (
20
62
episode : Episode ,
21
63
event : MouseEvent ,
22
64
disabledMenuItems ?: Partial < DisabledMenuItems >
23
65
) {
24
66
const menu = new Menu ( ) ;
67
+ const cachedState = getEpisodeCachedState ( episode ) ;
25
68
26
69
if ( ! disabledMenuItems ?. play ) {
27
70
menu . addItem ( item => item
@@ -34,12 +77,12 @@ export default function spawnEpisodeContextMenu(
34
77
}
35
78
36
79
if ( ! disabledMenuItems ?. markPlayed ) {
37
- const episodeIsPlayed = Object . values ( get ( playedEpisodes ) ) . find ( e => ( e . title === episode . title && e . finished ) ) ;
38
80
menu . addItem ( item => item
39
- . setIcon ( episodeIsPlayed ? "cross" : "check" )
40
- . setTitle ( `Mark as ${ episodeIsPlayed ? "Unplayed" : "Played" } ` )
81
+ . setIcon ( cachedState . isPlayed ? "cross" : "check" )
82
+ . setTitle ( `Mark as ${ cachedState . isPlayed ? "Unplayed" : "Played" } ` )
41
83
. onClick ( ( ) => {
42
- if ( episodeIsPlayed ) {
84
+ episodeLookupCache . delete ( getCacheKey ( episode ) ) ; // Invalidate cache
85
+ if ( cachedState . isPlayed ) {
43
86
playedEpisodes . markAsUnplayed ( episode ) ;
44
87
} else {
45
88
playedEpisodes . markAsPlayed ( episode ) ;
@@ -93,12 +136,12 @@ export default function spawnEpisodeContextMenu(
93
136
}
94
137
95
138
if ( ! disabledMenuItems ?. favorite ) {
96
- const episodeIsFavorite = get ( favorites ) . episodes . find ( e => e . title === episode . title ) ;
97
139
menu . addItem ( item => item
98
140
. setIcon ( "lucide-star" )
99
- . setTitle ( `${ episodeIsFavorite ? "Remove from" : "Add to" } Favorites` )
141
+ . setTitle ( `${ cachedState . isFavorite ? "Remove from" : "Add to" } Favorites` )
100
142
. onClick ( ( ) => {
101
- if ( episodeIsFavorite ) {
143
+ episodeLookupCache . delete ( getCacheKey ( episode ) ) ; // Invalidate cache
144
+ if ( cachedState . isFavorite ) {
102
145
favorites . update ( playlist => {
103
146
playlist . episodes = playlist . episodes . filter ( e => e . title !== episode . title ) ;
104
147
return playlist ;
@@ -115,12 +158,12 @@ export default function spawnEpisodeContextMenu(
115
158
}
116
159
117
160
if ( ! disabledMenuItems ?. queue ) {
118
- const episodeIsInQueue = get ( queue ) . episodes . find ( e => e . title === episode . title ) ;
119
161
menu . addItem ( item => item
120
162
. setIcon ( "list-ordered" )
121
- . setTitle ( `${ episodeIsInQueue ? "Remove from" : "Add to" } Queue` )
163
+ . setTitle ( `${ cachedState . isInQueue ? "Remove from" : "Add to" } Queue` )
122
164
. onClick ( ( ) => {
123
- if ( episodeIsInQueue ) {
165
+ episodeLookupCache . delete ( getCacheKey ( episode ) ) ; // Invalidate cache
166
+ if ( cachedState . isInQueue ) {
124
167
queue . update ( playlist => {
125
168
playlist . episodes = playlist . episodes . filter ( e => e . title !== episode . title ) ;
126
169
@@ -142,12 +185,13 @@ export default function spawnEpisodeContextMenu(
142
185
143
186
const playlistsInStore = get ( playlists ) ;
144
187
for ( const playlist of Object . values ( playlistsInStore ) ) {
145
- const episodeIsInPlaylist = playlist . episodes . find ( e => e . title === episode . title ) ;
188
+ const episodeIsInPlaylist = cachedState . playlists . has ( playlist . name ) ;
146
189
147
190
menu . addItem ( item => item
148
191
. setIcon ( playlist . icon )
149
192
. setTitle ( `${ episodeIsInPlaylist ? "Remove from" : "Add to" } ${ playlist . name } ` )
150
193
. onClick ( ( ) => {
194
+ episodeLookupCache . delete ( getCacheKey ( episode ) ) ; // Invalidate cache
151
195
if ( episodeIsInPlaylist ) {
152
196
playlists . update ( playlists => {
153
197
playlists [ playlist . name ] . episodes = playlists [ playlist . name ] . episodes . filter ( e => e . title !== episode . title ) ;
@@ -168,4 +212,4 @@ export default function spawnEpisodeContextMenu(
168
212
169
213
menu . showAtMouseEvent ( event ) ;
170
214
171
- }
215
+ }
0 commit comments