Skip to content

Commit

Permalink
Cleaned up components by refactoring out local/session storage logic …
Browse files Browse the repository at this point in the history
…into a custom hook
  • Loading branch information
mwhirls committed Feb 3, 2024
1 parent e150a29 commit e149b6f
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 138 deletions.
36 changes: 36 additions & 0 deletions src/common/hooks/useStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useState, useContext, useEffect } from "react";
import { ChromeExtensionContext } from "../../content-scripts/contexts/ExtensionContext";
import { BrowserStorage, BrowserStorageListener } from "../../content-scripts/util/browser-runtime";
import { StorageType } from "../../storage/storage";

export function useStorage<T>(key: string, storageType: StorageType, defaultValue: T): [T, (newValue: T) => void] {
const [value, setValue] = useState<T>(defaultValue);
const context = useContext(ChromeExtensionContext);

useEffect(() => {
const storage = new BrowserStorage<T>(key, storageType, context);
const onChanged = BrowserStorageListener.create(storage, (_, newValue) => setValue(newValue), context);
if (onChanged) {
storage.addOnChangedListener(onChanged);
}
storage.get().then(v => {
if (v !== undefined) {
setValue(v);
}
});

return () => {
if (onChanged) {
storage.removeOnChangedListener(onChanged);
}
}
}, []);

const setStorageValue = (newValue: T) => {
const storage = new BrowserStorage<T>(key, storageType, context);
storage.set(newValue);
setValue(newValue);
};

return [value, setStorageValue];
}
21 changes: 2 additions & 19 deletions src/content-scripts/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,26 @@ import React, { useState, useEffect } from "react";
import { StorageType } from "../../storage/storage";
import { ExtensionContext, ChromeExtensionContext } from "../contexts/ExtensionContext";
import { SegmenterContext } from "../contexts/SegmenterContext";
import { BrowserStorage, BrowserStorageListener } from "../util/browser-runtime";
import { AlertType } from "../../common/components/modal/Alert";
import Modal from "../../common/components/modal/Modal";
import { DBStatusResult } from "../../database/dbstatus";
import VideoContainer from "./VideoContainer";
import { useStorage } from "../../common/hooks/useStorage";

const DB_STATUS_KEY = 'lastDBStatusResult';
const KUROMOJI_DICTIONARIES = 'dict/';

