Skip to content

Commit 8f164b9

Browse files
committed
feat(context): adds open in context menu
1 parent fccb35e commit 8f164b9

20 files changed

+106
-30
lines changed

scripts/manifest.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ export const manifest: Manifest.WebExtensionManifest = {
4242
},
4343
background: {
4444
service_worker: 'scripts/background.js',
45+
type: 'module',
4546
},
46-
permissions: ['storage', 'tabs'],
47+
permissions: ['storage', 'tabs', 'contextMenus'],
4748
web_accessible_resources: [
4849
{
4950
resources: ['/views/options/index.html'],

src/components/common/navbar/NavbarComponent.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useRoute, useRouter } from 'vue-router';
55
66
import NavbarSettingsDropdown from '~/components/common/navbar/NavbarSettingsDopdown.vue';
77
8-
import { Route } from '~/router';
8+
import { Route } from '~/models/router.model';
99
import { NavbarService } from '~/services/navbar.service';
1010
import { useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
1111
import { useI18n } from '~/utils';

src/components/common/navbar/NavbarSettingsDopdown.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { computed, defineProps, h, ref } from 'vue';
66
import { useRouter } from 'vue-router';
77
88
import type { DropdownProps } from 'naive-ui';
9+
910
import type { Component } from 'vue';
1011
1112
import type { ArrayElement } from '~/utils/typescript.utils';
@@ -17,7 +18,7 @@ import IconExternalLink from '~/components/icons/IconExternalLink.vue';
1718
import IconLightBulb from '~/components/icons/IconLightBulb.vue';
1819
import IconLogOut from '~/components/icons/IconLogOut.vue';
1920
20-
import { Route } from '~/router';
21+
import { Route } from '~/models/router.model';
2122
import { TraktService } from '~/services/trakt.service';
2223
import { ExternaLinks } from '~/settings/external.links';
2324
import { logger } from '~/stores/settings/log.store';

src/components/views/search/SearchNavbar.vue

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
1212
import { type Component, computed, defineProps, h, onActivated, ref } from 'vue';
1313
14+
import { useRoute } from 'vue-router';
15+
1416
import type { TraktSearchType } from '~/models/trakt/trakt-search.model';
1517
1618
import ButtonLinkExternal from '~/components/common/buttons/ButtonLinkExternal.vue';
@@ -156,8 +158,11 @@ const hideTooltip = () => {
156158
157159
const inputRef = ref();
158160
161+
const route = useRoute();
162+
159163
onActivated(() => {
160164
if (!search.value) inputRef.value?.focus();
165+
if (typeof route?.query?.search === 'string') search.value = route.query.search;
161166
});
162167
</script>
163168

src/components/views/settings/SettingsTabs.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ref } from 'vue';
55
66
import SettingsFormItem from '~/components/views/settings/SettingsFormItem.vue';
77
import { pageSizeOptions } from '~/models/page-size.model';
8-
import { Route } from '~/router';
8+
import { Route } from '~/models/router.model';
99
import { useHistoryStoreRefs } from '~/stores/data/history.store';
1010
import { useListStoreRefs } from '~/stores/data/list.store';
1111
import { useSearchStoreRefs } from '~/stores/data/search.store';
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export const ContextMenuId = {
2+
OpenInSideTrakt: 'open-in-side-trakt',
3+
} as const;
4+
5+
export type ContextMenuIds = (typeof ContextMenuId)[keyof typeof ContextMenuId];
6+
7+
export type ContextMenu = {
8+
id: ContextMenuIds;
9+
title: string;
10+
contexts?: ('selection' | 'link')[];
11+
};
12+
13+
export const ContextMenus: Record<ContextMenuIds, ContextMenu> = {
14+
[ContextMenuId.OpenInSideTrakt]: {
15+
id: ContextMenuId.OpenInSideTrakt,
16+
title: 'Open in side trakt',
17+
contexts: ['selection'],
18+
},
19+
} as const;

src/models/router.model.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export const RouterStorageKey = {
2+
LastRoute: 'app.state.last-route',
3+
} as const;
4+
5+
export enum Route {
6+
Calendar = 'calendar',
7+
History = 'history',
8+
Watchlist = 'watchlist',
9+
Progress = 'progress',
10+
Search = 'search',
11+
Settings = 'settings',
12+
About = 'about',
13+
Login = 'login',
14+
}

src/router/create-router.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { createRouter as createVueRouter, createWebHashHistory, type LocationQueryRaw } from 'vue-router';
22

33
import { isLoginAuthResponseSuccess } from '~/models/login/login-auth-response';
4-
import { Route, routes } from '~/router/routes';
4+
import { Route } from '~/models/router.model';
5+
import { routes } from '~/router/routes';
56
import { TraktService } from '~/services/trakt.service';
67
import { useAppStateStoreRefs } from '~/stores/app-state.store';
78
import { useRouterStore, useRouterStoreRefs } from '~/stores/router.store';

src/router/routes.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
import type { RouteRecordRaw } from 'vue-router';
22

3-
export enum Route {
4-
Calendar = 'calendar',
5-
History = 'history',
6-
Watchlist = 'watchlist',
7-
Progress = 'progress',
8-
Search = 'search',
9-
Settings = 'settings',
10-
About = 'about',
11-
Login = 'login',
12-
}
3+
import { Route } from '~/models/router.model';
134

145
const panelRoute = (base: Route, partial: Partial<RouteRecordRaw> = {}): RouteRecordRaw =>
156
({

src/scripts/background/index.ts

+34
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,37 @@
1+
import type { RouteLocationNormalized } from 'vue-router';
2+
3+
import { ContextMenuId, ContextMenus } from '~/models/context/context-menu.model';
4+
import { Route, RouterStorageKey } from '~/models/router.model';
5+
import { action } from '~/utils/browser/borwser-action.utils';
6+
import { context } from '~/utils/browser/browser-context.utils';
7+
import { runtime } from '~/utils/browser/browser-runtime.utils';
8+
import { storage } from '~/utils/browser/browser-storage.utils';
9+
import { createTab } from '~/utils/browser/browser.utils';
10+
111
export {};
212

313
console.debug('Background script started');
14+
15+
runtime?.onInstalled.addListener(async () => {
16+
if (!context) return;
17+
await Promise.all(Object.values(ContextMenus).map(m => context!.create(m)));
18+
19+
context.onClicked.addListener(async info => {
20+
if (!context || !runtime) return;
21+
if (info.menuItemId !== ContextMenuId.OpenInSideTrakt) return;
22+
if (!info?.selectionText) return;
23+
24+
await storage.local.set(RouterStorageKey.LastRoute, {
25+
name: Route.Search,
26+
query: { search: info.selectionText },
27+
} satisfies Partial<RouteLocationNormalized>);
28+
29+
if (action?.openPopup) {
30+
await action.openPopup();
31+
} else {
32+
await createTab({
33+
url: runtime.getURL('views/options/index.html'),
34+
});
35+
}
36+
});
37+
});

src/services/loading-bar.service.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,21 @@ export class LoadingBarService {
1717
}
1818

1919
static start() {
20-
if (!this.instance) logger.warn('LoadingBarService instance is not initialized');
20+
if (!this.instance?.start) logger.warn('LoadingBarService instance is not initialized');
2121
this.instance.start();
2222
this.loading.value = true;
2323
}
2424

2525
static finish() {
2626
if (!this.isLoading) return;
27-
if (!this.instance) logger.warn('LoadingBarService instance is not initialized');
27+
if (!this.instance?.finish) logger.warn('LoadingBarService instance is not initialized');
2828
this.instance.finish();
2929
this.loading.value = false;
3030
}
3131

3232
static error() {
3333
if (!this.isLoading) return;
34-
if (!this.instance) logger.warn('LoadingBarService instance is not initialized');
34+
if (!this.instance?.error) logger.warn('LoadingBarService instance is not initialized');
3535
this.instance.error();
3636
this.loading.value = false;
3737
}

src/stores/router.store.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ref } from 'vue';
33

44
import type { RouteLocationNormalized } from 'vue-router';
55

6+
import { RouterStorageKey } from '~/models/router.model';
67
import { storage } from '~/utils/browser/browser-storage.utils';
78

89
const parseQuery = (location: Location) => {
@@ -28,11 +29,11 @@ export const useRouterStore = defineStore('router', () => {
2829
if (lastRoute.value?.name === _route.name) return;
2930
lastRoute.value = _route;
3031
const { matched, ...jsonSafeRoute } = _route;
31-
return storage.local.set('app.state.last-route', jsonSafeRoute);
32+
return storage.local.set(RouterStorageKey.LastRoute, jsonSafeRoute);
3233
};
3334

3435
const restoreLastRoute = async () => {
35-
const _route = await storage.local.get<RouteLocationNormalized>('app.state.last-route');
36+
const _route = await storage.local.get<RouteLocationNormalized>(RouterStorageKey.LastRoute);
3637
if (_route) lastRoute.value = _route;
3738
return _route;
3839
};

src/stores/settings/extension.store.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { defineStore, storeToRefs } from 'pinia';
22
import { computed, reactive, ref, toRaw } from 'vue';
33

4-
import { Route } from '~/router';
4+
import { Route } from '~/models/router.model';
55
import { TraktService } from '~/services/trakt.service';
66
import { logger } from '~/stores/settings/log.store';
77
import { storage } from '~/utils/browser/browser-storage.utils';

src/stores/settings/use-logout.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useRouter } from 'vue-router';
22

3-
import { Route } from '~/router';
3+
import { Route } from '~/models/router.model';
44
import { TraktService } from '~/services/trakt.service';
55
import { useAuthSettingsStore } from '~/stores/settings/auth.store';
66
import { defaultUser, useUserSettingsStore, useUserSettingsStoreRefs } from '~/stores/settings/user.store';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const action: typeof chrome.action | undefined = globalThis?.chrome?.action;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Browser context alias
3+
*/
4+
export const context: typeof chrome.contextMenus | undefined = globalThis?.chrome?.contextMenus;

src/utils/browser/browser-i18n.utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const i18nTranslate = (value: string | BrowserI18nInput, ...modules: stri
1818
key = path ? `${path}__${value.key}` : value.key;
1919
substitution = value?.substitutions;
2020
}
21-
return window?.chrome?.i18n.getMessage?.(key, substitution) || key;
21+
return globalThis?.chrome?.i18n.getMessage?.(key, substitution) || key;
2222
};
2323

2424
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Runtime alias
3+
*/
4+
export const runtime: typeof chrome.runtime | undefined = globalThis?.chrome?.runtime;

src/utils/browser/browser-storage.utils.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ import type { RecursiveRecord } from '~/utils/typescript.utils';
33
/**
44
* @see [chrome.storage.sync](https://developer.chrome.com/docs/extensions/reference/storage/#type-SyncStorageArea)
55
*/
6-
export const syncStorage: chrome.storage.SyncStorageArea = window?.chrome?.storage?.sync;
6+
export const syncStorage: chrome.storage.SyncStorageArea | undefined = globalThis?.chrome?.storage?.sync;
77

88
/**
99
* @see [chrome.storage.local](https://developer.chrome.com/docs/extensions/reference/storage/#type-LocalStorageArea)
1010
*/
11-
export const localStorage: chrome.storage.LocalStorageArea = window?.chrome?.storage?.local;
11+
export const localStorage: chrome.storage.LocalStorageArea | undefined = globalThis?.chrome?.storage?.local;
1212

1313
/**
1414
* @see [chrome.storage.session](https://developer.chrome.com/docs/extensions/reference/storage/#type-StorageArea)
1515
*/
16-
export const sessionStorage: chrome.storage.StorageArea = window?.chrome?.storage?.session;
16+
export const sessionStorage: chrome.storage.StorageArea | undefined = globalThis?.chrome?.storage?.session;
1717

1818
const filterObject = (object: Record<string, unknown>, regex: string | RegExp) =>
1919
Object.fromEntries(Object.entries(object).filter(([key]) => (typeof regex === 'string' ? new RegExp(regex) : regex).test(key)));
@@ -27,7 +27,7 @@ const reverseFilterObject = (object: Record<string, unknown>, regex: string | Re
2727
* @param name The name of the storage area.
2828
*/
2929
export const storageWrapper = (area: chrome.storage.StorageArea, name: string) => {
30-
if (!window?.chrome?.storage) {
30+
if (!globalThis?.chrome?.storage) {
3131
console.warn('Storage API is not available, using local storage instead.');
3232

3333
const storage = {

src/utils/browser/browser.utils.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ const openTab = (options: chrome.tabs.CreateProperties) => window.open(options.u
1010
/**
1111
* @see [chrome.tabs.create](https://developer.chrome.com/docs/extensions/reference/tabs/#method-create)
1212
*/
13-
export const createTab = (options: chrome.tabs.CreateProperties) => (window?.chrome?.tabs?.create ?? openTab)(options);
13+
export const createTab = (options: chrome.tabs.CreateProperties) => (globalThis?.chrome?.tabs?.create ?? openTab)(options);
1414

1515
/**
1616
* The ID of the current extension.
1717
* @see [chrome.runtime.id](https://developer.chrome.com/docs/extensions/reference/runtime/#property-id)
1818
*/
19-
export const chromeRuntimeId: typeof chrome.runtime.id = window?.chrome?.runtime?.id;
19+
export const chromeRuntimeId: typeof chrome.runtime.id | undefined = globalThis?.chrome?.runtime?.id;
2020

2121
/**
2222
* The i18n API for the current browser.
2323
* @see [chrome.i18n](https://developer.chrome.com/docs/extensions/reference/i18n/)
2424
*/
25-
export const chromeI18n: typeof chrome.i18n = window?.chrome?.i18n;
25+
export const chromeI18n: typeof chrome.i18n | undefined = globalThis?.chrome?.i18n;
2626

2727
/**
2828
* Returns the short locale (ISO 639-1) of the current browser.

0 commit comments

Comments
 (0)