Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
5a69d305af
|
|||
|
ce62cafd59
|
@@ -1,6 +1,5 @@
|
|||||||
--ignore-dir=.svelte-kit
|
--ignore-dir=.svelte-kit
|
||||||
--ignore-dir=android
|
--ignore-dir=android
|
||||||
--ignore-dir=target
|
|
||||||
--ignore-dir=build
|
--ignore-dir=build
|
||||||
--ignore-dir=ios/DerivedData
|
--ignore-dir=ios/DerivedData
|
||||||
--ignore-dir=ios/App/App/public
|
--ignore-dir=ios/App/App/public
|
||||||
|
|||||||
@@ -2,11 +2,3 @@ node_modules
|
|||||||
android
|
android
|
||||||
ios
|
ios
|
||||||
build
|
build
|
||||||
|
|
||||||
# Git
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
|
|
||||||
# Env files (keep .env for build; exclude local overrides)
|
|
||||||
.env.local
|
|
||||||
.env.*.local
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
src/assets
|
src/assets
|
||||||
target
|
|
||||||
build
|
build
|
||||||
.idea
|
.idea
|
||||||
.gradle
|
.gradle
|
||||||
|
|||||||
@@ -27,10 +27,6 @@ node_modules/
|
|||||||
build/
|
build/
|
||||||
.svelte-kit/
|
.svelte-kit/
|
||||||
|
|
||||||
# Rust/Tauri
|
|
||||||
*target/
|
|
||||||
src-tauri/binaries/
|
|
||||||
|
|
||||||
# iOS
|
# iOS
|
||||||
ios/App/App/public
|
ios/App/App/public
|
||||||
ios/DerivedData
|
ios/DerivedData
|
||||||
|
|||||||
@@ -170,15 +170,6 @@ src/
|
|||||||
- Do not define svelte event handlers inline, instead name them and put them in the script section of templates
|
- Do not define svelte event handlers inline, instead name them and put them in the script section of templates
|
||||||
- Avoid using `as`, except where necessary. Instead, annotate function parameters, and ensure upstream values are typed correctly.
|
- Avoid using `as`, except where necessary. Instead, annotate function parameters, and ensure upstream values are typed correctly.
|
||||||
|
|
||||||
**Human-First Simplicity (Jon Staab Style):**
|
|
||||||
|
|
||||||
- Prefer direct, readable code over layered abstractions.
|
|
||||||
- Do not add indirection (extra helpers, wrappers, stores, or derived state) unless it removes real repeated complexity.
|
|
||||||
- Reuse existing Welshman and Flotilla primitives before introducing new utilities or dependencies.
|
|
||||||
- Favor linear control flow and explicit naming over clever patterns.
|
|
||||||
- Remove defensive checks that do not apply in this runtime model.
|
|
||||||
- When two approaches work, pick the one that feels more human and easier to maintain.
|
|
||||||
|
|
||||||
## Common Tasks
|
## Common Tasks
|
||||||
|
|
||||||
### Adding a New Component
|
### Adding a New Component
|
||||||
|
|||||||
@@ -1,31 +1,24 @@
|
|||||||
# Stage 1: Build
|
FROM node:20-slim
|
||||||
# Uses .env from build context for config (logo, branding, etc.)
|
|
||||||
# Optional: docker build --build-arg VITE_BUILD_HASH=$(git rev-parse --short HEAD) -t flotilla .
|
|
||||||
|
|
||||||
FROM node:20-bookworm AS builder
|
|
||||||
|
|
||||||
|
# Install pnpm
|
||||||
RUN npm install -g pnpm@latest
|
RUN npm install -g pnpm@latest
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
COPY package.json pnpm-lock.yaml ./
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
RUN pnpm i
|
RUN pnpm i
|
||||||
|
|
||||||
# Copy everything (including .env when present) - build.sh will source it
|
# Copy the rest of the application
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ARG VITE_BUILD_HASH
|
# Build the application
|
||||||
ENV VITE_BUILD_HASH=${VITE_BUILD_HASH}
|
|
||||||
|
|
||||||
ENV NODE_OPTIONS=--max_old_space_size=16384
|
ENV NODE_OPTIONS=--max_old_space_size=16384
|
||||||
RUN pnpm run build
|
RUN pnpm run build
|
||||||
|
|
||||||
# Stage 2: Runtime
|
# Default to serving the build directory
|
||||||
FROM node:20-alpine
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy only the built output - no source, no .env, no dev deps
|
|
||||||
COPY --from=builder /app/build ./build
|
|
||||||
|
|
||||||
CMD ["npx", "serve", "build"]
|
CMD ["npx", "serve", "build"]
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ You can also optionally create an `.env` file and populate it with the following
|
|||||||
- `VITE_DEFAULT_PUBKEYS` - A comma-separated list of hex pubkeys for bootstrapping web of trust
|
- `VITE_DEFAULT_PUBKEYS` - A comma-separated list of hex pubkeys for bootstrapping web of trust
|
||||||
- `VITE_PLATFORM_URL` - The url where the app will be hosted
|
- `VITE_PLATFORM_URL` - The url where the app will be hosted
|
||||||
- `VITE_PLATFORM_NAME` - The name of the app
|
- `VITE_PLATFORM_NAME` - The name of the app
|
||||||
- `VITE_PLATFORM_LOGO` - A logo url for the app. Can be a local path or https link. Must be a PNG file.
|
- `VITE_PLATFORM_LOGO` - A logo url for the app
|
||||||
- `VITE_PLATFORM_RELAYS` - A list of comma-separated relay urls that will make flotilla operate in "platform mode". Disables all space browse/add/select functionality and makes the first platform relay the home page.
|
- `VITE_PLATFORM_RELAYS` - A list of comma-separated relay urls that will make flotilla operate in "platform mode". Disables all space browse/add/select functionality and makes the first platform relay the home page.
|
||||||
- `VITE_PLATFORM_ACCENT` - A hex color for the app's accent color
|
- `VITE_PLATFORM_ACCENT` - A hex color for the app's accent color
|
||||||
- `VITE_PLATFORM_DESCRIPTION` - A description of the app
|
- `VITE_PLATFORM_DESCRIPTION` - A description of the app
|
||||||
|
|||||||
@@ -27,4 +27,4 @@ include ':capawesome-capacitor-badge'
|
|||||||
project(':capawesome-capacitor-badge').projectDir = new File('../node_modules/.pnpm/@capawesome+capacitor-badge@8.0.0_@capacitor+core@8.0.1/node_modules/@capawesome/capacitor-badge/android')
|
project(':capawesome-capacitor-badge').projectDir = new File('../node_modules/.pnpm/@capawesome+capacitor-badge@8.0.0_@capacitor+core@8.0.1/node_modules/@capawesome/capacitor-badge/android')
|
||||||
|
|
||||||
include ':nostr-signer-capacitor-plugin'
|
include ':nostr-signer-capacitor-plugin'
|
||||||
project(':nostr-signer-capacitor-plugin').projectDir = new File('../node_modules/.pnpm/nostr-signer-capacitor-plugin@https+++codeload.github.com+coracle-social+nostr-signer-c_2704ecccfd05fcfb1ad8852744422b7c/node_modules/nostr-signer-capacitor-plugin/android')
|
project(':nostr-signer-capacitor-plugin').projectDir = new File('../node_modules/.pnpm/nostr-signer-capacitor-plugin@0.0.4_@capacitor+core@8.0.1/node_modules/nostr-signer-capacitor-plugin/android')
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ temp_env=$(declare -p -x)
|
|||||||
if [ -f .env.template ]; then
|
if [ -f .env.template ]; then
|
||||||
source .env.template
|
source .env.template
|
||||||
fi
|
fi
|
||||||
if [ -f .env ]; then
|
|
||||||
source .env
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Avoid overwriting env vars provided directly
|
# Avoid overwriting env vars provided directly
|
||||||
# https://stackoverflow.com/a/69127685/1467342
|
# https://stackoverflow.com/a/69127685/1467342
|
||||||
@@ -17,13 +14,12 @@ if [[ -z $VITE_BUILD_HASH ]]; then
|
|||||||
export VITE_BUILD_HASH=$(git rev-parse --short HEAD)
|
export VITE_BUILD_HASH=$(git rev-parse --short HEAD)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $VITE_PLATFORM_LOGO =~ ^https:// ]]; then
|
if [[ $VITE_PLATFORM_LOGO =~ ^https://* ]]; then
|
||||||
curl -fSL "$VITE_PLATFORM_LOGO" -o static/logo.png
|
curl $VITE_PLATFORM_LOGO > static/logo.png
|
||||||
export VITE_PLATFORM_LOGO=static/logo.png
|
export VITE_PLATFORM_LOGO=static/logo.png
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure generator uses local path (dotenv may have loaded URL from .env)
|
npx pwa-assets-generator
|
||||||
VITE_PLATFORM_LOGO="${VITE_PLATFORM_LOGO}" npx pwa-assets-generator
|
|
||||||
npx vite build
|
npx vite build
|
||||||
|
|
||||||
# Replace index.html variables with stuff from our env
|
# Replace index.html variables with stuff from our env
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ def capacitor_pods
|
|||||||
pod 'CapacitorPreferences', :path => '../../node_modules/.pnpm/@capacitor+preferences@8.0.0_@capacitor+core@8.0.1/node_modules/@capacitor/preferences'
|
pod 'CapacitorPreferences', :path => '../../node_modules/.pnpm/@capacitor+preferences@8.0.0_@capacitor+core@8.0.1/node_modules/@capacitor/preferences'
|
||||||
pod 'CapacitorPushNotifications', :path => '../../node_modules/.pnpm/@capacitor+push-notifications@8.0.0_@capacitor+core@8.0.1/node_modules/@capacitor/push-notifications'
|
pod 'CapacitorPushNotifications', :path => '../../node_modules/.pnpm/@capacitor+push-notifications@8.0.0_@capacitor+core@8.0.1/node_modules/@capacitor/push-notifications'
|
||||||
pod 'CapawesomeCapacitorBadge', :path => '../../node_modules/.pnpm/@capawesome+capacitor-badge@8.0.0_@capacitor+core@8.0.1/node_modules/@capawesome/capacitor-badge'
|
pod 'CapawesomeCapacitorBadge', :path => '../../node_modules/.pnpm/@capawesome+capacitor-badge@8.0.0_@capacitor+core@8.0.1/node_modules/@capawesome/capacitor-badge'
|
||||||
pod 'NostrSignerCapacitorPlugin', :path => '../../node_modules/.pnpm/nostr-signer-capacitor-plugin@https+++codeload.github.com+coracle-social+nostr-signer-c_2704ecccfd05fcfb1ad8852744422b7c/node_modules/nostr-signer-capacitor-plugin'
|
pod 'NostrSignerCapacitorPlugin', :path => '../../node_modules/.pnpm/nostr-signer-capacitor-plugin@0.0.4_@capacitor+core@8.0.1/node_modules/nostr-signer-capacitor-plugin'
|
||||||
end
|
end
|
||||||
|
|
||||||
target 'Flotilla Chat' do
|
target 'Flotilla Chat' do
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
import { execSync } from 'child_process'
|
import { execSync } from 'child_process'
|
||||||
|
|
||||||
const force = process.argv.includes('--force')
|
if (execSync('git status --porcelain', { encoding: 'utf8' }).trim()) {
|
||||||
|
console.error('Error: Git working tree is dirty. Please commit or stash your changes first.')
|
||||||
if (execSync('git status --porcelain', { encoding: 'utf8' }).trim() && !force) {
|
|
||||||
console.error('Error: Git working tree is dirty. Please commit or stash your changes first, or re-run with --force.')
|
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,9 +22,7 @@ pkg.pnpm.overrides["@welshman/router"] = "link:../welshman/packages/router"
|
|||||||
pkg.pnpm.overrides["@welshman/signer"] = "link:../welshman/packages/signer"
|
pkg.pnpm.overrides["@welshman/signer"] = "link:../welshman/packages/signer"
|
||||||
pkg.pnpm.overrides["@welshman/store"] = "link:../welshman/packages/store"
|
pkg.pnpm.overrides["@welshman/store"] = "link:../welshman/packages/store"
|
||||||
pkg.pnpm.overrides["@welshman/util"] = "link:../welshman/packages/util"
|
pkg.pnpm.overrides["@welshman/util"] = "link:../welshman/packages/util"
|
||||||
// pkg.pnpm.overrides["nostr-editor"] = "link:../nostr-editor"
|
|
||||||
// pkg.pnpm.overrides["@pomade/core"] = "link:../pomade/packages/core"
|
// pkg.pnpm.overrides["@pomade/core"] = "link:../pomade/packages/core"
|
||||||
// pkg.pnpm.overrides["nostr-signer-capacitor-plugin"] = "link:../nostr-signer-capacitor-plugin"
|
|
||||||
|
|
||||||
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n')
|
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n')
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,8 @@
|
|||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "./build.sh",
|
"build": "./build.sh",
|
||||||
"release:android": "./build.sh && cap build android --androidreleasetype APK --signing-type apksigner",
|
"release:android": "./build.sh && cap build android --androidreleasetype APK --signing-type apksigner",
|
||||||
"tauri:dev": "tauri dev",
|
|
||||||
"tauri:build": "tauri build",
|
|
||||||
"tauri:info": "tauri info",
|
|
||||||
"tauri:icons": "tauri icon assets/logo.png --output src-tauri/icons",
|
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "./check.sh",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "prettier --check src && eslint src",
|
"lint": "prettier --check src && eslint src",
|
||||||
"format": "git diff head --name-only --diff-filter d | grep -E '(js|ts|svelte|css)$' | xargs -r prettier --write",
|
"format": "git diff head --name-only --diff-filter d | grep -E '(js|ts|svelte|css)$' | xargs -r prettier --write",
|
||||||
"format:all": "prettier --write src",
|
"format:all": "prettier --write src",
|
||||||
@@ -22,7 +18,6 @@
|
|||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@sveltejs/kit": "^2.50.1",
|
"@sveltejs/kit": "^2.50.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
||||||
"@tauri-apps/cli": "^2.9.6",
|
|
||||||
"@types/eslint": "^9.6.1",
|
"@types/eslint": "^9.6.1",
|
||||||
"autoprefixer": "^10.4.23",
|
"autoprefixer": "^10.4.23",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
@@ -54,10 +49,9 @@
|
|||||||
"@capacitor/push-notifications": "^8.0.0",
|
"@capacitor/push-notifications": "^8.0.0",
|
||||||
"@capawesome/capacitor-android-dark-mode-support": "^8.0.0",
|
"@capawesome/capacitor-android-dark-mode-support": "^8.0.0",
|
||||||
"@capawesome/capacitor-badge": "^8.0.0",
|
"@capawesome/capacitor-badge": "^8.0.0",
|
||||||
"@getalby/lightning-tools": "^6.1.0",
|
|
||||||
"@getalby/sdk": "^5.1.2",
|
|
||||||
"@noble/curves": "^1.9.7",
|
"@noble/curves": "^1.9.7",
|
||||||
"@pomade/core": "^0.1.1",
|
"@noble/hashes": "^2.0.1",
|
||||||
|
"@pomade/core": "^0.0.12",
|
||||||
"@poppanator/sveltekit-svg": "^4.2.1",
|
"@poppanator/sveltekit-svg": "^4.2.1",
|
||||||
"@sveltejs/adapter-static": "^3.0.10",
|
"@sveltejs/adapter-static": "^3.0.10",
|
||||||
"@tiptap/core": "^2.27.2",
|
"@tiptap/core": "^2.27.2",
|
||||||
@@ -65,16 +59,16 @@
|
|||||||
"@types/throttle-debounce": "^5.0.2",
|
"@types/throttle-debounce": "^5.0.2",
|
||||||
"@vite-pwa/assets-generator": "^0.2.6",
|
"@vite-pwa/assets-generator": "^0.2.6",
|
||||||
"@vite-pwa/sveltekit": "^0.6.8",
|
"@vite-pwa/sveltekit": "^0.6.8",
|
||||||
"@welshman/app": "^0.8.7",
|
"@welshman/app": "^0.8.4",
|
||||||
"@welshman/content": "^0.8.7",
|
"@welshman/content": "^0.8.4",
|
||||||
"@welshman/editor": "^0.8.7",
|
"@welshman/editor": "^0.8.4",
|
||||||
"@welshman/feeds": "^0.8.7",
|
"@welshman/feeds": "^0.8.4",
|
||||||
"@welshman/lib": "^0.8.7",
|
"@welshman/lib": "^0.8.4",
|
||||||
"@welshman/net": "^0.8.7",
|
"@welshman/net": "^0.8.4",
|
||||||
"@welshman/router": "^0.8.7",
|
"@welshman/router": "^0.8.4",
|
||||||
"@welshman/signer": "^0.8.7",
|
"@welshman/signer": "^0.8.4",
|
||||||
"@welshman/store": "^0.8.7",
|
"@welshman/store": "^0.8.4",
|
||||||
"@welshman/util": "^0.8.7",
|
"@welshman/util": "^0.8.4",
|
||||||
"compressorjs-next": "^1.1.2",
|
"compressorjs-next": "^1.1.2",
|
||||||
"daisyui": "^4.12.24",
|
"daisyui": "^4.12.24",
|
||||||
"date-picker-svelte": "^2.17.0",
|
"date-picker-svelte": "^2.17.0",
|
||||||
@@ -83,7 +77,7 @@
|
|||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"nostr-signer-capacitor-plugin": "github:coracle-social/nostr-signer-capacitor-plugin#main",
|
"nostr-signer-capacitor-plugin": "^0.0.4",
|
||||||
"nostr-tools": "^2.19.4",
|
"nostr-tools": "^2.19.4",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||||
"qr-scanner": "^1.4.2",
|
"qr-scanner": "^1.4.2",
|
||||||
@@ -96,8 +90,7 @@
|
|||||||
"esbuild"
|
"esbuild"
|
||||||
],
|
],
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"sharp",
|
"sharp"
|
||||||
"nostr-signer-capacitor-plugin"
|
|
||||||
],
|
],
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"sharp": "0.35.0-rc.0"
|
"sharp": "0.35.0-rc.0"
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "1.92.0"
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "flotilla"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "flotilla_lib"
|
|
||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
tauri-build = { version = "2.5.3", features = [] }
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
tauri = { version = "2.9.5", features = [] }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["custom-protocol"]
|
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
tauri_build::build()
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../gen/schemas/desktop-schema.json",
|
|
||||||
"identifier": "default",
|
|
||||||
"description": "Default desktop capability for the main window",
|
|
||||||
"windows": ["main"],
|
|
||||||
"permissions": ["core:default"]
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
|
||||||
</adaptive-icon>
|
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="ic_launcher_background">#fff</color>
|
|
||||||
</resources>
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 668 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 926 B |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
@@ -1,2 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "1.92.0"
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
||||||
pub fn run() {
|
|
||||||
tauri::Builder::default()
|
|
||||||
.run(tauri::generate_context!())
|
|
||||||
.expect("error while running tauri application");
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
flotilla_lib::run();
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
|
||||||
"productName": "Flotilla",
|
|
||||||
"mainBinaryName": "flotilla",
|
|
||||||
"identifier": "social.flotilla.app",
|
|
||||||
"build": {
|
|
||||||
"beforeDevCommand": "pnpm dev",
|
|
||||||
"beforeBuildCommand": "pnpm build",
|
|
||||||
"devUrl": "http://localhost:1847",
|
|
||||||
"frontendDist": "../build"
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"security": {
|
|
||||||
"capabilities": ["default"]
|
|
||||||
},
|
|
||||||
"windows": [
|
|
||||||
{
|
|
||||||
"label": "main",
|
|
||||||
"title": "Flotilla",
|
|
||||||
"width": 1240,
|
|
||||||
"height": 775,
|
|
||||||
"resizable": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bundle": {
|
|
||||||
"active": false,
|
|
||||||
"targets": "all",
|
|
||||||
"icon": [
|
|
||||||
"icons/32x32.png",
|
|
||||||
"icons/128x128.png",
|
|
||||||
"icons/128x128@2x.png",
|
|
||||||
"icons/icon.icns",
|
|
||||||
"icons/icon.ico"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -274,7 +274,7 @@
|
|||||||
.input-editor,
|
.input-editor,
|
||||||
.chat-editor,
|
.chat-editor,
|
||||||
.note-editor {
|
.note-editor {
|
||||||
@apply -m-1 p-1;
|
@apply -m-1 min-h-12 p-1 text-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiptap {
|
.tiptap {
|
||||||
@@ -300,7 +300,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tiptap {
|
.tiptap {
|
||||||
@apply max-h-[350px] min-h-10 overflow-y-auto p-2 px-4;
|
@apply max-h-[350px] overflow-y-auto p-2 px-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiptap p.is-editor-empty:first-child::before {
|
.tiptap p.is-editor-empty:first-child::before {
|
||||||
@@ -425,9 +425,3 @@ body.keyboard-open .hide-on-keyboard {
|
|||||||
.chat__scroll-down {
|
.chat__scroll-down {
|
||||||
@apply pb-sai fixed bottom-28 right-4 z-feature md:bottom-16;
|
@apply pb-sai fixed bottom-28 right-4 z-feature md:bottom-16;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* content visibility */
|
|
||||||
|
|
||||||
.cv {
|
|
||||||
content-visibility: auto;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
class="cv col-3 card2 bg-alt w-full cursor-pointer shadow-md"
|
class="col-3 card2 bg-alt w-full cursor-pointer shadow-md"
|
||||||
href={makeCalendarPath(url, getAddress(event))}>
|
href={makeCalendarPath(url, getAddress(event))}>
|
||||||
<CalendarEventHeader {event} />
|
<CalendarEventHeader {event} />
|
||||||
<div class="flex w-full flex-col items-end justify-between gap-2 sm:flex-row">
|
<div class="flex w-full flex-col items-end justify-between gap-2 sm:flex-row">
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {goto} from "$app/navigation"
|
||||||
|
import {preventDefault} from "@lib/html"
|
||||||
|
import {shouldUnwrap} from "@welshman/app"
|
||||||
|
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||||
|
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||||
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import Spinner from "@lib/components/Spinner.svelte"
|
||||||
|
import Modal from "@lib/components/Modal.svelte"
|
||||||
|
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||||
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
|
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||||
|
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||||
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
|
import {PLATFORM_NAME} from "@app/core/state"
|
||||||
|
import {clearModals} from "@app/util/modal"
|
||||||
|
|
||||||
|
const {next} = $props()
|
||||||
|
|
||||||
|
const nextUrl = $state.snapshot(next)
|
||||||
|
|
||||||
|
let loading = $state(false)
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
loading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
shouldUnwrap.set(true)
|
||||||
|
clearModals()
|
||||||
|
goto(nextUrl)
|
||||||
|
} finally {
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const back = () => history.back()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal tag="form" onsubmit={preventDefault(submit)}>
|
||||||
|
<ModalBody>
|
||||||
|
<ModalHeader>
|
||||||
|
<ModalTitle>Enable Messages</ModalTitle>
|
||||||
|
<ModalSubtitle>Do you want to enable direct messages?</ModalSubtitle>
|
||||||
|
</ModalHeader>
|
||||||
|
<p>
|
||||||
|
By default, direct messages are disabled, since loading them requires
|
||||||
|
{PLATFORM_NAME} to download and decrypt a lot of data.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you'd like to enable them, please make sure your signer is set up to to auto-approve
|
||||||
|
requests to decrypt data.
|
||||||
|
</p>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button class="btn btn-link" onclick={back}>
|
||||||
|
<Icon icon={AltArrowLeft} />
|
||||||
|
Go back
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||||
|
<Spinner {loading}>Enable Messages</Spinner>
|
||||||
|
<Icon icon={AltArrowRight} />
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {uniq} from "@welshman/lib"
|
|
||||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {getTagValue, getTagValues, getAddress} from "@welshman/util"
|
import {getTagValue, getAddress} from "@welshman/util"
|
||||||
import {pubkey} from "@welshman/app"
|
import {pubkey} from "@welshman/app"
|
||||||
import Pen2 from "@assets/icons/pen-2.svg?dataurl"
|
import Pen2 from "@assets/icons/pen-2.svg?dataurl"
|
||||||
import {normalizeTopic} from "@lib/util"
|
|
||||||
import Link from "@lib/components/Link.svelte"
|
import Link from "@lib/components/Link.svelte"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
@@ -29,7 +27,6 @@
|
|||||||
const {url, event, showRoom, showActivity}: Props = $props()
|
const {url, event, showRoom, showActivity}: Props = $props()
|
||||||
|
|
||||||
const h = getTagValue("h", event.tags)
|
const h = getTagValue("h", event.tags)
|
||||||
const topics = getTagValues("t", event.tags)
|
|
||||||
const path = makeClassifiedPath(url, getAddress(event))
|
const path = makeClassifiedPath(url, getAddress(event))
|
||||||
const shouldProtect = canEnforceNip70(url)
|
const shouldProtect = canEnforceNip70(url)
|
||||||
|
|
||||||
@@ -48,13 +45,6 @@
|
|||||||
Posted in #<RoomName {h} {url} />
|
Posted in #<RoomName {h} {url} />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex min-w-0 flex-wrap gap-2">
|
|
||||||
{#each uniq(topics) as topic (topic)}
|
|
||||||
<button type="button" class="btn btn-xs rounded-full font-normal">
|
|
||||||
#{normalizeTopic(topic)}
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-left" />
|
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-left" />
|
||||||
<ThunkStatusOrDeleted {event}>
|
<ThunkStatusOrDeleted {event}>
|
||||||
<ClassifiedStatus {event} />
|
<ClassifiedStatus {event} />
|
||||||
|
|||||||
@@ -18,8 +18,7 @@
|
|||||||
const {d, title, status} = fromPairs(event.tags)
|
const {d, title, status} = fromPairs(event.tags)
|
||||||
const [_, price = 0, currency = "SAT"] = getTag("price", event.tags) || []
|
const [_, price = 0, currency = "SAT"] = getTag("price", event.tags) || []
|
||||||
const images = getTagValues("image", event.tags)
|
const images = getTagValues("image", event.tags)
|
||||||
const topics = getTagValues("t", event.tags)
|
const initialValues = {d, title, status, content, price: Number(price), currency, images}
|
||||||
const initialValues = {d, title, status, content, price: Number(price), currency, images, topics}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ClassifiedForm {url} {initialValues}>
|
<ClassifiedForm {url} {initialValues}>
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {Snippet} from "svelte"
|
import type {Snippet} from "svelte"
|
||||||
import {removeUndefined, randomId, uniq} from "@welshman/lib"
|
import {randomId} from "@welshman/lib"
|
||||||
import {makeEvent, CLASSIFIED} from "@welshman/util"
|
import {makeEvent, CLASSIFIED} from "@welshman/util"
|
||||||
import {publishThunk} from "@welshman/app"
|
import {publishThunk} from "@welshman/app"
|
||||||
import {isMobile, preventDefault} from "@lib/html"
|
import {isMobile, preventDefault} from "@lib/html"
|
||||||
import {normalizeTopic} from "@lib/util"
|
|
||||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Field from "@lib/components/Field.svelte"
|
import Field from "@lib/components/Field.svelte"
|
||||||
@@ -15,7 +14,6 @@
|
|||||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||||
import ImagesInput from "@lib/components/ImagesInput.svelte"
|
import ImagesInput from "@lib/components/ImagesInput.svelte"
|
||||||
import CurrencyInput from "@app/components/CurrencyInput.svelte"
|
import CurrencyInput from "@app/components/CurrencyInput.svelte"
|
||||||
import TopicMultiSelect from "@app/components/TopicMultiSelect.svelte"
|
|
||||||
import EditorContent from "@app/editor/EditorContent.svelte"
|
import EditorContent from "@app/editor/EditorContent.svelte"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {PROTECTED} from "@app/core/state"
|
import {PROTECTED} from "@app/core/state"
|
||||||
@@ -34,7 +32,6 @@
|
|||||||
currency?: string
|
currency?: string
|
||||||
images?: string[]
|
images?: string[]
|
||||||
status?: string
|
status?: string
|
||||||
topics?: string[]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,10 +71,6 @@
|
|||||||
...ed.storage.nostr.getEditorTags(),
|
...ed.storage.nostr.getEditorTags(),
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const topic of topics) {
|
|
||||||
tags.push(["t", topic])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await shouldProtect) {
|
if (await shouldProtect) {
|
||||||
tags.push(PROTECTED)
|
tags.push(PROTECTED)
|
||||||
}
|
}
|
||||||
@@ -125,7 +118,6 @@
|
|||||||
let price = $state(Number(initialValues?.price || 0))
|
let price = $state(Number(initialValues?.price || 0))
|
||||||
let currency = $state(initialValues?.currency || "SAT")
|
let currency = $state(initialValues?.currency || "SAT")
|
||||||
let images = $state<(string | File)[]>(initialValues?.images || [])
|
let images = $state<(string | File)[]>(initialValues?.images || [])
|
||||||
let topics = $state(uniq(removeUndefined((initialValues?.topics || []).map(normalizeTopic))))
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal tag="form" onsubmit={preventDefault(submit)}>
|
<Modal tag="form" onsubmit={preventDefault(submit)}>
|
||||||
@@ -158,14 +150,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</Field>
|
</Field>
|
||||||
<Field>
|
|
||||||
{#snippet label()}
|
|
||||||
<p>Topics</p>
|
|
||||||
{/snippet}
|
|
||||||
{#snippet input()}
|
|
||||||
<TopicMultiSelect bind:value={topics} />
|
|
||||||
{/snippet}
|
|
||||||
</Field>
|
|
||||||
<Field>
|
<Field>
|
||||||
{#snippet label()}
|
{#snippet label()}
|
||||||
<p>Price*</p>
|
<p>Price*</p>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
class="cv col-2 card2 bg-alt w-full cursor-pointer shadow-xl"
|
class="col-2 card2 bg-alt w-full cursor-pointer shadow-xl"
|
||||||
href={makeClassifiedPath(url, getAddress(event))}>
|
href={makeClassifiedPath(url, getAddress(event))}>
|
||||||
{#if title}
|
{#if title}
|
||||||
<div class="flex w-full items-center justify-between gap-2">
|
<div class="flex w-full items-center justify-between gap-2">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount, onDestroy} from "svelte"
|
import {onMount, onDestroy} from "svelte"
|
||||||
import {displayUrl, once} from "@welshman/lib"
|
import {displayUrl} from "@welshman/lib"
|
||||||
import {
|
import {
|
||||||
getTags,
|
getTags,
|
||||||
getBlob,
|
getBlob,
|
||||||
@@ -26,24 +26,8 @@
|
|||||||
const key = getTagValue("decryption-key", meta)
|
const key = getTagValue("decryption-key", meta)
|
||||||
const nonce = getTagValue("decryption-nonce", meta)
|
const nonce = getTagValue("decryption-nonce", meta)
|
||||||
const algorithm = getTagValue("encryption-algorithm", meta)
|
const algorithm = getTagValue("encryption-algorithm", meta)
|
||||||
const mime = getTagValue("m", meta)
|
|
||||||
const fileName =
|
|
||||||
getTagValue("filename", meta) ||
|
|
||||||
getTagValue("name", meta) ||
|
|
||||||
decodeURIComponent(new URL(url).pathname.split("/").filter(Boolean).at(-1) || "image")
|
|
||||||
|
|
||||||
const revokeSrc = () => {
|
const onError = async () => {
|
||||||
if (src.startsWith("blob:")) {
|
|
||||||
URL.revokeObjectURL(src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setBlobSrc = (data: Blob | Uint8Array<ArrayBuffer>, type?: string) => {
|
|
||||||
revokeSrc()
|
|
||||||
src = URL.createObjectURL(new File([data], fileName, type ? {type} : undefined))
|
|
||||||
}
|
|
||||||
|
|
||||||
const onError = once(async () => {
|
|
||||||
// If the image failed to load, try authenticating
|
// If the image failed to load, try authenticating
|
||||||
if (hash && $signer) {
|
if (hash && $signer) {
|
||||||
const server = new URL(url).origin
|
const server = new URL(url).origin
|
||||||
@@ -52,15 +36,14 @@
|
|||||||
const res = await getBlob(server, hash, {authEvent})
|
const res = await getBlob(server, hash, {authEvent})
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
const blob = await res.blob()
|
src = URL.createObjectURL(await res.blob())
|
||||||
setBlobSrc(blob, blob.type || undefined)
|
|
||||||
} else {
|
} else {
|
||||||
hasError = true
|
hasError = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hasError = true
|
hasError = true
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
let hasError = $state(false)
|
let hasError = $state(false)
|
||||||
let src = $state("")
|
let src = $state("")
|
||||||
@@ -74,7 +57,7 @@
|
|||||||
const ciphertext = new Uint8Array(await response.arrayBuffer())
|
const ciphertext = new Uint8Array(await response.arrayBuffer())
|
||||||
const decryptedData = await decryptFile({ciphertext, key, nonce, algorithm})
|
const decryptedData = await decryptFile({ciphertext, key, nonce, algorithm})
|
||||||
|
|
||||||
setBlobSrc(new Uint8Array(decryptedData), mime)
|
src = URL.createObjectURL(new Blob([new Uint8Array(decryptedData)]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
src = url
|
src = url
|
||||||
@@ -82,7 +65,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
revokeSrc()
|
URL.revokeObjectURL(src)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -96,10 +96,6 @@
|
|||||||
params={{
|
params={{
|
||||||
trigger: "manual",
|
trigger: "manual",
|
||||||
interactive: true,
|
interactive: true,
|
||||||
placement: "bottom",
|
|
||||||
getReferenceClientRect: () => wrapper!.getBoundingClientRect(),
|
getReferenceClientRect: () => wrapper!.getBoundingClientRect(),
|
||||||
onShow: (instance: Instance) => {
|
|
||||||
instance.popper.style.width = `${wrapper!.getBoundingClientRect().width + 8}px`
|
|
||||||
},
|
|
||||||
}} />
|
}} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<Icon icon={Reply} />
|
<Icon icon={Reply} />
|
||||||
<span>{$replies.length} {$replies.length === 1 ? "reply" : "replies"}</span>
|
<span>{$replies.length} {$replies.length === 1 ? "reply" : "replies"}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn btn-neutral btn-xs relative rounded-full">
|
<div class="btn btn-neutral btn-xs relative hidden rounded-full sm:flex">
|
||||||
{#if gt(lastActive, $checked)}
|
{#if gt(lastActive, $checked)}
|
||||||
<div class="h-2 w-2 rounded-full bg-primary"></div>
|
<div class="h-2 w-2 rounded-full bg-primary"></div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -20,9 +20,7 @@
|
|||||||
const h = getTagValue("h", event.tags)
|
const h = getTagValue("h", event.tags)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Link
|
<Link class="col-2 card2 bg-alt w-full cursor-pointer shadow-md" href={makeGoalPath(url, event.id)}>
|
||||||
class="cv col-2 card2 bg-alt w-full cursor-pointer shadow-md"
|
|
||||||
href={makeGoalPath(url, event.id)}>
|
|
||||||
<p class="text-2xl">{event.content}</p>
|
<p class="text-2xl">{event.content}</p>
|
||||||
<Content
|
<Content
|
||||||
event={{content: summary, tags: event.tags}}
|
event={{content: summary, tags: event.tags}}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
return pushToast({
|
return pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: `Failed to recover: ${request.messages[0]?.res?.message.toLowerCase()}`,
|
message: `Failed to recover: ${request.messages[0]?.payload.message.toLowerCase()}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
|
|
||||||
return pushToast({
|
return pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: `Failed to recover: ${result.messages[0]?.res?.message.toLowerCase()}`,
|
message: `Failed to recover: ${result.messages[0]?.payload.message.toLowerCase()}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import MenuSpacesItem from "@app/components/MenuSpacesItem.svelte"
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
urls: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const {urls}: Props = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="column menu gap-2">
|
||||||
|
{#each urls as url (url)}
|
||||||
|
<MenuSpacesItem {url} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Capacitor} from "@capacitor/core"
|
import {Capacitor} from "@capacitor/core"
|
||||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
|
||||||
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
||||||
import Server from "@assets/icons/server.svg?dataurl"
|
import Server from "@assets/icons/server.svg?dataurl"
|
||||||
import Moon from "@assets/icons/moon.svg?dataurl"
|
import Moon from "@assets/icons/moon.svg?dataurl"
|
||||||
@@ -20,8 +19,8 @@
|
|||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
import {theme} from "@app/util/theme"
|
import {theme} from "@app/util/theme"
|
||||||
|
|
||||||
const back = () => history.back()
|
|
||||||
const logout = () => pushModal(LogOut)
|
const logout = () => pushModal(LogOut)
|
||||||
|
|
||||||
const toggleTheme = () => theme.set($theme === "dark" ? "light" : "dark")
|
const toggleTheme = () => theme.set($theme === "dark" ? "light" : "dark")
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -124,10 +123,6 @@
|
|||||||
<Button onclick={logout} class="btn btn-neutral">
|
<Button onclick={logout} class="btn btn-neutral">
|
||||||
<Icon icon={Exit} /> Log Out
|
<Icon icon={Exit} /> Log Out
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="btn btn-link w-full md:hidden" onclick={back}>
|
|
||||||
<Icon icon={AltArrowLeft} />
|
|
||||||
Go back
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Link replaceState href={path}>
|
<Link replaceState href={path}>
|
||||||
<CardButton class="btn-neutral shadow-md bg-alt rounded-box border-none">
|
<CardButton class="btn-neutral shadow-md bg-alt">
|
||||||
{#snippet icon()}
|
{#snippet icon()}
|
||||||
<RelayIcon {url} size={12} class="rounded-full" />
|
<RelayIcon {url} size={12} class="rounded-full" />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
import type {Snippet} from "svelte"
|
import type {Snippet} from "svelte"
|
||||||
import {formatTimestamp} from "@welshman/lib"
|
import {formatTimestamp} from "@welshman/lib"
|
||||||
|
import {getListTags, getPubkeyTagValues} from "@welshman/util"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent} from "@welshman/util"
|
||||||
|
import {userMuteList} from "@welshman/app"
|
||||||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Profile from "@app/components/Profile.svelte"
|
import Profile from "@app/components/Profile.svelte"
|
||||||
import ProfileName from "@app/components/ProfileName.svelte"
|
import ProfileName from "@app/components/ProfileName.svelte"
|
||||||
import {goToEvent} from "@app/util/routes"
|
import {goToEvent} from "@app/util/routes"
|
||||||
import {isEventMuted} from "@app/core/state"
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
event,
|
event,
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
muted = false
|
muted = false
|
||||||
}
|
}
|
||||||
|
|
||||||
let muted = $state($isEventMuted(event))
|
let muted = $state(getPubkeyTagValues(getListTags($userMuteList)).includes(event.pubkey))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 shadow-md {restProps.class}">
|
<div class="flex flex-col gap-2 shadow-md {restProps.class}">
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<NoteCard {event} {url} class="cv card2 bg-alt">
|
<NoteCard {event} {url} class="card2 bg-alt">
|
||||||
<NoteContent {event} expandMode="inline" />
|
<NoteContent {event} expandMode="inline" />
|
||||||
<div class="flex w-full justify-between gap-2">
|
<div class="flex w-full justify-between gap-2">
|
||||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-right">
|
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-right">
|
||||||
|
|||||||
@@ -17,28 +17,35 @@
|
|||||||
if (!isPomadeSession($session)) return
|
if (!isPomadeSession($session)) return
|
||||||
|
|
||||||
const client = new Client($session.clientOptions)
|
const client = new Client($session.clientOptions)
|
||||||
const result = await client.listSessions()
|
|
||||||
const pubkey = await client.getPubkey()
|
|
||||||
|
|
||||||
if (result.ok) {
|
try {
|
||||||
// Group sessions by client pubkey and collect peers
|
const result = await client.listSessions()
|
||||||
const sessionMap = new Map<string, SessionWithPeers>()
|
const pubkey = await client.getPubkey()
|
||||||
|
|
||||||
for (const message of result.messages) {
|
if (result.ok) {
|
||||||
if (!message.res?.items) continue
|
// Group sessions by client pubkey and collect peers
|
||||||
|
const sessionMap = new Map<string, SessionWithPeers>()
|
||||||
|
|
||||||
for (const item of message.res.items) {
|
for (const message of result.messages) {
|
||||||
const existing = sessionMap.get(item.client)
|
if (!message?.payload.items) continue
|
||||||
|
|
||||||
if (existing) {
|
const peer = message.event.pubkey
|
||||||
existing.peers.push(message.url)
|
|
||||||
} else if (item.client !== pubkey) {
|
for (const item of message.payload.items) {
|
||||||
sessionMap.set(item.client, {...item, peers: [message.url]})
|
const existing = sessionMap.get(item.client)
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
existing.peers.push(peer)
|
||||||
|
} else if (item.client !== pubkey) {
|
||||||
|
sessionMap.set(item.client, {...item, peers: [peer]})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sessions = Array.from(sessionMap.values())
|
sessions = Array.from(sessionMap.values())
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
client.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +71,8 @@
|
|||||||
message: "Failed to delete session",
|
message: "Failed to delete session",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.stop()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
pushToast({
|
pushToast({
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {Snippet} from "svelte"
|
import type {Snippet} from "svelte"
|
||||||
|
import {goto} from "$app/navigation"
|
||||||
import {splitAt} from "@welshman/lib"
|
import {splitAt} from "@welshman/lib"
|
||||||
import {userProfile} from "@welshman/app"
|
import {userProfile, shouldUnwrap} from "@welshman/app"
|
||||||
import Widget from "@assets/icons/widget.svg?dataurl"
|
import Widget from "@assets/icons/widget.svg?dataurl"
|
||||||
import Compass from "@assets/icons/compass.svg?dataurl"
|
import Compass from "@assets/icons/compass.svg?dataurl"
|
||||||
import Letter from "@assets/icons/letter.svg?dataurl"
|
import Letter from "@assets/icons/letter.svg?dataurl"
|
||||||
@@ -13,12 +14,13 @@
|
|||||||
import ImageIcon from "@lib/components/ImageIcon.svelte"
|
import ImageIcon from "@lib/components/ImageIcon.svelte"
|
||||||
import Divider from "@lib/components/Divider.svelte"
|
import Divider from "@lib/components/Divider.svelte"
|
||||||
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
|
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
|
||||||
|
import ChatEnable from "@app/components/ChatEnable.svelte"
|
||||||
|
import MenuOtherSpaces from "@app/components/MenuOtherSpaces.svelte"
|
||||||
import MenuSettings from "@app/components/MenuSettings.svelte"
|
import MenuSettings from "@app/components/MenuSettings.svelte"
|
||||||
import PrimaryNavItemSpace from "@app/components/PrimaryNavItemSpace.svelte"
|
import PrimaryNavItemSpace from "@app/components/PrimaryNavItemSpace.svelte"
|
||||||
import {userSpaceUrls, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
|
import {userSpaceUrls, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
import {notifications} from "@app/util/notifications"
|
import {notifications} from "@app/util/notifications"
|
||||||
import {goToLastChat} from "@app/util/routes"
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children?: Snippet
|
children?: Snippet
|
||||||
@@ -26,8 +28,12 @@
|
|||||||
|
|
||||||
const {children}: Props = $props()
|
const {children}: Props = $props()
|
||||||
|
|
||||||
|
const showOtherSpacesMenu = () => pushModal(MenuOtherSpaces, {urls: secondarySpaceUrls})
|
||||||
|
|
||||||
const showSettingsMenu = () => pushModal(MenuSettings)
|
const showSettingsMenu = () => pushModal(MenuSettings)
|
||||||
|
|
||||||
|
const openChat = () => ($shouldUnwrap ? goto("/chat") : pushModal(ChatEnable, {next: "/chat"}))
|
||||||
|
|
||||||
let windowHeight = $state(0)
|
let windowHeight = $state(0)
|
||||||
|
|
||||||
const itemHeight = 56
|
const itemHeight = 56
|
||||||
@@ -54,13 +60,15 @@
|
|||||||
{#each primarySpaceUrls as url (url)}
|
{#each primarySpaceUrls as url (url)}
|
||||||
<PrimaryNavItemSpace {url} />
|
<PrimaryNavItemSpace {url} />
|
||||||
{/each}
|
{/each}
|
||||||
<PrimaryNavItem
|
{#if secondarySpaceUrls.length > 0}
|
||||||
href="/spaces"
|
<PrimaryNavItem
|
||||||
title="All Spaces"
|
title="Other Spaces"
|
||||||
class="tooltip-right"
|
class="tooltip-right"
|
||||||
notification={otherSpaceNotifications}>
|
onclick={showOtherSpacesMenu}
|
||||||
<ImageIcon alt="All Spaces" src={Widget} size={8} />
|
notification={otherSpaceNotifications}>
|
||||||
</PrimaryNavItem>
|
<ImageIcon alt="Other Spaces" src={Widget} size={8} />
|
||||||
|
</PrimaryNavItem>
|
||||||
|
{/if}
|
||||||
<PrimaryNavItem title="Add a Space" href="/discover" class="tooltip-right">
|
<PrimaryNavItem title="Add a Space" href="/discover" class="tooltip-right">
|
||||||
<ImageIcon alt="Add a Space" src={Compass} size={8} />
|
<ImageIcon alt="Add a Space" src={Compass} size={8} />
|
||||||
</PrimaryNavItem>
|
</PrimaryNavItem>
|
||||||
@@ -83,7 +91,7 @@
|
|||||||
</PrimaryNavItem>
|
</PrimaryNavItem>
|
||||||
<PrimaryNavItem
|
<PrimaryNavItem
|
||||||
title="Messages"
|
title="Messages"
|
||||||
onclick={goToLastChat}
|
onclick={openChat}
|
||||||
class="tooltip-right"
|
class="tooltip-right"
|
||||||
notification={$notifications.has("/chat")}>
|
notification={$notifications.has("/chat")}>
|
||||||
<ImageIcon alt="Messages" src={Letter} size={8} />
|
<ImageIcon alt="Messages" src={Letter} size={8} />
|
||||||
@@ -110,7 +118,7 @@
|
|||||||
</PrimaryNavItem>
|
</PrimaryNavItem>
|
||||||
<PrimaryNavItem
|
<PrimaryNavItem
|
||||||
title="Messages"
|
title="Messages"
|
||||||
onclick={goToLastChat}
|
onclick={openChat}
|
||||||
notification={$notifications.has("/chat")}>
|
notification={$notifications.has("/chat")}>
|
||||||
<ImageIcon alt="Messages" src={Letter} size={8} />
|
<ImageIcon alt="Messages" src={Letter} size={8} />
|
||||||
</PrimaryNavItem>
|
</PrimaryNavItem>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import {removeUndefined} from "@welshman/lib"
|
import {removeUndefined} from "@welshman/lib"
|
||||||
import {ManagementMethod} from "@welshman/util"
|
import {ManagementMethod} from "@welshman/util"
|
||||||
import {
|
import {
|
||||||
|
shouldUnwrap,
|
||||||
manageRelay,
|
manageRelay,
|
||||||
deriveProfile,
|
deriveProfile,
|
||||||
displayProfileByPubkey,
|
displayProfileByPubkey,
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
import ProfileInfo from "@app/components/ProfileInfo.svelte"
|
import ProfileInfo from "@app/components/ProfileInfo.svelte"
|
||||||
import EventInfo from "@app/components/EventInfo.svelte"
|
import EventInfo from "@app/components/EventInfo.svelte"
|
||||||
import ProfileBadges from "@app/components/ProfileBadges.svelte"
|
import ProfileBadges from "@app/components/ProfileBadges.svelte"
|
||||||
|
import ChatEnable from "@app/components/ChatEnable.svelte"
|
||||||
import {pubkeyLink, deriveUserIsSpaceAdmin, deriveSpaceBannedPubkeyItems} from "@app/core/state"
|
import {pubkeyLink, deriveUserIsSpaceAdmin, deriveSpaceBannedPubkeyItems} from "@app/core/state"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
|
|
||||||
const showInfo = () => pushModal(EventInfo, {url, event: $profile!.event})
|
const showInfo = () => pushModal(EventInfo, {url, event: $profile!.event})
|
||||||
|
|
||||||
const openChat = () => goto(chatPath)
|
const openChat = () => ($shouldUnwrap ? goto(chatPath) : pushModal(ChatEnable, {next: chatPath}))
|
||||||
|
|
||||||
const toggleMenu = (pubkey: string) => {
|
const toggleMenu = (pubkey: string) => {
|
||||||
showMenu = !showMenu
|
showMenu = !showMenu
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
import type {Profile} from "@welshman/util"
|
import type {Profile} from "@welshman/util"
|
||||||
import {getTag, makeProfile} from "@welshman/util"
|
import {getTag, makeProfile} from "@welshman/util"
|
||||||
import {pubkey, profilesByPubkey} from "@welshman/app"
|
import {pubkey, profilesByPubkey} from "@welshman/app"
|
||||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import ProfileEditForm from "@app/components/ProfileEditForm.svelte"
|
import ProfileEditForm from "@app/components/ProfileEditForm.svelte"
|
||||||
import {clearModals} from "@app/util/modal"
|
import {clearModals} from "@app/util/modal"
|
||||||
@@ -26,10 +24,9 @@
|
|||||||
|
|
||||||
<ProfileEditForm {initialValues} {onsubmit}>
|
<ProfileEditForm {initialValues} {onsubmit}>
|
||||||
{#snippet footer()}
|
{#snippet footer()}
|
||||||
<Button class="btn btn-link" onclick={back}>
|
<div class="mt-4 flex flex-row items-center justify-between gap-4">
|
||||||
<Icon icon={AltArrowLeft} />
|
<Button class="btn btn-neutral" onclick={back}>Discard Changes</Button>
|
||||||
Go Back
|
<Button type="submit" class="btn btn-primary">Save Changes</Button>
|
||||||
</Button>
|
</div>
|
||||||
<Button type="submit" class="btn btn-primary">Save Changes</Button>
|
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</ProfileEditForm>
|
</ProfileEditForm>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Modal from "@lib/components/Modal.svelte"
|
import Modal from "@lib/components/Modal.svelte"
|
||||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
|
||||||
import InputProfilePicture from "@app/components/InputProfilePicture.svelte"
|
import InputProfilePicture from "@app/components/InputProfilePicture.svelte"
|
||||||
import InfoHandle from "@app/components/InfoHandle.svelte"
|
import InfoHandle from "@app/components/InfoHandle.svelte"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
@@ -124,7 +123,5 @@
|
|||||||
</FieldInline>
|
</FieldInline>
|
||||||
{/if}
|
{/if}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
{@render footer()}
|
||||||
{@render footer()}
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let label: Element | undefined = $state()
|
let input: Element | undefined = $state()
|
||||||
let popover: Instance | undefined = $state()
|
let popover: Instance | undefined = $state()
|
||||||
let instance: any = $state()
|
let instance: any = $state()
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<label class="input input-bordered flex w-full items-center gap-2" bind:this={label}>
|
<label class="input input-bordered flex w-full items-center gap-2" bind:this={input}>
|
||||||
<Icon icon={Magnifier} />
|
<Icon icon={Magnifier} />
|
||||||
<!-- svelte-ignore a11y_autofocus -->
|
<!-- svelte-ignore a11y_autofocus -->
|
||||||
<input
|
<input
|
||||||
@@ -114,15 +114,12 @@
|
|||||||
select: selectPubkey,
|
select: selectPubkey,
|
||||||
component: ProfileSuggestion,
|
component: ProfileSuggestion,
|
||||||
class: "rounded-box",
|
class: "rounded-box",
|
||||||
style: `left: 4px; width: ${label?.clientWidth + 12}px`,
|
style: `left: 4px; width: ${input?.clientWidth + 12}px`,
|
||||||
}}
|
}}
|
||||||
params={{
|
params={{
|
||||||
trigger: "manual",
|
trigger: "manual",
|
||||||
interactive: true,
|
interactive: true,
|
||||||
placement: "bottom",
|
maxWidth: "none",
|
||||||
getReferenceClientRect: () => label!.getBoundingClientRect(),
|
getReferenceClientRect: () => input!.getBoundingClientRect(),
|
||||||
onShow: (instance: Instance) => {
|
|
||||||
instance.popper.style.width = `${label!.getBoundingClientRect().width + 8}px`
|
|
||||||
},
|
|
||||||
}} />
|
}} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
const onClick = () => goto(h ? makeRoomPath(url, h) : makeSpaceChatPath(url))
|
const onClick = () => goto(h ? makeRoomPath(url, h) : makeSpaceChatPath(url))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button class="cv card2 bg-alt shadow-md" onclick={onClick}>
|
<Button class="card2 bg-alt shadow-md" onclick={onClick}>
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
<div class="flex items-center gap-2 text-sm">
|
<div class="flex items-center gap-2 text-sm">
|
||||||
{#if h}
|
{#if h}
|
||||||
|
|||||||
@@ -152,18 +152,18 @@
|
|||||||
transition:fly
|
transition:fly
|
||||||
class="bg-alt menu absolute right-0 z-popover w-48 gap-1 rounded-box p-2 shadow-md">
|
class="bg-alt menu absolute right-0 z-popover w-48 gap-1 rounded-box p-2 shadow-md">
|
||||||
{#if $userIsAdmin}
|
{#if $userIsAdmin}
|
||||||
<li>
|
|
||||||
<Button onclick={startEdit}>
|
|
||||||
<Icon icon={Pen} />
|
|
||||||
Edit Room
|
|
||||||
</Button>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<Button class="text-error" onclick={startDelete}>
|
<Button class="text-error" onclick={startDelete}>
|
||||||
<Icon icon={TrashBin2} />
|
<Icon icon={TrashBin2} />
|
||||||
Delete Room
|
Delete Room
|
||||||
</Button>
|
</Button>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<Button onclick={startEdit}>
|
||||||
|
<Icon icon={Pen} />
|
||||||
|
Edit Room
|
||||||
|
</Button>
|
||||||
|
</li>
|
||||||
{:else if $membershipStatus === MembershipStatus.Initial}
|
{:else if $membershipStatus === MembershipStatus.Initial}
|
||||||
<li>
|
<li>
|
||||||
<Button disabled={loading} onclick={join}>
|
<Button disabled={loading} onclick={join}>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
|
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
|
let client: Client | undefined = undefined
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const secret = getKey<string>("signup.secret")!
|
const secret = getKey<string>("signup.secret")!
|
||||||
const {clientOptions, ...registerRes} = await Client.register(2, 3, secret)
|
const {clientOptions, ...registerRes} = await Client.register(2, 3, secret)
|
||||||
@@ -50,11 +52,12 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new Client(clientOptions)
|
client = new Client(clientOptions)
|
||||||
|
|
||||||
const setupRes = await client.setupRecovery(email, password)
|
const setupRes = await client.setupRecovery(email, password)
|
||||||
|
|
||||||
if (!setupRes.ok) {
|
if (!setupRes.ok) {
|
||||||
const message = setupRes.messages[0]?.res?.message || "Please try again."
|
const message = setupRes.messages[0]?.payload.message || "Please try again."
|
||||||
|
|
||||||
return pushToast({
|
return pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
@@ -83,6 +86,7 @@
|
|||||||
message: "Failed to register! Please try again.",
|
message: "Failed to register! Please try again.",
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
|
client?.stop()
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||