Skip to content

Commit 97664ed

Browse files
kommunarrabsidue
authored andcommitted
Keyword search history (FreeTubeApp#6414)
* Add search history DB integration * Implement search history display logic and UI * Modify search cache removal setting to remove search history as well * Exclude current search route from history suggestions and populate input with matching query * Modify new labels for clarity * Fix issues detected during review Fix focus being lost for a second during searching of search history entry causing clear text button to phase away for a moment. Fix clear text button event not being noticed by the logic in top-nav. Fix spacebar error issue by adding check in updateVisibleDataList function. * Implement fixes from code review Fixes clear text button to appear and stay visible when the dropdown options are visible in an ft-input. Fixes updateVisibleDataList not using trim(), thus incorrectly causing filtering of search history when space bar was used. * Update logic to allowing storing and searching by name rather than by route This is much more efficient for search history particularly. No need for app-specific routes and easier operations & logic due to name being the primary key for search history entries. This still allows for the route to be used as the _id for different kinds of search history (i.e., bookmarks) that could be added in the future. * Add back clear statement * Fix clear text button not working or appearing as active when arrowing through search history results * Add 'Remove' option * Implement search history entries showing up alongside YT search suggestions as queries are being typed * Improve code commenting * Implement code review changes Fix preventDefault being called for arrow left/right in search input. Revert ellipsis changes for overly long search results. Adjust matching search history logic to be non-heuristic. * Fix text overflow issue with long search history entries * Remove 'name' field and unused DB methods * Implement 'Remember Search History' setting and rename 'Remember History' to 'Remember Watch History' * Apply suggestions from code review Co-authored-by: absidue <[email protected]> * Update to not show search history suggestions if 'Remember Search History' is disabled * Update logic to make 'Enable Search Suggestions' being false still allow showing search history results * Revert hiding old search history entries if rememberSearchHistory is disabled * Fix searchOptions not closing in edge case Bug scenario: 1. click search history entry, 2. click clear text button, 3. see state of searchOptions not being closeable until re-interacting with the input * Update src/renderer/views/Search/Search.js Co-authored-by: absidue <[email protected]> * Add bolding to selected/focused 'Remove' button --------- Co-authored-by: absidue <[email protected]>
1 parent f9b8b0b commit 97664ed

File tree

23 files changed

+477
-55
lines changed

23 files changed

+477
-55
lines changed

src/constants.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ const IpcChannels = {
2525
DB_HISTORY: 'db-history',
2626
DB_PROFILES: 'db-profiles',
2727
DB_PLAYLISTS: 'db-playlists',
28+
DB_SEARCH_HISTORY: 'db-search-history',
2829
DB_SUBSCRIPTION_CACHE: 'db-subscription-cache',
2930

3031
SYNC_SETTINGS: 'sync-settings',
3132
SYNC_HISTORY: 'sync-history',
33+
SYNC_SEARCH_HISTORY: 'sync-search-history',
3234
SYNC_PROFILES: 'sync-profiles',
3335
SYNC_PLAYLISTS: 'sync-playlists',
3436
SYNC_SUBSCRIPTION_CACHE: 'sync-subscription-cache',
@@ -192,6 +194,12 @@ const PLAYLIST_HEIGHT_FORCE_LIST_THRESHOLD = 500
192194
// YouTube search character limit is 100 characters
193195
const SEARCH_CHAR_LIMIT = 100
194196

197+
// max # of results we show for search suggestions
198+
const SEARCH_RESULTS_DISPLAY_LIMIT = 14
199+
200+
// max # of search history results we show when mixed with YT search suggestions
201+
const MIXED_SEARCH_HISTORY_ENTRIES_DISPLAY_LIMIT = 4
202+
195203
// Displayed on the about page and used in the main.js file to only allow bitcoin URLs with this wallet address to be opened
196204
const ABOUT_BITCOIN_ADDRESS = '1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS'
197205

@@ -204,5 +212,7 @@ export {
204212
MOBILE_WIDTH_THRESHOLD,
205213
PLAYLIST_HEIGHT_FORCE_LIST_THRESHOLD,
206214
SEARCH_CHAR_LIMIT,
215+
SEARCH_RESULTS_DISPLAY_LIMIT,
216+
MIXED_SEARCH_HISTORY_ENTRIES_DISPLAY_LIMIT,
207217
ABOUT_BITCOIN_ADDRESS,
208218
}

src/datastores/handlers/base.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,24 @@ class Playlists {
226226
}
227227
}
228228

229+
class SearchHistory {
230+
static find() {
231+
return db.searchHistory.findAsync({}).sort({ lastUpdatedAt: -1 })
232+
}
233+
234+
static upsert(searchHistoryEntry) {
235+
return db.searchHistory.updateAsync({ _id: searchHistoryEntry._id }, searchHistoryEntry, { upsert: true })
236+
}
237+
238+
static delete(_id) {
239+
return db.searchHistory.removeAsync({ _id: _id })
240+
}
241+
242+
static deleteAll() {
243+
return db.searchHistory.removeAsync({}, { multi: true })
244+
}
245+
}
246+
229247
class SubscriptionCache {
230248
static find() {
231249
return db.subscriptionCache.findAsync({})
@@ -311,6 +329,7 @@ function compactAllDatastores() {
311329
db.history.compactDatafileAsync(),
312330
db.profiles.compactDatafileAsync(),
313331
db.playlists.compactDatafileAsync(),
332+
db.searchHistory.compactDatafileAsync(),
314333
db.subscriptionCache.compactDatafileAsync(),
315334
])
316335
}
@@ -320,6 +339,7 @@ export {
320339
History as history,
321340
Profiles as profiles,
322341
Playlists as playlists,
342+
SearchHistory as searchHistory,
323343
SubscriptionCache as subscriptionCache,
324344

325345
compactAllDatastores,

src/datastores/handlers/electron.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,36 @@ class Playlists {
218218
}
219219
}
220220

221+
class SearchHistory {
222+
static find() {
223+
return ipcRenderer.invoke(
224+
IpcChannels.DB_SEARCH_HISTORY,
225+
{ action: DBActions.GENERAL.FIND }
226+
)
227+
}
228+
229+
static upsert(searchHistoryEntry) {
230+
return ipcRenderer.invoke(
231+
IpcChannels.DB_SEARCH_HISTORY,
232+
{ action: DBActions.GENERAL.UPSERT, data: searchHistoryEntry }
233+
)
234+
}
235+
236+
static delete(_id) {
237+
return ipcRenderer.invoke(
238+
IpcChannels.DB_SEARCH_HISTORY,
239+
{ action: DBActions.GENERAL.DELETE, data: _id }
240+
)
241+
}
242+
243+
static deleteAll() {
244+
return ipcRenderer.invoke(
245+
IpcChannels.DB_SEARCH_HISTORY,
246+
{ action: DBActions.GENERAL.DELETE_ALL }
247+
)
248+
}
249+
}
250+
221251
class SubscriptionCache {
222252
static find() {
223253
return ipcRenderer.invoke(
@@ -296,5 +326,6 @@ export {
296326
History as history,
297327
Profiles as profiles,
298328
Playlists as playlists,
329+
SearchHistory as searchHistory,
299330
SubscriptionCache as subscriptionCache,
300331
}

src/datastores/handlers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export {
33
history as DBHistoryHandlers,
44
profiles as DBProfileHandlers,
55
playlists as DBPlaylistHandlers,
6+
searchHistory as DBSearchHistoryHandlers,
67
subscriptionCache as DBSubscriptionCacheHandlers,
78
} from 'DB_HANDLERS_ELECTRON_RENDERER_OR_WEB'

src/datastores/handlers/web.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ class Settings {
2424
export { Settings as settings }
2525

2626
// These classes don't require any changes from the base classes, so can be exported as-is.
27-
export { history, profiles, playlists, subscriptionCache } from './base'
27+
export { history, profiles, playlists, searchHistory, subscriptionCache } from './base'

src/datastores/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ export const settings = new Datastore({ filename: dbPath('settings'), autoload:
2626
export const profiles = new Datastore({ filename: dbPath('profiles'), autoload: true })
2727
export const playlists = new Datastore({ filename: dbPath('playlists'), autoload: true })
2828
export const history = new Datastore({ filename: dbPath('history'), autoload: true })
29+
export const searchHistory = new Datastore({ filename: dbPath('search-history'), autoload: true })
2930
export const subscriptionCache = new Datastore({ filename: dbPath('subscription-cache'), autoload: true })

src/main/index.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,51 @@ function runApp() {
13041304

13051305
// *********** //
13061306

1307+
// ************** //
1308+
// Search History
1309+
ipcMain.handle(IpcChannels.DB_SEARCH_HISTORY, async (event, { action, data }) => {
1310+
try {
1311+
switch (action) {
1312+
case DBActions.GENERAL.FIND:
1313+
return await baseHandlers.searchHistory.find()
1314+
1315+
case DBActions.GENERAL.UPSERT:
1316+
await baseHandlers.searchHistory.upsert(data)
1317+
syncOtherWindows(
1318+
IpcChannels.SYNC_SEARCH_HISTORY,
1319+
event,
1320+
{ event: SyncEvents.GENERAL.UPSERT, data }
1321+
)
1322+
return null
1323+
1324+
case DBActions.GENERAL.DELETE:
1325+
await baseHandlers.searchHistory.delete(data)
1326+
syncOtherWindows(
1327+
IpcChannels.SYNC_SEARCH_HISTORY,
1328+
event,
1329+
{ event: SyncEvents.GENERAL.DELETE, data }
1330+
)
1331+
return null
1332+
1333+
case DBActions.GENERAL.DELETE_ALL:
1334+
await baseHandlers.searchHistory.deleteAll()
1335+
syncOtherWindows(
1336+
IpcChannels.SYNC_SEARCH_HISTORY,
1337+
event,
1338+
{ event: SyncEvents.GENERAL.DELETE_ALL }
1339+
)
1340+
return null
1341+
1342+
default:
1343+
// eslint-disable-next-line no-throw-literal
1344+
throw 'invalid search history db action'
1345+
}
1346+
} catch (err) {
1347+
if (typeof err === 'string') throw err
1348+
else throw err.toString()
1349+
}
1350+
})
1351+
13071352
// *********** //
13081353
// Profiles
13091354
ipcMain.handle(IpcChannels.DB_SUBSCRIPTION_CACHE, async (event, { action, data }) => {

src/renderer/App.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ export default defineComponent({
188188
this.grabHistory()
189189
this.grabAllPlaylists()
190190
this.grabAllSubscriptions()
191+
this.grabSearchHistoryEntries()
191192

192193
if (process.env.IS_ELECTRON) {
193194
ipcRenderer = require('electron').ipcRenderer
@@ -568,6 +569,7 @@ export default defineComponent({
568569
'grabHistory',
569570
'grabAllPlaylists',
570571
'grabAllSubscriptions',
572+
'grabSearchHistoryEntries',
571573
'getYoutubeUrlInfo',
572574
'getExternalPlayerCmdArgumentsData',
573575
'fetchInvidiousInstances',

src/renderer/components/ft-input/ft-input.css

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ body[dir='rtl'] .ft-input-component.search.showClearTextButton:focus-within .inp
3131
padding-inline-start: 46px;
3232
}
3333

34-
.ft-input-component:focus-within .clearInputTextButton {
34+
.ft-input-component:focus-within .clearInputTextButton,
35+
.ft-input-component.showOptions .clearInputTextButton {
3536
opacity: 0.5;
3637
}
3738

38-
.clearTextButtonVisible .clearInputTextButton.visible,
39-
.ft-input-component:focus-within .clearInputTextButton.visible {
39+
40+
.ft-input-component.inputDataPresent .clearInputTextButton.visible,
41+
.clearTextButtonVisible:not(.showOptions) .clearInputTextButton.visible,
42+
.ft-input-component:focus-within:not(.showOptions) .clearInputTextButton.visible {
4043
cursor: pointer;
4144
opacity: 1;
4245
}
@@ -200,10 +203,30 @@ body[dir='rtl'] .ft-input-component.search.showClearTextButton:focus-within .inp
200203
}
201204

202205
.list li {
203-
display: block;
206+
display: flex;
207+
justify-content: space-between;
204208
padding-block: 0;
205-
padding-inline: 15px;
206209
line-height: 2rem;
210+
padding-inline: 15px;
211+
}
212+
213+
.searchResultIcon {
214+
opacity: 0.6;
215+
padding-inline-end: 10px;
216+
inline-size: 16px;
217+
block-size: 16px;
218+
}
219+
220+
.removeButton {
221+
text-decoration: none;
222+
float: var(--float-right-ltr-rtl-value);
223+
font-size: 13px;
224+
}
225+
226+
.removeButton:hover,
227+
.removeButtonSelected {
228+
text-decoration: underline;
229+
font-weight: bold;
207230
}
208231

209232
.hover {

0 commit comments

Comments
 (0)