function App() {
const [invalidated, setInvalidated] = useState(false);
const [dbStatus, setDBStatus] = useState<DBStatusResult | null>(null);
const [dbStatus] = useStorage<DBStatusResult | null>(DB_STATUS_KEY, StorageType.Local, null);
const [segmenter, setSegmenter] = useState<Segmenter | null>(null);

const context = new ExtensionContext(() => setInvalidated(true));

useEffect(() => {
const dbStatusStorage = new BrowserStorage<DBStatusResult>(DB_STATUS_KEY, StorageType.Local, context);
const onDBStatusChanged = BrowserStorageListener.create(dbStatusStorage, (_, newValue) => setDBStatus(newValue), context);
if (onDBStatusChanged) {
dbStatusStorage.addOnChangedListener(onDBStatusChanged);
}
dbStatusStorage.get().then(status => {
if (status) {
setDBStatus(status);
}
});

build(chrome.runtime.getURL(KUROMOJI_DICTIONARIES))
.then((segmenter) => setSegmenter(segmenter))
.catch((err) => console.error('[JIMAKUN] error when building tokenizer', err));

return () => {
if (onDBStatusChanged) {
dbStatusStorage.removeOnChangedListener(onDBStatusChanged);
}
};
}, []);

if (invalidated) {
Expand Down
37 changes: 4 additions & 33 deletions src/content-scripts/components/VideoContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { useState, useEffect, useContext } from "react";
import React, { useState, useEffect } from "react";
import { createPortal } from "react-dom";
import { RuntimeEvent } from "../../common/events";
import { TimedTextTrack, NetflixMetadata, TimedTextSwitch } from "../../common/netflix-types";
import { StorageType } from "../../storage/storage";
import { BrowserStorage, BrowserStorageListener } from "../util/browser-runtime";
import { WEBVTT_FORMAT, querySelectorMutation, ChildMutationType } from "../util/util";
import Video, { WebvttSubtitles } from "./Video";
import { DBStatusResult } from "../../database/dbstatus";
import { ChromeExtensionContext } from "../contexts/ExtensionContext";
import { useStorage } from "../../common/hooks/useStorage";

const NETFLIX_PLAYER_CLASS = "watch-video--player-view";
const NETFLIX_VIDEO_CLASS = `${NETFLIX_PLAYER_CLASS} video`
Expand Down Expand Up @@ -56,36 +55,14 @@ interface VideoContainerProps {
}

function VideoContainer({ dbStatus }: VideoContainerProps) {
const context = useContext(ChromeExtensionContext);
const [enabled, setEnabled] = useState(false);
const [currMovie] = useStorage<MovieId | null>(MOVIE_KEY, StorageType.Session, null);
const [enabled] = useStorage<boolean>(ENABLED_KEY, StorageType.Local, false);
const [subtitleData, setSubtitleData] = useState(new Map<MovieId, SubtitleTracks>);
const [currMovie, setCurrMovie] = useState<MovieId | null>(null);
const [currTrack, setCurrTrack] = useState("");
const [netflixPlayer, setNetflixPlayer] = useState<Element | null>(document.querySelector(`.${NETFLIX_PLAYER_CLASS}`));
const [videoElem, setVideoElem] = useState<HTMLVideoElement | null>(document.querySelector(`.${NETFLIX_VIDEO_CLASS}`) as HTMLVideoElement | null);

useEffect(() => {
const movieIdStorage = new BrowserStorage<MovieId>(MOVIE_KEY, StorageType.Session, context);
const onMovieIdChanged = BrowserStorageListener.create(movieIdStorage, (_, newValue) => setCurrMovie(newValue), context);
if (onMovieIdChanged) {
movieIdStorage.addOnChangedListener(onMovieIdChanged);
}
movieIdStorage.get().then(movieId => {
if (movieId) {
setCurrMovie(movieId);
}
});

const enabledStorage = new BrowserStorage<boolean>(ENABLED_KEY, StorageType.Local, context);
const onEnabledChanged = BrowserStorageListener.create(enabledStorage, (_, newValue) => setEnabled(newValue), context);
if (onEnabledChanged) {
enabledStorage.addOnChangedListener(onEnabledChanged);
}
enabledStorage.get().then(enabled => {
const value = enabled !== undefined ? enabled : true;
setEnabled(value);
});

const metadataListener = async (event: Event) => {
const metadata = (event as CustomEvent).detail as NetflixMetadata;
const movieId = metadata.movieId;
Expand Down Expand Up @@ -132,12 +109,6 @@ function VideoContainer({ dbStatus }: VideoContainerProps) {
netflixObserver.observe(document.body, config);

return () => {
if (onMovieIdChanged) {
movieIdStorage.removeOnChangedListener(onMovieIdChanged);
}
if (onEnabledChanged) {
enabledStorage.removeOnChangedListener(onEnabledChanged);
}
window.removeEventListener(RuntimeEvent.MetadataDetected, metadataListener);
window.removeEventListener(RuntimeEvent.MetadataDetected, trackSwitchedListener);
netflixObserver.disconnect();
Expand Down
18 changes: 4 additions & 14 deletions src/options/components/PurgeButton.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { DBStatusResult, Status } from "../../database/dbstatus";
import { LocalStorageChangedListener, LocalStorageObject } from "../../storage/local-storage";
import Spinner from "../../common/components/Spinner";
import { RuntimeMessage, RuntimeEvent } from "../../common/events";
import Modal from "../../common/components/modal/Modal";
import { AlertType } from "../../common/components/modal/Alert";
import { useStorage } from "../../common/hooks/useStorage";
import { StorageType } from "../../storage/storage";

const DB_STATUS_KEY = 'lastDBStatusResult'

Expand All @@ -14,21 +15,10 @@ async function purgeDictionaries(): Promise<number> {
}

function PurgeButton() {
const [dbStatus, setDBStatus] = useState<DBStatusResult | null>(null);
const [dbStatus] = useStorage<DBStatusResult | null>(DB_STATUS_KEY, StorageType.Local, null);
const [showAlert, setShowAlert] = useState(false);
const [purging, setPurging] = useState(false);

useEffect(() => {
const storage = new LocalStorageObject<DBStatusResult>(DB_STATUS_KEY);
const onStatusChanged = LocalStorageChangedListener.create(storage, (_, newValue) => setDBStatus(newValue));
storage.addOnChangedListener(onStatusChanged);
storage.get().then(result => setDBStatus(result));

return () => {
storage.removeOnChangedListener(onStatusChanged);
}
}, []);

const onPurgeClicked = () => {
setShowAlert(true);
};
Expand Down
12 changes: 8 additions & 4 deletions src/options/components/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React, { useState } from "react";
import React from "react";
import AppLogo from "../../common/components/AppLogo";
import PurgeButton from "./PurgeButton";
import EnabledToggle from "../../popup/components/EnabledToggle";
import { useStorage } from "../../common/hooks/useStorage";
import Toggle from "../../common/components/Toggle";
import { StorageType } from "../../storage/storage";

const ENABLED_KEY = 'enabled'; // todo: consolidate these keys somewhere

interface Setting {
name: string;
Expand Down Expand Up @@ -39,13 +43,13 @@ function SettingsList({ settings }: SettingsListProps) {
}

function Settings() {
const [enabled, setEnabled] = useState(false);
const [enabled, setEnabled] = useStorage<boolean>(ENABLED_KEY, StorageType.Local, false);

const settings = [
{
name: "Enabled",
infoText: `Enable/disable Jimakun. Disable Jimakun to re-enable the normal Japanese subtitles on Netflix.`,
component: <EnabledToggle enabled={enabled} onSetEnabled={(enabled) => setEnabled(enabled)} className="scale-[1.75]"></EnabledToggle>
component: <Toggle toggled={enabled} onToggle={(enabled) => setEnabled(enabled)} className="scale-[1.75]"></Toggle>
},
{
name: "Purge Dictionaries",
Expand Down
40 changes: 0 additions & 40 deletions src/popup/components/EnabledToggle.tsx

This file was deleted.

12 changes: 8 additions & 4 deletions src/popup/components/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import React, { useState } from 'react';
import EnabledToggle from './EnabledToggle';
import React from 'react';
import { StorageType } from '../../storage/storage';
import Toggle from '../../common/components/Toggle';
import { useStorage } from '../../common/hooks/useStorage';

const ENABLED_KEY = 'enabled';

function Popup() {
const [enabled, setEnabled] = useState<boolean | null>(null);
const [enabled, setEnabled] = useStorage<boolean>(ENABLED_KEY, StorageType.Local, false);

const textColor = enabled ? "text-slate-800" : "text-slate-400";
return (
<div className='flex flex-col justify-center m-auto gap-4 max-w-full max-h-full p-4'>
<div className='flex flex-row justify-between items-center gap-16 w-full'>
<span className={`text-2xl p-4 font-semibold ${textColor}`}>Enabled</span>
<EnabledToggle enabled={enabled !== null ? enabled : false} onSetEnabled={(enabled) => setEnabled(enabled)} className='scale-125 w-fit h-fit'></EnabledToggle>
<Toggle toggled={enabled} onToggle={value => setEnabled(value)} className='scale-125 w-fit h-fit'></Toggle>
</div>
</div>
)
Expand Down
27 changes: 3 additions & 24 deletions src/popup/popup.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,20 @@
import './popup.css'
import React, { useContext, useEffect, useState } from "react";
import React from "react";
import ReactDOM from 'react-dom/client';
import DatabaseBlocked from "../common/components/DatabaseBlocked";
import DatabaseBusy from "../common/components/DatabaseBusy";
import DatabaseError from "../common/components/DatabaseError";
import Popup from "./components/Popup";
import { DBStatusResult, Status } from '../database/dbstatus';
import { BrowserStorage, BrowserStorageListener } from '../content-scripts/util/browser-runtime';
import { ChromeExtensionContext } from '../content-scripts/contexts/ExtensionContext';
import { StorageType } from '../storage/storage';
import AppLogo from '../common/components/AppLogo';
import OptionsButton from '../common/components/OptionsButton';
import { useStorage } from '../common/hooks/useStorage';

const DB_STATUS_KEY = 'lastDBStatusResult'

function PopupContainer() {
const [dbStatus, setDBStatus] = useState<DBStatusResult | null>(null);
const context = useContext(ChromeExtensionContext);

useEffect(() => {
const storage = new BrowserStorage<DBStatusResult>(DB_STATUS_KEY, StorageType.Local, context);
const onStatusChanged = BrowserStorageListener.create(storage, (_, newValue) => setDBStatus(newValue), context);
if (onStatusChanged) {
storage.addOnChangedListener(onStatusChanged);
}
storage.get().then(status => {
if (status) {
setDBStatus(status);
}
});

return () => {
if (onStatusChanged) {
storage.removeOnChangedListener(onStatusChanged);
}
}
}, []);
const [dbStatus] = useStorage<DBStatusResult | null>(DB_STATUS_KEY, StorageType.Local, null);

if (!dbStatus) {
return <></>;
Expand Down

0 comments on commit e149b6f

Please sign in to comment.