mirror of
https://github.com/movie-web/extension.git
synced 2025-09-13 05:33:24 +00:00
Functionality and state for popout
This commit is contained in:
7
.editorconfig
Normal file
7
.editorconfig
Normal file
@@ -0,0 +1,7 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
@@ -40,7 +40,8 @@
|
||||
},
|
||||
"manifest": {
|
||||
"permissions": [
|
||||
"declarativeNetRequest"
|
||||
"declarativeNetRequest",
|
||||
"tabs"
|
||||
],
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
|
@@ -2,7 +2,7 @@ import type { PlasmoMessaging } from '@plasmohq/messaging';
|
||||
|
||||
import type { BaseRequest } from '~types/request';
|
||||
import type { BaseResponse } from '~types/response';
|
||||
import { validateDomainWhiteList } from '~utils/storage';
|
||||
import { assertDomainWhitelist } from '~utils/storage';
|
||||
|
||||
type Response = BaseResponse<{
|
||||
version: string;
|
||||
@@ -10,7 +10,7 @@ type Response = BaseResponse<{
|
||||
|
||||
const handler: PlasmoMessaging.MessageHandler<BaseRequest, Response> = async (req, res) => {
|
||||
try {
|
||||
await validateDomainWhiteList(req.body.requestDomain);
|
||||
await assertDomainWhitelist(req.body.requestDomain);
|
||||
|
||||
const version = chrome.runtime.getManifest().version;
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import type { PlasmoMessaging } from '@plasmohq/messaging';
|
||||
import type { BaseRequest } from '~types/request';
|
||||
import type { BaseResponse } from '~types/response';
|
||||
import { makeFullUrl } from '~utils/fetcher';
|
||||
import { validateDomainWhiteList } from '~utils/storage';
|
||||
import { assertDomainWhitelist } from '~utils/storage';
|
||||
|
||||
export interface Request extends BaseRequest {
|
||||
baseUrl?: string;
|
||||
@@ -26,7 +26,7 @@ type Response<T> = BaseResponse<{
|
||||
|
||||
const handler: PlasmoMessaging.MessageHandler<Request, Response<any>> = async (req, res) => {
|
||||
try {
|
||||
await validateDomainWhiteList(req.body.requestDomain);
|
||||
await assertDomainWhitelist(req.body.requestDomain);
|
||||
|
||||
const response = await fetch(makeFullUrl(req.body.url, req.body), {
|
||||
method: req.body.method,
|
||||
|
@@ -2,7 +2,7 @@ import type { PlasmoMessaging } from '@plasmohq/messaging';
|
||||
|
||||
import type { BaseRequest } from '~types/request';
|
||||
import type { BaseResponse } from '~types/response';
|
||||
import { validateDomainWhiteList } from '~utils/storage';
|
||||
import { assertDomainWhitelist } from '~utils/storage';
|
||||
|
||||
interface Request extends BaseRequest {
|
||||
ruleId: number;
|
||||
@@ -23,7 +23,7 @@ const mapHeadersToDeclarativeNetRequestHeaders = (
|
||||
|
||||
const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (req, res) => {
|
||||
try {
|
||||
await validateDomainWhiteList(req.body.requestDomain);
|
||||
await assertDomainWhitelist(req.body.requestDomain);
|
||||
|
||||
await chrome.declarativeNetRequest.updateDynamicRules({
|
||||
removeRuleIds: [req.body.ruleId],
|
||||
|
9
src/components/Frame.tsx
Normal file
9
src/components/Frame.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export interface FrameProps {
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export function Frame(props: FrameProps) {
|
||||
return <div style={{ width: 300, height: 300 }}>{props.children}</div>;
|
||||
}
|
12
src/components/ToggleButton.tsx
Normal file
12
src/components/ToggleButton.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export interface ToggleButtonProps {
|
||||
onClick?: () => void;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export function ToggleButton(props: ToggleButtonProps) {
|
||||
return (
|
||||
<button type="button" onClick={props.onClick}>
|
||||
{props.active ? 'ON' : 'OFF'}
|
||||
</button>
|
||||
);
|
||||
}
|
30
src/hooks/useDomain.ts
Normal file
30
src/hooks/useDomain.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { makeUrlIntoDomain } from '~utils/domains';
|
||||
|
||||
function queryCurrentDomain(cb: (domain: string | null) => void) {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
|
||||
const url = tabs[0]?.url;
|
||||
if (!url) cb(null);
|
||||
else cb(url);
|
||||
});
|
||||
}
|
||||
|
||||
export function useDomain(): null | string {
|
||||
const [domain, setDomain] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
queryCurrentDomain(setDomain);
|
||||
function listen() {
|
||||
queryCurrentDomain(setDomain);
|
||||
}
|
||||
chrome.tabs.onActivated.addListener(listen);
|
||||
chrome.tabs.onUpdated.addListener(listen);
|
||||
return () => {
|
||||
chrome.tabs.onActivated.removeListener(listen);
|
||||
chrome.tabs.onUpdated.removeListener(listen);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return makeUrlIntoDomain(domain);
|
||||
}
|
38
src/hooks/useDomainWhitelist.ts
Normal file
38
src/hooks/useDomainWhitelist.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useDomainStorage } from '~utils/storage';
|
||||
|
||||
export function useDomainWhitelist() {
|
||||
const [domainWhitelist, setDomainWhitelist] = useDomainStorage();
|
||||
|
||||
const removeDomain = useCallback((domain: string | null) => {
|
||||
if (!domain) return;
|
||||
setDomainWhitelist((s) => [...s.filter((v) => v !== domain)]);
|
||||
}, []);
|
||||
|
||||
const addDomain = useCallback((domain: string | null) => {
|
||||
if (!domain) return;
|
||||
setDomainWhitelist((s) => [...s.filter((v) => v !== domain), domain]);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
removeDomain,
|
||||
addDomain,
|
||||
domainWhitelist,
|
||||
};
|
||||
}
|
||||
|
||||
export function useToggleWhitelistDomain(domain: string) {
|
||||
const { domainWhitelist, addDomain, removeDomain } = useDomainWhitelist();
|
||||
const isWhitelisted = domainWhitelist.includes(domain);
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
if (isWhitelisted) removeDomain(domain);
|
||||
else addDomain(domain);
|
||||
}, [isWhitelisted, domain, addDomain, removeDomain]);
|
||||
|
||||
return {
|
||||
toggle,
|
||||
isWhitelisted,
|
||||
};
|
||||
}
|
4
src/hooks/useVersion.ts
Normal file
4
src/hooks/useVersion.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export function useVersion(ops?: { prefixed?: boolean }) {
|
||||
const prefix = ops?.prefixed ? 'v' : '';
|
||||
return `${prefix}${chrome.runtime.getManifest().version}`;
|
||||
}
|
@@ -1,61 +1,20 @@
|
||||
import { useStorage } from '@plasmohq/storage/hook';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { DEFAULT_DOMAIN_WHITELIST } from '~utils/storage';
|
||||
import { Frame } from '~components/Frame';
|
||||
import { ToggleButton } from '~components/ToggleButton';
|
||||
import { useDomain } from '~hooks/useDomain';
|
||||
import { useToggleWhitelistDomain } from '~hooks/useDomainWhitelist';
|
||||
import { useVersion } from '~hooks/useVersion';
|
||||
|
||||
function IndexPopup() {
|
||||
const [domainInput, setDomainInput] = useState('');
|
||||
const [domainWhiteist, setDomainWhitelist] = useStorage<string[]>(
|
||||
'domainWhitelist',
|
||||
(v) => v ?? DEFAULT_DOMAIN_WHITELIST,
|
||||
);
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleDomainSubmit = () => {
|
||||
try {
|
||||
const origin = new URL(domainInput).origin;
|
||||
setDomainWhitelist([...domainWhiteist, origin]);
|
||||
setDomainInput('');
|
||||
} catch (e) {
|
||||
setError('Invalid domain');
|
||||
}
|
||||
};
|
||||
const domain = useDomain();
|
||||
const { isWhitelisted, toggle } = useToggleWhitelistDomain(domain);
|
||||
const version = useVersion({ prefixed: true });
|
||||
|
||||
return (
|
||||
<div style={{ width: 300 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<h1 style={{ flexGrow: 1 }}>movie-web</h1>
|
||||
|
||||
<h3>v{chrome.runtime.getManifest().version}</h3>
|
||||
</div>
|
||||
|
||||
<h2 style={{ marginTop: 0 }}>Domains</h2>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<input type="text" value={domainInput} onChange={(e) => setDomainInput(e.target.value)} />
|
||||
<button type="button" onClick={handleDomainSubmit}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
{error && <span style={{ fontWeight: 'bold' }}>{error}</span>}
|
||||
<table>
|
||||
<tbody>
|
||||
{domainWhiteist.map((domain) => (
|
||||
<tr key={domain}>
|
||||
<td>{domain}</td>
|
||||
<td>
|
||||
<button type="button" onClick={() => setDomainWhitelist(domainWhiteist.filter((d) => d !== domain))}>
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<Frame>
|
||||
<ToggleButton active={isWhitelisted} onClick={toggle} />
|
||||
{!domain ? <p>Cant use extension on this page</p> : null}
|
||||
<h3>{version} - movie-web</h3>
|
||||
</Frame>
|
||||
);
|
||||
}
|
||||
|
||||
|
9
src/utils/domains.ts
Normal file
9
src/utils/domains.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function makeUrlIntoDomain(url: string): string | null {
|
||||
try {
|
||||
const u = new URL(url);
|
||||
if (!['http:', 'https:'].includes(u.protocol)) return null;
|
||||
return u.host.toLowerCase();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -1,15 +1,24 @@
|
||||
import { Storage } from '@plasmohq/storage';
|
||||
import { useStorage } from '@plasmohq/storage/hook';
|
||||
|
||||
import { makeUrlIntoDomain } from '~utils/domains';
|
||||
|
||||
export const DEFAULT_DOMAIN_WHITELIST = ['https://movie-web.app', 'http://localhost:5173'];
|
||||
|
||||
export const storage = new Storage();
|
||||
|
||||
export const domainIsInWhitelist = async (domain: string) => {
|
||||
const domainIsInWhitelist = async (domain: string) => {
|
||||
const whitelist = await storage.get<string[]>('domainWhitelist');
|
||||
return whitelist?.some((d) => d.includes(domain)) ?? false;
|
||||
};
|
||||
|
||||
export const validateDomainWhiteList = async (domain: string) => {
|
||||
export function useDomainStorage() {
|
||||
return useStorage<string[]>('domainWhitelist', (v) => v ?? DEFAULT_DOMAIN_WHITELIST);
|
||||
}
|
||||
|
||||
export const assertDomainWhitelist = async (url: string) => {
|
||||
const domain = makeUrlIntoDomain(url);
|
||||
if (!domain) throw new Error('Domain is from a normal tab');
|
||||
const isWhiteListed = await domainIsInWhitelist(domain);
|
||||
if (!isWhiteListed) throw new Error('Domain is not whitelisted');
|
||||
};
|
||||
|
Reference in New Issue
Block a user