refactor: upgrade
This commit is contained in:
parent
01eaf9996c
commit
e2714c4274
@ -1,7 +1,15 @@
|
||||
module.exports = {
|
||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint"],
|
||||
plugins: ["@typescript-eslint", "formatjs"],
|
||||
rules: {
|
||||
"formatjs/enforce-id": [
|
||||
"error",
|
||||
{
|
||||
idInterpolationPattern: "[sha512:contenthash:base64:6]",
|
||||
},
|
||||
],
|
||||
},
|
||||
root: true,
|
||||
ignorePatterns: ["build/", "*.test.ts", "*.js"],
|
||||
env: {
|
||||
@ -10,10 +18,4 @@ module.exports = {
|
||||
commonjs: true,
|
||||
node: false,
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"require-await": "error",
|
||||
eqeqeq: "error",
|
||||
"object-shorthand": "warn",
|
||||
},
|
||||
};
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -29,4 +29,6 @@ yarn-error.log*
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
!.yarn/versions
|
||||
|
||||
dev-dist/
|
@ -1,4 +1,6 @@
|
||||
.yarn/
|
||||
.vscode/
|
||||
node_modules/
|
||||
build/
|
||||
build/
|
||||
dist/
|
||||
dev-dist/
|
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
@ -1,3 +1,7 @@
|
||||
{
|
||||
"recommendations": ["arcanis.vscode-zipfs", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
||||
"recommendations": [
|
||||
"arcanis.vscode-zipfs",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
|
6
.yarn/sdks/eslint/bin/eslint.js
vendored
6
.yarn/sdks/eslint/bin/eslint.js
vendored
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
|
6
.yarn/sdks/eslint/lib/api.js
vendored
6
.yarn/sdks/eslint/lib/api.js
vendored
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
|
20
.yarn/sdks/eslint/lib/unsupported-api.js
vendored
Normal file
20
.yarn/sdks/eslint/lib/unsupported-api.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/use-at-your-own-risk
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real eslint/use-at-your-own-risk your application uses
|
||||
module.exports = absRequire(`eslint/use-at-your-own-risk`);
|
10
.yarn/sdks/eslint/package.json
vendored
10
.yarn/sdks/eslint/package.json
vendored
@ -2,5 +2,13 @@
|
||||
"name": "eslint",
|
||||
"version": "8.48.0-sdk",
|
||||
"main": "./lib/api.js",
|
||||
"type": "commonjs"
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
"eslint": "./bin/eslint.js"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": "./lib/api.js",
|
||||
"./use-at-your-own-risk": "./lib/unsupported-api.js"
|
||||
}
|
||||
}
|
||||
|
20
.yarn/sdks/prettier/bin-prettier.js
vendored
Executable file
20
.yarn/sdks/prettier/bin-prettier.js
vendored
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier/bin-prettier.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real prettier/bin-prettier.js your application uses
|
||||
module.exports = absRequire(`prettier/bin-prettier.js`);
|
6
.yarn/sdks/prettier/index.js
vendored
Executable file → Normal file
6
.yarn/sdks/prettier/index.js
vendored
Executable file → Normal file
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../.pnp.cjs";
|
||||
|
||||
|
3
.yarn/sdks/prettier/package.json
vendored
3
.yarn/sdks/prettier/package.json
vendored
@ -2,5 +2,6 @@
|
||||
"name": "prettier",
|
||||
"version": "2.8.8-sdk",
|
||||
"main": "./index.js",
|
||||
"type": "commonjs"
|
||||
"type": "commonjs",
|
||||
"bin": "./bin-prettier.js"
|
||||
}
|
||||
|
6
.yarn/sdks/typescript/lib/tsc.js
vendored
6
.yarn/sdks/typescript/lib/tsc.js
vendored
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
|
157
.yarn/sdks/typescript/lib/tsserver.js
vendored
157
.yarn/sdks/typescript/lib/tsserver.js
vendored
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
@ -14,18 +14,16 @@ const moduleWrapper = tsserver => {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const { isAbsolute } = require(`path`);
|
||||
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 dependencyTreeRoots = new Set(
|
||||
pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
})
|
||||
);
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
@ -47,10 +45,7 @@ const moduleWrapper = tsserver => {
|
||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||
if (resolved) {
|
||||
const locator = pnpApi.findPackageLocator(resolved);
|
||||
if (
|
||||
locator &&
|
||||
(dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))
|
||||
) {
|
||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
@ -78,55 +73,41 @@ const moduleWrapper = tsserver => {
|
||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`:
|
||||
{
|
||||
str = `^zip:${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.66`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode <1.66`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.68`:
|
||||
{
|
||||
str = `^/zip${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode <1.68`: {
|
||||
str = `^/zip${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
}
|
||||
break;
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
}
|
||||
break;
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
str = `zip:${str}`;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||
@ -138,30 +119,26 @@ const moduleWrapper = tsserver => {
|
||||
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.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:/, ``);
|
||||
}
|
||||
break;
|
||||
case `coc-nvim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.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:/, ``);
|
||||
} break;
|
||||
|
||||
case `neovim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
}
|
||||
break;
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
default:
|
||||
{
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`);
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,8 +150,8 @@ 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;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
@ -184,12 +161,12 @@ 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, {
|
||||
onMessage(/** @type {string | object} */ message) {
|
||||
const isStringMessage = typeof message === "string";
|
||||
const isStringMessage = typeof message === 'string';
|
||||
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||
|
||||
if (
|
||||
@ -200,12 +177,10 @@ const moduleWrapper = tsserver => {
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||
const [, major, minor] = (
|
||||
process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []
|
||||
).map(Number);
|
||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []).map(Number)
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
@ -220,22 +195,20 @@ const moduleWrapper = tsserver => {
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === "string" ? fromEditorPath(value) : 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) {
|
||||
return originalSend.call(
|
||||
this,
|
||||
JSON.parse(
|
||||
JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
|
157
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
157
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
@ -14,18 +14,16 @@ const moduleWrapper = tsserver => {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const { isAbsolute } = require(`path`);
|
||||
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 dependencyTreeRoots = new Set(
|
||||
pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
})
|
||||
);
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
@ -47,10 +45,7 @@ const moduleWrapper = tsserver => {
|
||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||
if (resolved) {
|
||||
const locator = pnpApi.findPackageLocator(resolved);
|
||||
if (
|
||||
locator &&
|
||||
(dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))
|
||||
) {
|
||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
@ -78,55 +73,41 @@ const moduleWrapper = tsserver => {
|
||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`:
|
||||
{
|
||||
str = `^zip:${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.66`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode <1.66`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.68`:
|
||||
{
|
||||
str = `^/zip${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode <1.68`: {
|
||||
str = `^/zip${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
}
|
||||
break;
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
}
|
||||
break;
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
str = `zip:${str}`;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||
@ -138,30 +119,26 @@ const moduleWrapper = tsserver => {
|
||||
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.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:/, ``);
|
||||
}
|
||||
break;
|
||||
case `coc-nvim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.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:/, ``);
|
||||
} break;
|
||||
|
||||
case `neovim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
}
|
||||
break;
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
default:
|
||||
{
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`);
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,8 +150,8 @@ 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;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
@ -184,12 +161,12 @@ 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, {
|
||||
onMessage(/** @type {string | object} */ message) {
|
||||
const isStringMessage = typeof message === "string";
|
||||
const isStringMessage = typeof message === 'string';
|
||||
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||
|
||||
if (
|
||||
@ -200,12 +177,10 @@ const moduleWrapper = tsserver => {
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||
const [, major, minor] = (
|
||||
process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []
|
||||
).map(Number);
|
||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []).map(Number)
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
@ -220,22 +195,20 @@ const moduleWrapper = tsserver => {
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === "string" ? fromEditorPath(value) : 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) {
|
||||
return originalSend.call(
|
||||
this,
|
||||
JSON.parse(
|
||||
JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
|
12
.yarn/sdks/typescript/lib/typescript.js
vendored
12
.yarn/sdks/typescript/lib/typescript.js
vendored
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
@ -11,10 +11,10 @@ const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/typescript.js
|
||||
// Setup the environment to be able to require typescript
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/typescript.js your application uses
|
||||
module.exports = absRequire(`typescript/lib/typescript.js`);
|
||||
// Defer to the real typescript your application uses
|
||||
module.exports = absRequire(`typescript`);
|
||||
|
6
.yarn/sdks/typescript/package.json
vendored
6
.yarn/sdks/typescript/package.json
vendored
@ -2,5 +2,9 @@
|
||||
"name": "typescript",
|
||||
"version": "5.2.2-sdk",
|
||||
"main": "./lib/typescript.js",
|
||||
"type": "commonjs"
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
"tsc": "./bin/tsc",
|
||||
"tsserver": "./bin/tsserver"
|
||||
}
|
||||
}
|
||||
|
@ -13,5 +13,6 @@
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="src/index.tsx" type="module"></script>
|
||||
</body>
|
||||
</html>
|
40
package.json
40
package.json
@ -14,14 +14,12 @@
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@react-hook/resize-observer": "^1.2.6",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@snort/shared": "^1.0.9",
|
||||
"@snort/system": "^1.1.4",
|
||||
"@snort/system-react": "^1.1.4",
|
||||
"@snort/shared": "^1.0.10",
|
||||
"@snort/system": "^1.1.5",
|
||||
"@snort/system-react": "^1.1.5",
|
||||
"@snort/system-wasm": "^1.0.1",
|
||||
"@snort/system-web": "^1.0.2",
|
||||
"@szhsin/react-menu": "^4.0.2",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@testing-library/user-event": "^13.2.1",
|
||||
"@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
|
||||
"@void-cat/api": "^1.0.7",
|
||||
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
|
||||
@ -32,7 +30,6 @@
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.uniqby": "^4.7.0",
|
||||
"marked": "^9.1.2",
|
||||
"moment": "^2.29.4",
|
||||
"qr-code-styling": "^1.6.0-rc.1",
|
||||
"react": "^18.2.0",
|
||||
"react-confetti": "^6.1.0",
|
||||
@ -55,8 +52,8 @@
|
||||
"workbox-strategies": "^7.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack serve --node-env=development --mode=development",
|
||||
"build": "webpack --node-env=production --mode=production",
|
||||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"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",
|
||||
"intl-extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file src/lang.json --flatten true",
|
||||
@ -83,40 +80,27 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.11",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
||||
"@babel/preset-env": "^7.21.5",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@formatjs/cli": "^6.1.3",
|
||||
"@formatjs/ts-transformer": "^3.13.3",
|
||||
"@testing-library/dom": "^9.3.1",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/lodash.uniqby": "^4.7.7",
|
||||
"@types/node": "^20.10.3",
|
||||
"@types/react": "^18.2.21",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/react-helmet": "^6.1.6",
|
||||
"@typescript-eslint/eslint-plugin": "^6.4.1",
|
||||
"@typescript-eslint/parser": "^6.4.1",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"@webbtc/webln-types": "^1.0.12",
|
||||
"babel-loader": "^9.1.3",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"css-minimizer-webpack-plugin": "^5.0.0",
|
||||
"eslint": "^8.48.0",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"html-webpack-plugin": "^5.5.1",
|
||||
"mini-css-extract-plugin": "^2.7.5",
|
||||
"eslint-plugin-formatjs": "^4.11.3",
|
||||
"prettier": "^2.8.8",
|
||||
"prop-types": "^15.8.1",
|
||||
"source-map-loader": "^4.0.1",
|
||||
"terser-webpack-plugin": "^5.3.9",
|
||||
"ts-loader": "^9.4.4",
|
||||
"typescript": "^5.2.2",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-bundle-analyzer": "^4.8.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"workbox-webpack-plugin": "^7.0.0"
|
||||
"vite": "^5.0.5",
|
||||
"vite-plugin-pwa": "^0.17.2",
|
||||
"vite-plugin-version-mark": "^0.0.10"
|
||||
},
|
||||
"packageManager": "yarn@3.6.3",
|
||||
"prettier": {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
@ -1,3 +0,0 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
@ -1 +0,0 @@
|
||||
[{ "id": "nsfw", "text": "NSFW" }]
|
@ -1,14 +1,14 @@
|
||||
import "./event.css";
|
||||
|
||||
import { type NostrLink, type NostrEvent as NostrEventType, EventKind } from "@snort/system";
|
||||
import { EventKind, type NostrEvent as NostrEventType, type NostrLink } from "@snort/system";
|
||||
|
||||
import { Icon } from "element/icon";
|
||||
import { Goal } from "element/goal";
|
||||
import { Note } from "element/note";
|
||||
import { EmojiPack } from "element/emoji-pack";
|
||||
import { Badge } from "element/badge";
|
||||
import { useEvent } from "hooks/event";
|
||||
import { GOAL, EMOJI_PACK } from "const";
|
||||
import { Icon } from "./icon";
|
||||
import { Goal } from "./goal";
|
||||
import { Note } from "./note";
|
||||
import { EmojiPack } from "./emoji-pack";
|
||||
import { Badge } from "./badge";
|
||||
import { useEvent } from "@/hooks/event";
|
||||
import { EMOJI_PACK, GOAL } from "@/const";
|
||||
|
||||
interface EventProps {
|
||||
link: NostrLink;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "./async-button.css";
|
||||
import { useState } from "react";
|
||||
import Spinner from "element/spinner";
|
||||
import Spinner from "./spinner";
|
||||
|
||||
interface AsyncButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
disabled?: boolean;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "./badge.css";
|
||||
import type { NostrEvent } from "@snort/system";
|
||||
import { findTag } from "utils";
|
||||
import { findTag } from "@/utils";
|
||||
|
||||
export function Badge({ ev }: { ev: NostrEvent }) {
|
||||
const name = findTag(ev, "name") || findTag(ev, "d");
|
||||
|
@ -1,20 +1,21 @@
|
||||
import { useUserProfile, SnortContext, useEventReactions } from "@snort/system-react";
|
||||
import { SnortContext, useEventReactions, useUserProfile } from "@snort/system-react";
|
||||
import { EventKind, NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||
import React, { useRef, useState, useMemo, useContext } from "react";
|
||||
import { useMediaQuery, useHover, useOnClickOutside, useIntersectionObserver } from "usehooks-ts";
|
||||
import React, { useContext, useMemo, useRef, useState } from "react";
|
||||
import { useHover, useIntersectionObserver, useMediaQuery, useOnClickOutside } from "usehooks-ts";
|
||||
import { dedupe } from "@snort/shared";
|
||||
|
||||
import { EmojiPicker } from "element/emoji-picker";
|
||||
import { Icon } from "element/icon";
|
||||
import { Emoji as EmojiComponent } from "element/emoji";
|
||||
import { EmojiPicker } from "./emoji-picker";
|
||||
import { Icon } from "./icon";
|
||||
import { Emoji as EmojiComponent } from "./emoji";
|
||||
import { Profile } from "./profile";
|
||||
import { Text } from "element/text";
|
||||
import { useMute } from "element/mute-button";
|
||||
import { SendZapsDialog } from "element/send-zap";
|
||||
import { CollapsibleEvent } from "element/collapsible";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { formatSats } from "number";
|
||||
import type { Badge, Emoji, EmojiPack } from "types";
|
||||
import { Text } from "./text";
|
||||
import { useMute } from "./mute-button";
|
||||
import { SendZapsDialog } from "./send-zap";
|
||||
import { CollapsibleEvent } from "./collapsible";
|
||||
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { formatSats } from "@/number";
|
||||
import type { Badge, Emoji, EmojiPack } from "@/types";
|
||||
|
||||
function emojifyReaction(reaction: string) {
|
||||
if (reaction === "+") {
|
||||
|
@ -8,10 +8,10 @@ import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
|
||||
import type { NostrLink } from "@snort/system";
|
||||
|
||||
import { Mention } from "element/mention";
|
||||
import { NostrEvent, EventIcon } from "element/Event";
|
||||
import { ExternalLink } from "element/external-link";
|
||||
import { useEvent } from "hooks/event";
|
||||
import { Mention } from "./mention";
|
||||
import { EventIcon, NostrEvent } from "./Event";
|
||||
import { ExternalLink } from "./external-link";
|
||||
import { useEvent } from "@/hooks/event";
|
||||
|
||||
interface MediaURLProps {
|
||||
url: URL;
|
||||
@ -32,7 +32,7 @@ export function MediaURL({ url, children }: MediaURLProps) {
|
||||
</div>
|
||||
<Dialog.Close asChild>
|
||||
<button className="btn delete-button" aria-label="Close">
|
||||
<FormattedMessage defaultMessage="Close" />
|
||||
<FormattedMessage defaultMessage="Close" id="rbrahO" />
|
||||
</button>
|
||||
</Dialog.Close>
|
||||
</Dialog.Content>
|
||||
@ -55,7 +55,11 @@ export function CollapsibleEvent({ link }: { link: NostrLink }) {
|
||||
</div>
|
||||
<Collapsible.Trigger asChild>
|
||||
<button className={`${open ? "btn btn-small delete-button" : "btn btn-small"}`}>
|
||||
{open ? <FormattedMessage defaultMessage="Hide" /> : <FormattedMessage defaultMessage="Show" />}
|
||||
{open ? (
|
||||
<FormattedMessage defaultMessage="Hide" id="VA/Z1S" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Show" id="K7AkdL" />
|
||||
)}
|
||||
</button>
|
||||
</Collapsible.Trigger>
|
||||
</div>
|
||||
|
@ -19,17 +19,17 @@ export function ContentWarningOverlay() {
|
||||
return (
|
||||
<div className="fullscreen-exclusive age-check">
|
||||
<h1>
|
||||
<FormattedMessage defaultMessage="Sexually explicit material ahead!" />
|
||||
<FormattedMessage defaultMessage="Sexually explicit material ahead!" id="rWBFZA" />
|
||||
</h1>
|
||||
<h2>
|
||||
<FormattedMessage defaultMessage="Confirm your age" />
|
||||
<FormattedMessage defaultMessage="Confirm your age" id="s7V+5p" />
|
||||
</h2>
|
||||
<div className="flex g24">
|
||||
<button className="btn btn-warning" onClick={grownUp}>
|
||||
<FormattedMessage defaultMessage="Yes, I am over 18" />
|
||||
<FormattedMessage defaultMessage="Yes, I am over 18" id="O2Cy6m" />
|
||||
</button>
|
||||
<button className="btn" onClick={() => navigate("/")}>
|
||||
<FormattedMessage defaultMessage="No, I am under 18" />
|
||||
<FormattedMessage defaultMessage="No, I am under 18" id="KkIL3s" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import "./copy.css";
|
||||
import { useCopy } from "hooks/copy";
|
||||
import { useCopy } from "@/hooks/copy";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
export interface CopyProps {
|
||||
|
@ -1,17 +1,17 @@
|
||||
import "./emoji-pack.css";
|
||||
import { type NostrEvent } from "@snort/system";
|
||||
|
||||
import { useLogin } from "hooks/login";
|
||||
import { toEmojiPack } from "hooks/emoji";
|
||||
import AsyncButton from "element/async-button";
|
||||
import { findTag } from "utils";
|
||||
import { USER_EMOJIS } from "const";
|
||||
import { Login } from "index";
|
||||
import type { EmojiPack as EmojiPackType } from "types";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useContext } from "react";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { toEmojiPack } from "@/hooks/emoji";
|
||||
import AsyncButton from "./async-button";
|
||||
import { findTag } from "@/utils";
|
||||
import { USER_EMOJIS } from "@/const";
|
||||
import { Login } from "@/index";
|
||||
import type { EmojiPack as EmojiPackType } from "@/types";
|
||||
|
||||
export function EmojiPack({ ev }: { ev: NostrEvent }) {
|
||||
const system = useContext(SnortContext);
|
||||
const login = useLogin();
|
||||
@ -49,7 +49,11 @@ export function EmojiPack({ ev }: { ev: NostrEvent }) {
|
||||
<AsyncButton
|
||||
className={`btn btn-small btn-primary ${isUsed ? "delete-button" : ""}`}
|
||||
onClick={toggleEmojiPack}>
|
||||
{isUsed ? <FormattedMessage defaultMessage="Remove" /> : <FormattedMessage defaultMessage="Add" />}
|
||||
{isUsed ? (
|
||||
<FormattedMessage defaultMessage="Remove" id="G/yZLu" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Add" id="2/2yg+" />
|
||||
)}
|
||||
</AsyncButton>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import data, { Emoji } from "@emoji-mart/data";
|
||||
import Picker from "@emoji-mart/react";
|
||||
import { RefObject } from "react";
|
||||
import { EmojiPack } from "types";
|
||||
import { EmojiPack } from "@/types";
|
||||
|
||||
interface EmojiPickerProps {
|
||||
topOffset: number;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "./emoji.css";
|
||||
import { useMemo } from "react";
|
||||
import { EmojiTag } from "types";
|
||||
import { EmojiTag } from "@/types";
|
||||
|
||||
export type EmojiProps = {
|
||||
name: string;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { Icon } from "element/icon";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
interface ExternalLinkProps {
|
||||
href: string;
|
||||
|
@ -79,15 +79,15 @@ export function FileUploader({ defaultImage, onClear, onFileUpload }: FileUpload
|
||||
<label className="file-uploader">
|
||||
<input type="file" onChange={onFileChange} />
|
||||
{isUploading ? (
|
||||
<FormattedMessage defaultMessage="Uploading..." />
|
||||
<FormattedMessage defaultMessage="Uploading..." id="JEsxDw" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Add File" />
|
||||
<FormattedMessage defaultMessage="Add File" id="fc2iho" />
|
||||
)}
|
||||
</label>
|
||||
<div className="file-uploader-preview">
|
||||
{img?.length > 0 && (
|
||||
<button className="btn btn-primary clear-button" onClick={clearImage}>
|
||||
<FormattedMessage defaultMessage="Clear" />
|
||||
<FormattedMessage defaultMessage="Clear" id="/GCoTA" />
|
||||
</button>
|
||||
)}
|
||||
{img && <img className="image-preview" src={img} />}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { EventKind } from "@snort/system";
|
||||
|
||||
import { useLogin } from "hooks/login";
|
||||
import AsyncButton from "element/async-button";
|
||||
import { Login } from "index";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useContext } from "react";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import AsyncButton from "./async-button";
|
||||
import { Login } from "@/index";
|
||||
|
||||
export function LoggedInFollowButton({ tag, value }: { tag: "p" | "t"; value: string }) {
|
||||
const system = useContext(SnortContext);
|
||||
const login = useLogin();
|
||||
@ -56,7 +56,11 @@ export function LoggedInFollowButton({ tag, value }: { tag: "p" | "t"; value: st
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={isFollowing ? unfollow : follow}>
|
||||
{isFollowing ? <FormattedMessage defaultMessage="Unfollow" /> : <FormattedMessage defaultMessage="Follow" />}
|
||||
{isFollowing ? (
|
||||
<FormattedMessage defaultMessage="Unfollow" id="izWS4J" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Follow" id="ieGrWo" />
|
||||
)}
|
||||
</AsyncButton>
|
||||
);
|
||||
}
|
||||
|
@ -2,18 +2,18 @@ import "./goal.css";
|
||||
import { useMemo } from "react";
|
||||
import * as Progress from "@radix-ui/react-progress";
|
||||
import Confetti from "react-confetti";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { NostrLink, type NostrEvent } from "@snort/system";
|
||||
import { type NostrEvent, NostrLink } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
import { findTag } from "utils";
|
||||
import { formatSats } from "number";
|
||||
import usePreviousValue from "hooks/usePreviousValue";
|
||||
import { SendZapsDialog } from "element/send-zap";
|
||||
import { getName } from "element/profile";
|
||||
import { findTag } from "@/utils";
|
||||
import { formatSats } from "@/number";
|
||||
import usePreviousValue from "@/hooks/usePreviousValue";
|
||||
import { SendZapsDialog } from "./send-zap";
|
||||
import { getName } from "./profile";
|
||||
import { Icon } from "./icon";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useZaps } from "hooks/zaps";
|
||||
import { useZaps } from "@/hooks/zaps";
|
||||
|
||||
export function Goal({ ev }: { ev: NostrEvent }) {
|
||||
const profile = useUserProfile(ev.pubkey);
|
||||
@ -48,7 +48,7 @@ export function Goal({ ev }: { ev: NostrEvent }) {
|
||||
{!isFinished && <span className="amount so-far">{formatSats(soFar)}</span>}
|
||||
</Progress.Indicator>
|
||||
<span className="amount target">
|
||||
<FormattedMessage defaultMessage="Goal: {amount}" values={{ amount: formatSats(goalAmount) }} />
|
||||
<FormattedMessage defaultMessage="Goal: {amount}" id="QceMQZ" values={{ amount: formatSats(goalAmount) }} />
|
||||
</span>
|
||||
</Progress.Root>
|
||||
<div className="zap-circle">
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { NostrLink } from "element/nostr-link";
|
||||
import { MediaURL } from "element/collapsible";
|
||||
import { NostrLink } from "./nostr-link";
|
||||
import { MediaURL } from "./collapsible";
|
||||
|
||||
const FileExtensionRegex = /\.([\w]+)$/i;
|
||||
|
||||
|
@ -1,29 +1,29 @@
|
||||
import "./live-chat.css";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { EventKind, NostrLink, ParsedZap, NostrEvent } from "@snort/system";
|
||||
import { EventKind, NostrEvent, NostrLink, ParsedZap } from "@snort/system";
|
||||
import { useEventReactions } from "@snort/system-react";
|
||||
import { unixNow } from "@snort/shared";
|
||||
import { useMemo } from "react";
|
||||
import uniqBy from "lodash.uniqby";
|
||||
|
||||
import { Icon } from "element/icon";
|
||||
import Spinner from "element/spinner";
|
||||
import { Text } from "element/text";
|
||||
import { Profile } from "element/profile";
|
||||
import { ChatMessage } from "element/chat-message";
|
||||
import { Goal } from "element/goal";
|
||||
import { Badge } from "element/badge";
|
||||
import { WriteMessage } from "element/write-message";
|
||||
import useEmoji, { packId } from "hooks/emoji";
|
||||
import { useLiveChatFeed } from "hooks/live-chat";
|
||||
import { useMutedPubkeys } from "hooks/lists";
|
||||
import { useBadges } from "hooks/badges";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { useAddress } from "hooks/event";
|
||||
import { formatSats } from "number";
|
||||
import { WEEK, LIVE_STREAM_CHAT } from "const";
|
||||
import { findTag, getTagValues, getHost } from "utils";
|
||||
import { TopZappers } from "element/top-zappers";
|
||||
import { Icon } from "./icon";
|
||||
import Spinner from "./spinner";
|
||||
import { Text } from "./text";
|
||||
import { Profile } from "./profile";
|
||||
import { ChatMessage } from "./chat-message";
|
||||
import { Goal } from "./goal";
|
||||
import { Badge } from "./badge";
|
||||
import { WriteMessage } from "./write-message";
|
||||
import useEmoji, { packId } from "@/hooks/emoji";
|
||||
import { useLiveChatFeed } from "@/hooks/live-chat";
|
||||
import { useMutedPubkeys } from "@/hooks/lists";
|
||||
import { useBadges } from "@/hooks/badges";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { useAddress } from "@/hooks/event";
|
||||
import { formatSats } from "@/number";
|
||||
import { LIVE_STREAM_CHAT, WEEK } from "@/const";
|
||||
import { findTag, getHost, getTagValues } from "@/utils";
|
||||
import { TopZappers } from "./top-zappers";
|
||||
|
||||
export interface LiveChatOptions {
|
||||
canWrite?: boolean;
|
||||
@ -95,7 +95,7 @@ export function LiveChat({
|
||||
{(options?.showHeader ?? true) && (
|
||||
<div className="header">
|
||||
<h2 className="title">
|
||||
<FormattedMessage defaultMessage="Stream Chat" />
|
||||
<FormattedMessage defaultMessage="Stream Chat" id="BGxpTN" />
|
||||
</h2>
|
||||
<Icon
|
||||
name="link"
|
||||
@ -108,7 +108,7 @@ export function LiveChat({
|
||||
{reactions.zaps.length > 0 && (
|
||||
<div className="top-zappers">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Top zappers" />
|
||||
<FormattedMessage defaultMessage="Top zappers" id="wzWWzV" />
|
||||
</h3>
|
||||
<div className="top-zappers-container">
|
||||
<TopZappers zaps={reactions.zaps} />
|
||||
@ -151,7 +151,7 @@ export function LiveChat({
|
||||
<WriteMessage emojiPacks={allEmojiPacks} link={link} />
|
||||
) : (
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Please login to write messages!" />
|
||||
<FormattedMessage defaultMessage="Please login to write messages!" id="RXQdxR" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@ -174,6 +174,7 @@ function ChatZap({ zap }: { zap: ParsedZap }) {
|
||||
<Icon name="zap-filled" className="zap-icon" />
|
||||
<FormattedMessage
|
||||
defaultMessage="{person} zapped {amount} sats"
|
||||
id="AIHaPH"
|
||||
values={{
|
||||
person: (
|
||||
<Profile
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Hls from "hls.js";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { WISH } from "wish";
|
||||
import { WISH } from "@/wish";
|
||||
|
||||
export enum VideoStatus {
|
||||
Online = "online",
|
||||
|
@ -20,13 +20,13 @@ import { VoidApi } from "@void-cat/api";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import AsyncButton from "./async-button";
|
||||
import { Login } from "index";
|
||||
import { Login } from "@/index";
|
||||
import { Icon } from "./icon";
|
||||
import Copy from "./copy";
|
||||
import { openFile } from "utils";
|
||||
import { LoginType } from "login";
|
||||
import { DefaultProvider, StreamProviderInfo } from "providers";
|
||||
import { Nip103StreamProvider } from "providers/zsz";
|
||||
import { openFile } from "@/utils";
|
||||
import { LoginType } from "@/login";
|
||||
import { DefaultProvider, StreamProviderInfo } from "@/providers";
|
||||
import { Nip103StreamProvider } from "@/providers/zsz";
|
||||
|
||||
enum Stage {
|
||||
Login = 0,
|
||||
@ -126,6 +126,7 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
throw new Error(
|
||||
formatMessage({
|
||||
defaultMessage: "Hmm, your lightning address looks wrong",
|
||||
id: "4l69eO",
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -157,29 +158,29 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
<img src={LoginHeader as string} srcSet={`${LoginHeader2x} 2x`} className="header-image" />
|
||||
<div className="content-inner">
|
||||
<h2>
|
||||
<FormattedMessage defaultMessage="Create an Account" />
|
||||
<FormattedMessage defaultMessage="Create an Account" id="u6uD94" />
|
||||
</h2>
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="No emails, just awesomeness!" />
|
||||
<FormattedMessage defaultMessage="No emails, just awesomeness!" id="+AcVD+" />
|
||||
</h3>
|
||||
<button type="button" className="btn btn-primary btn-block" onClick={createAccount}>
|
||||
<FormattedMessage defaultMessage="Create Account" />
|
||||
<FormattedMessage defaultMessage="Create Account" id="5JcXdV" />
|
||||
</button>
|
||||
|
||||
<div className="or-divider">
|
||||
<hr />
|
||||
<FormattedMessage defaultMessage="OR" />
|
||||
<FormattedMessage defaultMessage="OR" id="INlWvJ" />
|
||||
<hr />
|
||||
</div>
|
||||
{hasNostrExtension && (
|
||||
<>
|
||||
<AsyncButton type="button" className="btn btn-primary btn-block" onClick={loginNip7}>
|
||||
<FormattedMessage defaultMessage="Nostr Extension" />
|
||||
<FormattedMessage defaultMessage="Nostr Extension" id="ebmhes" />
|
||||
</AsyncButton>
|
||||
</>
|
||||
)}
|
||||
<button type="button" className="btn btn-primary btn-block" onClick={() => setStage(Stage.LoginInput)}>
|
||||
<FormattedMessage defaultMessage="Login with Private Key (insecure)" />
|
||||
<FormattedMessage defaultMessage="Login with Private Key (insecure)" id="feZ/kG" />
|
||||
</button>
|
||||
{error && <b className="error">{error}</b>}
|
||||
</div>
|
||||
@ -192,15 +193,16 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
<img src={LoginVault as string} srcSet={`${LoginVault2x} 2x`} className="header-image" />
|
||||
<div className="content-inner">
|
||||
<h2>
|
||||
<FormattedMessage defaultMessage="Login with private key" />
|
||||
<FormattedMessage defaultMessage="Login with private key" id="3df560" />
|
||||
</h2>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="This method is insecure. We recommend using a {nostrlink}"
|
||||
id="Z8ZOEY"
|
||||
values={{
|
||||
nostrlink: (
|
||||
<a href="">
|
||||
<FormattedMessage defaultMessage="nostr signer extension" />
|
||||
<FormattedMessage defaultMessage="nostr signer extension" id="/EvlqN" />
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
@ -211,7 +213,7 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
type="text"
|
||||
value={key}
|
||||
onChange={e => setNewKey(e.target.value)}
|
||||
placeholder={formatMessage({ defaultMessage: "eg. nsec1xyz" })}
|
||||
placeholder={formatMessage({ defaultMessage: "eg. nsec1xyz", id: "yzKwBQ" })}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex f-space">
|
||||
@ -224,10 +226,10 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
setNewKey("");
|
||||
setStage(Stage.Login);
|
||||
}}>
|
||||
<FormattedMessage defaultMessage="Cancel" />
|
||||
<FormattedMessage defaultMessage="Cancel" id="47FYwb" />
|
||||
</button>
|
||||
<AsyncButton onClick={doLoginNsec} className="btn btn-primary">
|
||||
<FormattedMessage defaultMessage="Log In" />
|
||||
<FormattedMessage defaultMessage="Log In" id="r2Jjms" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</div>
|
||||
@ -242,7 +244,7 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
<img src={LoginProfile as string} srcSet={`${LoginProfile2x} 2x`} className="header-image" />
|
||||
<div className="content-inner">
|
||||
<h2>
|
||||
<FormattedMessage defaultMessage="Setup Profile" />
|
||||
<FormattedMessage defaultMessage="Setup Profile" id="nOaArs" />
|
||||
</h2>
|
||||
<div className="flex f-center">
|
||||
<div
|
||||
@ -266,11 +268,11 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
/>
|
||||
</div>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="You can change this later" />
|
||||
<FormattedMessage defaultMessage="You can change this later" id="ZmqxZs" />
|
||||
</small>
|
||||
</div>
|
||||
<AsyncButton type="button" className="btn btn-primary" onClick={setupProfile}>
|
||||
<FormattedMessage defaultMessage="Save" />
|
||||
<FormattedMessage defaultMessage="Save" id="jvo0vs" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</>
|
||||
@ -282,15 +284,19 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
<img src={LoginWallet as string} srcSet={`${LoginWallet2x} 2x`} className="header-image" />
|
||||
<div className="content-inner">
|
||||
<h2>
|
||||
<FormattedMessage defaultMessage="Get paid by viewers" />
|
||||
<FormattedMessage defaultMessage="Get paid by viewers" id="Fodi9+" />
|
||||
</h2>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="We hooked you up with a lightning wallet so you can get paid by viewers right away!" />
|
||||
<FormattedMessage
|
||||
defaultMessage="We hooked you up with a lightning wallet so you can get paid by viewers right away!"
|
||||
id="Oxqtyf"
|
||||
/>
|
||||
</p>
|
||||
{providerInfo?.balance && (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Oh, and you have {n} sats of free streaming on us! 💜"
|
||||
id="f6biFA"
|
||||
values={{
|
||||
n: <FormattedNumber value={providerInfo.balance} />,
|
||||
}}
|
||||
@ -301,18 +307,18 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
<div className="paper">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={formatMessage({ defaultMessage: "eg. name@wallet.com" })}
|
||||
placeholder={formatMessage({ defaultMessage: "eg. name@wallet.com", id: "1qsXCO" })}
|
||||
value={lnAddress}
|
||||
onChange={e => setLnAddress(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="You can always replace it with your own address later." />
|
||||
<FormattedMessage defaultMessage="You can always replace it with your own address later." id="FjDlus" />
|
||||
</small>
|
||||
</div>
|
||||
{error && <b className="error">{error}</b>}
|
||||
<AsyncButton type="button" className="btn btn-primary" onClick={saveProfile}>
|
||||
<FormattedMessage defaultMessage="Amazing! Continue.." />
|
||||
<FormattedMessage defaultMessage="Amazing! Continue.." id="tM6fNW" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</>
|
||||
@ -324,16 +330,19 @@ export function LoginSignup({ close }: { close: () => void }) {
|
||||
<img src={LoginKey as string} srcSet={`${LoginKey2x} 2x`} className="header-image" />
|
||||
<div className="content-inner">
|
||||
<h2>
|
||||
<FormattedMessage defaultMessage="Save Key" />
|
||||
<FormattedMessage defaultMessage="Save Key" id="04lmFi" />
|
||||
</h2>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Save this and keep it safe! If you lose this key, you won't be able to access your account ever again. Yep, it's that serious!" />
|
||||
<FormattedMessage
|
||||
defaultMessage="Save this and keep it safe! If you lose this key, you won't be able to access your account ever again. Yep, it's that serious!"
|
||||
id="H/bNs9"
|
||||
/>
|
||||
</p>
|
||||
<div className="paper">
|
||||
<Copy text={hexToBech32("nsec", key)} />
|
||||
</div>
|
||||
<button type="button" className="btn btn-primary" onClick={loginWithKey}>
|
||||
<FormattedMessage defaultMessage="Ok, it's safe" />
|
||||
<FormattedMessage defaultMessage="Ok, it's safe" id="My6HwN" />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import "./markdown.css";
|
||||
|
||||
import { ReactNode, forwardRef, useMemo } from "react";
|
||||
import { marked, Token } from "marked";
|
||||
import { Token, marked } from "marked";
|
||||
import { HyperText } from "./hypertext";
|
||||
import { Text } from "./text";
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { useContext, useMemo } from "react";
|
||||
import { useLogin } from "hooks/login";
|
||||
import AsyncButton from "element/async-button";
|
||||
import { Login } from "index";
|
||||
import { MUTED } from "const";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import AsyncButton from "./async-button";
|
||||
import { Login } from "@/index";
|
||||
import { MUTED } from "@/const";
|
||||
|
||||
export function useMute(pubkey: string) {
|
||||
const system = useContext(SnortContext);
|
||||
const login = useLogin();
|
||||
@ -55,7 +56,11 @@ export function LoggedInMuteButton({ pubkey }: { pubkey: string }) {
|
||||
|
||||
return (
|
||||
<AsyncButton type="button" className="btn delete-button" onClick={() => (isMuted ? unmute() : mute())}>
|
||||
{isMuted ? <FormattedMessage defaultMessage="Unmute" /> : <FormattedMessage defaultMessage="Mute" />}
|
||||
{isMuted ? (
|
||||
<FormattedMessage defaultMessage="Unmute" id="W9355R" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Mute" id="x82IOl" />
|
||||
)}
|
||||
</AsyncButton>
|
||||
);
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import "./new-goal.css";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useContext, useState } from "react";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import AsyncButton from "./async-button";
|
||||
import { Icon } from "element/icon";
|
||||
import { useContext, useState } from "react";
|
||||
import { GOAL } from "const";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { defaultRelays } from "const";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
import { Icon } from "./icon";
|
||||
import { GOAL } from "@/const";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { defaultRelays } from "@/const";
|
||||
|
||||
export function NewGoalDialog() {
|
||||
const system = useContext(SnortContext);
|
||||
@ -44,7 +44,7 @@ export function NewGoalDialog() {
|
||||
<span>
|
||||
<Icon name="zap-filled" size={12} />
|
||||
<span>
|
||||
<FormattedMessage defaultMessage="Add stream goal" />
|
||||
<FormattedMessage defaultMessage="Add stream goal" id="wOy57k" />
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
@ -56,12 +56,12 @@ export function NewGoalDialog() {
|
||||
<div className="zap-goals">
|
||||
<Icon name="zap-filled" className="stream-zap-goals-icon" size={16} />
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Stream Zap Goals" />
|
||||
<FormattedMessage defaultMessage="Stream Zap Goals" id="0GfNiL" />
|
||||
</h3>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Name" />
|
||||
<FormattedMessage defaultMessage="Name" id="HAlOn1" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<input
|
||||
@ -74,7 +74,7 @@ export function NewGoalDialog() {
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Amount" />
|
||||
<FormattedMessage defaultMessage="Amount" id="/0TOL5" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<input
|
||||
@ -89,7 +89,7 @@ export function NewGoalDialog() {
|
||||
</div>
|
||||
<div className="create-goal">
|
||||
<AsyncButton type="button" className="btn btn-primary wide" disabled={!isValid} onClick={publishGoal}>
|
||||
<FormattedMessage defaultMessage="Create Goal" />
|
||||
<FormattedMessage defaultMessage="Create Goal" id="X2PZ7D" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,18 +1,18 @@
|
||||
import "./new-stream.css";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
|
||||
import { Icon } from "element/icon";
|
||||
import { useStreamProvider } from "hooks/stream-provider";
|
||||
import { StreamProvider, StreamProviders } from "providers";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { StreamEditor, StreamEditorProps } from "./stream-editor";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { eventLink, findTag } from "utils";
|
||||
import { NostrProviderDialog } from "./nostr-provider-dialog";
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import { Icon } from "./icon";
|
||||
import { useStreamProvider } from "@/hooks/stream-provider";
|
||||
import { StreamProvider, StreamProviders } from "@/providers";
|
||||
import { StreamEditor, StreamEditorProps } from "./stream-editor";
|
||||
import { eventLink, findTag } from "@/utils";
|
||||
import { NostrProviderDialog } from "./nostr-provider-dialog";
|
||||
|
||||
function NewStream({ ev, onFinish }: StreamEditorProps) {
|
||||
const system = useContext(SnortContext);
|
||||
const providers = useStreamProvider();
|
||||
@ -65,7 +65,7 @@ function NewStream({ ev, onFinish }: StreamEditorProps) {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Stream Providers" />
|
||||
<FormattedMessage defaultMessage="Stream Providers" id="6Z2pvJ" />
|
||||
</p>
|
||||
<div className="flex g12">
|
||||
{providers.map(v => (
|
||||
@ -94,7 +94,7 @@ export function NewStreamDialog(props: NewStreamDialogProps & StreamEditorProps)
|
||||
{!props.text && (
|
||||
<>
|
||||
<span className="hide-on-mobile">
|
||||
<FormattedMessage defaultMessage="Stream" />
|
||||
<FormattedMessage defaultMessage="Stream" id="uYw2LD" />
|
||||
</span>
|
||||
<Icon name="signal" />
|
||||
</>
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { NostrEvent } from "@snort/system";
|
||||
import { StreamProvider, StreamProviderEndpoint, StreamProviderInfo } from "providers";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import { StreamProvider, StreamProviderEndpoint, StreamProviderInfo } from "@/providers";
|
||||
import { SendZaps } from "./send-zap";
|
||||
import { StreamEditor, StreamEditorProps } from "./stream-editor";
|
||||
import Spinner from "./spinner";
|
||||
import AsyncButton from "./async-button";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
export function NostrProviderDialog({ provider, ...others }: { provider: StreamProvider } & StreamEditorProps) {
|
||||
const system = useContext(SnortContext);
|
||||
@ -92,13 +93,14 @@ export function NostrProviderDialog({ provider, ...others }: { provider: StreamP
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="I have read and agree with {provider}'s {terms}."
|
||||
id="RJOmzk"
|
||||
values={{
|
||||
provider: info.name,
|
||||
terms: (
|
||||
<span
|
||||
className="tos-link"
|
||||
onClick={() => window.open(info.tosLink, "popup", "width=400,height=800")}>
|
||||
<FormattedMessage defaultMessage="terms and conditions" />
|
||||
<FormattedMessage defaultMessage="terms and conditions" id="thsiMl" />
|
||||
</span>
|
||||
),
|
||||
}}
|
||||
@ -108,7 +110,7 @@ export function NostrProviderDialog({ provider, ...others }: { provider: StreamP
|
||||
</div>
|
||||
<div>
|
||||
<AsyncButton type="button" className="btn btn-primary wide" disabled={!tos} onClick={acceptTos}>
|
||||
<FormattedMessage defaultMessage="Continue" />
|
||||
<FormattedMessage defaultMessage="Continue" id="acrOoz" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</>
|
||||
@ -120,7 +122,7 @@ export function NostrProviderDialog({ provider, ...others }: { provider: StreamP
|
||||
{info.endpoints.length > 1 && (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Endpoint" />
|
||||
<FormattedMessage defaultMessage="Endpoint" id="ljmS5P" />
|
||||
</p>
|
||||
<div className="flex g12">
|
||||
{sortEndpoints(info.endpoints).map(a => (
|
||||
@ -133,7 +135,7 @@ export function NostrProviderDialog({ provider, ...others }: { provider: StreamP
|
||||
)}
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Server Url" />
|
||||
<FormattedMessage defaultMessage="Server Url" id="5kx+2v" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<input type="text" value={ep?.url} disabled />
|
||||
@ -141,36 +143,40 @@ export function NostrProviderDialog({ provider, ...others }: { provider: StreamP
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Stream Key" />
|
||||
<FormattedMessage defaultMessage="Stream Key" id="LknBsU" />
|
||||
</p>
|
||||
<div className="flex g12">
|
||||
<div className="paper f-grow">
|
||||
<input type="password" value={ep?.key} disabled />
|
||||
</div>
|
||||
<button className="btn btn-primary" onClick={() => window.navigator.clipboard.writeText(ep?.key ?? "")}>
|
||||
<FormattedMessage defaultMessage="Copy" />
|
||||
<FormattedMessage defaultMessage="Copy" id="4l6vz1" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Balance" />
|
||||
<FormattedMessage defaultMessage="Balance" id="H5+NAX" />
|
||||
</p>
|
||||
<div className="flex g12">
|
||||
<div className="paper f-grow">
|
||||
<FormattedMessage defaultMessage="{amount} sats" values={{ amount: info.balance?.toLocaleString() }} />
|
||||
<FormattedMessage
|
||||
defaultMessage="{amount} sats"
|
||||
id="vrTOHJ"
|
||||
values={{ amount: info.balance?.toLocaleString() }}
|
||||
/>
|
||||
</div>
|
||||
<button className="btn btn-primary" onClick={() => setTopup(true)}>
|
||||
<FormattedMessage defaultMessage="Topup" />
|
||||
<FormattedMessage defaultMessage="Topup" id="nBCvvJ" />
|
||||
</button>
|
||||
</div>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="About {estimate}" values={{ estimate: calcEstimate() }} />
|
||||
<FormattedMessage defaultMessage="About {estimate}" id="Q3au2v" values={{ estimate: calcEstimate() }} />
|
||||
</small>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Resolutions" />
|
||||
<FormattedMessage defaultMessage="Resolutions" id="4uI538" />
|
||||
</p>
|
||||
<div className="flex g12">
|
||||
{ep?.capabilities?.map(a => (
|
||||
|
@ -1,11 +1,11 @@
|
||||
import "./note.css";
|
||||
import { type NostrEvent, NostrPrefix } from "@snort/system";
|
||||
|
||||
import { Markdown } from "element/markdown";
|
||||
import { ExternalIconLink } from "element/external-link";
|
||||
import { Profile } from "element/profile";
|
||||
import { hexToBech32 } from "@snort/shared";
|
||||
|
||||
import { Markdown } from "./markdown";
|
||||
import { ExternalIconLink } from "./external-link";
|
||||
import { Profile } from "./profile";
|
||||
|
||||
export function Note({ ev }: { ev: NostrEvent }) {
|
||||
return (
|
||||
<div className="surface note">
|
||||
|
@ -4,11 +4,11 @@ import { Link } from "react-router-dom";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import { UserMetadata } from "@snort/system";
|
||||
import { hexToBech32 } from "@snort/shared";
|
||||
|
||||
import { Icon } from "element/icon";
|
||||
import usePlaceholder from "hooks/placeholders";
|
||||
import { useInView } from "react-intersection-observer";
|
||||
|
||||
import { Icon } from "./icon";
|
||||
import usePlaceholder from "@/hooks/placeholders";
|
||||
|
||||
export interface ProfileOptions {
|
||||
showName?: boolean;
|
||||
showAvatar?: boolean;
|
||||
|
@ -1,20 +1,20 @@
|
||||
import "./send-zap.css";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { type ReactNode, useEffect, useState } from "react";
|
||||
import { LNURL } from "@snort/shared";
|
||||
import { NostrEvent, EventPublisher } from "@snort/system";
|
||||
import { EventPublisher, NostrEvent } from "@snort/system";
|
||||
import { secp256k1 } from "@noble/curves/secp256k1";
|
||||
import { bytesToHex } from "@noble/curves/abstract/utils";
|
||||
import { FormattedMessage, FormattedNumber } from "react-intl";
|
||||
|
||||
import { formatSats } from "../number";
|
||||
import { Icon } from "./icon";
|
||||
import AsyncButton from "./async-button";
|
||||
import QrCode from "./qr-code";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import Copy from "./copy";
|
||||
import { defaultRelays } from "const";
|
||||
import { FormattedMessage, FormattedNumber } from "react-intl";
|
||||
import { useRates } from "hooks/rates";
|
||||
import { defaultRelays } from "@/const";
|
||||
import { useRates } from "@/hooks/rates";
|
||||
|
||||
export interface LNURLLike {
|
||||
get name(): string;
|
||||
@ -131,6 +131,7 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
|
||||
<small>
|
||||
<FormattedMessage
|
||||
defaultMessage="Zap amount in {currency}"
|
||||
id="IJDKz3"
|
||||
values={{ currency: isFiat ? "USD" : "SATS" }}
|
||||
/>
|
||||
{isFiat && (
|
||||
@ -138,6 +139,7 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
|
||||
|
||||
<FormattedMessage
|
||||
defaultMessage="@ {rate}"
|
||||
id="YPh5Nq"
|
||||
description="Showing zap amount in USD @ rate"
|
||||
values={{
|
||||
rate: <FormattedNumber value={usdRate} />,
|
||||
@ -157,7 +159,7 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
|
||||
{svc && (svc.maxCommentLength > 0 || svc.canZap) && (
|
||||
<div>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="Your comment for {name}" values={{ name }} />
|
||||
<FormattedMessage defaultMessage="Your comment for {name}" id="ESyhzp" values={{ name }} />
|
||||
</small>
|
||||
<div className="paper">
|
||||
<textarea placeholder="Nice!" value={comment} onChange={e => setComment(e.target.value)} />
|
||||
@ -166,7 +168,7 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
|
||||
)}
|
||||
<div>
|
||||
<AsyncButton onClick={send} className="btn btn-primary">
|
||||
<FormattedMessage defaultMessage="Zap!" />
|
||||
<FormattedMessage defaultMessage="Zap!" id="3HwrQo" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</>
|
||||
@ -184,7 +186,7 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
|
||||
<Copy text={invoice} />
|
||||
</div>
|
||||
<button className="btn btn-primary wide" onClick={() => onFinish()}>
|
||||
<FormattedMessage defaultMessage="Back" />
|
||||
<FormattedMessage defaultMessage="Back" id="cyR7Kh" />
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
@ -193,7 +195,7 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
|
||||
return (
|
||||
<div className="send-zap">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Zap {name}" values={{ name }} />
|
||||
<FormattedMessage defaultMessage="Zap {name}" id="oHPB8Q" values={{ name }} />
|
||||
<Icon name="zap" />
|
||||
</h3>
|
||||
{input()}
|
||||
@ -212,7 +214,7 @@ export function SendZapsDialog(props: Omit<SendZapsProps, "onFinish">) {
|
||||
) : (
|
||||
<button className="btn btn-primary zap">
|
||||
<span className="hide-on-mobile">
|
||||
<FormattedMessage defaultMessage="Zap" />
|
||||
<FormattedMessage defaultMessage="Zap" id="fBI91o" />
|
||||
</span>
|
||||
<Icon name="zap-filled" size={16} />
|
||||
</button>
|
||||
|
@ -2,15 +2,15 @@ import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useContext, useState } from "react";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import { Icon } from "./icon";
|
||||
import { useContext, useState } from "react";
|
||||
import { Textarea } from "./textarea";
|
||||
import { findTag } from "utils";
|
||||
import { findTag } from "@/utils";
|
||||
import AsyncButton from "./async-button";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
|
||||
type ShareOn = "nostr" | "twitter";
|
||||
|
||||
@ -41,7 +41,7 @@ export function ShareMenu({ ev }: { ev: NostrEvent }) {
|
||||
menuClassName="ctx-menu"
|
||||
menuButton={
|
||||
<button type="button" className="btn btn-secondary">
|
||||
<FormattedMessage defaultMessage="Share" />
|
||||
<FormattedMessage defaultMessage="Share" id="OKhRC6" />
|
||||
</button>
|
||||
}>
|
||||
<MenuItem
|
||||
@ -50,7 +50,7 @@ export function ShareMenu({ ev }: { ev: NostrEvent }) {
|
||||
setShare("nostr");
|
||||
}}>
|
||||
<Icon name="nostrich" size={24} />
|
||||
<FormattedMessage defaultMessage="Broadcast on Nostr" />
|
||||
<FormattedMessage defaultMessage="Broadcast on Nostr" id="wCIL7o" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Dialog.Root open={Boolean(share)} onOpenChange={() => setShare(undefined)}>
|
||||
@ -59,7 +59,7 @@ export function ShareMenu({ ev }: { ev: NostrEvent }) {
|
||||
<Dialog.Content className="dialog-content">
|
||||
<div className="content-inner">
|
||||
<h2>
|
||||
<FormattedMessage defaultMessage="Share" />
|
||||
<FormattedMessage defaultMessage="Share" id="OKhRC6" />
|
||||
</h2>
|
||||
<div className="paper">
|
||||
<Textarea
|
||||
@ -73,7 +73,7 @@ export function ShareMenu({ ev }: { ev: NostrEvent }) {
|
||||
/>
|
||||
</div>
|
||||
<AsyncButton className="btn btn-primary" onClick={sendMessage}>
|
||||
<FormattedMessage defaultMessage="Send" />
|
||||
<FormattedMessage defaultMessage="Send" id="9WRlF4" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import "./state-pill.css";
|
||||
import { StreamState } from "index";
|
||||
import { StreamState } from "@/index";
|
||||
|
||||
export function StatePill({ state }: { state: StreamState }) {
|
||||
return <span className={`state pill${state === StreamState.Live ? " live" : ""}`}>{state}</span>;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "./stream-cards.css";
|
||||
|
||||
import { useState, forwardRef, useContext } from "react";
|
||||
import { forwardRef, useContext, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import { DndProvider, useDrag, useDrop } from "react-dnd";
|
||||
@ -8,20 +8,20 @@ import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
|
||||
import { removeUndefined, unwrap } from "@snort/shared";
|
||||
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||
|
||||
import { Toggle } from "element/toggle";
|
||||
import { Icon } from "element/icon";
|
||||
import { ExternalLink } from "element/external-link";
|
||||
import { FileUploader } from "element/file-uploader";
|
||||
import { Markdown } from "element/markdown";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { useCards, useUserCards } from "hooks/cards";
|
||||
import { CARD, USER_CARDS } from "const";
|
||||
import { findTag } from "utils";
|
||||
import { Login } from "index";
|
||||
import type { Tags } from "types";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
|
||||
import { Toggle } from "./toggle";
|
||||
import { Icon } from "./icon";
|
||||
import { ExternalLink } from "./external-link";
|
||||
import { FileUploader } from "./file-uploader";
|
||||
import { Markdown } from "./markdown";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { useCards, useUserCards } from "@/hooks/cards";
|
||||
import { CARD, USER_CARDS } from "@/const";
|
||||
import { findTag } from "@/utils";
|
||||
import { Login } from "@/index";
|
||||
import type { Tags } from "@/types";
|
||||
|
||||
interface CardType {
|
||||
identifier: string;
|
||||
content: string;
|
||||
@ -187,28 +187,28 @@ function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDial
|
||||
|
||||
return (
|
||||
<div className="new-card">
|
||||
<h3>{header || <FormattedMessage defaultMessage="Add card" />}</h3>
|
||||
<h3>{header || <FormattedMessage defaultMessage="Add card" id="nwA8Os" />}</h3>
|
||||
<div className="form-control">
|
||||
<label htmlFor="card-title">
|
||||
<FormattedMessage defaultMessage="Title" />
|
||||
<FormattedMessage defaultMessage="Title" id="9a9+ww" />
|
||||
</label>
|
||||
<input
|
||||
id="card-title"
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
placeholder={formatMessage({ defaultMessage: "e.g. about me" })}
|
||||
placeholder={formatMessage({ defaultMessage: "e.g. about me", id: "k21gTS" })}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label htmlFor="card-image">
|
||||
<FormattedMessage defaultMessage="Image" />
|
||||
<FormattedMessage defaultMessage="Image" id="+0zv6g" />
|
||||
</label>
|
||||
<FileUploader defaultImage={image} onFileUpload={setImage} onClear={() => setImage("")} />
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label htmlFor="card-image-link">
|
||||
<FormattedMessage defaultMessage="Image Link" />
|
||||
<FormattedMessage defaultMessage="Image Link" id="s5ksS7" />
|
||||
</label>
|
||||
<input
|
||||
id="card-image-link"
|
||||
@ -220,20 +220,21 @@ function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDial
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label htmlFor="card-content">
|
||||
<FormattedMessage defaultMessage="Content" />
|
||||
<FormattedMessage defaultMessage="Content" id="Jq3FDz" />
|
||||
</label>
|
||||
<textarea
|
||||
placeholder={formatMessage({ defaultMessage: "Start typing" })}
|
||||
placeholder={formatMessage({ defaultMessage: "Start typing", id: "w0Xm2F" })}
|
||||
value={content}
|
||||
onChange={e => setContent(e.target.value)}
|
||||
/>
|
||||
<span className="help-text">
|
||||
<FormattedMessage
|
||||
defaultMessage="Supports {markdown}"
|
||||
id="I1kjHI"
|
||||
values={{
|
||||
markdown: (
|
||||
<ExternalLink href="https://www.markdownguide.org/cheat-sheet">
|
||||
<FormattedMessage defaultMessage="Markdown" />
|
||||
<FormattedMessage defaultMessage="Markdown" id="jr4+vD" />
|
||||
</ExternalLink>
|
||||
),
|
||||
}}
|
||||
@ -242,10 +243,10 @@ function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDial
|
||||
</div>
|
||||
<div className="new-card-buttons">
|
||||
<button className="btn btn-primary add-button" onClick={() => onSave({ title, image, content, link })}>
|
||||
{cta || <FormattedMessage defaultMessage="Add Card" />}
|
||||
{cta || <FormattedMessage defaultMessage="Add Card" id="UJBFYK" />}
|
||||
</button>
|
||||
<button className="btn delete-button" onClick={onCancel}>
|
||||
{cancelCta || <FormattedMessage defaultMessage="Cancel" />}
|
||||
{cancelCta || <FormattedMessage defaultMessage="Cancel" id="47FYwb" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -310,7 +311,7 @@ function EditCard({ card, cards }: EditCardProps) {
|
||||
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||
<Dialog.Trigger asChild>
|
||||
<button className="btn btn-primary">
|
||||
<FormattedMessage defaultMessage="Edit" />
|
||||
<FormattedMessage defaultMessage="Edit" id="wEQDC6" />
|
||||
</button>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
@ -318,9 +319,9 @@ function EditCard({ card, cards }: EditCardProps) {
|
||||
<Dialog.Content className="dialog-content">
|
||||
<div className="content-inner">
|
||||
<CardDialog
|
||||
header={formatMessage({ defaultMessage: "Edit card" })}
|
||||
cta={formatMessage({ defaultMessage: "Save card" })}
|
||||
cancelCta={formatMessage({ defaultMessage: "Delete" })}
|
||||
header={formatMessage({ defaultMessage: "Edit card", id: "OWgHbg" })}
|
||||
cta={formatMessage({ defaultMessage: "Save card", id: "rfC1Zq" })}
|
||||
cancelCta={formatMessage({ defaultMessage: "Delete", id: "K3r6DQ" })}
|
||||
card={card}
|
||||
onSave={editCard}
|
||||
onCancel={onCancel}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import "./stream-editor.css";
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { NostrEvent } from "@snort/system";
|
||||
import { unixNow } from "@snort/shared";
|
||||
import { TagsInput } from "react-tag-input-component";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import AsyncButton from "./async-button";
|
||||
import { StreamState } from "../index";
|
||||
import { findTag } from "../utils";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { NewGoalDialog } from "element/new-goal";
|
||||
import { useGoals } from "hooks/goals";
|
||||
import { StreamState } from "@/index";
|
||||
import { findTag } from "@/utils";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { NewGoalDialog } from "./new-goal";
|
||||
import { useGoals } from "@/hooks/goals";
|
||||
|
||||
export interface StreamEditorProps {
|
||||
ev?: NostrEvent;
|
||||
@ -37,9 +37,9 @@ function GoalSelector({ goal, pubkey, onGoalSelect }: GoalSelectorProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
return (
|
||||
<select onChange={ev => onGoalSelect(ev.target.value)}>
|
||||
<option value="">{formatMessage({ defaultMessage: "Select a goal..." })}</option>
|
||||
<option value={goal}>{formatMessage({ defaultMessage: "Select a goal...", id: "I/TubD" })}</option>
|
||||
{goals?.map(x => (
|
||||
<option key={x.id} value={x.id} selected={goal === x.id}>
|
||||
<option key={x.id} value={x.id}>
|
||||
{x.content}
|
||||
</option>
|
||||
))}
|
||||
@ -139,12 +139,12 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
{(options?.canSetTitle ?? true) && (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Title" />
|
||||
<FormattedMessage defaultMessage="Title" id="9a9+ww" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={formatMessage({ defaultMessage: "What are we steaming today?" })}
|
||||
placeholder={formatMessage({ defaultMessage: "What are we steaming today?", id: "QRHNuF" })}
|
||||
value={title}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
/>
|
||||
@ -154,12 +154,12 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
{(options?.canSetSummary ?? true) && (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Summary" />
|
||||
<FormattedMessage defaultMessage="Summary" id="RrCui3" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={formatMessage({ defaultMessage: "A short description of the content" })}
|
||||
placeholder={formatMessage({ defaultMessage: "A short description of the content", id: "mtNGwh" })}
|
||||
value={summary}
|
||||
onChange={e => setSummary(e.target.value)}
|
||||
/>
|
||||
@ -169,7 +169,7 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
{(options?.canSetImage ?? true) && (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Cover Image" />
|
||||
<FormattedMessage defaultMessage="Cover Image" id="Gq6x9o" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<input type="text" placeholder="https://" value={image} onChange={e => setImage(e.target.value)} />
|
||||
@ -179,13 +179,13 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
{(options?.canSetStream ?? true) && (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Stream URL" />
|
||||
<FormattedMessage defaultMessage="Stream URL" id="QRRCp0" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<input type="text" placeholder="https://" value={stream} onChange={e => setStream(e.target.value)} />
|
||||
</div>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="Stream type should be HLS" />
|
||||
<FormattedMessage defaultMessage="Stream type should be HLS" id="oZrFyI" />
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
@ -193,7 +193,7 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
<>
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Status" />
|
||||
<FormattedMessage defaultMessage="Status" id="tzMNF3" />
|
||||
</p>
|
||||
<div className="flex g12">
|
||||
{[StreamState.Live, StreamState.Planned, StreamState.Ended].map(v => (
|
||||
@ -206,7 +206,7 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
{status === StreamState.Planned && (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Start Time" />
|
||||
<FormattedMessage defaultMessage="Start Time" id="5QYdPU" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<input
|
||||
@ -222,7 +222,7 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
{(options?.canSetTags ?? true) && (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Tags" />
|
||||
<FormattedMessage defaultMessage="Tags" id="1EYCdR" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<TagsInput value={tags} onChange={setTags} placeHolder="Music,DJ,English" separators={["Enter", ","]} />
|
||||
@ -233,7 +233,7 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
<>
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Goal" />
|
||||
<FormattedMessage defaultMessage="Goal" id="0VV/sK" />
|
||||
</p>
|
||||
<div className="paper">
|
||||
<GoalSelector goal={goal} pubkey={login?.pubkey} onGoalSelect={setGoal} />
|
||||
@ -249,15 +249,22 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
||||
</div>
|
||||
<div>
|
||||
<div className="warning">
|
||||
<FormattedMessage defaultMessage="NSFW Content" />
|
||||
<FormattedMessage defaultMessage="NSFW Content" id="Atr2p4" />
|
||||
</div>
|
||||
<FormattedMessage defaultMessage="Check here if this stream contains nudity or pornographic content." />
|
||||
<FormattedMessage
|
||||
defaultMessage="Check here if this stream contains nudity or pornographic content."
|
||||
id="lZpRMR"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<AsyncButton type="button" className="btn btn-primary wide" disabled={!isValid} onClick={publishStream}>
|
||||
{ev ? <FormattedMessage defaultMessage="Save" /> : <FormattedMessage defaultMessage="Start Stream" />}
|
||||
{ev ? (
|
||||
<FormattedMessage defaultMessage="Save" id="jvo0vs" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Start Stream" id="TaTRKo" />
|
||||
)}
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { NostrEvent } from "@snort/system";
|
||||
import { unixNow } from "@snort/shared";
|
||||
import { findTag } from "../utils";
|
||||
import { findTag } from "@/utils";
|
||||
|
||||
export function StreamTimer({ ev }: { ev?: NostrEvent }) {
|
||||
const [time, setTime] = useState("");
|
||||
|
@ -1,15 +1,13 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import moment from "moment";
|
||||
|
||||
import { NostrEvent } from "@snort/system";
|
||||
|
||||
import { StreamState } from "index";
|
||||
import { findTag, getTagValues } from "utils";
|
||||
import { StreamState } from "@/index";
|
||||
import { findTag, getTagValues } from "@/utils";
|
||||
|
||||
export function Tags({ children, max, ev }: { children?: ReactNode; max?: number; ev: NostrEvent }) {
|
||||
const status = findTag(ev, "status");
|
||||
const start = findTag(ev, "starts");
|
||||
const hashtags = getTagValues(ev.tags, "t");
|
||||
const tags = max ? hashtags.slice(0, max) : hashtags;
|
||||
|
||||
@ -18,8 +16,7 @@ export function Tags({ children, max, ev }: { children?: ReactNode; max?: number
|
||||
{children}
|
||||
{status === StreamState.Planned && (
|
||||
<span className="pill">
|
||||
{status === StreamState.Planned ? <FormattedMessage defaultMessage="Starts " /> : ""}
|
||||
{moment(Number(start) * 1000).fromNow()}
|
||||
{status === StreamState.Planned ? <FormattedMessage defaultMessage="Starts " id="0hNxBy" /> : ""}
|
||||
</span>
|
||||
)}
|
||||
{tags.map(a => (
|
||||
|
@ -1,5 +1,5 @@
|
||||
import "./textarea.css";
|
||||
import { type KeyboardEvent, type ChangeEvent, useContext } from "react";
|
||||
import { type ChangeEvent, type KeyboardEvent, useContext } from "react";
|
||||
import ReactTextareaAutocomplete, { TriggerType } from "@webscopeio/react-textarea-autocomplete";
|
||||
import "@webscopeio/react-textarea-autocomplete/style.css";
|
||||
import uniqWith from "lodash/uniqWith";
|
||||
@ -9,9 +9,9 @@ import { hexToBech32 } from "@snort/shared";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
import { MetadataCache, NostrPrefix, UserProfileCache } from "@snort/system";
|
||||
|
||||
import { Emoji } from "element/emoji";
|
||||
import { Avatar } from "element/avatar";
|
||||
import type { EmojiTag } from "types";
|
||||
import { Emoji } from "./emoji";
|
||||
import { Avatar } from "./avatar";
|
||||
import type { EmojiTag } from "@/types";
|
||||
|
||||
interface EmojiItemProps {
|
||||
name: string;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as BaseToggle from "@radix-ui/react-toggle";
|
||||
import "./toggle.css";
|
||||
import { Icon } from "element/icon";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
interface ToggleProps {
|
||||
label: string;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ParsedZap } from "@snort/system";
|
||||
import useTopZappers from "hooks/top-zappers";
|
||||
import { formatSats } from "number";
|
||||
import useTopZappers from "@/hooks/top-zappers";
|
||||
import { formatSats } from "@/number";
|
||||
import { Icon } from "./icon";
|
||||
import { Profile } from "./profile";
|
||||
|
||||
|
@ -1,17 +1,18 @@
|
||||
import "./video-tile.css";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Profile } from "./profile";
|
||||
import "./video-tile.css";
|
||||
import { NostrEvent, encodeTLV, NostrPrefix } from "@snort/system";
|
||||
import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
|
||||
import { useInView } from "react-intersection-observer";
|
||||
import { StatePill } from "./state-pill";
|
||||
import { StreamState } from "index";
|
||||
import { findTag, getHost } from "utils";
|
||||
import { formatSats } from "number";
|
||||
import ZapStream from "../../public/zap-stream.svg";
|
||||
import { isContentWarningAccepted } from "./content-warning";
|
||||
import { Tags } from "element/tags";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { StatePill } from "./state-pill";
|
||||
import { StreamState } from "@/index";
|
||||
import { findTag, getHost } from "@/utils";
|
||||
import { formatSats } from "@/number";
|
||||
import ZapStream from "/zap-stream.svg";
|
||||
import { isContentWarningAccepted } from "./content-warning";
|
||||
import { Tags } from "./tags";
|
||||
|
||||
export function VideoTile({
|
||||
ev,
|
||||
showAuthor = true,
|
||||
@ -42,7 +43,7 @@ export function VideoTile({
|
||||
{showStatus && <StatePill state={status as StreamState} />}
|
||||
{viewers && (
|
||||
<span className="pill viewers">
|
||||
<FormattedMessage defaultMessage="{n} viewers" values={{ n: formatSats(Number(viewers)) }} />
|
||||
<FormattedMessage defaultMessage="{n} viewers" id="3adEeb" values={{ n: formatSats(Number(viewers)) }} />
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { NostrLink, EventKind } from "@snort/system";
|
||||
import { EventKind, NostrLink } from "@snort/system";
|
||||
import React, { useContext, useRef, useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { useLogin } from "hooks/login";
|
||||
import AsyncButton from "element/async-button";
|
||||
import { Icon } from "element/icon";
|
||||
import { Textarea } from "element/textarea";
|
||||
import { EmojiPicker } from "element/emoji-picker";
|
||||
import type { EmojiPack, Emoji } from "types";
|
||||
import { LIVE_STREAM_CHAT } from "const";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
import { unixNowMs } from "@snort/shared";
|
||||
import { TimeSync } from "index";
|
||||
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import AsyncButton from "./async-button";
|
||||
import { Icon } from "./icon";
|
||||
import { Textarea } from "./textarea";
|
||||
import { EmojiPicker } from "./emoji-picker";
|
||||
import type { Emoji, EmojiPack } from "@/types";
|
||||
import { LIVE_STREAM_CHAT } from "@/const";
|
||||
import { TimeSync } from "@/index";
|
||||
|
||||
export function WriteMessage({ link, emojiPacks }: { link: NostrLink; emojiPacks: EmojiPack[] }) {
|
||||
const system = useContext(SnortContext);
|
||||
@ -99,7 +99,7 @@ export function WriteMessage({ link, emojiPacks }: { link: NostrLink; emojiPacks
|
||||
)}
|
||||
</div>
|
||||
<AsyncButton onClick={sendChatMessage} className="btn btn-border">
|
||||
<FormattedMessage defaultMessage="Send" />
|
||||
<FormattedMessage defaultMessage="Send" id="9WRlF4" />
|
||||
</AsyncButton>
|
||||
</>
|
||||
);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { TaggedNostrEvent, EventKind, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { EventKind, NoteCollection, RequestBuilder, TaggedNostrEvent } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { findTag, toAddress, getTagValues } from "utils";
|
||||
import type { Badge } from "types";
|
||||
import { findTag, getTagValues, toAddress } from "@/utils";
|
||||
import type { Badge } from "@/types";
|
||||
|
||||
export function useBadges(
|
||||
pubkey: string,
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { TaggedNostrEvent, ReplaceableNoteStore, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { NoteCollection, ReplaceableNoteStore, RequestBuilder, TaggedNostrEvent } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { USER_CARDS, CARD } from "const";
|
||||
import { findTag } from "utils";
|
||||
import { CARD, USER_CARDS } from "@/const";
|
||||
import { findTag } from "@/utils";
|
||||
|
||||
export function useUserCards(pubkey: string, userCards: Array<string[]>, leaveOpen = false): TaggedNostrEvent[] {
|
||||
const related = useMemo(() => {
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { NostrEvent, NostrLink, NostrPrefix, NoteCollection, RequestBuilder, TaggedNostrEvent } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { LIVE_STREAM } from "const";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { LIVE_STREAM } from "@/const";
|
||||
|
||||
export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false, evPreload?: NostrEvent) {
|
||||
const author = link.type === NostrPrefix.Address ? unwrap(link.author) : link.id;
|
||||
const sub = useMemo(() => {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { useMemo } from "react";
|
||||
import uniqBy from "lodash.uniqby";
|
||||
|
||||
import { RequestBuilder, ReplaceableNoteStore, NoteCollection, NostrEvent } from "@snort/system";
|
||||
import { NostrEvent, NoteCollection, ReplaceableNoteStore, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { findTag } from "utils";
|
||||
import { EMOJI_PACK, USER_EMOJIS } from "const";
|
||||
import type { EmojiPack, Tags, EmojiTag } from "types";
|
||||
import { findTag } from "@/utils";
|
||||
import { EMOJI_PACK, USER_EMOJIS } from "@/const";
|
||||
import type { EmojiPack, EmojiTag, Tags } from "@/types";
|
||||
|
||||
function cleanShortcode(shortcode?: string) {
|
||||
return shortcode?.replace(/\s+/g, "_").replace(/_$/, "");
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useMemo } from "react";
|
||||
import { NostrPrefix, RequestBuilder, ReplaceableNoteStore, NostrLink } from "@snort/system";
|
||||
import { NostrLink, NostrPrefix, ReplaceableNoteStore, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
export default function useEventFeed(link: NostrLink, leaveOpen = false) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { NostrPrefix, ReplaceableNoteStore, RequestBuilder, type NostrLink } from "@snort/system";
|
||||
import { type NostrLink, NostrPrefix, ReplaceableNoteStore, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
export function useAddress(kind: number, pubkey: string, identifier: string) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useMemo } from "react";
|
||||
import { RequestBuilder, FlatNoteStore, ReplaceableNoteStore } from "@snort/system";
|
||||
import { FlatNoteStore, ReplaceableNoteStore, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { GOAL } from "const";
|
||||
import { GOAL } from "@/const";
|
||||
|
||||
export function useZapGoal(id?: string) {
|
||||
const sub = useMemo(() => {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { RequestBuilder, ReplaceableNoteStore } from "@snort/system";
|
||||
import { ReplaceableNoteStore, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { MUTED } from "const";
|
||||
import { getTagValues } from "utils";
|
||||
import { MUTED } from "@/const";
|
||||
import { getTagValues } from "@/utils";
|
||||
|
||||
export function useMutedPubkeys(host?: string, leaveOpen = false) {
|
||||
const mutedSub = useMemo(() => {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { NostrLink, RequestBuilder, NoteCollection } from "@snort/system";
|
||||
import { NostrLink, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { useReactions, useRequestBuilder } from "@snort/system-react";
|
||||
import { unixNow } from "@snort/shared";
|
||||
import { useMemo } from "react";
|
||||
import { LIVE_STREAM_CHAT, WEEK } from "const";
|
||||
import { LIVE_STREAM_CHAT, WEEK } from "@/const";
|
||||
|
||||
export function useLiveChatFeed(link?: NostrLink, eZaps?: Array<string>, limit = 100) {
|
||||
const since = useMemo(() => unixNow() - WEEK, [link?.id]);
|
||||
|
@ -4,10 +4,10 @@ import { NostrEvent, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { unixNow } from "@snort/shared";
|
||||
import { LIVE_STREAM } from "const";
|
||||
import { StreamState } from "index";
|
||||
import { findTag, getHost } from "utils";
|
||||
import { WEEK } from "const";
|
||||
import { LIVE_STREAM } from "@/const";
|
||||
import { StreamState } from "@/index";
|
||||
import { findTag, getHost } from "@/utils";
|
||||
import { WEEK } from "@/const";
|
||||
|
||||
export function useStreamsFeed(tag?: string) {
|
||||
const since = useMemo(() => unixNow() - WEEK, [tag]);
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { useSyncExternalStore, useMemo, useState, useEffect } from "react";
|
||||
import { useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
||||
|
||||
import { EventKind, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { useUserEmojiPacks } from "hooks/emoji";
|
||||
import { MUTED, USER_CARDS, USER_EMOJIS } from "const";
|
||||
import type { Tags } from "types";
|
||||
import { getPublisher } from "login";
|
||||
import { Login } from "index";
|
||||
import { useUserEmojiPacks } from "@/hooks/emoji";
|
||||
import { MUTED, USER_CARDS, USER_EMOJIS } from "@/const";
|
||||
import type { Tags } from "@/types";
|
||||
import { getPublisher } from "@/login";
|
||||
import { Login } from "@/index";
|
||||
|
||||
export function useLogin() {
|
||||
const session = useSyncExternalStore(
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useMemo } from "react";
|
||||
import { RequestBuilder, NoteCollection, NostrLink } from "@snort/system";
|
||||
import { NostrLink, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { LIVE_STREAM } from "const";
|
||||
import { LIVE_STREAM } from "@/const";
|
||||
import { useZaps } from "./zaps";
|
||||
|
||||
export function useProfile(link: NostrLink, leaveOpen = false) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { fetchNip05Pubkey } from "@snort/shared";
|
||||
import { NostrLink, tryParseNostrLink, NostrPrefix } from "@snort/system";
|
||||
import { useState, useEffect } from "react";
|
||||
import { NostrLink, NostrPrefix, tryParseNostrLink } from "@snort/system";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
export function useStreamLink() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { StreamProviderStore } from "providers";
|
||||
import { StreamProviderStore } from "@/providers";
|
||||
import { useSyncExternalStore } from "react";
|
||||
|
||||
export function useStreamProvider() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useMemo } from "react";
|
||||
import { NostrLink, RequestBuilder, EventKind, NoteCollection, parseZap } from "@snort/system";
|
||||
import { EventKind, NostrLink, NoteCollection, RequestBuilder, parseZap } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
export function useZaps(link?: NostrLink, leaveOpen = false) {
|
||||
|
@ -8,24 +8,24 @@ import { NostrSystem } from "@snort/system";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
import { SnortSystemDb } from "@snort/system-web";
|
||||
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||
|
||||
import { RootPage } from "pages/root";
|
||||
import { TagPage } from "pages/tag";
|
||||
import { LayoutPage } from "pages/layout";
|
||||
import { ProfilePage } from "pages/profile-page";
|
||||
import { StreamPageHandler } from "pages/stream-page";
|
||||
import { ChatPopout } from "pages/chat-popout";
|
||||
import { LoginStore } from "login";
|
||||
import { StreamProvidersPage } from "pages/providers";
|
||||
import { defaultRelays } from "const";
|
||||
import { CatchAllRoutePage } from "pages/catch-all";
|
||||
import { SettingsPage } from "pages/settings-page";
|
||||
import { register } from "serviceWorker";
|
||||
import { IntlProvider } from "intl";
|
||||
import { WidgetsPage } from "pages/widgets";
|
||||
import { AlertsPage } from "pages/alerts";
|
||||
import { unixNowMs } from "@snort/shared";
|
||||
import { StreamSummaryPage } from "pages/summary";
|
||||
|
||||
import { RootPage } from "@/pages/root";
|
||||
import { TagPage } from "@/pages/tag";
|
||||
import { LayoutPage } from "@/pages/layout";
|
||||
import { ProfilePage } from "@/pages/profile-page";
|
||||
import { StreamPageHandler } from "@/pages/stream-page";
|
||||
import { ChatPopout } from "@/pages/chat-popout";
|
||||
import { LoginStore } from "@/login";
|
||||
import { StreamProvidersPage } from "@/pages/providers";
|
||||
import { defaultRelays } from "@/const";
|
||||
import { CatchAllRoutePage } from "@/pages/catch-all";
|
||||
import { SettingsPage } from "@/pages/settings-page";
|
||||
import { register } from "@/serviceWorker";
|
||||
import { IntlProvider } from "@/intl";
|
||||
import { WidgetsPage } from "@/pages/widgets";
|
||||
import { AlertsPage } from "@/pages/alerts";
|
||||
import { StreamSummaryPage } from "@/pages/summary";
|
||||
|
||||
export enum StreamState {
|
||||
Live = "live",
|
||||
|
53
src/intl.tsx
53
src/intl.tsx
@ -1,15 +1,8 @@
|
||||
import { DefaultLocale, useLang } from "hooks/lang";
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { DefaultLocale, useLang } from "@/hooks/lang";
|
||||
import { type ReactNode, useEffect, useState } from "react";
|
||||
import { IntlProvider as ReactIntlProvider } from "react-intl";
|
||||
|
||||
import enMessages from "translations/en.json";
|
||||
|
||||
async function importLang(code: string) {
|
||||
const src = await import(`translations/${code}.json`);
|
||||
const typed = src.default as Record<string, { defaultMessage: string }>;
|
||||
const ent = Object.entries(typed).map(([k, v]) => [k, v.defaultMessage]);
|
||||
return Object.fromEntries(ent) as Record<string, string>;
|
||||
}
|
||||
import enMessages from "@/translations/en.json";
|
||||
|
||||
export const AllLocales = [
|
||||
DefaultLocale,
|
||||
@ -29,7 +22,13 @@ export const AllLocales = [
|
||||
"fr-FR",
|
||||
"pt-BR",
|
||||
"ru-RU",
|
||||
];
|
||||
] as const;
|
||||
|
||||
function importLang(src: any) {
|
||||
const typed = src.default as Record<string, { defaultMessage: string }>;
|
||||
const ent = Object.entries(typed).map(([k, v]) => [k, v.defaultMessage]);
|
||||
return Object.fromEntries(ent) as Record<string, string>;
|
||||
}
|
||||
|
||||
const getMessages = (locale: string) => {
|
||||
const truncatedLocale = locale.toLowerCase().split(/[_-]+/)[0];
|
||||
@ -38,50 +37,50 @@ const getMessages = (locale: string) => {
|
||||
switch (lng) {
|
||||
case "de":
|
||||
case "de-DE":
|
||||
return await importLang("de_DE");
|
||||
return importLang(await import("@/translations/de_DE.json"));
|
||||
case "es":
|
||||
case "es-ES":
|
||||
return await importLang("es_ES");
|
||||
return importLang(await import("@/translations/es_ES.json"));
|
||||
case "th":
|
||||
case "th-TH":
|
||||
return await importLang("th_TH");
|
||||
return importLang(await import("@/translations/th_TH.json"));
|
||||
case "nl":
|
||||
case "nl-NL":
|
||||
return await importLang("nl_NL");
|
||||
return importLang(await import("@/translations/nl_NL.json"));
|
||||
case "ja":
|
||||
case "ja-JP":
|
||||
return await importLang("ja_JP");
|
||||
return importLang(await import("@/translations/ja_JP.json"));
|
||||
case "fa":
|
||||
case "fa-IR":
|
||||
return await importLang("fa_IR");
|
||||
return importLang(await import("@/translations/fa_IR.json"));
|
||||
case "sw":
|
||||
case "sw-KE":
|
||||
return await importLang("sw_KE");
|
||||
return importLang(await import("@/translations/sw_KE.json"));
|
||||
case "sv":
|
||||
case "sv-SE":
|
||||
return await importLang("sv_SE");
|
||||
return importLang(await import("@/translations/sv_SE.json"));
|
||||
case "bn":
|
||||
case "bn-BD":
|
||||
return await importLang("bn_BD");
|
||||
return importLang(await import("@/translations/bn_BD.json"));
|
||||
case "bg":
|
||||
case "bg-BG":
|
||||
return await importLang("bg_BG");
|
||||
return importLang(await import("@/translations/bg_BG.json"));
|
||||
case "zh":
|
||||
case "zh-CN":
|
||||
return await importLang("zh_CN");
|
||||
return importLang(await import("@/translations/zh_CN.json"));
|
||||
case "zh-TW":
|
||||
return await importLang("zh_TW");
|
||||
return importLang(await import("@/translations/zh_TW.json"));
|
||||
case "fi":
|
||||
case "fi-FI":
|
||||
return await importLang("fi_FI");
|
||||
return importLang(await import("@/translations/fi_FI.json"));
|
||||
case "fr":
|
||||
case "fr-FR":
|
||||
return await importLang("fr_FR");
|
||||
return importLang(await import("@/translations/fr_FR.json"));
|
||||
case "pt-BR":
|
||||
return await importLang("pt_BR");
|
||||
return importLang(await import("@/translations/pt_BR.json"));
|
||||
case "ru":
|
||||
case "ru-RU":
|
||||
return await importLang("ru_RU");
|
||||
return importLang(await import("@/translations/ru_RU.json"));
|
||||
case DefaultLocale:
|
||||
case "en":
|
||||
return enMessages;
|
||||
|
@ -26,6 +26,9 @@
|
||||
"0VV/sK": {
|
||||
"defaultMessage": "Goal"
|
||||
},
|
||||
"0hNxBy": {
|
||||
"defaultMessage": "Starts"
|
||||
},
|
||||
"1EYCdR": {
|
||||
"defaultMessage": "Tags"
|
||||
},
|
||||
@ -74,6 +77,9 @@
|
||||
"6pr6hJ": {
|
||||
"defaultMessage": "Minimum amount for text to speech"
|
||||
},
|
||||
"79lLl+": {
|
||||
"defaultMessage": "Music"
|
||||
},
|
||||
"8YT6ja": {
|
||||
"defaultMessage": "Insert text to speak"
|
||||
},
|
||||
@ -83,9 +89,6 @@
|
||||
"9a9+ww": {
|
||||
"defaultMessage": "Title"
|
||||
},
|
||||
"9anxhq": {
|
||||
"defaultMessage": "Starts"
|
||||
},
|
||||
"AIHaPH": {
|
||||
"defaultMessage": "{person} zapped {amount} sats"
|
||||
},
|
||||
@ -107,6 +110,9 @@
|
||||
"C81/uG": {
|
||||
"defaultMessage": "Logout"
|
||||
},
|
||||
"CsCUYo": {
|
||||
"defaultMessage": "{n} sats"
|
||||
},
|
||||
"D3idYv": {
|
||||
"defaultMessage": "Settings"
|
||||
},
|
||||
@ -128,6 +134,9 @@
|
||||
"G/yZLu": {
|
||||
"defaultMessage": "Remove"
|
||||
},
|
||||
"GGaJMU": {
|
||||
"defaultMessage": "Top Chatters"
|
||||
},
|
||||
"Gq6x9o": {
|
||||
"defaultMessage": "Cover Image"
|
||||
},
|
||||
@ -152,6 +161,9 @@
|
||||
"INlWvJ": {
|
||||
"defaultMessage": "OR"
|
||||
},
|
||||
"J/+m9y": {
|
||||
"defaultMessage": "Stream Duration {duration} mins"
|
||||
},
|
||||
"JEsxDw": {
|
||||
"defaultMessage": "Uploading..."
|
||||
},
|
||||
@ -239,6 +251,9 @@
|
||||
"X2PZ7D": {
|
||||
"defaultMessage": "Create Goal"
|
||||
},
|
||||
"XgWvGA": {
|
||||
"defaultMessage": "Reactions"
|
||||
},
|
||||
"YPh5Nq": {
|
||||
"defaultMessage": "@ {rate}",
|
||||
"description": "Showing zap amount in USD @ rate"
|
||||
@ -282,9 +297,15 @@
|
||||
"feZ/kG": {
|
||||
"defaultMessage": "Login with Private Key (insecure)"
|
||||
},
|
||||
"gzsn7k": {
|
||||
"defaultMessage": "{n} messages"
|
||||
},
|
||||
"hGQqkW": {
|
||||
"defaultMessage": "Schedule"
|
||||
},
|
||||
"hMzcSq": {
|
||||
"defaultMessage": "Messages"
|
||||
},
|
||||
"heyxZL": {
|
||||
"defaultMessage": "Enable text to speech"
|
||||
},
|
||||
@ -300,6 +321,9 @@
|
||||
"izWS4J": {
|
||||
"defaultMessage": "Unfollow"
|
||||
},
|
||||
"jgOqxt": {
|
||||
"defaultMessage": "Widgets"
|
||||
},
|
||||
"jr4+vD": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
|
@ -1,7 +1,8 @@
|
||||
import "./alerts.css";
|
||||
import Spinner from "element/spinner";
|
||||
import { useStreamLink } from "hooks/stream-link";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import Spinner from "@/element/spinner";
|
||||
import { useStreamLink } from "@/hooks/stream-link";
|
||||
import { ZapAlerts } from "./widgets/zaps";
|
||||
import { Views } from "./widgets/views";
|
||||
import { TopZappersWidget } from "./widgets/top-zappers";
|
||||
|
@ -1,11 +1,12 @@
|
||||
import "./chat-popout.css";
|
||||
import { LiveChat } from "element/live-chat";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { NostrPrefix, encodeTLV, parseNostrLink } from "@snort/system";
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { findTag } from "utils";
|
||||
import { useZapGoal } from "hooks/goals";
|
||||
|
||||
import { LiveChat } from "@/element/live-chat";
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { findTag } from "@/utils";
|
||||
import { useZapGoal } from "@/hooks/goals";
|
||||
|
||||
export function ChatPopout() {
|
||||
const params = useParams();
|
||||
|
@ -34,7 +34,7 @@ header {
|
||||
}
|
||||
|
||||
header .logo {
|
||||
background: url("public/logo.png") no-repeat #171717;
|
||||
background: url("/logo.png") no-repeat #171717;
|
||||
background-size: cover;
|
||||
border-radius: 16px;
|
||||
width: 48px;
|
||||
@ -148,7 +148,7 @@ header .profile img {
|
||||
|
||||
.age-check::after {
|
||||
content: " ";
|
||||
background: url("public/zap-stream.svg") no-repeat;
|
||||
background: url("/zap-stream.svg") no-repeat;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
position: absolute;
|
||||
|
@ -8,14 +8,14 @@ import { FormattedMessage } from "react-intl";
|
||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||
import { hexToBech32 } from "@snort/shared";
|
||||
|
||||
import { Icon } from "element/icon";
|
||||
import { useLogin, useLoginEvents } from "hooks/login";
|
||||
import { Profile } from "element/profile";
|
||||
import { NewStreamDialog } from "element/new-stream";
|
||||
import { LoginSignup } from "element/login-signup";
|
||||
import { Login } from "index";
|
||||
import { useLang } from "hooks/lang";
|
||||
import { AllLocales } from "intl";
|
||||
import { Icon } from "@/element/icon";
|
||||
import { useLogin, useLoginEvents } from "@/hooks/login";
|
||||
import { Profile } from "@/element/profile";
|
||||
import { NewStreamDialog } from "@/element/new-stream";
|
||||
import { LoginSignup } from "@/element/login-signup";
|
||||
import { Login } from "@/index";
|
||||
import { useLang } from "@/hooks/lang";
|
||||
import { AllLocales } from "@/intl";
|
||||
|
||||
export function LayoutPage() {
|
||||
const navigate = useNavigate();
|
||||
@ -76,19 +76,19 @@ export function LayoutPage() {
|
||||
gap={5}>
|
||||
<MenuItem onClick={() => navigate(`/p/${hexToBech32("npub", login.pubkey)}`)}>
|
||||
<Icon name="user" size={24} />
|
||||
<FormattedMessage defaultMessage="Profile" />
|
||||
<FormattedMessage defaultMessage="Profile" id="itPgxd" />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate("/settings")}>
|
||||
<Icon name="settings" size={24} />
|
||||
<FormattedMessage defaultMessage="Settings" />
|
||||
<FormattedMessage defaultMessage="Settings" id="D3idYv" />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate("/widgets")}>
|
||||
<Icon name="widget" size={24} />
|
||||
<FormattedMessage defaultMessage="Widgets" />
|
||||
<FormattedMessage defaultMessage="Widgets" id="jgOqxt" />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => Login.logout()}>
|
||||
<Icon name="logout" size={24} />
|
||||
<FormattedMessage defaultMessage="Logout" />
|
||||
<FormattedMessage defaultMessage="Logout" id="C81/uG" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
@ -105,7 +105,7 @@ export function LayoutPage() {
|
||||
return (
|
||||
<Dialog.Root open={showLogin} onOpenChange={setShowLogin}>
|
||||
<button type="button" className="btn btn-border" onClick={handleLogin}>
|
||||
<FormattedMessage defaultMessage="Login" />
|
||||
<FormattedMessage defaultMessage="Login" id="AyGauy" />
|
||||
<Icon name="login" />
|
||||
</button>
|
||||
<Dialog.Portal>
|
||||
|
@ -1,26 +1,26 @@
|
||||
import "./profile-page.css";
|
||||
import { useMemo } from "react";
|
||||
import moment from "moment";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import * as Tabs from "@radix-ui/react-tabs";
|
||||
import { parseNostrLink, NostrPrefix, ParsedZap, encodeTLV } from "@snort/system";
|
||||
import { NostrPrefix, ParsedZap, encodeTLV, parseNostrLink } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { Profile } from "element/profile";
|
||||
import { Icon } from "element/icon";
|
||||
import { SendZapsDialog } from "element/send-zap";
|
||||
import { VideoTile } from "element/video-tile";
|
||||
import { FollowButton } from "element/follow-button";
|
||||
import { MuteButton } from "element/mute-button";
|
||||
import { useProfile } from "hooks/profile";
|
||||
import useTopZappers from "hooks/top-zappers";
|
||||
import usePlaceholder from "hooks/placeholders";
|
||||
import { Text } from "element/text";
|
||||
import { StreamState } from "index";
|
||||
import { findTag } from "utils";
|
||||
import { formatSats } from "number";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { Profile } from "@/element/profile";
|
||||
import { Icon } from "@/element/icon";
|
||||
import { SendZapsDialog } from "@/element/send-zap";
|
||||
import { VideoTile } from "@/element/video-tile";
|
||||
import { FollowButton } from "@/element/follow-button";
|
||||
import { MuteButton } from "@/element/mute-button";
|
||||
import { useProfile } from "@/hooks/profile";
|
||||
import useTopZappers from "@/hooks/top-zappers";
|
||||
import usePlaceholder from "@/hooks/placeholders";
|
||||
import { Text } from "@/element/text";
|
||||
import { StreamState } from "@/index";
|
||||
import { findTag } from "@/utils";
|
||||
import { formatSats } from "@/number";
|
||||
|
||||
function Zapper({ pubkey, total }: { pubkey: string; total: number }) {
|
||||
return (
|
||||
<div className="zapper">
|
||||
@ -92,12 +92,12 @@ export function ProfilePage() {
|
||||
<div className="live-button pill live" onClick={goToLive}>
|
||||
<Icon name="signal" />
|
||||
<span>
|
||||
<FormattedMessage defaultMessage="live" />
|
||||
<FormattedMessage defaultMessage="live" id="2CGh/0" />
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<span className="pill offline">
|
||||
<FormattedMessage defaultMessage="offline" />
|
||||
<FormattedMessage defaultMessage="offline" id="K3uH1C" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@ -111,7 +111,7 @@ export function ProfilePage() {
|
||||
<div className="zap-button">
|
||||
<Icon name="zap-filled" className="zap-button-icon" />
|
||||
<span>
|
||||
<FormattedMessage defaultMessage="Zap" />
|
||||
<FormattedMessage defaultMessage="Zap" id="fBI91o" />
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
@ -133,15 +133,15 @@ export function ProfilePage() {
|
||||
<Tabs.Root className="tabs-root" defaultValue="top-zappers">
|
||||
<Tabs.List className="tabs-list" aria-label={`Information about ${profile ? profile.name : link.id}`}>
|
||||
<Tabs.Trigger className="tabs-tab" value="top-zappers">
|
||||
<FormattedMessage defaultMessage="Top Zappers" />
|
||||
<FormattedMessage defaultMessage="Top Zappers" id="dVD/AR" />
|
||||
<div className="tab-border"></div>
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger className="tabs-tab" value="past-streams">
|
||||
<FormattedMessage defaultMessage="Past Streams" />
|
||||
<FormattedMessage defaultMessage="Past Streams" id="UfSot5" />
|
||||
<div className="tab-border"></div>
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger className="tabs-tab" value="schedule">
|
||||
<FormattedMessage defaultMessage="Schedule" />
|
||||
<FormattedMessage defaultMessage="Schedule" id="hGQqkW" />
|
||||
<div className="tab-border"></div>
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
@ -156,8 +156,9 @@ export function ProfilePage() {
|
||||
<span className="timestamp">
|
||||
<FormattedMessage
|
||||
defaultMessage="Streamed on {date}"
|
||||
id="cvAsEh"
|
||||
values={{
|
||||
date: moment(Number(ev.created_at) * 1000).format("MMM DD, YYYY"),
|
||||
date: new Date(ev.created_at * 1000).toLocaleDateString(),
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
@ -173,8 +174,9 @@ export function ProfilePage() {
|
||||
<span className="timestamp">
|
||||
<FormattedMessage
|
||||
defaultMessage="Scheduled for {date}"
|
||||
id="pO/lPX"
|
||||
values={{
|
||||
date: moment(Number(ev.created_at) * 1000).format("MMM DD, YYYY h:mm:ss a"),
|
||||
date: new Date(ev.created_at * 1000).toLocaleDateString(),
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import "./index.css";
|
||||
import { StreamProviders } from "providers";
|
||||
|
||||
import Owncast from "owncast.png";
|
||||
import Cloudflare from "cloudflare.png";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import { StreamProviders } from "@/providers";
|
||||
import Owncast from "@/owncast.png";
|
||||
import Cloudflare from "@/cloudflare.png";
|
||||
import { ConfigureOwncast } from "./owncast";
|
||||
import { ConfigureNostrType } from "./nostr";
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import AsyncButton from "element/async-button";
|
||||
import { StatePill } from "element/state-pill";
|
||||
import { StreamState } from "index";
|
||||
import { StreamProviderInfo, StreamProviderStore } from "providers";
|
||||
import { Nip103StreamProvider } from "providers/zsz";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import AsyncButton from "@/element/async-button";
|
||||
import { StatePill } from "@/element/state-pill";
|
||||
import { StreamState } from "@/index";
|
||||
import { StreamProviderInfo, StreamProviderStore } from "@/providers";
|
||||
import { Nip103StreamProvider } from "@/providers/zsz";
|
||||
|
||||
export function ConfigureNostrType() {
|
||||
const [url, setUrl] = useState("");
|
||||
const [info, setInfo] = useState<StreamProviderInfo>();
|
||||
@ -61,7 +61,7 @@ export function ConfigureNostrType() {
|
||||
StreamProviderStore.add(new Nip103StreamProvider(new URL(url).host, url));
|
||||
navigate("/");
|
||||
}}>
|
||||
<FormattedMessage defaultMessage="Save" />
|
||||
<FormattedMessage defaultMessage="Save" id="jvo0vs" />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
@ -78,7 +78,7 @@ export function ConfigureNostrType() {
|
||||
</div>
|
||||
</div>
|
||||
<AsyncButton className="btn btn-primary" onClick={tryConnect}>
|
||||
<FormattedMessage defaultMessage="Connect" />
|
||||
<FormattedMessage defaultMessage="Connect" id="+vVZ/G" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
<div>{status()}</div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
import AsyncButton from "element/async-button";
|
||||
import { StatePill } from "element/state-pill";
|
||||
import { StreamState } from "index";
|
||||
import { StreamProviderInfo, StreamProviderStore } from "providers";
|
||||
import { OwncastProvider } from "providers/owncast";
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import AsyncButton from "@/element/async-button";
|
||||
import { StatePill } from "@/element/state-pill";
|
||||
import { StreamState } from "@/index";
|
||||
import { StreamProviderInfo, StreamProviderStore } from "@/providers";
|
||||
import { OwncastProvider } from "@/providers/owncast";
|
||||
|
||||
export function ConfigureOwncast() {
|
||||
const [url, setUrl] = useState("");
|
||||
const [token, setToken] = useState("");
|
||||
|
@ -3,10 +3,10 @@ import { FormattedMessage } from "react-intl";
|
||||
import { useCallback } from "react";
|
||||
import type { NostrEvent } from "@snort/system";
|
||||
|
||||
import { VideoTile } from "element/video-tile";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { getHost, getTagValues } from "utils";
|
||||
import { useStreamsFeed } from "hooks/live-streams";
|
||||
import { VideoTile } from "@/element/video-tile";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { getHost, getTagValues } from "@/utils";
|
||||
import { useStreamsFeed } from "@/hooks/live-streams";
|
||||
|
||||
export function RootPage() {
|
||||
const login = useLogin();
|
||||
@ -33,7 +33,7 @@ export function RootPage() {
|
||||
{hasFollowingLive && (
|
||||
<>
|
||||
<h2 className="divider line one-line">
|
||||
<FormattedMessage defaultMessage="Following" />
|
||||
<FormattedMessage defaultMessage="Following" id="cPIKU2" />
|
||||
</h2>
|
||||
<div className="video-grid">
|
||||
{following.map(e => (
|
||||
@ -70,7 +70,7 @@ export function RootPage() {
|
||||
{hasFollowingLive && liveNow.length > 0 && (
|
||||
<>
|
||||
<h2 className="divider line one-line">
|
||||
<FormattedMessage defaultMessage="Live" />
|
||||
<FormattedMessage defaultMessage="Live" id="Dn82AL" />
|
||||
</h2>
|
||||
<div className="video-grid">
|
||||
{liveNow
|
||||
@ -84,7 +84,7 @@ export function RootPage() {
|
||||
{plannedEvents.length > 0 && (
|
||||
<>
|
||||
<h2 className="divider line one-line">
|
||||
<FormattedMessage defaultMessage="Planned" />
|
||||
<FormattedMessage defaultMessage="Planned" id="kp0NPF" />
|
||||
</h2>
|
||||
<div className="video-grid">
|
||||
{plannedEvents.map(e => (
|
||||
@ -96,7 +96,7 @@ export function RootPage() {
|
||||
{endedEvents.length > 0 && (
|
||||
<>
|
||||
<h2 className="divider line one-line">
|
||||
<FormattedMessage defaultMessage="Ended" />
|
||||
<FormattedMessage defaultMessage="Ended" id="TP/cMX" />
|
||||
</h2>
|
||||
<div className="video-grid">
|
||||
{endedEvents.map(e => (
|
||||
|
@ -5,8 +5,8 @@ import { FormattedMessage } from "react-intl";
|
||||
import { Button as AlbyZapsButton } from "@getalby/bitcoin-connect-react";
|
||||
import { hexToBech32 } from "@snort/shared";
|
||||
|
||||
import { useLogin } from "hooks/login";
|
||||
import Copy from "element/copy";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import Copy from "@/element/copy";
|
||||
|
||||
const enum Tab {
|
||||
Account,
|
||||
@ -30,12 +30,12 @@ export function SettingsPage() {
|
||||
return (
|
||||
<>
|
||||
<h1>
|
||||
<FormattedMessage defaultMessage="Account" />
|
||||
<FormattedMessage defaultMessage="Account" id="TwyMau" />
|
||||
</h1>
|
||||
{login?.pubkey && (
|
||||
<div className="public-key">
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Logged in as" />
|
||||
<FormattedMessage defaultMessage="Logged in as" id="DZKuuP" />
|
||||
</p>
|
||||
<Copy text={hexToBech32("npub", login.pubkey)} />
|
||||
</div>
|
||||
@ -43,13 +43,13 @@ export function SettingsPage() {
|
||||
{login?.privateKey && (
|
||||
<div className="private-key">
|
||||
<p>
|
||||
<FormattedMessage defaultMessage="Private key" />
|
||||
<FormattedMessage defaultMessage="Private key" id="Bep/gA" />
|
||||
</p>
|
||||
<Copy text={hexToBech32("nsec", login.privateKey)} />
|
||||
</div>
|
||||
)}
|
||||
<h1>
|
||||
<FormattedMessage defaultMessage="Zaps" />
|
||||
<FormattedMessage defaultMessage="Zaps" id="OEW7yJ" />
|
||||
</h1>
|
||||
<AlbyZapsButton />
|
||||
</>
|
||||
@ -61,12 +61,12 @@ export function SettingsPage() {
|
||||
<div className="settings-page">
|
||||
<div className="flex f-col g48">
|
||||
<h1>
|
||||
<FormattedMessage defaultMessage="Settings" />
|
||||
<FormattedMessage defaultMessage="Settings" id="D3idYv" />
|
||||
</h1>
|
||||
<div className="flex g24 f-col-mobile">
|
||||
<div className="flex f-col g24 tab-options">
|
||||
<div onClick={() => setTab(Tab.Account)}>
|
||||
<FormattedMessage defaultMessage="Account" />
|
||||
<FormattedMessage defaultMessage="Account" id="TwyMau" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="tab-content">{tabContent()}</div>
|
||||
|
@ -2,31 +2,31 @@ import "./stream-page.css";
|
||||
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import { LiveVideoPlayer } from "element/live-video-player";
|
||||
import { findTag, getEventFromLocationState, getHost } from "utils";
|
||||
import { Profile, getName } from "element/profile";
|
||||
import { LiveChat } from "element/live-chat";
|
||||
import AsyncButton from "element/async-button";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { useZapGoal } from "hooks/goals";
|
||||
import { StreamState } from "index";
|
||||
import { SendZapsDialog } from "element/send-zap";
|
||||
import { NostrEvent } from "@snort/system";
|
||||
import { SnortContext, useUserProfile } from "@snort/system-react";
|
||||
import { NewStreamDialog } from "element/new-stream";
|
||||
import { Tags } from "element/tags";
|
||||
import { StatePill } from "element/state-pill";
|
||||
import { StreamCards } from "element/stream-cards";
|
||||
import { formatSats } from "number";
|
||||
import { StreamTimer } from "element/stream-time";
|
||||
import { ShareMenu } from "element/share-menu";
|
||||
import { ContentWarningOverlay, isContentWarningAccepted } from "element/content-warning";
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { useStreamLink } from "hooks/stream-link";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useContext } from "react";
|
||||
import { FollowButton } from "element/follow-button";
|
||||
|
||||
import { LiveVideoPlayer } from "@/element/live-video-player";
|
||||
import { findTag, getEventFromLocationState, getHost } from "@/utils";
|
||||
import { Profile, getName } from "@/element/profile";
|
||||
import { LiveChat } from "@/element/live-chat";
|
||||
import AsyncButton from "@/element/async-button";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { useZapGoal } from "@/hooks/goals";
|
||||
import { StreamState } from "@/index";
|
||||
import { SendZapsDialog } from "@/element/send-zap";
|
||||
import { NewStreamDialog } from "@/element/new-stream";
|
||||
import { Tags } from "@/element/tags";
|
||||
import { StatePill } from "@/element/state-pill";
|
||||
import { StreamCards } from "@/element/stream-cards";
|
||||
import { formatSats } from "@/number";
|
||||
import { StreamTimer } from "@/element/stream-time";
|
||||
import { ShareMenu } from "@/element/share-menu";
|
||||
import { ContentWarningOverlay, isContentWarningAccepted } from "@/element/content-warning";
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { useStreamLink } from "@/hooks/stream-link";
|
||||
import { FollowButton } from "@/element/follow-button";
|
||||
|
||||
function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent }) {
|
||||
const system = useContext(SnortContext);
|
||||
@ -60,7 +60,7 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent })
|
||||
<StatePill state={status as StreamState} />
|
||||
{viewers > 0 && (
|
||||
<span className="pill viewers">
|
||||
<FormattedMessage defaultMessage="{n} viewers" values={{ n: formatSats(viewers) }} />
|
||||
<FormattedMessage defaultMessage="{n} viewers" id="3adEeb" values={{ n: formatSats(viewers) }} />
|
||||
</span>
|
||||
)}
|
||||
{status === StreamState.Live && (
|
||||
@ -74,7 +74,7 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent })
|
||||
<div className="actions">
|
||||
{ev && <NewStreamDialog text="Edit" ev={ev} btnClassName="btn" />}
|
||||
<AsyncButton type="button" className="btn btn-warning" onClick={deleteStream}>
|
||||
<FormattedMessage defaultMessage="Delete" />
|
||||
<FormattedMessage defaultMessage="Delete" id="K3r6DQ" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
)}
|
||||
|
@ -6,15 +6,15 @@ import { useEventReactions } from "@snort/system-react";
|
||||
import { FormattedDate, FormattedMessage, FormattedNumber } from "react-intl";
|
||||
import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
|
||||
|
||||
import { LIVE_STREAM_CHAT } from "const";
|
||||
import { Profile } from "element/profile";
|
||||
import { StatePill } from "element/state-pill";
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { useLiveChatFeed } from "hooks/live-chat";
|
||||
import { useStreamLink } from "hooks/stream-link";
|
||||
import { StreamState } from "index";
|
||||
import { formatSats } from "number";
|
||||
import { findTag, getEventFromLocationState } from "utils";
|
||||
import { LIVE_STREAM_CHAT } from "@/const";
|
||||
import { Profile } from "@/element/profile";
|
||||
import { StatePill } from "@/element/state-pill";
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { useLiveChatFeed } from "@/hooks/live-chat";
|
||||
import { useStreamLink } from "@/hooks/stream-link";
|
||||
import { StreamState } from "@/index";
|
||||
import { formatSats } from "@/number";
|
||||
import { findTag, getEventFromLocationState } from "@/utils";
|
||||
|
||||
export function StreamSummaryPage() {
|
||||
const location = useLocation();
|
||||
|
@ -2,9 +2,9 @@ import "./tag.css";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { unwrap } from "@snort/shared";
|
||||
|
||||
import { VideoTile } from "element/video-tile";
|
||||
import { FollowTagButton } from "element/follow-button";
|
||||
import { useStreamsFeed } from "hooks/live-streams";
|
||||
import { VideoTile } from "@/element/video-tile";
|
||||
import { FollowTagButton } from "@/element/follow-button";
|
||||
import { useStreamsFeed } from "@/hooks/live-streams";
|
||||
|
||||
export function TagPage() {
|
||||
const { tag } = useParams();
|
||||
|
@ -1,13 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import "./widgets.css";
|
||||
import { useState, useMemo } from "react";
|
||||
import { useIntl, FormattedMessage } from "react-intl";
|
||||
import { useMemo, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { NostrLink, NostrPrefix } from "@snort/system";
|
||||
|
||||
import Copy from "element/copy";
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { getVoices, speak, toTextToSpeechParams } from "text2speech";
|
||||
import { useLogin } from "hooks/login";
|
||||
import Copy from "@/element/copy";
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { getVoices, speak, toTextToSpeechParams } from "@/text2speech";
|
||||
import { useLogin } from "@/hooks/login";
|
||||
import { ZapAlertItem } from "./widgets/zaps";
|
||||
import { TopZappersWidget } from "./widgets/top-zappers";
|
||||
import { Views } from "./widgets/views";
|
||||
@ -64,7 +64,7 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Zap Alert" />
|
||||
<FormattedMessage defaultMessage="Zap Alert" id="zVDHAu" />
|
||||
</h3>
|
||||
<Copy text={`${baseUrl}/alert/${npub}/zaps${query}`} />
|
||||
<ZapAlertItem
|
||||
@ -91,13 +91,13 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
|
||||
checked={textToSpeech}
|
||||
onChange={ev => setTextToSpeech(ev.target.checked)}
|
||||
/>
|
||||
<FormattedMessage defaultMessage="Enable text to speech" />
|
||||
<FormattedMessage defaultMessage="Enable text to speech" id="heyxZL" />
|
||||
</div>
|
||||
{isTextToSpeechEnabled && (
|
||||
<>
|
||||
<div className="paper labeled-input">
|
||||
<label htmlFor="minimum-sats">
|
||||
<FormattedMessage defaultMessage="Minimum amount for text to speech" />
|
||||
<FormattedMessage defaultMessage="Minimum amount for text to speech" id="6pr6hJ" />
|
||||
</label>
|
||||
<input
|
||||
id="minimum-sats"
|
||||
@ -109,7 +109,7 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
|
||||
</div>
|
||||
<div className="paper labeled-input">
|
||||
<label htmlFor="volume">
|
||||
<FormattedMessage defaultMessage="Volume" />
|
||||
<FormattedMessage defaultMessage="Volume" id="y867Vs" />
|
||||
</label>
|
||||
<input
|
||||
id="volume"
|
||||
@ -123,11 +123,11 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
|
||||
</div>
|
||||
<div className="paper labeled-input">
|
||||
<label htmlFor="voice-selector">
|
||||
<FormattedMessage defaultMessage="Voice" />
|
||||
<FormattedMessage defaultMessage="Voice" id="mnJYBQ" />
|
||||
</label>
|
||||
<select id="voice-selector" onChange={ev => setVoice(ev.target.value)}>
|
||||
<option value="">
|
||||
<FormattedMessage defaultMessage="Select voice..." />
|
||||
<FormattedMessage defaultMessage="Select voice..." id="wMKVFz" />
|
||||
</option>
|
||||
{languages.map(l => (
|
||||
<optgroup label={formatDisplayName(l, { type: "language" })}>
|
||||
@ -142,17 +142,17 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
|
||||
<>
|
||||
<div className="paper labeled-input">
|
||||
<label htmlFor="zap-alert-text">
|
||||
<FormattedMessage defaultMessage="Zap message" />
|
||||
<FormattedMessage defaultMessage="Zap message" id="sInm1h" />
|
||||
</label>
|
||||
<textarea
|
||||
id="zap-alert-text"
|
||||
placeholder={formatMessage({ defaultMessage: "Insert text to speak" })}
|
||||
placeholder={formatMessage({ defaultMessage: "Insert text to speak", id: "8YT6ja" })}
|
||||
value={testText}
|
||||
onChange={ev => setTestText(ev.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button disabled={testText.length === 0} className="btn" onClick={testVoice}>
|
||||
<FormattedMessage defaultMessage="Test voice" />
|
||||
<FormattedMessage defaultMessage="Test voice" id="d5zWyh" />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
@ -176,7 +176,7 @@ export function WidgetsPage() {
|
||||
<div className="widgets g8">
|
||||
<div className="flex f-col g8">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Chat Widget" />
|
||||
<FormattedMessage defaultMessage="Chat Widget" id="hpl4BP" />
|
||||
</h3>
|
||||
<Copy text={`${baseUrl}/chat/${npub}`} />
|
||||
</div>
|
||||
@ -185,21 +185,21 @@ export function WidgetsPage() {
|
||||
</div>
|
||||
<div className="flex f-col g8">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Top Zappers" />
|
||||
<FormattedMessage defaultMessage="Top Zappers" id="dVD/AR" />
|
||||
</h3>
|
||||
<Copy text={`${baseUrl}/alert/${npub}/top-zappers`} />
|
||||
{currentLink && <TopZappersWidget link={currentLink} />}
|
||||
</div>
|
||||
<div className="flex f-col g8">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Current Viewers" />
|
||||
<FormattedMessage defaultMessage="Current Viewers" id="rgsbu9" />
|
||||
</h3>
|
||||
<Copy text={`${baseUrl}/alert/${npub}/views`} />
|
||||
{currentLink && <Views link={currentLink} />}
|
||||
</div>
|
||||
<div className="flex f-col g8">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Music" />
|
||||
<FormattedMessage defaultMessage="Music" id="79lLl+" />
|
||||
</h3>
|
||||
<Copy text={`${baseUrl}/alert/${npub}/music`} />
|
||||
{currentLink && <Music link={currentLink} />}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { NostrLink } from "@snort/system";
|
||||
import { unixNow } from "@snort/shared";
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { useStatus } from "hooks/status";
|
||||
import { getHost, findTag } from "utils";
|
||||
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { useStatus } from "@/hooks/status";
|
||||
import { findTag, getHost } from "@/utils";
|
||||
|
||||
export function Music({ link }: { link: NostrLink }) {
|
||||
const currentEvent = useCurrentStreamFeed(link, true);
|
||||
|
@ -1,16 +1,17 @@
|
||||
import { NostrLink } from "@snort/system";
|
||||
import { TopZappers } from "element/top-zappers";
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { useZaps } from "hooks/zaps";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { TopZappers } from "@/element/top-zappers";
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { useZaps } from "@/hooks/zaps";
|
||||
|
||||
export function TopZappersWidget({ link }: { link: NostrLink }) {
|
||||
const currentEvent = useCurrentStreamFeed(link, true);
|
||||
const zaps = useZaps(currentEvent ? NostrLink.fromEvent(currentEvent) : undefined, true);
|
||||
return (
|
||||
<div className="top-zappers-widget">
|
||||
<div>
|
||||
<FormattedMessage defaultMessage="Top Zappers" />
|
||||
<FormattedMessage defaultMessage="Top Zappers" id="dVD/AR" />
|
||||
</div>
|
||||
<div className="flex g8">
|
||||
<TopZappers zaps={zaps} limit={3} />
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { NostrLink } from "@snort/system";
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { findTag } from "utils";
|
||||
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { findTag } from "@/utils";
|
||||
|
||||
export function Views({ link }: { link: NostrLink }) {
|
||||
const current = useCurrentStreamFeed(link, true);
|
||||
@ -11,9 +12,9 @@ export function Views({ link }: { link: NostrLink }) {
|
||||
return (
|
||||
<div className="views">
|
||||
{isNaN(n) ? (
|
||||
<FormattedMessage defaultMessage="No viewer data available" />
|
||||
<FormattedMessage defaultMessage="No viewer data available" id="AukrPM" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="{n} viewers" values={{ n }} />
|
||||
<FormattedMessage defaultMessage="{n} viewers" id="3adEeb" values={{ n }} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { useMemo, useState, useEffect } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { hexToBech32 } from "@snort/shared";
|
||||
import { NostrLink, ParsedZap } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { useZaps } from "hooks/zaps";
|
||||
import { useMutedPubkeys } from "hooks/lists";
|
||||
import { formatSats } from "number";
|
||||
import { useTextToSpeechParams, getVoices, speak } from "text2speech";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { getHost } from "utils";
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { useZaps } from "@/hooks/zaps";
|
||||
import { useMutedPubkeys } from "@/hooks/lists";
|
||||
import { formatSats } from "@/number";
|
||||
import { getVoices, speak, useTextToSpeechParams } from "@/text2speech";
|
||||
import { getHost } from "@/utils";
|
||||
|
||||
function useZapQueue(zapStream: ParsedZap[], zapTime = 10_000) {
|
||||
const zaps = useMemo(() => {
|
||||
@ -76,11 +76,12 @@ export function ZapAlertItem({ item }: { item: ParsedZap }) {
|
||||
<>
|
||||
<div className="zap-alert">
|
||||
<div className="zap-alert-title">
|
||||
<FormattedMessage defaultMessage="Incoming Zap" />
|
||||
<FormattedMessage defaultMessage="Incoming Zap" id="tG1ST3" />
|
||||
</div>
|
||||
<div className="zap-alert-header">
|
||||
<FormattedMessage
|
||||
defaultMessage="{name} with {amount}"
|
||||
id="Qe1MJu"
|
||||
values={{
|
||||
name: (
|
||||
<span className="highlight">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { StreamState } from "index";
|
||||
import { StreamState } from "@/index";
|
||||
import { NostrEvent, SystemInterface } from "@snort/system";
|
||||
import { ExternalStore } from "@snort/shared";
|
||||
import { Nip103StreamProvider } from "./zsz";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { NostrEvent, SystemInterface } from "@snort/system";
|
||||
import { StreamProvider, StreamProviderInfo, StreamProviders } from "providers";
|
||||
import { StreamProvider, StreamProviderInfo, StreamProviders } from "@/providers";
|
||||
|
||||
export class ManualProvider implements StreamProvider {
|
||||
get name(): string {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { StreamState } from "index";
|
||||
import { StreamProvider, StreamProviderInfo, StreamProviders } from "providers";
|
||||
import { StreamState } from "@/index";
|
||||
import { StreamProvider, StreamProviderInfo, StreamProviders } from "@/providers";
|
||||
|
||||
export class OwncastProvider implements StreamProvider {
|
||||
#url: string;
|
||||
|
@ -7,9 +7,9 @@ import {
|
||||
StreamProviders,
|
||||
} from ".";
|
||||
import { EventKind, EventPublisher, NostrEvent, SystemInterface } from "@snort/system";
|
||||
import { Login, StreamState } from "index";
|
||||
import { getPublisher } from "login";
|
||||
import { findTag } from "utils";
|
||||
import { Login, StreamState } from "@/index";
|
||||
import { getPublisher } from "@/login";
|
||||
import { findTag } from "@/utils";
|
||||
|
||||
export class Nip103StreamProvider implements StreamProvider {
|
||||
#publisher?: EventPublisher;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { NostrEvent, NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||
|
||||
import type { Tags } from "types";
|
||||
import { LIVE_STREAM } from "const";
|
||||
import type { Tags } from "@/types";
|
||||
import { LIVE_STREAM } from "@/const";
|
||||
|
||||
export function toAddress(e: NostrEvent): string {
|
||||
if (e.kind && e.kind >= 30000 && e.kind <= 40000) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import adapter from "webrtc-adapter";
|
||||
import { CandidateInfo, SDPInfo } from "semantic-sdp";
|
||||
import { TypedEventTarget, type StatusEvent, type LogEvent } from "./events";
|
||||
import { type LogEvent, type StatusEvent, TypedEventTarget } from "./events";
|
||||
import { parserLinkHeader } from "./parser";
|
||||
|
||||
export const DEFAULT_ICE_SERVERS = ["stun:stun.cloudflare.com:3478", "stun:stun.l.google.com:19302"];
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user