From 920bf92fc24efa48df6671b52ca726eb7d16217d Mon Sep 17 00:00:00 2001 From: Jason Henriquez Date: Mon, 22 Apr 2024 00:06:23 -0500 Subject: [PATCH 01/23] Implement page bookmarking modal & call, icon, & handling/calls --- src/constants.js | 5 ++ src/datastores/handlers/base.js | 28 ++++++ src/datastores/handlers/electron.js | 40 ++++++++- src/datastores/handlers/index.js | 3 +- src/datastores/handlers/web.js | 25 +++++- src/datastores/index.js | 1 + src/main/index.js | 49 +++++++++++ src/renderer/App.js | 5 ++ src/renderer/App.vue | 3 + .../page-bookmark-prompt.css | 9 ++ .../page-bookmark-prompt.js | 87 +++++++++++++++++++ .../page-bookmark-prompt.vue | 40 +++++++++ src/renderer/components/top-nav/top-nav.js | 39 +++++++++ src/renderer/components/top-nav/top-nav.scss | 6 +- src/renderer/components/top-nav/top-nav.vue | 10 +++ src/renderer/main.js | 2 + src/renderer/store/modules/index.js | 2 + src/renderer/store/modules/search-history.js | 82 +++++++++++++++++ src/renderer/store/modules/utils.js | 17 ++++ static/locales/en-US.yaml | 13 +++ 20 files changed, 462 insertions(+), 4 deletions(-) create mode 100644 src/renderer/components/page-bookmark-prompt/page-bookmark-prompt.css create mode 100644 src/renderer/components/page-bookmark-prompt/page-bookmark-prompt.js create mode 100644 src/renderer/components/page-bookmark-prompt/page-bookmark-prompt.vue create mode 100644 src/renderer/store/modules/search-history.js diff --git a/src/constants.js b/src/constants.js index 3224725035b5..282443998201 100644 --- a/src/constants.js +++ b/src/constants.js @@ -17,11 +17,13 @@ const IpcChannels = { DB_SETTINGS: 'db-settings', DB_HISTORY: 'db-history', + DB_SEARCH_HISTORY: 'db-search-history', DB_PROFILES: 'db-profiles', DB_PLAYLISTS: 'db-playlists', SYNC_SETTINGS: 'sync-settings', SYNC_HISTORY: 'sync-history', + SYNC_SEARCH_HISTORY: 'sync-search-history', SYNC_PROFILES: 'sync-profiles', SYNC_PLAYLISTS: 'sync-playlists', @@ -45,6 +47,9 @@ const DBActions = { UPDATE_PLAYLIST: 'db-action-history-update-playlist', }, + // SEARCH_HISTORY: { + // }, + PLAYLISTS: { UPSERT_VIDEO: 'db-action-playlists-upsert-video-by-playlist-name', UPSERT_VIDEOS: 'db-action-playlists-upsert-videos-by-playlist-name', diff --git a/src/datastores/handlers/base.js b/src/datastores/handlers/base.js index 7c2eb4ec0539..302a567f65fe 100644 --- a/src/datastores/handlers/base.js +++ b/src/datastores/handlers/base.js @@ -183,12 +183,39 @@ class Playlists { } } +class SearchHistory { + static create(pageBookmark) { + return db.searchHistory.insertAsync(pageBookmark) + } + + static find() { + return db.searchHistory.findAsync({}) + } + + static upsert(pageBookmark) { + return db.searchHistory.updateAsync({ _id: pageBookmark.route }, pageBookmark, { upsert: true }) + } + + static delete(route) { + return db.searchHistory.removeAsync({ _id: route }) + } + + static deleteAll() { + return db.searchHistory.removeAsync({}, { multi: true }) + } + + static persist() { + return db.searchHistory.compactDatafileAsync() + } +} + function compactAllDatastores() { return Promise.allSettled([ Settings.persist(), History.persist(), Profiles.persist(), Playlists.persist(), + SearchHistory.persist() ]) } @@ -197,6 +224,7 @@ export { History as history, Profiles as profiles, Playlists as playlists, + SearchHistory as searchHistory, compactAllDatastores, } diff --git a/src/datastores/handlers/electron.js b/src/datastores/handlers/electron.js index 31e8c96305ec..f4a4536580c1 100644 --- a/src/datastores/handlers/electron.js +++ b/src/datastores/handlers/electron.js @@ -205,9 +205,47 @@ class Playlists { } } +class SearchHistory { + static create(pageBookmark) { + return ipcRenderer.invoke( + IpcChannels.DB_SEARCH_HISTORY, + { action: DBActions.GENERAL.CREATE, data: pageBookmark } + ) + } + + static find() { + return ipcRenderer.invoke( + IpcChannels.DB_SEARCH_HISTORY, + { action: DBActions.GENERAL.FIND } + ) + } + + static upsert(pageBookmark) { + return ipcRenderer.invoke( + IpcChannels.DB_SEARCH_HISTORY, + { action: DBActions.GENERAL.UPSERT, data: pageBookmark } + ) + } + + static delete(route) { + return ipcRenderer.invoke( + IpcChannels.DB_SEARCH_HISTORY, + { action: DBActions.GENERAL.DELETE, data: route } + ) + } + + static deleteAll() { + return ipcRenderer.invoke( + IpcChannels.DB_SEARCH_HISTORY, + { action: DBActions.GENERAL.DELETE_ALL } + ) + } +} + export { Settings as settings, History as history, Profiles as profiles, - Playlists as playlists + Playlists as playlists, + SearchHistory as searchHistory } diff --git a/src/datastores/handlers/index.js b/src/datastores/handlers/index.js index df6ebffd9d80..80a954f3841e 100644 --- a/src/datastores/handlers/index.js +++ b/src/datastores/handlers/index.js @@ -2,5 +2,6 @@ export { settings as DBSettingHandlers, history as DBHistoryHandlers, profiles as DBProfileHandlers, - playlists as DBPlaylistHandlers + playlists as DBPlaylistHandlers, + searchHistory as DBSearchHistoryHandlers } from 'DB_HANDLERS_ELECTRON_RENDERER_OR_WEB' diff --git a/src/datastores/handlers/web.js b/src/datastores/handlers/web.js index d5feccc998d0..8685220cf290 100644 --- a/src/datastores/handlers/web.js +++ b/src/datastores/handlers/web.js @@ -118,9 +118,32 @@ class Playlists { } } +class SearchHistory { + static create(pageBookmark) { + return baseHandlers.searchHistory.create(pageBookmark) + } + + static find() { + return baseHandlers.searchHistory.find() + } + + static upsert(pageBookmark) { + return baseHandlers.searchHistory.upsert(pageBookmark) + } + + static delete(route) { + return baseHandlers.searchHistory.delete(route) + } + + static deleteAll() { + return baseHandlers.searchHistory.deleteAll() + } +} + export { Settings as settings, History as history, Profiles as profiles, - Playlists as playlists + Playlists as playlists, + SearchHistory as searchHistory } diff --git a/src/datastores/index.js b/src/datastores/index.js index 442fed6497bf..20b1331ab6b3 100644 --- a/src/datastores/index.js +++ b/src/datastores/index.js @@ -26,3 +26,4 @@ export const settings = new Datastore({ filename: dbPath('settings'), autoload: export const profiles = new Datastore({ filename: dbPath('profiles'), autoload: true }) export const playlists = new Datastore({ filename: dbPath('playlists'), autoload: true }) export const history = new Datastore({ filename: dbPath('history'), autoload: true }) +export const searchHistory = new Datastore({ filename: dbPath('search-history'), autoload: true }) diff --git a/src/main/index.js b/src/main/index.js index 928dfd7b6597..d364a5454db8 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1066,6 +1066,55 @@ function runApp() { } }) + // ************** // + // Search History + ipcMain.handle(IpcChannels.DB_SEARCH_HISTORY, async (event, { action, data }) => { + try { + switch (action) { + case DBActions.GENERAL.CREATE: + await baseHandlers.searchHistory.create(data) + syncOtherWindows( + IpcChannels.SYNC_SEARCH_HISTORY, + event, + { event: SyncEvents.GENERAL.CREATE, data } + ) + return null + + case DBActions.GENERAL.FIND: + return await baseHandlers.searchHistory.find() + + case DBActions.GENERAL.UPSERT: + await baseHandlers.searchHistory.upsert(data) + syncOtherWindows( + IpcChannels.SYNC_SEARCH_HISTORY, + event, + { event: SyncEvents.GENERAL.UPSERT, data } + ) + return null + + case DBActions.GENERAL.DELETE: + await baseHandlers.searchHistory.delete(data) + syncOtherWindows( + IpcChannels.SYNC_SEARCH_HISTORY, + event, + { event: SyncEvents.GENERAL.DELETE, data } + ) + return null + + case DBActions.GENERAL.DELETE_ALL: + await baseHandlers.searchHistory.deleteAll() + return null + + default: + // eslint-disable-next-line no-throw-literal + throw 'invalid search history db action' + } + } catch (err) { + if (typeof err === 'string') throw err + else throw err.toString() + } + }) + // *********** // function syncOtherWindows(channel, event, payload) { diff --git a/src/renderer/App.js b/src/renderer/App.js index eade3f2166a9..ffef75879d29 100644 --- a/src/renderer/App.js +++ b/src/renderer/App.js @@ -11,6 +11,7 @@ import FtToast from './components/ft-toast/ft-toast.vue' import FtProgressBar from './components/ft-progress-bar/ft-progress-bar.vue' import FtPlaylistAddVideoPrompt from './components/ft-playlist-add-video-prompt/ft-playlist-add-video-prompt.vue' import FtCreatePlaylistPrompt from './components/ft-create-playlist-prompt/ft-create-playlist-prompt.vue' +import PageBookmarkPrompt from './components/page-bookmark-prompt/page-bookmark-prompt.vue' import { marked } from 'marked' import { IpcChannels } from '../constants' import packageDetails from '../../package.json' @@ -34,6 +35,7 @@ export default defineComponent({ FtProgressBar, FtPlaylistAddVideoPrompt, FtCreatePlaylistPrompt, + PageBookmarkPrompt }, data: function () { return { @@ -77,6 +79,9 @@ export default defineComponent({ showCreatePlaylistPrompt: function () { return this.$store.getters.getShowCreatePlaylistPrompt }, + showPageBookmarkPrompt: function () { + return this.$store.getters.getShowPageBookmarkPrompt + }, windowTitle: function () { const routePath = this.$route.path if (!routePath.startsWith('/channel/') && !routePath.startsWith('/watch/') && !routePath.startsWith('/hashtag/')) { diff --git a/src/renderer/App.vue b/src/renderer/App.vue index fb49f342eec8..54326b8b4e8c 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -81,6 +81,9 @@ + { + this.bookmarkName = this.pageBookmark?.bookmarkName ?? document.title + this.lastActiveElement = document.activeElement + this.$refs.pageBookmarkNameInput.focus() + }) + }, + beforeDestroy() { + nextTick(() => this.lastActiveElement?.focus()) + }, + methods: { + hide: function () { + this.hidePageBookmarkPrompt() + }, + + cancelAction: function () { + if (!this.isBookmarkBeingCreated) { + this.removeBookmark() + } + this.hide() + }, + + removeBookmark: function () { + this.removePageBookmark(this.page) + showToast(this.$t('Page Bookmark.Removed page bookmark', { bookmarkName: this.bookmarkName })) + }, + + save: function () { + const pageBookmark = { + route: this.$router.currentRoute.fullPath, + bookmarkName: this.bookmarkName + } + + if (this.isBookmarkBeingCreated) { + this.createPageBookmark(pageBookmark) + showToast(this.$t('Page Bookmark.Created page bookmark', { bookmarkName: this.bookmarkName })) + } else if (this.pageBookmark.bookmarkName !== pageBookmark.bookmarkName) { + this.updatePageBookmark(pageBookmark) + showToast(this.$t('Page Bookmark.Updated page bookmark', { bookmarkName: this.bookmarkName })) + } + + this.hide() + }, + + ...mapActions([ + 'hidePageBookmarkPrompt', + 'createPageBookmark', + 'removePageBookmark', + 'updatePageBookmark' + ]) + } +}) diff --git a/src/renderer/components/page-bookmark-prompt/page-bookmark-prompt.vue b/src/renderer/components/page-bookmark-prompt/page-bookmark-prompt.vue new file mode 100644 index 000000000000..d99e9238b38b --- /dev/null +++ b/src/renderer/components/page-bookmark-prompt/page-bookmark-prompt.vue @@ -0,0 +1,40 @@ + + +