From 2669af3250d7edebebb294a2d58498e23a108dd6 Mon Sep 17 00:00:00 2001 From: Kieran Date: Sun, 27 Aug 2023 16:25:27 +0100 Subject: [PATCH] Setup lang --- .vscode/extensions.json | 6 +- .yarn/sdks/typescript/lib/tsserver.js | 50 ++-- .yarn/sdks/typescript/lib/tsserverlibrary.js | 50 ++-- package.json | 12 +- src/element/Event.tsx | 6 +- src/element/async-button.tsx | 14 +- src/element/avatar.tsx | 16 +- src/element/badge.tsx | 4 +- src/element/chat-message.tsx | 67 ++--- src/element/collapsible.tsx | 21 +- src/element/content-warning.tsx | 13 +- src/element/copy.tsx | 21 +- src/element/emoji-pack.tsx | 25 +- src/element/emoji-picker.tsx | 7 +- src/element/emoji.tsx | 12 +- src/element/external-link.tsx | 13 +- src/element/file-uploader.tsx | 13 +- src/element/follow-button.tsx | 28 +-- src/element/goal.tsx | 27 +- src/element/hypertext.tsx | 9 +- src/element/icon.tsx | 7 +- src/element/live-chat.css | 10 +- src/element/live-chat.tsx | 84 +++---- src/element/live-video-player.tsx | 12 +- src/element/login-signup.tsx | 65 ++--- src/element/mute-button.tsx | 22 +- src/element/new-goal.tsx | 38 +-- src/element/new-stream.tsx | 40 ++- src/element/nostr-link.tsx | 5 +- src/element/nostr-provider-dialog.tsx | 108 ++++---- src/element/note.tsx | 5 +- src/element/profile.tsx | 17 +- src/element/qr-code.tsx | 7 +- src/element/send-zap.tsx | 94 +++---- src/element/share-menu.tsx | 36 +-- src/element/spinner.tsx | 8 +- src/element/state-pill.tsx | 6 +- src/element/stream-cards.tsx | 163 ++++++------ src/element/stream-editor.tsx | 108 ++++---- src/element/stream-time.tsx | 4 +- src/element/tags.tsx | 15 +- src/element/text.tsx | 58 ++--- src/element/textarea.tsx | 13 +- src/element/video-tile.tsx | 28 +-- src/element/write-message.tsx | 28 +-- src/fonts/outfit/outfit.css | 36 ++- src/hooks/badges.ts | 41 +-- src/hooks/cards.ts | 92 +++---- src/hooks/current-stream-feed.ts | 31 +-- src/hooks/emoji.tsx | 29 +-- src/hooks/event-feed.ts | 9 +- src/hooks/event.ts | 9 +- src/hooks/goals.ts | 11 +- src/hooks/live-chat.tsx | 17 +- src/hooks/live-streams.ts | 18 +- src/hooks/login.ts | 4 +- src/hooks/placeholders.ts | 5 +- src/hooks/profile.ts | 14 +- src/hooks/stream-provider.tsx | 2 +- src/hooks/top-zappers.ts | 10 +- src/index.css | 15 +- src/index.tsx | 15 +- src/intl.tsx | 40 +++ src/lang.json | 248 +++++++++++++++++++ src/login.ts | 5 +- src/pages/chat-popout.tsx | 10 +- src/pages/layout.tsx | 26 +- src/pages/profile-page.css | 7 +- src/pages/profile-page.tsx | 102 +++----- src/pages/providers/index.tsx | 11 +- src/pages/providers/nostr.tsx | 10 +- src/pages/providers/owncast.tsx | 16 +- src/pages/root.tsx | 32 ++- src/pages/stream-page.css | 13 +- src/pages/stream-page.tsx | 72 +----- src/pages/tag.tsx | 2 +- src/providers/index.ts | 13 +- src/providers/owncast.ts | 6 +- src/providers/zsz.ts | 23 +- src/service-worker.ts | 2 +- src/translations/en.json | 5 + src/utils.ts | 51 +--- src/wish/index.ts | 113 ++------- webpack.config.js | 5 +- yarn.lock | 102 +++++++- 85 files changed, 1152 insertions(+), 1515 deletions(-) create mode 100644 src/intl.tsx create mode 100644 src/lang.json create mode 100644 src/translations/en.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json index daaa5ee..da487ff 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,3 @@ { - "recommendations": [ - "arcanis.vscode-zipfs", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode" - ] + "recommendations": ["arcanis.vscode-zipfs", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] } diff --git a/.yarn/sdks/typescript/lib/tsserver.js b/.yarn/sdks/typescript/lib/tsserver.js index 456a518..5dcd6d9 100644 --- a/.yarn/sdks/typescript/lib/tsserver.js +++ b/.yarn/sdks/typescript/lib/tsserver.js @@ -9,7 +9,7 @@ const relPnpApiPath = "../../../../.pnp.cjs"; const absPnpApiPath = resolve(__dirname, relPnpApiPath); const absRequire = createRequire(absPnpApiPath); -const moduleWrapper = (tsserver) => { +const moduleWrapper = tsserver => { if (!process.versions.pnp) { return tsserver; } @@ -17,12 +17,12 @@ const moduleWrapper = (tsserver) => { const { isAbsolute } = require(`path`); const pnpApi = require(`pnpapi`); - const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//); - const isPortal = (str) => str.startsWith("portal:/"); - const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); + const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//); + const isPortal = str => str.startsWith("portal:/"); + const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); const dependencyTreeRoots = new Set( - pnpApi.getDependencyTreeRoots().map((locator) => { + pnpApi.getDependencyTreeRoots().map(locator => { return `${locator.name}@${locator.reference}`; }) ); @@ -33,11 +33,7 @@ const moduleWrapper = (tsserver) => { function toEditorPath(str) { // We add the `zip:` prefix to both `.zip/` paths and virtual paths - if ( - isAbsolute(str) && - !str.match(/^\^?(zip:|\/zip\/)/) && - (str.match(/\.zip\//) || isVirtual(str)) - ) { + if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) { // We also take the opportunity to turn virtual paths into physical ones; // this makes it much easier to work with workspaces that list peer // dependencies, since otherwise Ctrl+Click would bring us to the virtual @@ -53,8 +49,7 @@ const moduleWrapper = (tsserver) => { const locator = pnpApi.findPackageLocator(resolved); if ( locator && - (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || - isPortal(locator.reference)) + (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference)) ) { str = resolved; } @@ -149,9 +144,7 @@ const moduleWrapper = (tsserver) => { // The path for coc-nvim is in format of //zipfile://.yarn/... // So in order to convert it back, we use .* to match all the thing // before `zipfile:` - return process.platform === `win32` - ? str.replace(/^.*zipfile:\//, ``) - : str.replace(/^.*zipfile:/, ``); + return process.platform === `win32` ? str.replace(/^.*zipfile:\//, ``) : str.replace(/^.*zipfile:/, ``); } break; @@ -166,10 +159,7 @@ const moduleWrapper = (tsserver) => { case `vscode`: default: { - return str.replace( - /^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, - process.platform === `win32` ? `` : `/` - ); + return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`); } break; } @@ -183,8 +173,7 @@ const moduleWrapper = (tsserver) => { // TypeScript already does local loads and if this code is running the user trusts the workspace // https://github.com/microsoft/vscode/issues/45856 const ConfiguredProject = tsserver.server.ConfiguredProject; - const { enablePluginsWithOptions: originalEnablePluginsWithOptions } = - ConfiguredProject.prototype; + const { enablePluginsWithOptions: originalEnablePluginsWithOptions } = ConfiguredProject.prototype; ConfiguredProject.prototype.enablePluginsWithOptions = function () { this.projectService.allowLocalPluginLoads = true; return originalEnablePluginsWithOptions.apply(this, arguments); @@ -195,8 +184,7 @@ const moduleWrapper = (tsserver) => { // like an absolute path of ours and normalize it. const Session = tsserver.server.Session; - const { onMessage: originalOnMessage, send: originalSend } = - Session.prototype; + const { onMessage: originalOnMessage, send: originalSend } = Session.prototype; let hostInfo = `unknown`; Object.assign(Session.prototype, { @@ -231,19 +219,11 @@ const moduleWrapper = (tsserver) => { } } - const processedMessageJSON = JSON.stringify( - parsedMessage, - (key, value) => { - return typeof value === "string" ? fromEditorPath(value) : value; - } - ); + const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => { + return typeof value === "string" ? fromEditorPath(value) : value; + }); - return originalOnMessage.call( - this, - isStringMessage - ? processedMessageJSON - : JSON.parse(processedMessageJSON) - ); + return originalOnMessage.call(this, isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)); }, send(/** @type {any} */ msg) { diff --git a/.yarn/sdks/typescript/lib/tsserverlibrary.js b/.yarn/sdks/typescript/lib/tsserverlibrary.js index d367b43..d8dae69 100644 --- a/.yarn/sdks/typescript/lib/tsserverlibrary.js +++ b/.yarn/sdks/typescript/lib/tsserverlibrary.js @@ -9,7 +9,7 @@ const relPnpApiPath = "../../../../.pnp.cjs"; const absPnpApiPath = resolve(__dirname, relPnpApiPath); const absRequire = createRequire(absPnpApiPath); -const moduleWrapper = (tsserver) => { +const moduleWrapper = tsserver => { if (!process.versions.pnp) { return tsserver; } @@ -17,12 +17,12 @@ const moduleWrapper = (tsserver) => { const { isAbsolute } = require(`path`); const pnpApi = require(`pnpapi`); - const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//); - const isPortal = (str) => str.startsWith("portal:/"); - const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); + const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//); + const isPortal = str => str.startsWith("portal:/"); + const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); const dependencyTreeRoots = new Set( - pnpApi.getDependencyTreeRoots().map((locator) => { + pnpApi.getDependencyTreeRoots().map(locator => { return `${locator.name}@${locator.reference}`; }) ); @@ -33,11 +33,7 @@ const moduleWrapper = (tsserver) => { function toEditorPath(str) { // We add the `zip:` prefix to both `.zip/` paths and virtual paths - if ( - isAbsolute(str) && - !str.match(/^\^?(zip:|\/zip\/)/) && - (str.match(/\.zip\//) || isVirtual(str)) - ) { + if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) { // We also take the opportunity to turn virtual paths into physical ones; // this makes it much easier to work with workspaces that list peer // dependencies, since otherwise Ctrl+Click would bring us to the virtual @@ -53,8 +49,7 @@ const moduleWrapper = (tsserver) => { const locator = pnpApi.findPackageLocator(resolved); if ( locator && - (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || - isPortal(locator.reference)) + (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference)) ) { str = resolved; } @@ -149,9 +144,7 @@ const moduleWrapper = (tsserver) => { // The path for coc-nvim is in format of //zipfile://.yarn/... // So in order to convert it back, we use .* to match all the thing // before `zipfile:` - return process.platform === `win32` - ? str.replace(/^.*zipfile:\//, ``) - : str.replace(/^.*zipfile:/, ``); + return process.platform === `win32` ? str.replace(/^.*zipfile:\//, ``) : str.replace(/^.*zipfile:/, ``); } break; @@ -166,10 +159,7 @@ const moduleWrapper = (tsserver) => { case `vscode`: default: { - return str.replace( - /^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, - process.platform === `win32` ? `` : `/` - ); + return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`); } break; } @@ -183,8 +173,7 @@ const moduleWrapper = (tsserver) => { // TypeScript already does local loads and if this code is running the user trusts the workspace // https://github.com/microsoft/vscode/issues/45856 const ConfiguredProject = tsserver.server.ConfiguredProject; - const { enablePluginsWithOptions: originalEnablePluginsWithOptions } = - ConfiguredProject.prototype; + const { enablePluginsWithOptions: originalEnablePluginsWithOptions } = ConfiguredProject.prototype; ConfiguredProject.prototype.enablePluginsWithOptions = function () { this.projectService.allowLocalPluginLoads = true; return originalEnablePluginsWithOptions.apply(this, arguments); @@ -195,8 +184,7 @@ const moduleWrapper = (tsserver) => { // like an absolute path of ours and normalize it. const Session = tsserver.server.Session; - const { onMessage: originalOnMessage, send: originalSend } = - Session.prototype; + const { onMessage: originalOnMessage, send: originalSend } = Session.prototype; let hostInfo = `unknown`; Object.assign(Session.prototype, { @@ -231,19 +219,11 @@ const moduleWrapper = (tsserver) => { } } - const processedMessageJSON = JSON.stringify( - parsedMessage, - (key, value) => { - return typeof value === "string" ? fromEditorPath(value) : value; - } - ); + const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => { + return typeof value === "string" ? fromEditorPath(value) : value; + }); - return originalOnMessage.call( - this, - isStringMessage - ? processedMessageJSON - : JSON.parse(processedMessageJSON) - ); + return originalOnMessage.call(this, isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)); }, send(/** @type {any} */ msg) { diff --git a/package.json b/package.json index d11d1ad..7e6dfe1 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "react-dom": "^18.2.0", "react-helmet": "^6.1.0", "react-intersection-observer": "^9.5.1", + "react-intl": "^6.4.4", "react-markdown": "^8.0.7", "react-router-dom": "^6.13.0", "react-tag-input-component": "^2.0.2", @@ -54,7 +55,9 @@ "start": "webpack serve", "build": "webpack --node-env=production --mode=production", "deploy": "__XXX='false' && yarn build && npx wrangler pages publish --project-name nostr-live build", - "deploy:xxzap": "__XXX='true' && yarn build && npx wrangler pages publish --project-name xxzap build" + "deploy:xxzap": "__XXX='true' && yarn build && npx wrangler pages publish --project-name xxzap build", + "intl-extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file src/lang.json --flatten true", + "intl-compile": "formatjs compile src/lang.json --out-file src/translations/en.json" }, "eslintConfig": { "extends": [ @@ -114,5 +117,10 @@ "webpack-dev-server": "^4.15.0", "workbox-webpack-plugin": "^7.0.0" }, - "packageManager": "yarn@3.6.3" + "packageManager": "yarn@3.6.3", + "prettier": { + "printWidth": 120, + "bracketSameLine": true, + "arrowParens": "avoid" + } } diff --git a/src/element/Event.tsx b/src/element/Event.tsx index f07fae3..0f6130b 100644 --- a/src/element/Event.tsx +++ b/src/element/Event.tsx @@ -1,10 +1,6 @@ import "./event.css"; -import { - type NostrLink, - type NostrEvent as NostrEventType, - EventKind, -} from "@snort/system"; +import { type NostrLink, type NostrEvent as NostrEventType, EventKind } from "@snort/system"; import { Icon } from "element/icon"; import { Goal } from "element/goal"; diff --git a/src/element/async-button.tsx b/src/element/async-button.tsx index 95d05a3..4e8153a 100644 --- a/src/element/async-button.tsx +++ b/src/element/async-button.tsx @@ -2,8 +2,7 @@ import "./async-button.css"; import { useState } from "react"; import Spinner from "element/spinner"; -interface AsyncButtonProps - extends React.ButtonHTMLAttributes { +interface AsyncButtonProps extends React.ButtonHTMLAttributes { disabled?: boolean; onClick(e: React.MouseEvent): Promise | void; children?: React.ReactNode; @@ -29,15 +28,8 @@ export default function AsyncButton(props: AsyncButtonProps) { } return ( - @@ -46,29 +47,19 @@ export function CollapsibleEvent({ link }: { link: NostrLink }) { const author = event?.pubkey || link.author; return ( - +
{event && } {author && }
-
- - {open && event && } - + {open && event && }
); } diff --git a/src/element/content-warning.tsx b/src/element/content-warning.tsx index 78e7e69..498ec27 100644 --- a/src/element/content-warning.tsx +++ b/src/element/content-warning.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; export function isContentWarningAccepted() { @@ -17,14 +18,18 @@ export function ContentWarningOverlay() { return (
-

Sexually explicit material ahead!

-

Confirm your age

+

+ +

+

+ +

diff --git a/src/element/copy.tsx b/src/element/copy.tsx index 8ec7be6..2ccc0ba 100644 --- a/src/element/copy.tsx +++ b/src/element/copy.tsx @@ -10,26 +10,13 @@ export interface CopyProps { export default function Copy({ text, maxSize = 32, className }: CopyProps) { const { copy, copied } = useCopy(); const sliceLength = maxSize / 2; - const trimmed = - text.length > maxSize - ? `${text.slice(0, sliceLength)}...${text.slice(-sliceLength)}` - : text; + const trimmed = text.length > maxSize ? `${text.slice(0, sliceLength)}...${text.slice(-sliceLength)}` : text; return ( -
copy(text)} - > +
copy(text)}> {trimmed} - - {copied ? ( - - ) : ( - - )} + + {copied ? : }
); diff --git a/src/element/emoji-pack.tsx b/src/element/emoji-pack.tsx index 4213728..609b17b 100644 --- a/src/element/emoji-pack.tsx +++ b/src/element/emoji-pack.tsx @@ -8,28 +8,24 @@ import { findTag } from "utils"; import { USER_EMOJIS } from "const"; import { Login, System } from "index"; import type { EmojiPack as EmojiPackType } from "types"; +import { FormattedMessage } from "react-intl"; export function EmojiPack({ ev }: { ev: NostrEvent }) { const login = useLogin(); const name = findTag(ev, "d"); - const isUsed = login?.emojis.find( - (e) => e.author === ev.pubkey && e.name === name - ); - const emoji = ev.tags.filter((e) => e.at(0) === "emoji"); + const isUsed = login?.emojis.find(e => e.author === ev.pubkey && e.name === name); + const emoji = ev.tags.filter(e => e.at(0) === "emoji"); async function toggleEmojiPack() { let newPacks = [] as EmojiPackType[]; if (isUsed) { - newPacks = - login?.emojis.filter( - (e) => e.author !== ev.pubkey && e.name !== name - ) ?? []; + newPacks = login?.emojis.filter(e => e.author !== ev.pubkey && e.name !== name) ?? []; } else { newPacks = [...(login?.emojis ?? []), toEmojiPack(ev)]; } const pub = login?.publisher(); if (pub) { - const ev = await pub.generic((eb) => { + const ev = await pub.generic(eb => { eb.kind(USER_EMOJIS).content(""); for (const e of newPacks) { eb.tag(["a", e.address]); @@ -48,17 +44,14 @@ export function EmojiPack({ ev }: { ev: NostrEvent }) {

{name}

{login?.pubkey && ( - {isUsed ? "Remove" : "Add"} + className={`btn btn-small btn-primary ${isUsed ? "delete-button" : ""}`} + onClick={toggleEmojiPack}> + )}
- {emoji.map((e) => { + {emoji.map(e => { const [, name, image] = e; return (
diff --git a/src/element/emoji-picker.tsx b/src/element/emoji-picker.tsx index cb0ac08..212b638 100644 --- a/src/element/emoji-picker.tsx +++ b/src/element/emoji-picker.tsx @@ -22,11 +22,11 @@ export function EmojiPicker({ height = 300, ref, }: EmojiPickerProps) { - const customEmojiList = emojiPacks.map((pack) => { + const customEmojiList = emojiPacks.map(pack => { return { id: pack.address, name: pack.name, - emojis: pack.emojis.map((e) => { + emojis: pack.emojis.map(e => { const [, name, url] = e; return { id: name, @@ -45,8 +45,7 @@ export function EmojiPicker({ left: leftOffset, zIndex: 1, }} - ref={ref} - > + ref={ref}>