Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
275eb46565
|
|||
|
0b887d62f6
|
@@ -1,6 +1,5 @@
|
||||
--ignore-dir=.svelte-kit
|
||||
--ignore-dir=android
|
||||
--ignore-dir=target
|
||||
--ignore-dir=build
|
||||
--ignore-dir=ios/DerivedData
|
||||
--ignore-dir=ios/App/App/public
|
||||
|
||||
@@ -2,11 +2,3 @@ node_modules
|
||||
android
|
||||
ios
|
||||
build
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Env files (keep .env for build; exclude local overrides)
|
||||
.env.local
|
||||
.env.*.local
|
||||
@@ -27,10 +27,6 @@ node_modules/
|
||||
build/
|
||||
.svelte-kit/
|
||||
|
||||
# Rust/Tauri
|
||||
*target/
|
||||
src-tauri/binaries/
|
||||
|
||||
# iOS
|
||||
ios/App/App/public
|
||||
ios/DerivedData
|
||||
|
||||
@@ -1,31 +1,24 @@
|
||||
# Stage 1: Build
|
||||
# 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
|
||||
FROM node:20-slim
|
||||
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm@latest
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# Install dependencies
|
||||
RUN pnpm i
|
||||
|
||||
# Copy everything (including .env when present) - build.sh will source it
|
||||
# Copy the rest of the application
|
||||
COPY . .
|
||||
|
||||
ARG VITE_BUILD_HASH
|
||||
ENV VITE_BUILD_HASH=${VITE_BUILD_HASH}
|
||||
|
||||
# Build the application
|
||||
ENV NODE_OPTIONS=--max_old_space_size=16384
|
||||
RUN pnpm run build
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy only the built output - no source, no .env, no dev deps
|
||||
COPY --from=builder /app/build ./build
|
||||
|
||||
# Default to serving the build directory
|
||||
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_PLATFORM_URL` - The url where the app will be hosted
|
||||
- `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_ACCENT` - A hex color for the app's accent color
|
||||
- `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')
|
||||
|
||||
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
|
||||
source .env.template
|
||||
fi
|
||||
if [ -f .env ]; then
|
||||
source .env
|
||||
fi
|
||||
|
||||
# Avoid overwriting env vars provided directly
|
||||
# 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)
|
||||
fi
|
||||
|
||||
if [[ $VITE_PLATFORM_LOGO =~ ^https:// ]]; then
|
||||
curl -fSL "$VITE_PLATFORM_LOGO" -o static/logo.png
|
||||
if [[ $VITE_PLATFORM_LOGO =~ ^https://* ]]; then
|
||||
curl $VITE_PLATFORM_LOGO > static/logo.png
|
||||
export VITE_PLATFORM_LOGO=static/logo.png
|
||||
fi
|
||||
|
||||
# Ensure generator uses local path (dotenv may have loaded URL from .env)
|
||||
VITE_PLATFORM_LOGO="${VITE_PLATFORM_LOGO}" npx pwa-assets-generator
|
||||
npx pwa-assets-generator
|
||||
npx vite build
|
||||
|
||||
# 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 '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 '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
|
||||
|
||||
target 'Flotilla Chat' do
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { execSync } from 'child_process'
|
||||
|
||||
const force = process.argv.includes('--force')
|
||||
|
||||
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.')
|
||||
if (execSync('git status --porcelain', { encoding: 'utf8' }).trim()) {
|
||||
console.error('Error: Git working tree is dirty. Please commit or stash your changes first.')
|
||||
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/store"] = "link:../welshman/packages/store"
|
||||
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["nostr-signer-capacitor-plugin"] = "link:../nostr-signer-capacitor-plugin"
|
||||
|
||||
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n')
|
||||
|
||||
|
||||
@@ -6,12 +6,8 @@
|
||||
"dev": "vite dev",
|
||||
"build": "./build.sh",
|
||||
"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:watch": "./check.sh",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"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:all": "prettier --write src",
|
||||
@@ -22,7 +18,6 @@
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@sveltejs/kit": "^2.50.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
||||
"@tauri-apps/cli": "^2.9.6",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"autoprefixer": "^10.4.23",
|
||||
"classnames": "^2.5.1",
|
||||
@@ -57,7 +52,7 @@
|
||||
"@getalby/lightning-tools": "^6.1.0",
|
||||
"@getalby/sdk": "^5.1.2",
|
||||
"@noble/curves": "^1.9.7",
|
||||
"@pomade/core": "^0.1.1",
|
||||
"@pomade/core": "^0.0.12",
|
||||
"@poppanator/sveltekit-svg": "^4.2.1",
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@tiptap/core": "^2.27.2",
|
||||
@@ -65,16 +60,16 @@
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
"@vite-pwa/assets-generator": "^0.2.6",
|
||||
"@vite-pwa/sveltekit": "^0.6.8",
|
||||
"@welshman/app": "^0.8.7",
|
||||
"@welshman/content": "^0.8.7",
|
||||
"@welshman/editor": "^0.8.7",
|
||||
"@welshman/feeds": "^0.8.7",
|
||||
"@welshman/lib": "^0.8.7",
|
||||
"@welshman/net": "^0.8.7",
|
||||
"@welshman/router": "^0.8.7",
|
||||
"@welshman/signer": "^0.8.7",
|
||||
"@welshman/store": "^0.8.7",
|
||||
"@welshman/util": "^0.8.7",
|
||||
"@welshman/app": "^0.8.4",
|
||||
"@welshman/content": "^0.8.4",
|
||||
"@welshman/editor": "^0.8.4",
|
||||
"@welshman/feeds": "^0.8.4",
|
||||
"@welshman/lib": "^0.8.4",
|
||||
"@welshman/net": "^0.8.4",
|
||||
"@welshman/router": "^0.8.4",
|
||||
"@welshman/signer": "^0.8.4",
|
||||
"@welshman/store": "^0.8.4",
|
||||
"@welshman/util": "^0.8.4",
|
||||
"compressorjs-next": "^1.1.2",
|
||||
"daisyui": "^4.12.24",
|
||||
"date-picker-svelte": "^2.17.0",
|
||||
@@ -83,7 +78,7 @@
|
||||
"fuse.js": "^7.1.0",
|
||||
"husky": "^9.1.7",
|
||||
"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",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"qr-scanner": "^1.4.2",
|
||||
@@ -96,8 +91,7 @@
|
||||
"esbuild"
|
||||
],
|
||||
"onlyBuiltDependencies": [
|
||||
"sharp",
|
||||
"nostr-signer-capacitor-plugin"
|
||||
"sharp"
|
||||
],
|
||||
"overrides": {
|
||||
"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,
|
||||
.chat-editor,
|
||||
.note-editor {
|
||||
@apply -m-1 p-1;
|
||||
@apply -m-1 min-h-12 p-1 text-sm;
|
||||
}
|
||||
|
||||
.tiptap {
|
||||
@@ -300,7 +300,7 @@
|
||||
}
|
||||
|
||||
.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 {
|
||||
@@ -425,9 +425,3 @@ body.keyboard-open .hide-on-keyboard {
|
||||
.chat__scroll-down {
|
||||
@apply pb-sai fixed bottom-28 right-4 z-feature md:bottom-16;
|
||||
}
|
||||
|
||||
/* content visibility */
|
||||
|
||||
.cv {
|
||||
content-visibility: auto;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</script>
|
||||
|
||||
<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))}>
|
||||
<CalendarEventHeader {event} />
|
||||
<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">
|
||||
import {uniq} from "@welshman/lib"
|
||||
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 Pen2 from "@assets/icons/pen-2.svg?dataurl"
|
||||
import {normalizeTopic} from "@lib/util"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -29,7 +27,6 @@
|
||||
const {url, event, showRoom, showActivity}: Props = $props()
|
||||
|
||||
const h = getTagValue("h", event.tags)
|
||||
const topics = getTagValues("t", event.tags)
|
||||
const path = makeClassifiedPath(url, getAddress(event))
|
||||
const shouldProtect = canEnforceNip70(url)
|
||||
|
||||
@@ -48,13 +45,6 @@
|
||||
Posted in #<RoomName {h} {url} />
|
||||
</Link>
|
||||
{/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" />
|
||||
<ThunkStatusOrDeleted {event}>
|
||||
<ClassifiedStatus {event} />
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
const {d, title, status} = fromPairs(event.tags)
|
||||
const [_, price = 0, currency = "SAT"] = getTag("price", 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, topics}
|
||||
const initialValues = {d, title, status, content, price: Number(price), currency, images}
|
||||
</script>
|
||||
|
||||
<ClassifiedForm {url} {initialValues}>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type {Snippet} from "svelte"
|
||||
import {removeUndefined, randomId, uniq} from "@welshman/lib"
|
||||
import {randomId} from "@welshman/lib"
|
||||
import {makeEvent, CLASSIFIED} from "@welshman/util"
|
||||
import {publishThunk} from "@welshman/app"
|
||||
import {isMobile, preventDefault} from "@lib/html"
|
||||
import {normalizeTopic} from "@lib/util"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
@@ -15,7 +14,6 @@
|
||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||
import ImagesInput from "@lib/components/ImagesInput.svelte"
|
||||
import CurrencyInput from "@app/components/CurrencyInput.svelte"
|
||||
import TopicMultiSelect from "@app/components/TopicMultiSelect.svelte"
|
||||
import EditorContent from "@app/editor/EditorContent.svelte"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {PROTECTED} from "@app/core/state"
|
||||
@@ -34,7 +32,6 @@
|
||||
currency?: string
|
||||
images?: string[]
|
||||
status?: string
|
||||
topics?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +71,6 @@
|
||||
...ed.storage.nostr.getEditorTags(),
|
||||
]
|
||||
|
||||
for (const topic of topics) {
|
||||
tags.push(["t", topic])
|
||||
}
|
||||
|
||||
if (await shouldProtect) {
|
||||
tags.push(PROTECTED)
|
||||
}
|
||||
@@ -125,7 +118,6 @@
|
||||
let price = $state(Number(initialValues?.price || 0))
|
||||
let currency = $state(initialValues?.currency || "SAT")
|
||||
let images = $state<(string | File)[]>(initialValues?.images || [])
|
||||
let topics = $state(uniq(removeUndefined((initialValues?.topics || []).map(normalizeTopic))))
|
||||
</script>
|
||||
|
||||
<Modal tag="form" onsubmit={preventDefault(submit)}>
|
||||
@@ -158,14 +150,6 @@
|
||||
</div>
|
||||
{/snippet}
|
||||
</Field>
|
||||
<Field>
|
||||
{#snippet label()}
|
||||
<p>Topics</p>
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<TopicMultiSelect bind:value={topics} />
|
||||
{/snippet}
|
||||
</Field>
|
||||
<Field>
|
||||
{#snippet label()}
|
||||
<p>Price*</p>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</script>
|
||||
|
||||
<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))}>
|
||||
{#if title}
|
||||
<div class="flex w-full items-center justify-between gap-2">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {onMount, onDestroy} from "svelte"
|
||||
import {displayUrl, once} from "@welshman/lib"
|
||||
import {displayUrl} from "@welshman/lib"
|
||||
import {
|
||||
getTags,
|
||||
getBlob,
|
||||
@@ -26,24 +26,8 @@
|
||||
const key = getTagValue("decryption-key", meta)
|
||||
const nonce = getTagValue("decryption-nonce", 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 = () => {
|
||||
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 () => {
|
||||
const onError = async () => {
|
||||
// If the image failed to load, try authenticating
|
||||
if (hash && $signer) {
|
||||
const server = new URL(url).origin
|
||||
@@ -52,15 +36,14 @@
|
||||
const res = await getBlob(server, hash, {authEvent})
|
||||
|
||||
if (res.status === 200) {
|
||||
const blob = await res.blob()
|
||||
setBlobSrc(blob, blob.type || undefined)
|
||||
src = URL.createObjectURL(await res.blob())
|
||||
} else {
|
||||
hasError = true
|
||||
}
|
||||
} else {
|
||||
hasError = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let hasError = $state(false)
|
||||
let src = $state("")
|
||||
@@ -74,7 +57,7 @@
|
||||
const ciphertext = new Uint8Array(await response.arrayBuffer())
|
||||
const decryptedData = await decryptFile({ciphertext, key, nonce, algorithm})
|
||||
|
||||
setBlobSrc(new Uint8Array(decryptedData), mime)
|
||||
src = URL.createObjectURL(new Blob([new Uint8Array(decryptedData)]))
|
||||
}
|
||||
} else {
|
||||
src = url
|
||||
@@ -82,7 +65,7 @@
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
revokeSrc()
|
||||
URL.revokeObjectURL(src)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -96,10 +96,6 @@
|
||||
params={{
|
||||
trigger: "manual",
|
||||
interactive: true,
|
||||
placement: "bottom",
|
||||
getReferenceClientRect: () => wrapper!.getBoundingClientRect(),
|
||||
onShow: (instance: Instance) => {
|
||||
instance.popper.style.width = `${wrapper!.getBoundingClientRect().width + 8}px`
|
||||
},
|
||||
}} />
|
||||
</button>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<Icon icon={Reply} />
|
||||
<span>{$replies.length} {$replies.length === 1 ? "reply" : "replies"}</span>
|
||||
</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)}
|
||||
<div class="h-2 w-2 rounded-full bg-primary"></div>
|
||||
{/if}
|
||||
|
||||
@@ -20,9 +20,7 @@
|
||||
const h = getTagValue("h", event.tags)
|
||||
</script>
|
||||
|
||||
<Link
|
||||
class="cv col-2 card2 bg-alt w-full cursor-pointer shadow-md"
|
||||
href={makeGoalPath(url, event.id)}>
|
||||
<Link class="col-2 card2 bg-alt w-full cursor-pointer shadow-md" href={makeGoalPath(url, event.id)}>
|
||||
<p class="text-2xl">{event.content}</p>
|
||||
<Content
|
||||
event={{content: summary, tags: event.tags}}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
return pushToast({
|
||||
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({
|
||||
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">
|
||||
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 Server from "@assets/icons/server.svg?dataurl"
|
||||
import Moon from "@assets/icons/moon.svg?dataurl"
|
||||
@@ -20,8 +19,8 @@
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {theme} from "@app/util/theme"
|
||||
|
||||
const back = () => history.back()
|
||||
const logout = () => pushModal(LogOut)
|
||||
|
||||
const toggleTheme = () => theme.set($theme === "dark" ? "light" : "dark")
|
||||
</script>
|
||||
|
||||
@@ -124,10 +123,6 @@
|
||||
<Button onclick={logout} class="btn btn-neutral">
|
||||
<Icon icon={Exit} /> Log Out
|
||||
</Button>
|
||||
<Button class="btn btn-link w-full md:hidden" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
</div>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</script>
|
||||
|
||||
<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()}
|
||||
<RelayIcon {url} size={12} class="rounded-full" />
|
||||
{/snippet}
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
import cx from "classnames"
|
||||
import type {Snippet} from "svelte"
|
||||
import {formatTimestamp} from "@welshman/lib"
|
||||
import {getListTags, getPubkeyTagValues} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {userMuteList} from "@welshman/app"
|
||||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Profile from "@app/components/Profile.svelte"
|
||||
import ProfileName from "@app/components/ProfileName.svelte"
|
||||
import {goToEvent} from "@app/util/routes"
|
||||
import {isEventMuted} from "@app/core/state"
|
||||
|
||||
const {
|
||||
event,
|
||||
@@ -31,7 +32,7 @@
|
||||
muted = false
|
||||
}
|
||||
|
||||
let muted = $state($isEventMuted(event))
|
||||
let muted = $state(getPubkeyTagValues(getListTags($userMuteList)).includes(event.pubkey))
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2 shadow-md {restProps.class}">
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<NoteCard {event} {url} class="cv card2 bg-alt">
|
||||
<NoteCard {event} {url} class="card2 bg-alt">
|
||||
<NoteContent {event} expandMode="inline" />
|
||||
<div class="flex w-full justify-between gap-2">
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-right">
|
||||
|
||||
@@ -17,28 +17,35 @@
|
||||
if (!isPomadeSession($session)) return
|
||||
|
||||
const client = new Client($session.clientOptions)
|
||||
const result = await client.listSessions()
|
||||
const pubkey = await client.getPubkey()
|
||||
|
||||
if (result.ok) {
|
||||
// Group sessions by client pubkey and collect peers
|
||||
const sessionMap = new Map<string, SessionWithPeers>()
|
||||
try {
|
||||
const result = await client.listSessions()
|
||||
const pubkey = await client.getPubkey()
|
||||
|
||||
for (const message of result.messages) {
|
||||
if (!message.res?.items) continue
|
||||
if (result.ok) {
|
||||
// Group sessions by client pubkey and collect peers
|
||||
const sessionMap = new Map<string, SessionWithPeers>()
|
||||
|
||||
for (const item of message.res.items) {
|
||||
const existing = sessionMap.get(item.client)
|
||||
for (const message of result.messages) {
|
||||
if (!message?.payload.items) continue
|
||||
|
||||
if (existing) {
|
||||
existing.peers.push(message.url)
|
||||
} else if (item.client !== pubkey) {
|
||||
sessionMap.set(item.client, {...item, peers: [message.url]})
|
||||
const peer = message.event.pubkey
|
||||
|
||||
for (const item of message.payload.items) {
|
||||
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",
|
||||
})
|
||||
}
|
||||
|
||||
client.stop()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
pushToast({
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type {Snippet} from "svelte"
|
||||
import {goto} from "$app/navigation"
|
||||
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 Compass from "@assets/icons/compass.svg?dataurl"
|
||||
import Letter from "@assets/icons/letter.svg?dataurl"
|
||||
@@ -13,12 +14,13 @@
|
||||
import ImageIcon from "@lib/components/ImageIcon.svelte"
|
||||
import Divider from "@lib/components/Divider.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 PrimaryNavItemSpace from "@app/components/PrimaryNavItemSpace.svelte"
|
||||
import {userSpaceUrls, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {notifications} from "@app/util/notifications"
|
||||
import {goToLastChat} from "@app/util/routes"
|
||||
|
||||
type Props = {
|
||||
children?: Snippet
|
||||
@@ -26,8 +28,12 @@
|
||||
|
||||
const {children}: Props = $props()
|
||||
|
||||
const showOtherSpacesMenu = () => pushModal(MenuOtherSpaces, {urls: secondarySpaceUrls})
|
||||
|
||||
const showSettingsMenu = () => pushModal(MenuSettings)
|
||||
|
||||
const openChat = () => ($shouldUnwrap ? goto("/chat") : pushModal(ChatEnable, {next: "/chat"}))
|
||||
|
||||
let windowHeight = $state(0)
|
||||
|
||||
const itemHeight = 56
|
||||
@@ -54,13 +60,15 @@
|
||||
{#each primarySpaceUrls as url (url)}
|
||||
<PrimaryNavItemSpace {url} />
|
||||
{/each}
|
||||
<PrimaryNavItem
|
||||
href="/spaces"
|
||||
title="All Spaces"
|
||||
class="tooltip-right"
|
||||
notification={otherSpaceNotifications}>
|
||||
<ImageIcon alt="All Spaces" src={Widget} size={8} />
|
||||
</PrimaryNavItem>
|
||||
{#if secondarySpaceUrls.length > 0}
|
||||
<PrimaryNavItem
|
||||
title="Other Spaces"
|
||||
class="tooltip-right"
|
||||
onclick={showOtherSpacesMenu}
|
||||
notification={otherSpaceNotifications}>
|
||||
<ImageIcon alt="Other Spaces" src={Widget} size={8} />
|
||||
</PrimaryNavItem>
|
||||
{/if}
|
||||
<PrimaryNavItem title="Add a Space" href="/discover" class="tooltip-right">
|
||||
<ImageIcon alt="Add a Space" src={Compass} size={8} />
|
||||
</PrimaryNavItem>
|
||||
@@ -83,7 +91,7 @@
|
||||
</PrimaryNavItem>
|
||||
<PrimaryNavItem
|
||||
title="Messages"
|
||||
onclick={goToLastChat}
|
||||
onclick={openChat}
|
||||
class="tooltip-right"
|
||||
notification={$notifications.has("/chat")}>
|
||||
<ImageIcon alt="Messages" src={Letter} size={8} />
|
||||
@@ -110,7 +118,7 @@
|
||||
</PrimaryNavItem>
|
||||
<PrimaryNavItem
|
||||
title="Messages"
|
||||
onclick={goToLastChat}
|
||||
onclick={openChat}
|
||||
notification={$notifications.has("/chat")}>
|
||||
<ImageIcon alt="Messages" src={Letter} size={8} />
|
||||
</PrimaryNavItem>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import {removeUndefined} from "@welshman/lib"
|
||||
import {ManagementMethod} from "@welshman/util"
|
||||
import {
|
||||
shouldUnwrap,
|
||||
manageRelay,
|
||||
deriveProfile,
|
||||
displayProfileByPubkey,
|
||||
@@ -29,6 +30,7 @@
|
||||
import ProfileInfo from "@app/components/ProfileInfo.svelte"
|
||||
import EventInfo from "@app/components/EventInfo.svelte"
|
||||
import ProfileBadges from "@app/components/ProfileBadges.svelte"
|
||||
import ChatEnable from "@app/components/ChatEnable.svelte"
|
||||
import {pubkeyLink, deriveUserIsSpaceAdmin, deriveSpaceBannedPubkeyItems} from "@app/core/state"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
@@ -55,7 +57,7 @@
|
||||
|
||||
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) => {
|
||||
showMenu = !showMenu
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {getTag, makeProfile} from "@welshman/util"
|
||||
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 ProfileEditForm from "@app/components/ProfileEditForm.svelte"
|
||||
import {clearModals} from "@app/util/modal"
|
||||
@@ -26,10 +24,9 @@
|
||||
|
||||
<ProfileEditForm {initialValues} {onsubmit}>
|
||||
{#snippet footer()}
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go Back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary">Save Changes</Button>
|
||||
<div class="mt-4 flex flex-row items-center justify-between gap-4">
|
||||
<Button class="btn btn-neutral" onclick={back}>Discard Changes</Button>
|
||||
<Button type="submit" class="btn btn-primary">Save Changes</Button>
|
||||
</div>
|
||||
{/snippet}
|
||||
</ProfileEditForm>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Modal from "@lib/components/Modal.svelte"
|
||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import InputProfilePicture from "@app/components/InputProfilePicture.svelte"
|
||||
import InfoHandle from "@app/components/InfoHandle.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
@@ -124,7 +123,5 @@
|
||||
</FieldInline>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{@render footer()}
|
||||
</ModalFooter>
|
||||
{@render footer()}
|
||||
</Modal>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
let label: Element | undefined = $state()
|
||||
let input: Element | undefined = $state()
|
||||
let popover: Instance | undefined = $state()
|
||||
let instance: any = $state()
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
</div>
|
||||
{/each}
|
||||
</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} />
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<input
|
||||
@@ -114,15 +114,12 @@
|
||||
select: selectPubkey,
|
||||
component: ProfileSuggestion,
|
||||
class: "rounded-box",
|
||||
style: `left: 4px; width: ${label?.clientWidth + 12}px`,
|
||||
style: `left: 4px; width: ${input?.clientWidth + 12}px`,
|
||||
}}
|
||||
params={{
|
||||
trigger: "manual",
|
||||
interactive: true,
|
||||
placement: "bottom",
|
||||
getReferenceClientRect: () => label!.getBoundingClientRect(),
|
||||
onShow: (instance: Instance) => {
|
||||
instance.popper.style.width = `${label!.getBoundingClientRect().width + 8}px`
|
||||
},
|
||||
maxWidth: "none",
|
||||
getReferenceClientRect: () => input!.getBoundingClientRect(),
|
||||
}} />
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
const onClick = () => goto(h ? makeRoomPath(url, h) : makeSpaceChatPath(url))
|
||||
</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 items-center gap-2 text-sm">
|
||||
{#if h}
|
||||
|
||||
@@ -152,18 +152,18 @@
|
||||
transition:fly
|
||||
class="bg-alt menu absolute right-0 z-popover w-48 gap-1 rounded-box p-2 shadow-md">
|
||||
{#if $userIsAdmin}
|
||||
<li>
|
||||
<Button onclick={startEdit}>
|
||||
<Icon icon={Pen} />
|
||||
Edit Room
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<Button class="text-error" onclick={startDelete}>
|
||||
<Icon icon={TrashBin2} />
|
||||
Delete Room
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<Button onclick={startEdit}>
|
||||
<Icon icon={Pen} />
|
||||
Edit Room
|
||||
</Button>
|
||||
</li>
|
||||
{:else if $membershipStatus === MembershipStatus.Initial}
|
||||
<li>
|
||||
<Button disabled={loading} onclick={join}>
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
loading = true
|
||||
|
||||
let client: Client | undefined = undefined
|
||||
|
||||
try {
|
||||
const secret = getKey<string>("signup.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)
|
||||
|
||||
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({
|
||||
theme: "error",
|
||||
@@ -83,6 +86,7 @@
|
||||
message: "Failed to register! Please try again.",
|
||||
})
|
||||
} finally {
|
||||
client?.stop()
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Modal from "@lib/components/Modal.svelte"
|
||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import ProfileEditForm from "@app/components/ProfileEditForm.svelte"
|
||||
|
||||
type Props = {
|
||||
@@ -31,14 +32,16 @@
|
||||
<ModalBody>
|
||||
<ProfileEditForm isSignup {initialValues} {onsubmit}>
|
||||
{#snippet footer()}
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="btn btn-primary" type="submit">
|
||||
Create Account
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="btn btn-primary" type="submit">
|
||||
Create Account
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
{/snippet}
|
||||
</ProfileEditForm>
|
||||
</ModalBody>
|
||||
|
||||