diff --git a/index.html b/index.html index 3eb390e8..1885d9e4 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,10 @@ - Vite App + REVOLT
- + diff --git a/package.json b/package.json index d6845c45..b1b5ddef 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "scripts": { "dev": "vite", "build": "rimraf build && tsc && vite build", - "serve": "vite preview", + "preview": "vite preview", "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", "fmt": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'" }, @@ -30,14 +30,22 @@ "@styled-icons/feather": "^10.34.0", "@types/node": "^15.12.3", "@types/preact-i18n": "^2.3.0", + "@types/react-helmet": "^6.1.1", "@types/styled-components": "^5.1.10", "@typescript-eslint/eslint-plugin": "^4.27.0", "@typescript-eslint/parser": "^4.27.0", + "dayjs": "^1.10.5", "eslint": "^7.28.0", "eslint-config-preact": "^1.1.4", + "localforage": "^1.9.0", "preact-i18n": "^2.4.0-preactx", "prettier": "^2.3.1", + "react-device-detect": "^1.17.0", + "react-helmet": "^6.1.0", "react-overlapping-panels": "1.1.2-patch.0", + "react-redux": "^7.2.4", + "redux": "^4.1.0", + "revolt.js": "4.2.0-alpha.3-patch.0", "rimraf": "^3.0.2", "sass": "^1.35.1", "styled-components": "^5.3.0", diff --git a/src/app.tsx b/src/app.tsx index f47efe3c..9e16c37c 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,7 +1,17 @@ +import { Text } from "preact-i18n"; +import Context from "./context"; + +import dayjs from "dayjs"; + +import localeData from 'dayjs/plugin/localeData'; +dayjs.extend(localeData) + export function App() { return ( - <> -

REVOLT

- + +

+

{ dayjs.locale() }

+

{ dayjs.months() }

+
); } diff --git a/src/context/Locale.tsx b/src/context/Locale.tsx index 0403fedf..c33aad86 100644 --- a/src/context/Locale.tsx +++ b/src/context/Locale.tsx @@ -1,10 +1,162 @@ import { IntlProvider } from "preact-i18n"; +import { connectState } from "../redux/connector"; import definition from "../../external/lang/en.json"; +import { useEffect, useState } from "preact/hooks"; + +import dayjs from "dayjs"; +import calendar from "dayjs/plugin/calendar"; +import update from "dayjs/plugin/updateLocale"; +import format from "dayjs/plugin/localizedFormat"; +dayjs.extend(calendar); +dayjs.extend(format); +dayjs.extend(update); + +export enum Language { + ENGLISH = "en", + + ARABIC = "ar", + AZERBAIJANI = "az", + CZECH = "cs", + GERMAN = "de", + SPANISH = "es", + FINNISH = "fi", + FRENCH = "fr", + HINDI = "hi", + CROATIAN = "hr", + HUNGARIAN = "hu", + INDONESIAN = "id", + LITHUANIAN = "lt", + MACEDONIAN = "mk", + DUTCH = "nl", + POLISH = "pl", + PORTUGUESE_BRAZIL = "pt_BR", + ROMANIAN = "ro", + RUSSIAN = "ru", + SERBIAN = "sr", + SWEDISH = "sv", + TURKISH = "tr", + UKRANIAN = "uk", + CHINESE_SIMPLIFIED = "zh_Hans", + + OWO = "owo", + PIRATE = "pr", + BOTTOM = "bottom", + PIGLATIN = "piglatin", + HARDCORE = "hardcore" +} + +export interface LanguageEntry { + display: string; + emoji: string; + i18n: string; + dayjs?: string; + rtl?: boolean; +} + +export const Languages: { [key in Language]: LanguageEntry } = { + en: { + display: "English (Traditional)", + emoji: "🇬🇧", + i18n: "en", + dayjs: "en-gb" + }, + + ar: { display: "عربي", emoji: "🇸🇦", i18n: "ar", rtl: true }, + az: { display: "Azərbaycan dili", emoji: "🇦🇿", i18n: "az" }, + cs: { display: "Čeština", emoji: "🇨🇿", i18n: "cs" }, + de: { display: "Deutsch", emoji: "🇩🇪", i18n: "de" }, + es: { display: "Español", emoji: "🇪🇸", i18n: "es" }, + fi: { display: "suomi", emoji: "🇫🇮", i18n: "fi" }, + fr: { display: "Français", emoji: "🇫🇷", i18n: "fr" }, + hi: { display: "हिन्दी", emoji: "🇮🇳", i18n: "hi" }, + hr: { display: "Hrvatski", emoji: "🇭🇷", i18n: "hr" }, + hu: { display: "magyar", emoji: "🇭🇺", i18n: "hu" }, + id: { display: "bahasa Indonesia", emoji: "🇮🇩", i18n: "id" }, + lt: { display: "Lietuvių", emoji: "🇱🇹", i18n: "lt" }, + mk: { display: "Македонски", emoji: "🇲🇰", i18n: "mk" }, + nl: { display: "Nederlands", emoji: "🇳🇱", i18n: "nl" }, + pl: { display: "Polski", emoji: "🇵🇱", i18n: "pl" }, + pt_BR: { + display: "Português (do Brasil)", + emoji: "🇧🇷", + i18n: "pt_BR", + dayjs: "pt-br" + }, + ro: { display: "Română", emoji: "🇷🇴", i18n: "ro" }, + ru: { display: "Русский", emoji: "🇷🇺", i18n: "ru" }, + sr: { display: "Српски", emoji: "🇷🇸", i18n: "sr" }, + sv: { display: "Svenska", emoji: "🇸🇪", i18n: "sv" }, + tr: { display: "Türkçe", emoji: "🇹🇷", i18n: "tr" }, + uk: { display: "Українська", emoji: "🇺🇦", i18n: "uk" }, + zh_Hans: { + display: "中文 (简体)", + emoji: "🇨🇳", + i18n: "zh_Hans", + dayjs: "zh" + }, + + owo: { display: "OwO", emoji: "🐱", i18n: "owo", dayjs: "en-gb" }, + pr: { display: "Pirate", emoji: "🏴‍☠️", i18n: "pr", dayjs: "en-gb" }, + bottom: { display: "Bottom", emoji: "🥺", i18n: "bottom", dayjs: "en-gb" }, + piglatin: { display: "Pig Latin", emoji: "🐖", i18n: "piglatin", dayjs: "en-gb" }, + hardcore: { + display: "Hardcore Mode", + emoji: "🔥", + i18n: "hardcore", + dayjs: "en-gb" + } +}; interface Props { children: JSX.Element | JSX.Element[]; + locale: Language; } -export default function Locale({ children }: Props) { - return {children}; +function Locale({ children, locale }: Props) { + const [defns, setDefinition] = useState(definition); + const lang = Languages[locale]; + + useEffect(() => { + if (locale === 'en') { + setDefinition(definition); + dayjs.locale('en'); + return; + } + + if (lang.i18n === "hardcore") { + setDefinition({} as any); + return; + } + + import( + `../../external/lang/${lang.i18n}.json` + ).then(async lang_file => { + let defn = lang_file.default; + let target = lang.dayjs ?? lang.i18n; + let dayjs_locale = await import(/* @vite-ignore */ `/node_modules/dayjs/esm/locale/${target}.js`); + + if (defn.dayjs) { + dayjs.updateLocale(target, { calendar: defn.dayjs }); + } + + dayjs.locale(dayjs_locale.default); + setDefinition(defn); + }); + }, [locale]); + + useEffect(() => { + document.body.style.direction = lang.rtl ? "rtl" : ""; + }, [ lang.rtl ]); + + return {children}; } + +export default connectState>( + Locale, + state => { + return { + locale: state.locale + }; + }, + true +); diff --git a/src/context/Theme.tsx b/src/context/Theme.tsx index 4066eb99..76fb20e1 100644 --- a/src/context/Theme.tsx +++ b/src/context/Theme.tsx @@ -1,41 +1,137 @@ +import { isTouchscreenDevice } from "../lib/isTouchscreenDevice"; import { createGlobalStyle } from "styled-components"; +import { Children } from "../types/Preact"; +import { Helmet } from "react-helmet"; -// ! TEMP START -const a = { - light: false, - accent: "#FD6671", - background: "#191919", - foreground: "#F6F6F6", - block: "#2D2D2D", - "message-box": "#363636", - mention: "rgba(251, 255, 0, 0.06)", - success: "#65E572", - warning: "#FAA352", - error: "#F06464", - hover: "rgba(0, 0, 0, 0.1)", - "sidebar-active": "#FD6671", - "scrollbar-thumb": "#CA525A", - "scrollbar-track": "transparent", - "primary-background": "#242424", - "primary-header": "#363636", - "secondary-background": "#1E1E1E", - "secondary-foreground": "#C8C8C8", - "secondary-header": "#2D2D2D", - "tertiary-background": "#4D4D4D", - "tertiary-foreground": "#848484", - "status-online": "#3ABF7E", - "status-away": "#F39F00", - "status-busy": "#F84848", - "status-streaming": "#977EFF", - "status-invisible": "#A5A5A5", +export type Variables = + | "accent" + | "background" + | "foreground" + | "block" + | "message-box" + | "mention" + | "success" + | "warning" + | "error" + | "hover" + | "sidebar-active" + | "scrollbar-thumb" + | "scrollbar-track" + | "primary-background" + | "primary-header" + | "secondary-background" + | "secondary-foreground" + | "secondary-header" + | "tertiary-background" + | "tertiary-foreground" + | "status-online" + | "status-away" + | "status-busy" + | "status-streaming" + | "status-invisible"; + +export type Theme = { + [variable in Variables]: string; +} & { + light?: boolean; + css?: string; }; -export const GlobalTheme = createGlobalStyle` +export interface ThemeOptions { + preset?: string; + custom?: Partial; +} + +// Generated from https://gitlab.insrt.uk/revolt/community/themes +export const PRESETS: { [key: string]: Theme } = { + light: { + light: true, + accent: "#FD6671", + background: "#F6F6F6", + foreground: "#101010", + block: "#414141", + "message-box": "#F1F1F1", + mention: "rgba(251, 255, 0, 0.40)", + success: "#65E572", + warning: "#FAA352", + error: "#F06464", + hover: "rgba(0, 0, 0, 0.2)", + "sidebar-active": "#FD6671", + "scrollbar-thumb": "#CA525A", + "scrollbar-track": "transparent", + "primary-background": "#FFFFFF", + "primary-header": "#F1F1F1", + "secondary-background": "#F1F1F1", + "secondary-foreground": "#888888", + "secondary-header": "#F1F1F1", + "tertiary-background": "#4D4D4D", + "tertiary-foreground": "#646464", + "status-online": "#3ABF7E", + "status-away": "#F39F00", + "status-busy": "#F84848", + "status-streaming": "#977EFF", + "status-invisible": "#A5A5A5", + }, + dark: { + light: false, + accent: "#FD6671", + background: "#191919", + foreground: "#F6F6F6", + block: "#2D2D2D", + "message-box": "#363636", + mention: "rgba(251, 255, 0, 0.06)", + success: "#65E572", + warning: "#FAA352", + error: "#F06464", + hover: "rgba(0, 0, 0, 0.1)", + "sidebar-active": "#FD6671", + "scrollbar-thumb": "#CA525A", + "scrollbar-track": "transparent", + "primary-background": "#242424", + "primary-header": "#363636", + "secondary-background": "#1E1E1E", + "secondary-foreground": "#C8C8C8", + "secondary-header": "#2D2D2D", + "tertiary-background": "#4D4D4D", + "tertiary-foreground": "#848484", + "status-online": "#3ABF7E", + "status-away": "#F39F00", + "status-busy": "#F84848", + "status-streaming": "#977EFF", + "status-invisible": "#A5A5A5", + }, +}; + +const GlobalTheme = createGlobalStyle<{ theme: Theme }>` :root { - ${Object.keys(a).map((key) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return `--${key}: ${(a as any)[key]};`; - })} + ${(props) => + (Object.keys(props.theme) as Variables[]).map((key) => { + return `--${key}: ${props.theme[key]};`; + })} } `; -// ! TEMP END + +interface Props { + children: Children; +} + +export default function Theme(props: Props) { + const theme = PRESETS.dark; + + return ( + <> + + + + + {props.children} + + ); +} diff --git a/src/context/index.tsx b/src/context/index.tsx new file mode 100644 index 00000000..95219c43 --- /dev/null +++ b/src/context/index.tsx @@ -0,0 +1,15 @@ +import State from "../redux/State"; +import { Children } from "../types/Preact"; + +import Locale from "./Locale"; +import Theme from "./Theme"; + +export default function Context({ children }: { children: Children }) { + return ( + + + {children} + + + ); +} diff --git a/src/context/revoltjs/RevoltClient.tsx b/src/context/revoltjs/RevoltClient.tsx new file mode 100644 index 00000000..fdf0f9b2 --- /dev/null +++ b/src/context/revoltjs/RevoltClient.tsx @@ -0,0 +1,19 @@ +import { Client } from 'revolt.js'; + +export enum ClientStatus { + LOADING, + READY, + OFFLINE, + DISCONNECTED, + CONNECTING, + RECONNECTING, + ONLINE +} + +export const RevoltJSClient = new Client({ + autoReconnect: false, + apiURL: process.env.API_SERVER, + debug: process.env.NODE_ENV === "development", + // Match sw.js#13 + // db: new Db("state", 3, ["channels", "servers", "users", "members"]) +}); diff --git a/src/context/revoltjs/messages.ts b/src/context/revoltjs/messages.ts new file mode 100644 index 00000000..8db3e427 --- /dev/null +++ b/src/context/revoltjs/messages.ts @@ -0,0 +1,10 @@ +import { Message } from "revolt.js/dist/api/objects"; + +export type MessageObject = Omit & { edited?: string }; +export function mapMessage(message: Partial) { + const { edited, ...msg } = message; + return { + ...msg, + edited: edited?.$date + } as MessageObject; +} diff --git a/src/lib/isTouchscreenDevice.ts b/src/lib/isTouchscreenDevice.ts new file mode 100644 index 00000000..a91bb23b --- /dev/null +++ b/src/lib/isTouchscreenDevice.ts @@ -0,0 +1,7 @@ +import { isDesktop, isMobile, isTablet } from "react-device-detect"; +export const isTouchscreenDevice = + isDesktop && !isTablet + ? false + : (typeof window !== "undefined" + ? navigator.maxTouchPoints > 0 + : false) || isMobile; diff --git a/src/main.tsx b/src/main.tsx index ea0c7dfb..0e16b26f 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,5 +1,5 @@ import { render } from "preact"; -import "../styles/index.scss"; +import "./styles/index.scss"; import { App } from "./app"; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/src/redux/State.tsx b/src/redux/State.tsx new file mode 100644 index 00000000..21985295 --- /dev/null +++ b/src/redux/State.tsx @@ -0,0 +1,32 @@ +import { store } from "."; +import localForage from "localforage"; +import { Provider } from 'react-redux'; +import { Children } from "../types/Preact"; +import { useEffect, useState } from "preact/hooks"; + +async function loadState() { + const state = await localForage.getItem("state"); + if (state) { + store.dispatch({ type: "__INIT", state }); + } +} + +interface Props { + children: Children +} + +export default function State(props: Props) { + const [loaded, setLoaded] = useState(false); + + useEffect(() => { + loadState().then(() => setLoaded(true)); + }, []); + + if (!loaded) return null; + + return ( + + { props.children } + + ) +} diff --git a/src/redux/connector.tsx b/src/redux/connector.tsx new file mode 100644 index 00000000..3a90faa4 --- /dev/null +++ b/src/redux/connector.tsx @@ -0,0 +1,16 @@ +import { State } from "."; +import { h } from "preact"; +//import { memo } from "preact/compat"; +import { connect, ConnectedComponent } from "react-redux"; + +export function connectState( + component: (props: any) => h.JSX.Element | null, + mapKeys: (state: State, props: T) => any, + useDispatcher?: boolean +): ConnectedComponent<(props: any) => h.JSX.Element | null, T> { + return ( + useDispatcher + ? connect(mapKeys, dispatcher => { return { dispatcher } }) + : connect(mapKeys) + )(component);//(memo(component)); +} diff --git a/src/redux/index.ts b/src/redux/index.ts new file mode 100644 index 00000000..06e58eae --- /dev/null +++ b/src/redux/index.ts @@ -0,0 +1,62 @@ +import { createStore } from "redux"; +import rootReducer from "./reducers"; +import localForage from "localforage"; + +import { Typing } from "./reducers/typing"; +import { Drafts } from "./reducers/drafts"; +import { AuthState } from "./reducers/auth"; +import { Language } from "../context/Locale"; +import { Unreads } from "./reducers/unreads"; +import { SyncOptions } from "./reducers/sync"; +import { Settings } from "./reducers/settings"; +import { QueuedMessage } from "./reducers/queue"; +import { ExperimentOptions } from "./reducers/experiments"; + +export type State = { + locale: Language; + auth: AuthState; + settings: Settings; + unreads: Unreads; + queue: QueuedMessage[]; + typing: Typing; + drafts: Drafts; + sync: SyncOptions; + experiments: ExperimentOptions; +}; + +export const store = createStore((state: any, action: any) => { + if (process.env.NODE_ENV === "development") { + console.debug("State Update:", action); + } + + if (action.type === "__INIT") { + return action.state; + } + + return rootReducer(state, action); +}); + +// Save state using localForage. +store.subscribe(() => { + const { + locale, + auth, + settings, + unreads, + queue, + drafts, + sync, + experiments + } = store.getState() as State; + + localForage.setItem("state", { + locale, + auth, + settings, + unreads, + queue, + drafts, + sync, + experiments + }); +}); diff --git a/src/redux/reducers/auth.ts b/src/redux/reducers/auth.ts new file mode 100644 index 00000000..9d5cbde4 --- /dev/null +++ b/src/redux/reducers/auth.ts @@ -0,0 +1,48 @@ +import { Auth } from "revolt.js/dist/api/objects"; + +export interface AuthState { + accounts: { + [key: string]: { + session: Auth.Session; + }; + }; + active?: string; +} + +export type AuthAction = + | { type: undefined } + | { + type: "LOGIN"; + session: Auth.Session; + } + | { + type: "LOGOUT"; + user_id?: string; + }; + +export function auth( + state = { accounts: {} } as AuthState, + action: AuthAction +): AuthState { + switch (action.type) { + case "LOGIN": + return { + accounts: { + ...state.accounts, + [action.session.user_id]: { + session: action.session + } + }, + active: action.session.user_id + }; + case "LOGOUT": + const accounts = Object.assign({}, state.accounts); + action.user_id && delete accounts[action.user_id]; + + return { + accounts + }; + default: + return state; + } +} diff --git a/src/redux/reducers/drafts.ts b/src/redux/reducers/drafts.ts new file mode 100644 index 00000000..a000e412 --- /dev/null +++ b/src/redux/reducers/drafts.ts @@ -0,0 +1,33 @@ +export type Drafts = { [key: string]: string }; + +export type DraftAction = + | { type: undefined } + | { + type: "SET_DRAFT"; + channel: string; + content: string; + } + | { + type: "CLEAR_DRAFT"; + channel: string; + } + | { + type: "RESET"; + }; + +export function drafts(state: Drafts = {}, action: DraftAction): Drafts { + switch (action.type) { + case "SET_DRAFT": + return { + ...state, + [action.channel]: action.content + }; + case "CLEAR_DRAFT": + const { [action.channel]: _, ...newState } = state; + return newState; + case "RESET": + return {}; + default: + return state; + } +} diff --git a/src/redux/reducers/experiments.ts b/src/redux/reducers/experiments.ts new file mode 100644 index 00000000..89a13e09 --- /dev/null +++ b/src/redux/reducers/experiments.ts @@ -0,0 +1,43 @@ +export type Experiments = never; +export const AVAILABLE_EXPERIMENTS: Experiments[] = [ ]; + +export interface ExperimentOptions { + enabled?: Experiments[] +} + +export type ExperimentsAction = + | { type: undefined } + | { + type: "EXPERIMENTS_ENABLE"; + key: Experiments; + } + | { + type: "EXPERIMENTS_DISABLE"; + key: Experiments; + }; + +export function experiments( + state = {} as ExperimentOptions, + action: ExperimentsAction +): ExperimentOptions { + switch (action.type) { + case "EXPERIMENTS_ENABLE": + return { + ...state, + enabled: [ + ...(state.enabled ?? []) + .filter(x => AVAILABLE_EXPERIMENTS.includes(x)) + .filter(v => v !== action.key), + action.key + ] + }; + case "EXPERIMENTS_DISABLE": + return { + ...state, + enabled: state.enabled?.filter(v => v !== action.key) + .filter(x => AVAILABLE_EXPERIMENTS.includes(x)) + }; + default: + return state; + } +} diff --git a/src/redux/reducers/index.ts b/src/redux/reducers/index.ts new file mode 100644 index 00000000..2dc3ce7f --- /dev/null +++ b/src/redux/reducers/index.ts @@ -0,0 +1,47 @@ +import { combineReducers } from "redux"; + +import { settings, SettingsAction } from "./settings"; +import { locale, LocaleAction } from "./locale"; +import { auth, AuthAction } from "./auth"; +import { unreads, UnreadsAction } from "./unreads"; +import { queue, QueueAction } from "./queue"; +import { typing, TypingAction } from "./typing"; +import { drafts, DraftAction } from "./drafts"; +import { sync, SyncAction } from "./sync"; +import { experiments, ExperimentsAction } from "./experiments"; + +export default combineReducers({ + locale, + auth, + settings, + unreads, + queue, + typing, + drafts, + sync, + experiments +}); + +export type Action = + | LocaleAction + | AuthAction + | SettingsAction + | UnreadsAction + | QueueAction + | TypingAction + | DraftAction + | SyncAction + | ExperimentsAction + | { type: "__INIT"; state: any }; + +export type WithDispatcher = { dispatcher: (action: Action) => void }; + +export function filter(obj: any, keys: string[]) { + const newObj: any = {}; + for (const key of keys) { + const v = obj[key]; + if (v) newObj[key] = v; + } + + return newObj; +} diff --git a/src/redux/reducers/locale.ts b/src/redux/reducers/locale.ts new file mode 100644 index 00000000..b573e62a --- /dev/null +++ b/src/redux/reducers/locale.ts @@ -0,0 +1,50 @@ +import { Language } from "../../context/Locale"; +import { SyncData, SyncKeys, SyncUpdateAction } from "./sync"; + +export type LocaleAction = + | { type: undefined } + | { + type: "SET_LOCALE"; + locale: Language; + } + | SyncUpdateAction; + +export function findLanguage(lang?: string): Language { + if (!lang) { + if (typeof navigator === "undefined") { + lang = Language.ENGLISH; + } else { + lang = navigator.language; + } + } + + const code = lang.replace("-", "_"); + + const short = code.split("_")[0]; + for (const key of Object.keys(Language)) { + const value = (Language as any)[key]; + if (value.startsWith(code)) { + return value; + } + } + + for (const key of Object.keys(Language).reverse()) { + const value = (Language as any)[key]; + if (value.startsWith(short)) { + return value; + } + } + + return Language.ENGLISH; +} + +export function locale(state = findLanguage(), action: LocaleAction): Language { + switch (action.type) { + case "SET_LOCALE": + return action.locale; + case "SYNC_UPDATE": + return (action.update.locale?.[1] ?? state) as Language; + default: + return state; + } +} diff --git a/src/redux/reducers/queue.ts b/src/redux/reducers/queue.ts new file mode 100644 index 00000000..c2f0fdfa --- /dev/null +++ b/src/redux/reducers/queue.ts @@ -0,0 +1,103 @@ +import { MessageObject } from "../../context/revoltjs/messages"; + +export enum QueueStatus { + SENDING = "sending", + ERRORED = "errored" +} + +export interface QueuedMessage { + id: string; + channel: string; + data: MessageObject; + status: QueueStatus; + error?: string; +} + +export type QueueAction = + | { type: undefined } + | { + type: "QUEUE_ADD"; + nonce: string; + channel: string; + message: MessageObject; + } + | { + type: "QUEUE_FAIL"; + nonce: string; + error: string; + } + | { + type: "QUEUE_START"; + nonce: string; + } + | { + type: "QUEUE_REMOVE"; + nonce: string; + } + | { + type: "QUEUE_DROP_ALL"; + } + | { + type: "QUEUE_FAIL_ALL"; + } + | { + type: "RESET"; + }; + +export function queue( + state: QueuedMessage[] = [], + action: QueueAction +): QueuedMessage[] { + switch (action.type) { + case "QUEUE_ADD": { + return [ + ...state.filter(x => x.id !== action.nonce), + { + id: action.nonce, + data: action.message, + channel: action.channel, + status: QueueStatus.SENDING + } + ]; + } + case "QUEUE_FAIL": { + const entry = state.find( + x => x.id === action.nonce + ) as QueuedMessage; + return [ + ...state.filter(x => x.id !== action.nonce), + { + ...entry, + status: QueueStatus.ERRORED, + error: action.error + } + ]; + } + case "QUEUE_START": { + const entry = state.find( + x => x.id === action.nonce + ) as QueuedMessage; + return [ + ...state.filter(x => x.id !== action.nonce), + { + ...entry, + status: QueueStatus.SENDING + } + ]; + } + case "QUEUE_REMOVE": + return state.filter(x => x.id !== action.nonce); + case "QUEUE_FAIL_ALL": + return state.map(x => { + return { + ...x, + status: QueueStatus.ERRORED + }; + }); + case "QUEUE_DROP_ALL": + case "RESET": + return []; + default: + return state; + } +} diff --git a/src/redux/reducers/settings.ts b/src/redux/reducers/settings.ts new file mode 100644 index 00000000..744c3289 --- /dev/null +++ b/src/redux/reducers/settings.ts @@ -0,0 +1,98 @@ +import { filter } from "."; +import { SyncUpdateAction } from "./sync"; +import { Theme, ThemeOptions } from "../../context/Theme"; + +export interface NotificationOptions { + desktopEnabled?: boolean; + soundEnabled?: boolean; + outgoingSoundEnabled?: boolean; +} + +export type EmojiPacks = 'mutant' | 'twemoji' | 'noto' | 'openmoji'; +export interface AppearanceOptions { + emojiPack?: EmojiPacks +} + +export interface Settings { + theme?: ThemeOptions; + appearance?: AppearanceOptions; + notification?: NotificationOptions; +} + +export type SettingsAction = + | { type: undefined } + | { + type: "SETTINGS_SET_THEME"; + theme: ThemeOptions; + } + | { + type: "SETTINGS_SET_THEME_OVERRIDE"; + custom?: Partial; + } + | { + type: "SETTINGS_SET_NOTIFICATION_OPTIONS"; + options: NotificationOptions; + } + | { + type: "SETTINGS_SET_APPEARANCE"; + options: Partial; + } + | SyncUpdateAction + | { + type: "RESET"; + }; + +export function settings( + state = {} as Settings, + action: SettingsAction +): Settings { + // setEmojiPack(state.appearance?.emojiPack ?? 'mutant'); + + switch (action.type) { + case "SETTINGS_SET_THEME": + return { + ...state, + theme: { + ...filter(state.theme, [ 'custom', 'preset' ]), + ...action.theme, + } + }; + case "SETTINGS_SET_THEME_OVERRIDE": + return { + ...state, + theme: { + ...state.theme, + custom: { + ...state.theme?.custom, + ...action.custom + } + } + }; + case "SETTINGS_SET_NOTIFICATION_OPTIONS": + return { + ...state, + notification: { + ...state.notification, + ...action.options + } + }; + case "SETTINGS_SET_APPEARANCE": + return { + ...state, + appearance: { + ...filter(state.appearance, [ 'emojiPack' ]), + ...action.options + } + } + case "SYNC_UPDATE": + return { + ...state, + appearance: action.update.appearance?.[1] ?? state.appearance, + theme: action.update.theme?.[1] ?? state.theme + } + case "RESET": + return {}; + default: + return state; + } +} diff --git a/src/redux/reducers/sync.ts b/src/redux/reducers/sync.ts new file mode 100644 index 00000000..2edf5e90 --- /dev/null +++ b/src/redux/reducers/sync.ts @@ -0,0 +1,85 @@ +import { AppearanceOptions } from "./settings"; +import { Language } from "../../context/Locale"; +import { ThemeOptions } from "../../context/Theme"; + +export type SyncKeys = 'theme' | 'appearance' | 'locale'; + +export interface SyncData { + locale?: Language; + theme?: ThemeOptions; + appearance?: AppearanceOptions; +} + +export const DEFAULT_ENABLED_SYNC: SyncKeys[] = [ 'theme', 'appearance', 'locale' ]; +export interface SyncOptions { + disabled?: SyncKeys[] + revision?: { + [key: string]: number + } +} + +export type SyncUpdateAction = { + type: "SYNC_UPDATE"; + update: { [key in SyncKeys]?: [ number, SyncData[key] ] } +}; + +export type SyncAction = + | { type: undefined } + | { + type: "SYNC_ENABLE_KEY"; + key: SyncKeys; + } + | { + type: "SYNC_DISABLE_KEY"; + key: SyncKeys; + } + | { + type: "SYNC_SET_REVISION"; + key: SyncKeys; + timestamp: number; + } + | SyncUpdateAction; + +export function sync( + state = {} as SyncOptions, + action: SyncAction +): SyncOptions { + switch (action.type) { + case "SYNC_DISABLE_KEY": + return { + ...state, + disabled: [ + ...(state.disabled ?? []).filter(v => v !== action.key), + action.key + ] + }; + case "SYNC_ENABLE_KEY": + return { + ...state, + disabled: state.disabled?.filter(v => v !== action.key) + }; + case "SYNC_SET_REVISION": + return { + ...state, + revision: { + ...state.revision, + [action.key]: action.timestamp + } + }; + case "SYNC_UPDATE": + const revision = { ...state.revision }; + for (const key of Object.keys(action.update)) { + const value = action.update[key as SyncKeys]; + if (value) { + revision[key] = value[0]; + } + } + + return { + ...state, + revision + } + default: + return state; + } +} diff --git a/src/redux/reducers/typing.ts b/src/redux/reducers/typing.ts new file mode 100644 index 00000000..b3436219 --- /dev/null +++ b/src/redux/reducers/typing.ts @@ -0,0 +1,46 @@ +export type TypingUser = { id: string, started: number }; +export type Typing = { [key: string]: TypingUser[] }; + +export type TypingAction = + | { type: undefined } + | { + type: "TYPING_START"; + channel: string; + user: string; + } + | { + type: "TYPING_STOP"; + channel: string; + user: string; + } + | { + type: "RESET"; + }; + +export function typing(state: Typing = {}, action: TypingAction): Typing { + switch (action.type) { + case "TYPING_START": + return { + ...state, + [action.channel]: [ + ...(state[action.channel] ?? []).filter( + x => x.id !== action.user + ), + { + id: action.user, + started: + new Date() + } + ] + }; + case "TYPING_STOP": + return { + ...state, + [action.channel]: + state[action.channel]?.filter(x => x.id !== action.user) ?? [] + }; + case "RESET": + return {}; + default: + return state; + } +} diff --git a/src/redux/reducers/unreads.ts b/src/redux/reducers/unreads.ts new file mode 100644 index 00000000..a81a55d3 --- /dev/null +++ b/src/redux/reducers/unreads.ts @@ -0,0 +1,68 @@ +import { Sync } from "revolt.js/dist/api/objects"; + +export interface Unreads { + [key: string]: Partial>; +} + +export type UnreadsAction = + | { type: undefined } + | { + type: "UNREADS_MARK_READ"; + channel: string; + message: string; + request: boolean; + } + | { + type: "UNREADS_SET"; + unreads: Sync.ChannelUnread[]; + } + | { + type: "UNREADS_MENTION"; + channel: string; + message: string; + } + | { + type: "RESET"; + }; + +export function unreads(state = {}, action: UnreadsAction): Unreads { + switch (action.type) { + case "UNREADS_MARK_READ": + if (action.request) { + // client.req('PUT', `/channels/${action.channel}/ack/${action.message}` as '/channels/id/ack/id'); + } + + return { + ...state, + [action.channel]: { + last_id: action.message + } + }; + case "UNREADS_SET": + { + const obj: Unreads = {}; + for (const entry of action.unreads) { + const { _id, ...v } = entry; + obj[_id.channel] = v; + } + + return obj; + } + case "UNREADS_MENTION": + { + const obj = (state as any)[action.channel]; + + return { + ...state, + [action.channel]: { + ...obj, + mentions: [ ...(obj?.mentions ?? []), action.message ] + } + } + } + case "RESET": + return {}; + default: + return state; + } +} diff --git a/ui/ui.tsx b/ui/ui.tsx index 0e642dad..cf8f0a79 100644 --- a/ui/ui.tsx +++ b/ui/ui.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import '../src/styles/index.scss' import { render } from 'preact' -import { GlobalTheme } from '../src/context/Theme'; +import Theme from '../src/context/Theme'; export const UIDemo = styled.div` gap: 12px; @@ -61,8 +61,9 @@ export function UI() { } render(<> - - - - + + + + + , document.getElementById('app')!) diff --git a/yarn.lock b/yarn.lock index 07a04f06..bbbd0a37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -183,7 +183,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.6.tgz#d85cc68ca3cac84eae384c06f032921f5227f4b2" integrity sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ== -"@babel/runtime@^7.10.5", "@babel/runtime@^7.14.0": +"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.1", "@babel/runtime@^7.14.0", "@babel/runtime@^7.9.2": version "7.14.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== @@ -264,6 +264,26 @@ resolved "https://registry.yarnpkg.com/@fontsource/open-sans/-/open-sans-4.4.5.tgz#07b31617e62ed753c94cabcf552ebaed4de497ce" integrity sha512-PDWEvO1/p8OAHHiielvEmwGXHNbZhrZn96ojV7+/mKgFu+cCUcGVJl9sFs97rCWLe3hKQsYLEsJs4EiLjwa+UQ== +"@insertish/mutable@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@insertish/mutable/-/mutable-1.0.6.tgz#f42eaba8528ff68cc8065d51f9bbbd30a24f34de" + integrity sha512-FTaPbesmBwcr3iKfbA2udFto61/sL7rOiCM08vBbE2X0wC63nsvTos6gnkwa1Nwom1v15jjrc/4B0YqI3vbZ/Q== + dependencies: + "@insertish/zangodb" "^1.0.12" + eventemitter3 "^4.0.7" + lodash.isequal "^4.5.0" + +"@insertish/zangodb@1.0.12", "@insertish/zangodb@^1.0.12": + version "1.0.12" + resolved "https://registry.yarnpkg.com/@insertish/zangodb/-/zangodb-1.0.12.tgz#25264ec065720fa43c7549ec7245e4f3839cb0ea" + integrity sha512-JlLI12Xqt1xvv/p2/AHs163ZYMZsB3sJyjB8yaAs6QcG0tyRBTIyxV5ISEAkAPo5kzlFza5z5oH82yQe/qw5RQ== + dependencies: + clone "^2.1.2" + deepmerge "^4.2.2" + memoizee "^0.4.15" + object-hash "^2.1.1" + q "^1.5.1" + "@mdn/browser-compat-data@^2.0.7": version "2.0.7" resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-2.0.7.tgz#72ec37b9c1e00ce0b4e0309d753be18e2da12ee3" @@ -360,7 +380,7 @@ "@babel/runtime" "^7.10.5" "@emotion/is-prop-valid" "^0.8.7" -"@types/hoist-non-react-statics@*": +"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== @@ -390,6 +410,23 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/react-helmet@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.1.tgz#4fde22cbcaa1b461642e1d719cc6162d95acb110" + integrity sha512-VmSCMz6jp/06DABoY60vQa++h1YFt0PfAI23llxBJHbowqFgLUL0dhS1AQeVPNqYfRp9LAfokrfWACTNeobOrg== + dependencies: + "@types/react" "*" + +"@types/react-redux@^7.1.16": + version "7.1.16" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.16.tgz#0fbd04c2500c12105494c83d4a3e45c084e3cb21" + integrity sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react@*": version "17.0.11" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.11.tgz#67fcd0ddbf5a0b083a0f94e926c7d63f3b836451" @@ -611,6 +648,13 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +axios@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + babel-eslint@^10.0.1: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -739,6 +783,11 @@ chalk@^4.0.0: optionalDependencies: fsevents "~2.3.2" +clone@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -785,7 +834,7 @@ core-js@^3.6.5: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.14.0.tgz#62322b98c71cc2018b027971a69419e2425c2a6c" integrity sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA== -cross-spawn@^7.0.2: +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -813,6 +862,26 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dayjs@^1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.5.tgz#5600df4548fc2453b3f163ebb2abbe965ccfb986" + integrity sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g== + +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" @@ -825,6 +894,11 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -858,6 +932,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + electron-to-chromium@^1.3.723: version "1.3.752" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09" @@ -906,6 +985,42 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + esbuild@^0.12.5: version "0.12.9" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.9.tgz#bed4e7087c286cd81d975631f77d47feb1660070" @@ -1106,6 +1221,44 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +exponential-backoff@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.0.tgz#9409c7e579131f8bd4b32d7d8094a911040f2e68" + integrity sha512-oBuz5SYz5zzyuHINoe9ooePwSu0xApKWgeNzok4hZ5YKXFh9zrQBEM15CXqoZkJJPuI2ArvqjPQd8UKJA753XA== + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + extend@3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -1180,6 +1333,18 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1284,7 +1449,7 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -1301,6 +1466,11 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -1404,6 +1574,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + is-regex@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" @@ -1429,6 +1604,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -1490,6 +1670,20 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4= + dependencies: + immediate "~3.0.5" + +localforage@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.9.0.tgz#f3e4d32a8300b362b4634cc4e066d9d00d2f09d1" + integrity sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g== + dependencies: + lie "3.1.1" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -1502,6 +1696,16 @@ lodash.clonedeep@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.defaultsdeep@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6" + integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + lodash.memoize@4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -1536,6 +1740,32 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + +memoizee@^0.4.15: + version "0.4.15" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" + integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== + dependencies: + d "^1.0.1" + es5-ext "^0.10.53" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -1561,6 +1791,11 @@ minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -1576,6 +1811,21 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +next-tick@1, next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +node-cleanup@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c" + integrity sha1-esGavSl+Caf3KnFUXZUbUX5N3iw= + node-releases@^1.1.71: version "1.1.73" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" @@ -1591,6 +1841,11 @@ object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-hash@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== + object-inspect@^1.10.3, object-inspect@^1.9.0: version "1.10.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" @@ -1709,6 +1964,13 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" @@ -1770,17 +2032,51 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +ps-tree@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +q@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -react-is@^16.7.0, react-is@^16.8.1: +react-device-detect@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-1.17.0.tgz#a00b4fd6880cebfab3fd8a42a79dc0290cdddca9" + integrity sha512-bBblIStwpHmoS281JFIVqeimcN3LhpoP5YKDWzxQdBIUP8S2xPvHDgizLDhUq2ScguLfVPmwfF5y268EEQR60w== + dependencies: + ua-parser-js "^0.7.24" + +react-fast-compare@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + +react-helmet@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.1.1" + react-side-effect "^2.1.0" + +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -1790,6 +2086,23 @@ react-overlapping-panels@1.1.2-patch.0: resolved "https://registry.yarnpkg.com/react-overlapping-panels/-/react-overlapping-panels-1.1.2-patch.0.tgz#335649735c029d334daea19ef6e30efc76b128fd" integrity sha512-PaXxk5HxBMYg46iADGGhkgXqqweJWo7yjSeT4/o0Q3s6Q7pl7Rz23lM3oW2gdJHBDOs/zBpZ+ZIP4j6grQlCOA== +react-redux@^7.2.4: + version "7.2.4" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225" + integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA== + dependencies: + "@babel/runtime" "^7.12.1" + "@types/react-redux" "^7.1.16" + hoist-non-react-statics "^3.3.2" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.13.1" + +react-side-effect@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3" + integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ== + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -1797,6 +2110,13 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redux@^4.0.0, redux@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4" + integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g== + dependencies: + "@babel/runtime" "^7.9.2" + regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -1846,6 +2166,22 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +revolt.js@4.2.0-alpha.3-patch.0: + version "4.2.0-alpha.3-patch.0" + resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.2.0-alpha.3-patch.0.tgz#ca79731c2b2fa9a8dbfbc5c9f84bef6ee2759918" + integrity sha512-g4eXHDbQyjKEiDOjj+3BxbRwPuVfOCYsnVqOiOXoAib4k48c27N+ZU0apYV25/AzCvIoYGDtVfY3I33UkTl1Rw== + dependencies: + "@insertish/mutable" "1.0.6" + "@insertish/zangodb" "1.0.12" + axios "^0.19.2" + eventemitter3 "^4.0.7" + exponential-backoff "^3.1.0" + isomorphic-ws "^4.0.1" + lodash.defaultsdeep "^4.6.1" + tsc-watch "^4.1.0" + ulid "^2.3.0" + ws "^7.2.1" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -1946,11 +2282,30 @@ source-map@^0.5.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + +string-argv@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" + integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== + string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" @@ -2049,6 +2404,19 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +through@2, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timers-ext@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -2061,6 +2429,17 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tsc-watch@^4.1.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/tsc-watch/-/tsc-watch-4.4.0.tgz#3ebbf1db54bcef6bfe534b330fa87284a4139320" + integrity sha512-+0Yey6ptOOXAnt44OKTk2/EnQnmA0auL7VWXm9d9abMS4tabt0Xdr9B4AK6OJbWAre9ZdLA81+Nk8sz9unptyA== + dependencies: + cross-spawn "^7.0.3" + node-cleanup "^2.1.2" + ps-tree "^1.2.0" + string-argv "^0.1.1" + strip-ansi "^6.0.0" + tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -2085,11 +2464,31 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + typescript@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.3.tgz#5401db69bd3203daf1851a1a74d199cb3112c11a" integrity sha512-rUvLW0WtF7PF2b9yenwWUi9Da9euvDRhmH7BLyBG4DCFfOJ850LGNknmRpp8Z8kXNUPObdZQEfKOiHtXuQHHKA== +ua-parser-js@^0.7.24: + version "0.7.28" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" + integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== + +ulid@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/ulid/-/ulid-2.3.0.tgz#93063522771a9774121a84d126ecd3eb9804071f" + integrity sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw== + unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" @@ -2152,6 +2551,11 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +ws@^7.2.1: + version "7.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691" + integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"