forked from coracle/flotilla
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7334cd26f8 | |||
| 44555215cf | |||
| 0cc25913c0 | |||
| 004b30b737 | |||
| 632f330b4c | |||
| 666433912f | |||
| db98ce8db7 | |||
| 71dcfae5ff | |||
| 04155f5b23 | |||
| b4058389ec | |||
| 483fa81b74 | |||
| a8d1c4bbbc | |||
| 0a8c2faa74 | |||
| dd3231e70f | |||
| 7ff9c00032 | |||
| 9ed483abf7 | |||
| b9aeaf29a4 | |||
| 65e3f81f36 | |||
| c6641dba31 | |||
| e48d1e0e59 | |||
| d1e5aee84e | |||
| 5cb22d0bed | |||
| d1c6f53d7c | |||
| 6e238f98c0 | |||
| 290274d6c8 | |||
| e1de0239c9 | |||
| bec77d59e8 | |||
| 84f8794d7c | |||
| 4cddf41bf3 | |||
| 125a7e238e | |||
| 468200b717 | |||
| bdfcb99781 | |||
| 38da650861 | |||
| dd006badfc | |||
| 87e4e3fe5b | |||
| af3e38254f | |||
| 70843f54d3 | |||
| bda75b29b4 | |||
| 750830d593 | |||
| 3c0f1a1d2f | |||
| 4253b0ed29 | |||
| 3c9b3f23df | |||
| e0d83608be | |||
| a0301d599b | |||
| 7dcaa0e8d7 | |||
| 129f49bcc7 | |||
| fc3b68c390 | |||
| 52c7df8a15 | |||
| ce1c4dd488 | |||
| fc6a1a3819 | |||
| 69bd6d0e70 | |||
| 6d383d54e8 | |||
| 998c48b1d3 | |||
| 7217d122b5 | |||
| 1c37c5bb3d | |||
| e8f785b558 | |||
| c94d314f6d | |||
| 2672a8f922 | |||
| 8a8d80d692 | |||
| 95698813c6 | |||
| 4001e877b4 | |||
| 99defc6d79 | |||
| a94883089e |
+3
-2
@@ -1,16 +1,17 @@
|
||||
VITE_DEFAULT_PUBKEYS=06639a386c9c1014217622ccbcf40908c4f1a0c33e23f8d6d68f4abf655f8f71,266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5,391819e2f2f13b90cac7209419eb574ef7c0d1f4e81867fc24c47a3ce5e8a248,3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d,3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24,55f04590674f3648f4cdc9dc8ce32da2a282074cd0b020596ee033d12d385185,58c741aa630c2da35a56a77c1d05381908bd10504fdd2d8b43f725efa6d23196,61066504617ee79387021e18c89fb79d1ddbc3e7bff19cf2298f40466f8715e9,6389be6491e7b693e9f368ece88fcd145f07c068d2c1bbae4247b9b5ef439d32,63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed,6e75f7972397ca3295e0f4ca0fbc6eb9cc79be85bafdd56bd378220ca8eee74e,76c71aae3a491f1d9eec47cba17e229cda4113a0bbb6e6ae1776d7643e29cafa,7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194,82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2,84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240,97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322,b676ded7c768d66a757aa3967b1243d90bf57afb09d1044d3219d8d424e4aea0,dace63b00c42e6e017d00dd190a9328386002ff597b841eb5ef91de4f1ce8491,eeb11961b25442b16389fe6c7ebea9adf0ac36dd596816ea7119e521b8821b9e,fe7f6bc6f7338b76bbf80db402ade65953e20b2f23e66e898204b63cc42539a3
|
||||
VITE_DEFAULT_BLOSSOM_SERVERS=https://blossom.primal.net/
|
||||
VITE_BURROW_URL=
|
||||
VITE_PLATFORM_URL=https://flotilla.social
|
||||
VITE_PLATFORM_TERMS=https://flotilla.social/terms
|
||||
VITE_PLATFORM_PRIVACY=https://flotilla.social/privacy
|
||||
VITE_PLATFORM_NAME=Flotilla
|
||||
VITE_PLATFORM_LOGO=static/flotilla.png
|
||||
VITE_PLATFORM_LOGO=static/logo.png
|
||||
VITE_PLATFORM_RELAYS=
|
||||
VITE_PLATFORM_ACCENT="#7161FF"
|
||||
VITE_PLATFORM_SECONDARY="#EB5E28"
|
||||
VITE_PLATFORM_DESCRIPTION="Flotilla is nostr — for communities."
|
||||
VITE_INDEXER_RELAYS=wss://purplepag.es/,wss://relay.damus.io/,wss://relay.nostr.band/,wss://indexer.coracle.social/
|
||||
VITE_SIGNER_RELAYS=wss://relay.nsec.app/,wss://bucket.coracle.social/
|
||||
VITE_SIGNER_RELAYS=wss://relay.nsec.app/,wss://offchain.pub/
|
||||
VITE_NOTIFIER_PUBKEY=27b7c2ed89ef78322114225ea3ebf5f72c7767c2528d4d0c1854d039c00085df
|
||||
VITE_NOTIFIER_RELAY=wss://anchor.coracle.social/
|
||||
VITE_VAPID_PUBLIC_KEY=BIt2D4BdgdbCowD_0d3Np6GbrIGHxd7aIEUeZNe3hQuRlHz02OhzvDaai0XSFoJYVzSzdMjdyW-QhvW9_yq8j4Y
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
# Changelog
|
||||
|
||||
# 1.3.1
|
||||
|
||||
* Fix memory leak in storage adapter
|
||||
* Show fewer annoying toast messages
|
||||
|
||||
# 1.3.0
|
||||
|
||||
* Add optional badge and sound for notifications
|
||||
* Improve link rendering
|
||||
* Remove imgproxy
|
||||
* Bring back blossom feature detection for spaces
|
||||
* Improve light theme
|
||||
* Add more info to signer status
|
||||
* Simplify navigation for adding a space
|
||||
* Add ability to scan QR code for invite links
|
||||
* Streamline wallet setup and move receive address setting
|
||||
* Remove indexeddb on mobile, use capacitor file storage API
|
||||
* Fix duplicate DMs showing up
|
||||
|
||||
# 1.2.5
|
||||
|
||||
* Fix icons in build
|
||||
|
||||
# 1.2.4
|
||||
|
||||
* Add direct message alerts
|
||||
* Add alert settings page
|
||||
* Add instructions to key download
|
||||
* Add option that allows relays to strip signatures
|
||||
* Detect relays that mostly refuse to serve requests
|
||||
* Compress and upload profile images
|
||||
* Use system theme by default
|
||||
* Switch icon set, refactor how they're included
|
||||
* Use capacitor's preferences for storage instead of localStorage
|
||||
|
||||
# 1.2.3
|
||||
|
||||
* Add `created_at` to event info dialog
|
||||
|
||||
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "social.flotilla"
|
||||
minSdk rootProject.ext.minSdkVersion
|
||||
targetSdk rootProject.ext.targetSdkVersion
|
||||
versionCode 24
|
||||
versionName "1.2.3"
|
||||
versionCode 28
|
||||
versionName "1.3.1"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -11,8 +11,11 @@ apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||
dependencies {
|
||||
implementation project(':capacitor-community-safe-area')
|
||||
implementation project(':capacitor-app')
|
||||
implementation project(':capacitor-filesystem')
|
||||
implementation project(':capacitor-keyboard')
|
||||
implementation project(':capacitor-preferences')
|
||||
implementation project(':capacitor-push-notifications')
|
||||
implementation project(':capawesome-capacitor-android-dark-mode-support')
|
||||
implementation project(':capawesome-capacitor-badge')
|
||||
implementation project(':nostr-signer-capacitor-plugin')
|
||||
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
include ':capacitor-android'
|
||||
project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@7.2.0_@capacitor+core@7.2.0/node_modules/@capacitor/android/capacitor')
|
||||
project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@7.4.3_@capacitor+core@7.4.3/node_modules/@capacitor/android/capacitor')
|
||||
|
||||
include ':capacitor-community-safe-area'
|
||||
project(':capacitor-community-safe-area').projectDir = new File('../node_modules/.pnpm/@capacitor-community+safe-area@7.0.0-alpha.1_@capacitor+core@7.2.0/node_modules/@capacitor-community/safe-area/android')
|
||||
project(':capacitor-community-safe-area').projectDir = new File('../node_modules/.pnpm/@capacitor-community+safe-area@7.0.0-alpha.1_@capacitor+core@7.4.3/node_modules/@capacitor-community/safe-area/android')
|
||||
|
||||
include ':capacitor-app'
|
||||
project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/app/android')
|
||||
project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@7.1.0_@capacitor+core@7.4.3/node_modules/@capacitor/app/android')
|
||||
|
||||
include ':capacitor-filesystem'
|
||||
project(':capacitor-filesystem').projectDir = new File('../node_modules/.pnpm/@capacitor+filesystem@7.1.4_@capacitor+core@7.4.3/node_modules/@capacitor/filesystem/android')
|
||||
|
||||
include ':capacitor-keyboard'
|
||||
project(':capacitor-keyboard').projectDir = new File('../node_modules/.pnpm/@capacitor+keyboard@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/keyboard/android')
|
||||
project(':capacitor-keyboard').projectDir = new File('../node_modules/.pnpm/@capacitor+keyboard@7.0.3_@capacitor+core@7.4.3/node_modules/@capacitor/keyboard/android')
|
||||
|
||||
include ':capacitor-preferences'
|
||||
project(':capacitor-preferences').projectDir = new File('../node_modules/.pnpm/@capacitor+preferences@7.0.2_@capacitor+core@7.4.3/node_modules/@capacitor/preferences/android')
|
||||
|
||||
include ':capacitor-push-notifications'
|
||||
project(':capacitor-push-notifications').projectDir = new File('../node_modules/.pnpm/@capacitor+push-notifications@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/push-notifications/android')
|
||||
project(':capacitor-push-notifications').projectDir = new File('../node_modules/.pnpm/@capacitor+push-notifications@7.0.3_@capacitor+core@7.4.3/node_modules/@capacitor/push-notifications/android')
|
||||
|
||||
include ':capawesome-capacitor-android-dark-mode-support'
|
||||
project(':capawesome-capacitor-android-dark-mode-support').projectDir = new File('../node_modules/.pnpm/@capawesome+capacitor-android-dark-mode-support@7.0.0_@capacitor+core@7.4.3/node_modules/@capawesome/capacitor-android-dark-mode-support/android')
|
||||
|
||||
include ':capawesome-capacitor-badge'
|
||||
project(':capawesome-capacitor-badge').projectDir = new File('../node_modules/.pnpm/@capawesome+capacitor-badge@7.0.1_@capacitor+core@7.2.0/node_modules/@capawesome/capacitor-badge/android')
|
||||
project(':capawesome-capacitor-badge').projectDir = new File('../node_modules/.pnpm/@capawesome+capacitor-badge@7.0.1_@capacitor+core@7.4.3/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@0.0.4_@capacitor+core@7.2.0/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@7.4.3/node_modules/nostr-signer-capacitor-plugin/android')
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
|
||||
3478F0332E846FEB002431E0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3478F0322E846FEB002431E0 /* PrivacyInfo.xcprivacy */; };
|
||||
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
|
||||
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
|
||||
504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
|
||||
@@ -21,6 +22,7 @@
|
||||
051414282E0CC28400BE0BC8 /* Flotilla Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Flotilla Chat.entitlements"; sourceTree = "<group>"; };
|
||||
1F53EE54954731A2328CBC4B /* Pods-Flotilla Chat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Flotilla Chat.release.xcconfig"; path = "Pods/Target Support Files/Pods-Flotilla Chat/Pods-Flotilla Chat.release.xcconfig"; sourceTree = "<group>"; };
|
||||
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
|
||||
3478F0322E846FEB002431E0 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
|
||||
504EC3041FED79650016851F /* Flotilla Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Flotilla Chat.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
@@ -58,6 +60,7 @@
|
||||
504EC2FB1FED79650016851F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3478F0322E846FEB002431E0 /* PrivacyInfo.xcprivacy */,
|
||||
051414282E0CC28400BE0BC8 /* Flotilla Chat.entitlements */,
|
||||
504EC3061FED79650016851F /* App */,
|
||||
504EC3051FED79650016851F /* Products */,
|
||||
@@ -162,6 +165,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,
|
||||
3478F0332E846FEB002431E0 /* PrivacyInfo.xcprivacy in Resources */,
|
||||
50B271D11FEDC1A000F3C39B /* public in Resources */,
|
||||
504EC30F1FED79650016851F /* Assets.xcassets in Resources */,
|
||||
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
|
||||
@@ -354,14 +358,14 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 17;
|
||||
CURRENT_PROJECT_VERSION = 19;
|
||||
DEVELOPMENT_TEAM = S26U9DYW3A;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.2.3;
|
||||
MARKETING_VERSION = 1.3.1;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -380,14 +384,14 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 17;
|
||||
CURRENT_PROJECT_VERSION = 19;
|
||||
DEVELOPMENT_TEAM = S26U9DYW3A;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.2.3;
|
||||
MARKETING_VERSION = 1.3.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
+11
-9
@@ -1,4 +1,4 @@
|
||||
require_relative '../../node_modules/.pnpm/@capacitor+ios@7.2.0_@capacitor+core@7.2.0/node_modules/@capacitor/ios/scripts/pods_helpers'
|
||||
require_relative '../../node_modules/.pnpm/@capacitor+ios@7.4.3_@capacitor+core@7.4.3/node_modules/@capacitor/ios/scripts/pods_helpers'
|
||||
|
||||
platform :ios, '14.0'
|
||||
use_frameworks!
|
||||
@@ -9,14 +9,16 @@ use_frameworks!
|
||||
install! 'cocoapods', :disable_input_output_paths => true
|
||||
|
||||
def capacitor_pods
|
||||
pod 'Capacitor', :path => '../../node_modules/.pnpm/@capacitor+ios@7.2.0_@capacitor+core@7.2.0/node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCordova', :path => '../../node_modules/.pnpm/@capacitor+ios@7.2.0_@capacitor+core@7.2.0/node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCommunitySafeArea', :path => '../../node_modules/.pnpm/@capacitor-community+safe-area@7.0.0-alpha.1_@capacitor+core@7.2.0/node_modules/@capacitor-community/safe-area'
|
||||
pod 'CapacitorApp', :path => '../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/app'
|
||||
pod 'CapacitorKeyboard', :path => '../../node_modules/.pnpm/@capacitor+keyboard@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/keyboard'
|
||||
pod 'CapacitorPushNotifications', :path => '../../node_modules/.pnpm/@capacitor+push-notifications@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/push-notifications'
|
||||
pod 'CapawesomeCapacitorBadge', :path => '../../node_modules/.pnpm/@capawesome+capacitor-badge@7.0.1_@capacitor+core@7.2.0/node_modules/@capawesome/capacitor-badge'
|
||||
pod 'NostrSignerCapacitorPlugin', :path => '../../node_modules/.pnpm/nostr-signer-capacitor-plugin@0.0.4_@capacitor+core@7.2.0/node_modules/nostr-signer-capacitor-plugin'
|
||||
pod 'Capacitor', :path => '../../node_modules/.pnpm/@capacitor+ios@7.4.3_@capacitor+core@7.4.3/node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCordova', :path => '../../node_modules/.pnpm/@capacitor+ios@7.4.3_@capacitor+core@7.4.3/node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCommunitySafeArea', :path => '../../node_modules/.pnpm/@capacitor-community+safe-area@7.0.0-alpha.1_@capacitor+core@7.4.3/node_modules/@capacitor-community/safe-area'
|
||||
pod 'CapacitorApp', :path => '../../node_modules/.pnpm/@capacitor+app@7.1.0_@capacitor+core@7.4.3/node_modules/@capacitor/app'
|
||||
pod 'CapacitorFilesystem', :path => '../../node_modules/.pnpm/@capacitor+filesystem@7.1.4_@capacitor+core@7.4.3/node_modules/@capacitor/filesystem'
|
||||
pod 'CapacitorKeyboard', :path => '../../node_modules/.pnpm/@capacitor+keyboard@7.0.3_@capacitor+core@7.4.3/node_modules/@capacitor/keyboard'
|
||||
pod 'CapacitorPreferences', :path => '../../node_modules/.pnpm/@capacitor+preferences@7.0.2_@capacitor+core@7.4.3/node_modules/@capacitor/preferences'
|
||||
pod 'CapacitorPushNotifications', :path => '../../node_modules/.pnpm/@capacitor+push-notifications@7.0.3_@capacitor+core@7.4.3/node_modules/@capacitor/push-notifications'
|
||||
pod 'CapawesomeCapacitorBadge', :path => '../../node_modules/.pnpm/@capawesome+capacitor-badge@7.0.1_@capacitor+core@7.4.3/node_modules/@capawesome/capacitor-badge'
|
||||
pod 'NostrSignerCapacitorPlugin', :path => '../../node_modules/.pnpm/nostr-signer-capacitor-plugin@0.0.4_@capacitor+core@7.4.3/node_modules/nostr-signer-capacitor-plugin'
|
||||
end
|
||||
|
||||
target 'Flotilla Chat' do
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
+55
-52
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "flotilla",
|
||||
"version": "1.2.3",
|
||||
"version": "1.3.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
@@ -15,69 +15,72 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@capacitor/assets": "^3.0.5",
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@sentry/cli": "^2.40.0",
|
||||
"@sveltejs/kit": "^2.5.27",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||
"@types/eslint": "^9.6.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"@eslint/js": "^9.37.0",
|
||||
"@sentry/cli": "^2.56.1",
|
||||
"@sveltejs/kit": "^2.46.5",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"classnames": "^2.5.1",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.45.1",
|
||||
"globals": "^15.0.0",
|
||||
"postcss": "^8.4.40",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^3.4.7",
|
||||
"typescript": "^5.5.0",
|
||||
"typescript-eslint": "^8.0.0",
|
||||
"vite": "^5.4.4"
|
||||
"eslint": "^9.37.0",
|
||||
"eslint-config-prettier": "^9.1.2",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"globals": "^15.15.0",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"svelte": "^5.39.12",
|
||||
"svelte-check": "^4.3.3",
|
||||
"tailwindcss": "^3.4.18",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.1",
|
||||
"vite": "^5.4.20"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@capacitor-community/safe-area": "7.0.0-alpha.1",
|
||||
"@capacitor/android": "^7.0.0",
|
||||
"@capacitor/app": "^7.0.0",
|
||||
"@capacitor/cli": "^7.0.0",
|
||||
"@capacitor/core": "^7.0.1",
|
||||
"@capacitor/ios": "^7.0.0",
|
||||
"@capacitor/keyboard": "^7.0.0",
|
||||
"@capacitor/push-notifications": "^7.0.1",
|
||||
"@capacitor/android": "^7.4.3",
|
||||
"@capacitor/app": "^7.1.0",
|
||||
"@capacitor/cli": "^7.4.3",
|
||||
"@capacitor/core": "^7.4.3",
|
||||
"@capacitor/filesystem": "^7.1.4",
|
||||
"@capacitor/ios": "^7.4.3",
|
||||
"@capacitor/keyboard": "^7.0.3",
|
||||
"@capacitor/preferences": "^7.0.2",
|
||||
"@capacitor/push-notifications": "^7.0.3",
|
||||
"@capawesome/capacitor-android-dark-mode-support": "^7.0.0",
|
||||
"@capawesome/capacitor-badge": "^7.0.1",
|
||||
"@getalby/sdk": "^5.1.0",
|
||||
"@getalby/sdk": "^5.1.2",
|
||||
"@poppanator/sveltekit-svg": "^4.2.1",
|
||||
"@sentry/browser": "^8.35.0",
|
||||
"@sveltejs/adapter-static": "^3.0.4",
|
||||
"@tiptap/core": "^2.12.0",
|
||||
"@sentry/browser": "^8.55.0",
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@tiptap/core": "^2.26.3",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
"@vite-pwa/assets-generator": "^0.2.6",
|
||||
"@vite-pwa/sveltekit": "^0.6.6",
|
||||
"@welshman/app": "^0.4.3",
|
||||
"@welshman/content": "^0.4.3",
|
||||
"@welshman/editor": "^0.4.3",
|
||||
"@welshman/feeds": "^0.4.3",
|
||||
"@welshman/lib": "^0.4.3",
|
||||
"@welshman/net": "^0.4.3",
|
||||
"@welshman/relay": "^0.4.3",
|
||||
"@welshman/router": "^0.4.3",
|
||||
"@welshman/signer": "^0.4.3",
|
||||
"@welshman/store": "^0.4.3",
|
||||
"@welshman/util": "^0.4.3",
|
||||
"@vite-pwa/sveltekit": "^0.6.8",
|
||||
"@welshman/app": "^0.5.3",
|
||||
"@welshman/content": "^0.5.3",
|
||||
"@welshman/editor": "^0.5.3",
|
||||
"@welshman/feeds": "^0.5.3",
|
||||
"@welshman/lib": "^0.5.3",
|
||||
"@welshman/net": "^0.5.3",
|
||||
"@welshman/relay": "^0.5.3",
|
||||
"@welshman/router": "^0.5.3",
|
||||
"@welshman/signer": "^0.5.3",
|
||||
"@welshman/store": "^0.5.3",
|
||||
"@welshman/util": "^0.5.3",
|
||||
"compressorjs": "^1.2.1",
|
||||
"daisyui": "^4.12.10",
|
||||
"date-picker-svelte": "^2.13.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"emoji-picker-element": "^1.22.8",
|
||||
"fuse.js": "^7.0.0",
|
||||
"husky": "^9.1.6",
|
||||
"idb": "^8.0.0",
|
||||
"daisyui": "^4.12.24",
|
||||
"date-picker-svelte": "^2.16.0",
|
||||
"dotenv": "^16.6.1",
|
||||
"emoji-picker-element": "^1.27.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"husky": "^9.1.7",
|
||||
"idb": "^8.0.3",
|
||||
"nostr-signer-capacitor-plugin": "^0.0.4",
|
||||
"nostr-tools": "^2.14.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||
"nostr-tools": "^2.17.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"qr-scanner": "^1.4.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"throttle-debounce": "^5.0.2",
|
||||
|
||||
Generated
+2028
-1833
File diff suppressed because it is too large
Load Diff
+4
-8
@@ -62,6 +62,8 @@
|
||||
--primary-content: oklch(var(--pc));
|
||||
--secondary: oklch(var(--s));
|
||||
--secondary-content: oklch(var(--sc));
|
||||
--neutral: oklch(var(--n));
|
||||
--neutral-content: oklch(var(--nc));
|
||||
}
|
||||
|
||||
/* safe area insets */
|
||||
@@ -215,12 +217,6 @@
|
||||
@apply ellipsize;
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
[data-tip]::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.content-padding-x {
|
||||
@apply px-4 sm:px-8 md:px-12;
|
||||
}
|
||||
@@ -278,8 +274,8 @@
|
||||
}
|
||||
|
||||
.tiptap {
|
||||
--tiptap-object-bg: var(--base-100);
|
||||
--tiptap-object-fg: var(--base-content);
|
||||
--tiptap-object-bg: var(--neutral);
|
||||
--tiptap-object-fg: var(--neutral-content);
|
||||
--tiptap-active-bg: var(--primary);
|
||||
--tiptap-active-fg: var(--primary-content);
|
||||
}
|
||||
|
||||
@@ -1,37 +1,22 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import {decrypt} from "@welshman/signer"
|
||||
import {randomInt, parseJson, fromPairs, displayList, TIMEZONE, identity} from "@welshman/lib"
|
||||
import {
|
||||
displayRelayUrl,
|
||||
getTagValue,
|
||||
getAddress,
|
||||
THREAD,
|
||||
MESSAGE,
|
||||
EVENT_TIME,
|
||||
COMMENT,
|
||||
} from "@welshman/util"
|
||||
import {randomInt, displayList, TIMEZONE, identity} from "@welshman/lib"
|
||||
import {displayRelayUrl, getTagValue, THREAD, MESSAGE, EVENT_TIME, COMMENT} from "@welshman/util"
|
||||
import type {Filter} from "@welshman/util"
|
||||
import {makeIntersectionFeed, makeRelayFeed, feedFromFilters} from "@welshman/feeds"
|
||||
import {pubkey, signer, getThunkError} 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 FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {
|
||||
alerts,
|
||||
getMembershipUrls,
|
||||
userMembership,
|
||||
NOTIFIER_PUBKEY,
|
||||
NOTIFIER_RELAY,
|
||||
} from "@app/core/state"
|
||||
import {loadAlertStatuses, requestRelayClaim} from "@app/core/requests"
|
||||
import {publishAlert, attemptAuth} from "@app/core/commands"
|
||||
import type {AlertParams} from "@app/core/commands"
|
||||
import {platform, platformName, canSendPushNotifications, getPushInfo} from "@app/util/push"
|
||||
import {alerts, getMembershipUrls, userMembership} from "@app/core/state"
|
||||
import {requestRelayClaim} from "@app/core/requests"
|
||||
import {createAlert} from "@app/core/commands"
|
||||
import {canSendPushNotifications} from "@app/util/push"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
|
||||
type Props = {
|
||||
@@ -110,66 +95,20 @@
|
||||
|
||||
try {
|
||||
const claim = url ? await requestRelayClaim(url) : undefined
|
||||
const claims = claim ? {[url]: claim} : {}
|
||||
const feed = makeIntersectionFeed(feedFromFilters(filters), makeRelayFeed(url))
|
||||
const description = `for ${displayList(display)} on ${displayRelayUrl(url)}`
|
||||
const params: AlertParams = {feed, claims, description}
|
||||
|
||||
if (channel === "email") {
|
||||
const cadence = cron?.endsWith("1") ? "Weekly" : "Daily"
|
||||
|
||||
params.description = `${cadence} alert ${description}, sent via email.`
|
||||
params.email = {
|
||||
cron,
|
||||
email,
|
||||
handler: [
|
||||
"31990:97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322:1737058597050",
|
||||
"wss://relay.nostr.band/",
|
||||
"web",
|
||||
],
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
// @ts-ignore
|
||||
params[platform] = await getPushInfo()
|
||||
params.description = `${platformName} push notification ${description}.`
|
||||
} catch (e: any) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: String(e),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't do this we'll get an event rejection
|
||||
await attemptAuth(NOTIFIER_RELAY)
|
||||
|
||||
const thunk = await publishAlert(params)
|
||||
const error = await getThunkError(thunk)
|
||||
const {error} = await createAlert({
|
||||
feed: makeIntersectionFeed(feedFromFilters(filters), makeRelayFeed(url)),
|
||||
claims: claim ? {[url]: claim} : {},
|
||||
description: `for ${displayList(display)} on ${displayRelayUrl(url)}`,
|
||||
email: channel === "email" ? {cron, email} : undefined,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: `Failed to send your alert to the notification server (${error}).`,
|
||||
})
|
||||
pushToast({theme: "error", message: error})
|
||||
} else {
|
||||
pushToast({message: "Your alert has been successfully created!"})
|
||||
back()
|
||||
}
|
||||
|
||||
// Fetch our new status to make sure it's active
|
||||
const address = getAddress(thunk.event)
|
||||
const statusEvents = await loadAlertStatuses($pubkey!)
|
||||
const statusEvent = statusEvents.find(event => getTagValue("d", event.tags) === address)
|
||||
const statusTags = statusEvent
|
||||
? parseJson(await decrypt(signer.get(), NOTIFIER_PUBKEY, statusEvent.content))
|
||||
: []
|
||||
const {status = "error", message = "Your alert was not activated"}: Record<string, string> =
|
||||
fromPairs(statusTags)
|
||||
|
||||
if (status === "error") {
|
||||
return pushToast({theme: "error", message})
|
||||
}
|
||||
|
||||
pushToast({message: "Your alert has been successfully created!"})
|
||||
back()
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
@@ -187,6 +126,9 @@
|
||||
{#snippet title()}
|
||||
Add an Alert
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
Enable notifications to keep up to date on activity you care about.
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
{#if canSendPushNotifications()}
|
||||
<FieldInline>
|
||||
@@ -262,12 +204,12 @@
|
||||
</FieldInline>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Confirm</Spinner>
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Confirm from "@lib/components/Confirm.svelte"
|
||||
import type {Alert} from "@app/core/state"
|
||||
import {NOTIFIER_RELAY, NOTIFIER_PUBKEY} from "@app/core/state"
|
||||
import {publishDelete} from "@app/core/commands"
|
||||
import {deleteAlert} from "@app/core/commands"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
|
||||
type Props = {
|
||||
@@ -12,10 +11,7 @@
|
||||
const {alert}: Props = $props()
|
||||
|
||||
const confirm = () => {
|
||||
const relays = [NOTIFIER_RELAY]
|
||||
const tags = [["p", NOTIFIER_PUBKEY]]
|
||||
|
||||
publishDelete({event: alert.event, relays, tags, protect: false})
|
||||
deleteAlert(alert)
|
||||
pushToast({message: "Your alert has been deleted!"})
|
||||
history.back()
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import {parseJson} from "@welshman/lib"
|
||||
import {displayFeeds} from "@welshman/feeds"
|
||||
import {getAddress, getTagValue, getTagValues} from "@welshman/util"
|
||||
import {getTagValue, getTagValues} from "@welshman/util"
|
||||
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import AlertDelete from "@app/components/AlertDelete.svelte"
|
||||
import AlertStatus from "@app/components/AlertStatus.svelte"
|
||||
import type {Alert} from "@app/core/state"
|
||||
import {deriveAlertStatus} from "@app/core/state"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
|
||||
type Props = {
|
||||
@@ -15,7 +16,6 @@
|
||||
|
||||
const {alert}: Props = $props()
|
||||
|
||||
const status = deriveAlertStatus(getAddress(alert.event))
|
||||
const cron = $derived(getTagValue("cron", alert.tags))
|
||||
const channel = $derived(getTagValue("channel", alert.tags))
|
||||
const feeds = $derived(getTagValues("feed", alert.tags))
|
||||
@@ -34,36 +34,9 @@
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex items-start gap-4">
|
||||
<Button class="py-1" onclick={startDelete}>
|
||||
<Icon icon="trash-bin-2" />
|
||||
<Icon icon={TrashBin2} />
|
||||
</Button>
|
||||
<div class="flex-inline gap-1">{description}</div>
|
||||
</div>
|
||||
{#if $status}
|
||||
{@const statusText = getTagValue("status", $status.tags) || "error"}
|
||||
{#if statusText === "ok"}
|
||||
<span
|
||||
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-base-content px-3 py-1 text-sm"
|
||||
data-tip={getTagValue("message", $status.tags)}>
|
||||
Active
|
||||
</span>
|
||||
{:else if statusText === "pending"}
|
||||
<span
|
||||
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-base-content border-yellow-500 px-3 py-1 text-sm text-yellow-500"
|
||||
data-tip={getTagValue("message", $status.tags)}>
|
||||
Pending
|
||||
</span>
|
||||
{:else}
|
||||
<span
|
||||
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-error px-3 py-1 text-sm text-error"
|
||||
data-tip={getTagValue("message", $status.tags)}>
|
||||
{statusText.replace("-", " ").replace(/^(.)/, x => x.toUpperCase())}
|
||||
</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<span
|
||||
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-error px-3 py-1 text-sm text-error"
|
||||
data-tip="The notification server did not respond to your request.">
|
||||
Inactive
|
||||
</span>
|
||||
{/if}
|
||||
<AlertStatus {alert} />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import {getAddress, getTagValue} from "@welshman/util"
|
||||
import type {Alert} from "@app/core/state"
|
||||
import {deriveAlertStatus} from "@app/core/state"
|
||||
|
||||
type Props = {
|
||||
alert: Alert
|
||||
}
|
||||
|
||||
const {alert}: Props = $props()
|
||||
|
||||
const status = deriveAlertStatus(getAddress(alert.event))
|
||||
</script>
|
||||
|
||||
{#if $status}
|
||||
{@const statusText = getTagValue("status", $status.tags) || "error"}
|
||||
{#if statusText === "ok"}
|
||||
<span
|
||||
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-base-content px-3 py-1 text-sm"
|
||||
data-tip={getTagValue("message", $status.tags)}>
|
||||
Active
|
||||
</span>
|
||||
{:else if statusText === "pending"}
|
||||
<span
|
||||
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-base-content border-yellow-500 px-3 py-1 text-sm text-yellow-500"
|
||||
data-tip={getTagValue("message", $status.tags)}>
|
||||
Pending
|
||||
</span>
|
||||
{:else}
|
||||
<span
|
||||
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-error px-3 py-1 text-sm text-error"
|
||||
data-tip={getTagValue("message", $status.tags)}>
|
||||
{statusText.replace("-", " ").replace(/^(.)/, x => x.toUpperCase())}
|
||||
</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<span
|
||||
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-error px-3 py-1 text-sm text-error"
|
||||
data-tip="The notification server did not respond to your request.">
|
||||
Inactive
|
||||
</span>
|
||||
{/if}
|
||||
@@ -1,11 +1,26 @@
|
||||
<script lang="ts">
|
||||
import {getTagValue} from "@welshman/util"
|
||||
import {sleep} from "@welshman/lib"
|
||||
import {getTagValue, getAddress} from "@welshman/util"
|
||||
import {isRelayFeed, findFeed} from "@welshman/feeds"
|
||||
import Inbox from "@assets/icons/inbox.svg?dataurl"
|
||||
import Bell from "@assets/icons/bell.svg?dataurl"
|
||||
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import AlertAdd from "@app/components/AlertAdd.svelte"
|
||||
import AlertItem from "@app/components/AlertItem.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {alerts} from "@app/core/state"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {
|
||||
alerts,
|
||||
dmAlert,
|
||||
deriveAlertStatus,
|
||||
userInboxRelays,
|
||||
getAlertFeed,
|
||||
userSettingsValues,
|
||||
} from "@app/core/state"
|
||||
import {deleteAlert, createDmAlert} from "@app/core/commands"
|
||||
import {clearBadges} from "../util/notifications"
|
||||
|
||||
type Props = {
|
||||
url?: string
|
||||
@@ -15,29 +30,126 @@
|
||||
|
||||
const {url = "", channel = "push", hideSpaceField = false}: Props = $props()
|
||||
|
||||
const startAlert = () => pushModal(AlertAdd, {url, channel, hideSpaceField})
|
||||
const dmStatus = $derived($dmAlert ? deriveAlertStatus(getAddress($dmAlert.event)) : undefined)
|
||||
|
||||
const filteredAlerts = $derived(
|
||||
url ? $alerts.filter(a => getTagValue("feed", a.tags)?.includes(url)) : $alerts,
|
||||
$alerts.filter(alert => {
|
||||
const feed = getAlertFeed(alert)
|
||||
|
||||
// Skip non-feeds and DM alerts
|
||||
if (!feed || alert === $dmAlert) return false
|
||||
|
||||
// If we have a space url, only match feeds for this space
|
||||
if (url) return findFeed(feed, f => isRelayFeed(f) && f.includes(url))
|
||||
|
||||
return true
|
||||
}),
|
||||
)
|
||||
|
||||
const startAlert = () => pushModal(AlertAdd, {url, channel, hideSpaceField})
|
||||
|
||||
const uncheckDmAlert = async (message: string) => {
|
||||
await sleep(100)
|
||||
|
||||
directMessagesNotificationToggle.checked = false
|
||||
pushToast({theme: "error", message})
|
||||
}
|
||||
|
||||
const onDirectMessagesNotificationToggle = async () => {
|
||||
if ($dmAlert) {
|
||||
deleteAlert($dmAlert)
|
||||
} else {
|
||||
if ($userInboxRelays.length === 0) {
|
||||
return uncheckDmAlert("Please set up your messaging relays before enabling alerts.")
|
||||
}
|
||||
|
||||
const {error} = await createDmAlert()
|
||||
|
||||
if (error) {
|
||||
return uncheckDmAlert(error)
|
||||
}
|
||||
|
||||
pushToast({message: "Your alert has been successfully created!"})
|
||||
}
|
||||
}
|
||||
|
||||
const onShowBadgeOnUnreadToggle = async () => {
|
||||
$userSettingsValues.show_notifications_badge = !$userSettingsValues.show_notifications_badge
|
||||
|
||||
if (!$userSettingsValues.show_notifications_badge) {
|
||||
await clearBadges()
|
||||
}
|
||||
}
|
||||
|
||||
const onDirectMessagesNotificationSoundToggle = async () => {
|
||||
$userSettingsValues.play_notification_sound = !$userSettingsValues.play_notification_sound
|
||||
}
|
||||
|
||||
let directMessagesNotificationToggle: HTMLInputElement
|
||||
</script>
|
||||
|
||||
<div class="card2 bg-alt flex flex-col gap-6 shadow-xl">
|
||||
<div class="flex items-center justify-between">
|
||||
<strong class="flex items-center gap-3">
|
||||
<Icon icon="inbox" />
|
||||
Alerts
|
||||
</strong>
|
||||
<Button class="btn btn-primary btn-sm" onclick={startAlert}>
|
||||
<Icon icon="add-circle" />
|
||||
Add Alert
|
||||
</Button>
|
||||
<div class="col-4">
|
||||
<div class="card2 bg-alt flex flex-col gap-6 shadow-xl">
|
||||
<div class="flex items-center justify-between">
|
||||
<strong class="flex items-center gap-3">
|
||||
<Icon icon={Inbox} />
|
||||
Alerts
|
||||
</strong>
|
||||
<Button class="btn btn-primary btn-sm" onclick={startAlert}>
|
||||
<Icon icon={AddCircle} />
|
||||
Add Alert
|
||||
</Button>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
{#each filteredAlerts as alert (alert.event.id)}
|
||||
<AlertItem {alert} />
|
||||
{:else}
|
||||
<p class="text-center opacity-75 py-12">Nothing here yet!</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
{#each filteredAlerts as alert (alert.event.id)}
|
||||
<AlertItem {alert} />
|
||||
{:else}
|
||||
<p class="text-center opacity-75 py-12">Nothing here yet!</p>
|
||||
{/each}
|
||||
<div class="card2 bg-alt flex flex-col gap-4 shadow-xl">
|
||||
<div class="flex items-center justify-between">
|
||||
<strong class="flex items-center gap-3">
|
||||
<Icon icon={Bell} />
|
||||
Notifications
|
||||
</strong>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<p>Notify me about new direct messages</p>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-primary"
|
||||
bind:this={directMessagesNotificationToggle}
|
||||
checked={Boolean($dmAlert)}
|
||||
oninput={onDirectMessagesNotificationToggle} />
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<p>Show badge for unread direct messages</p>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-primary"
|
||||
checked={Boolean($userSettingsValues.show_notifications_badge)}
|
||||
oninput={onShowBadgeOnUnreadToggle} />
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<p>Play sound for new direct messages</p>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-primary"
|
||||
checked={Boolean($userSettingsValues.play_notification_sound)}
|
||||
oninput={onDirectMessagesNotificationSoundToggle} />
|
||||
</div>
|
||||
{#if $dmStatus}
|
||||
{@const status = getTagValue("status", $dmStatus.tags) || "error"}
|
||||
{#if status !== "ok"}
|
||||
<div class="alert alert-error border border-solid border-error bg-transparent text-error">
|
||||
<p>
|
||||
{getTagValue("message", $dmStatus.tags) ||
|
||||
"The notification server did not respond to your request."}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import Scanner from "@lib/components/Scanner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
import CpuBolt from "@assets/icons/cpu-bolt.svg?dataurl"
|
||||
import QrCode from "@assets/icons/qr-code.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import InfoBunker from "@app/components/InfoBunker.svelte"
|
||||
import type {Nip46Controller} from "@app/util/nip46"
|
||||
@@ -33,10 +35,10 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="cpu" />
|
||||
<Icon icon={CpuBolt} />
|
||||
<input disabled={$loading} bind:value={$bunker} class="grow" placeholder="bunker://" />
|
||||
<Button onclick={toggleScanner}>
|
||||
<Icon icon="qr-code" />
|
||||
<Icon icon={QrCode} />
|
||||
</Button>
|
||||
</label>
|
||||
{/snippet}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import {publishDelete, publishReaction, canEnforceNip70} from "@app/core/commands"
|
||||
import {makeCalendarPath} from "@app/util/routes"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import Pen2 from "@assets/icons/pen-2.svg?dataurl"
|
||||
|
||||
const {
|
||||
url,
|
||||
@@ -47,7 +48,7 @@
|
||||
{#if event.pubkey === $pubkey}
|
||||
<li>
|
||||
<Button onclick={editEvent}>
|
||||
<Icon size={4} icon="pen" />
|
||||
<Icon size={4} icon={Pen2} />
|
||||
Edit Event
|
||||
</Button>
|
||||
</li>
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
import {publishThunk} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import {daysBetween} from "@lib/util"
|
||||
import GallerySend from "@assets/icons/gallery-send.svg?dataurl"
|
||||
import MapPoint from "@assets/icons/map-point.svg?dataurl"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -131,7 +134,7 @@
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="gallery-send" />
|
||||
<Icon icon={GallerySend} />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -159,14 +162,14 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="map-point" />
|
||||
<Icon icon={MapPoint} />
|
||||
<input bind:value={location} class="grow" type="text" />
|
||||
</label>
|
||||
{/snippet}
|
||||
</Field>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={$uploading}>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
formatTimestampAsTime,
|
||||
} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import ClockCircle from "@assets/icons/clock-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
|
||||
type Props = {
|
||||
@@ -24,7 +25,7 @@
|
||||
<div class="flex flex-grow flex-wrap justify-between gap-2">
|
||||
<p class="text-xl">{meta.title || meta.name}</p>
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<Icon icon="clock-circle" size={4} />
|
||||
<Icon icon={ClockCircle} size={4} />
|
||||
<span class="sm:hidden">{formatTimestampAsDate(start)}</span>
|
||||
{formatTimestampAsTime(start)} — {isSingleDay
|
||||
? formatTimestampAsTime(end)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {fromPairs} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import UserCircle from "@assets/icons/user-circle.svg?dataurl"
|
||||
import MapPoint from "@assets/icons/map-point.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
|
||||
@@ -15,12 +17,12 @@
|
||||
|
||||
<div class="flex min-w-0 flex-col gap-1 text-sm opacity-75">
|
||||
<span class="flex items-center gap-1">
|
||||
<Icon icon="user-circle" size={4} />
|
||||
<Icon icon={UserCircle} size={4} />
|
||||
Posted by <ProfileLink pubkey={event.pubkey} {url} />
|
||||
</span>
|
||||
{#if meta.location}
|
||||
<span class="flex items-start gap-1">
|
||||
<Icon icon="map-point" class="mt-[2px]" size={4} />
|
||||
<Icon icon={MapPoint} class="mt-[2px]" size={4} />
|
||||
<span class="break-words">{meta.location}</span>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import {writable} from "svelte/store"
|
||||
import type {EventContent} from "@welshman/util"
|
||||
import {isMobile, preventDefault} from "@lib/html"
|
||||
import GallerySend from "@assets/icons/gallery-send.svg?dataurl"
|
||||
import Plane from "@assets/icons/plane-2.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import EditorContent from "@app/editor/EditorContent.svelte"
|
||||
@@ -48,7 +50,7 @@
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="gallery-send" />
|
||||
<Icon icon={GallerySend} />
|
||||
{/if}
|
||||
</Button>
|
||||
<div class="chat-editor flex-grow overflow-hidden">
|
||||
@@ -59,6 +61,6 @@
|
||||
class="center tooltip tooltip-left absolute right-4 h-10 w-10 min-w-10 rounded-full"
|
||||
disabled={$uploading}
|
||||
onclick={submit}>
|
||||
<Icon icon="plain" />
|
||||
<Icon icon={Plane} />
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {displayProfileByPubkey} from "@welshman/app"
|
||||
import {slide} from "@lib/transition"
|
||||
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
@@ -30,6 +31,6 @@
|
||||
expandMode="disabled" />
|
||||
{/key}
|
||||
<Button class="absolute right-2 top-2 cursor-pointer" onclick={clear}>
|
||||
<Icon icon="close-circle" />
|
||||
<Icon icon={CloseCircle} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import {isMobile} from "@lib/html"
|
||||
import TapTarget from "@lib/components/TapTarget.svelte"
|
||||
import Avatar from "@lib/components/Avatar.svelte"
|
||||
import Reply from "@assets/icons/reply-2.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Content from "@app/components/Content.svelte"
|
||||
@@ -103,7 +104,7 @@
|
||||
<ChannelMessageEmojiButton {url} {event} />
|
||||
{#if replyTo}
|
||||
<Button class="btn join-item btn-xs" onclick={reply}>
|
||||
<Icon icon="reply" size={4} />
|
||||
<Icon icon={Reply} size={4} />
|
||||
</Button>
|
||||
{/if}
|
||||
<ChannelMessageMenuButton {url} {event} />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {NativeEmoji} from "emoji-picker-element/shared"
|
||||
import EmojiButton from "@lib/components/EmojiButton.svelte"
|
||||
import SmileCircle from "@assets/icons/smile-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import {publishReaction, canEnforceNip70} from "@app/core/commands"
|
||||
|
||||
@@ -18,5 +19,5 @@
|
||||
</script>
|
||||
|
||||
<EmojiButton {onEmoji} class="btn join-item btn-xs">
|
||||
<Icon icon="smile-circle" size={4} />
|
||||
<Icon icon={SmileCircle} size={4} />
|
||||
</EmojiButton>
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
import EventReport from "@app/components/EventReport.svelte"
|
||||
import EventDeleteConfirm from "@app/components/EventDeleteConfirm.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import Code2 from "@assets/icons/code-2.svg?dataurl"
|
||||
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
||||
import Danger from "@assets/icons/danger.svg?dataurl"
|
||||
|
||||
const {url, event, onClick} = $props()
|
||||
|
||||
@@ -28,21 +31,21 @@
|
||||
<ul class="menu whitespace-nowrap rounded-box bg-base-100 p-2 shadow-xl">
|
||||
<li>
|
||||
<Button onclick={showInfo}>
|
||||
<Icon size={4} icon="code-2" />
|
||||
<Icon size={4} icon={Code2} />
|
||||
Message Details
|
||||
</Button>
|
||||
</li>
|
||||
{#if event.pubkey === $pubkey}
|
||||
<li>
|
||||
<Button onclick={showDelete} class="text-error">
|
||||
<Icon size={4} icon="trash-bin-2" />
|
||||
<Icon size={4} icon={TrashBin2} />
|
||||
Delete Message
|
||||
</Button>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<Button class="text-error" onclick={report}>
|
||||
<Icon size={4} icon="danger" />
|
||||
<Icon size={4} icon={Danger} />
|
||||
Report Content
|
||||
</Button>
|
||||
</li>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {type Instance} from "tippy.js"
|
||||
import {between} from "@welshman/lib"
|
||||
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Tippy from "@lib/components/Tippy.svelte"
|
||||
@@ -29,7 +30,7 @@
|
||||
|
||||
<div class="flex">
|
||||
<Button class="btn join-item btn-xs" onclick={open}>
|
||||
<Icon icon="menu-dots" size={4} />
|
||||
<Icon icon={MenuDots} size={4} />
|
||||
</Button>
|
||||
<Tippy
|
||||
bind:popover
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
import {ENABLE_ZAPS} from "@app/core/state"
|
||||
import {publishReaction, canEnforceNip70} from "@app/core/commands"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import SmileCircle from "@assets/icons/smile-circle.svg?dataurl"
|
||||
import Bolt from "@assets/icons/bolt.svg?dataurl"
|
||||
import Reply from "@assets/icons/reply-2.svg?dataurl"
|
||||
import Code2 from "@assets/icons/code-2.svg?dataurl"
|
||||
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
@@ -46,26 +51,26 @@
|
||||
|
||||
<div class="col-2">
|
||||
<Button class="btn btn-primary w-full" onclick={showEmojiPicker}>
|
||||
<Icon size={4} icon="smile-circle" />
|
||||
<Icon size={4} icon={SmileCircle} />
|
||||
Send Reaction
|
||||
</Button>
|
||||
{#if ENABLE_ZAPS}
|
||||
<ZapButton replaceState {url} {event} class="btn btn-secondary w-full">
|
||||
<Icon size={4} icon="bolt" />
|
||||
<Icon size={4} icon={Bolt} />
|
||||
Send Zap
|
||||
</ZapButton>
|
||||
{/if}
|
||||
<Button class="btn btn-neutral w-full" onclick={sendReply}>
|
||||
<Icon size={4} icon="reply" />
|
||||
<Icon size={4} icon={Reply} />
|
||||
Send Reply
|
||||
</Button>
|
||||
<Button class="btn btn-neutral" onclick={showInfo}>
|
||||
<Icon size={4} icon="code-2" />
|
||||
<Icon size={4} icon={Code2} />
|
||||
Message Details
|
||||
</Button>
|
||||
{#if event.pubkey === $pubkey}
|
||||
<Button class="btn btn-neutral text-error" onclick={showDelete}>
|
||||
<Icon size={4} icon="trash-bin-2" />
|
||||
<Icon size={4} icon={TrashBin2} />
|
||||
Delete Message
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Bolt from "@assets/icons/bolt.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ZapButton from "@app/components/ZapButton.svelte"
|
||||
|
||||
@@ -6,5 +7,5 @@
|
||||
</script>
|
||||
|
||||
<ZapButton {url} {event} class="btn join-item btn-xs">
|
||||
<Icon icon="bolt" size={4} />
|
||||
<Icon icon={Bolt} size={4} />
|
||||
</ZapButton>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
inboxRelaySelectionsByPubkey,
|
||||
} from "@welshman/app"
|
||||
import type {AbstractThunk} from "@welshman/app"
|
||||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
@@ -49,7 +50,7 @@
|
||||
import ThunkToast from "@app/components/ThunkToast.svelte"
|
||||
import {
|
||||
INDEXER_RELAYS,
|
||||
userSettingValues,
|
||||
userSettingsValues,
|
||||
deriveChat,
|
||||
splitChatId,
|
||||
PLATFORM_NAME,
|
||||
@@ -130,7 +131,7 @@
|
||||
const template = templates[i]
|
||||
|
||||
thunks.push(
|
||||
await sendWrapped({pubkeys, template, delay: $userSettingValues.send_delay + ms(i)}),
|
||||
await sendWrapped({pubkeys, template, delay: $userSettingsValues.send_delay + ms(i)}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -250,7 +251,7 @@
|
||||
<div
|
||||
class="row-2 badge badge-error badge-lg tooltip tooltip-left cursor-pointer"
|
||||
data-tip="{count} {label} not configured.">
|
||||
<Icon icon="danger" />
|
||||
<Icon icon={Danger} />
|
||||
{count}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -264,7 +265,7 @@
|
||||
<div class="py-12">
|
||||
<div class="card2 col-2 m-auto max-w-md items-center text-center">
|
||||
<p class="row-2 text-lg text-error">
|
||||
<Icon icon="danger" />
|
||||
<Icon icon={Danger} />
|
||||
Your inbox is not configured.
|
||||
</p>
|
||||
<p>
|
||||
@@ -277,7 +278,7 @@
|
||||
<div class="py-12">
|
||||
<div class="card2 col-2 m-auto max-w-md items-center text-center">
|
||||
<p class="row-2 text-lg text-error">
|
||||
<Icon icon="danger" />
|
||||
<Icon icon={Danger} />
|
||||
{missingInboxes.length}
|
||||
{missingInboxes.length > 1 ? "inboxes are" : "inbox is"} not configured.
|
||||
</p>
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
import {writable} from "svelte/store"
|
||||
import type {EventContent} from "@welshman/util"
|
||||
import {isMobile, preventDefault} from "@lib/html"
|
||||
import GallerySend from "@assets/icons/gallery-send.svg?dataurl"
|
||||
import Plane from "@assets/icons/plane-2.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import EditorContent from "@app/editor/EditorContent.svelte"
|
||||
import {makeEditor} from "@app/editor"
|
||||
|
||||
type Props = {
|
||||
url?: string
|
||||
onSubmit: (event: EventContent) => void
|
||||
}
|
||||
|
||||
const {onSubmit, url}: Props = $props()
|
||||
const {onSubmit}: Props = $props()
|
||||
|
||||
const autofocus = !isMobile
|
||||
|
||||
@@ -37,11 +38,11 @@
|
||||
}
|
||||
|
||||
const editor = makeEditor({
|
||||
url,
|
||||
autofocus,
|
||||
submit,
|
||||
uploading,
|
||||
aggressive: true,
|
||||
encryptFiles: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -54,7 +55,7 @@
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="gallery-send" />
|
||||
<Icon icon={GallerySend} />
|
||||
{/if}
|
||||
</Button>
|
||||
<div class="chat-editor flex-grow overflow-hidden">
|
||||
@@ -65,6 +66,6 @@
|
||||
class="center tooltip tooltip-left absolute right-4 h-10 w-10 min-w-10 rounded-full"
|
||||
disabled={$uploading}
|
||||
onclick={submit}>
|
||||
<Icon icon="plain" />
|
||||
<Icon icon={Plane} />
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {displayProfileByPubkey} from "@welshman/app"
|
||||
import {slide} from "@lib/transition"
|
||||
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
@@ -30,6 +31,6 @@
|
||||
expandMode="disabled" />
|
||||
{/key}
|
||||
<Button class="absolute right-2 top-2 cursor-pointer" onclick={clear}>
|
||||
<Icon icon="close-circle" />
|
||||
<Icon icon={CloseCircle} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<script lang="ts">
|
||||
import {goto} from "$app/navigation"
|
||||
import {WRAP} from "@welshman/util"
|
||||
import {repository} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
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 ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {canDecrypt, PLATFORM_NAME, ensureUnwrapped} from "@app/core/state"
|
||||
import {PLATFORM_NAME} from "@app/core/state"
|
||||
import {enableGiftWraps} from "@app/core/commands"
|
||||
import {clearModals} from "@app/util/modal"
|
||||
|
||||
const {next} = $props()
|
||||
@@ -18,12 +19,7 @@
|
||||
let loading = $state(false)
|
||||
|
||||
const enableChat = async () => {
|
||||
canDecrypt.set(true)
|
||||
|
||||
for (const event of repository.query([{kinds: [WRAP]}])) {
|
||||
ensureUnwrapped(event)
|
||||
}
|
||||
|
||||
enableGiftWraps()
|
||||
clearModals()
|
||||
goto(nextUrl)
|
||||
}
|
||||
@@ -60,12 +56,12 @@
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Enable Messages</Spinner>
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
<script lang="ts">
|
||||
import {waitForThunkCompletion} from "@welshman/app"
|
||||
import ChatSquare from "@assets/icons/chat-square.svg?dataurl"
|
||||
import Check from "@assets/icons/check.svg?dataurl"
|
||||
import Bell from "@assets/icons/bell.svg?dataurl"
|
||||
import BellOff from "@assets/icons/bell-off.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import ChatStart from "@app/components/ChatStart.svelte"
|
||||
import {setChecked} from "@app/util/notifications"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {dmAlert, userInboxRelays} from "@app/core/state"
|
||||
import {deleteAlert, createDmAlert} from "@app/core/commands"
|
||||
|
||||
const startChat = () => pushModal(ChatStart, {}, {replaceState: true})
|
||||
|
||||
@@ -11,15 +20,64 @@
|
||||
setChecked("/chat/*")
|
||||
history.back()
|
||||
}
|
||||
|
||||
const enableAlerts = async () => {
|
||||
if ($userInboxRelays.length === 0) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Please set up your messaging relays before enabling alerts.",
|
||||
})
|
||||
}
|
||||
|
||||
enablingAlert = true
|
||||
|
||||
try {
|
||||
const {error} = await createDmAlert()
|
||||
|
||||
if (error) {
|
||||
return pushToast({theme: "error", message: error})
|
||||
}
|
||||
} finally {
|
||||
enablingAlert = false
|
||||
}
|
||||
}
|
||||
|
||||
const disableAlerts = async () => {
|
||||
disablingAlert = true
|
||||
|
||||
try {
|
||||
await waitForThunkCompletion(deleteAlert($dmAlert!))
|
||||
} finally {
|
||||
disablingAlert = false
|
||||
}
|
||||
}
|
||||
|
||||
let enablingAlert = $state(false)
|
||||
let disablingAlert = $state(false)
|
||||
</script>
|
||||
|
||||
<div class="col-2">
|
||||
<Button class="btn btn-primary" onclick={startChat}>
|
||||
<Icon size={4} icon="add-circle" />
|
||||
<Icon size={5} icon={ChatSquare} />
|
||||
Start chat
|
||||
</Button>
|
||||
<Button class="btn btn-neutral" onclick={markAsRead}>
|
||||
<Icon size={4} icon="check-circle" />
|
||||
<Icon size={5} icon={Check} />
|
||||
Mark all read
|
||||
</Button>
|
||||
{#if (!enablingAlert && $dmAlert) || disablingAlert}
|
||||
<Button class="btn btn-neutral" onclick={disableAlerts} disabled={disablingAlert}>
|
||||
{#if !disablingAlert}
|
||||
<Icon size={4} icon={BellOff} />
|
||||
{/if}
|
||||
<Spinner loading={disablingAlert}>Disable alerts</Spinner>
|
||||
</Button>
|
||||
{:else}
|
||||
<Button class="btn btn-neutral" onclick={enableAlerts} disabled={enablingAlert}>
|
||||
{#if !enablingAlert}
|
||||
<Icon size={4} icon={Bell} />
|
||||
{/if}
|
||||
<Spinner loading={enablingAlert}>Enable alerts</Spinner>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import {thunks, pubkey, deriveProfile, deriveProfileDisplay, sendWrapped} from "@welshman/app"
|
||||
import {isMobile} from "@lib/html"
|
||||
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Tippy from "@lib/components/Tippy.svelte"
|
||||
@@ -87,7 +88,7 @@
|
||||
class="opacity-0 transition-all"
|
||||
class:group-hover:opacity-100={!isMobile}
|
||||
onclick={togglePopover}>
|
||||
<Icon icon="menu-dots" size={4} />
|
||||
<Icon icon={MenuDots} size={4} />
|
||||
</button>
|
||||
</Tippy>
|
||||
{/if}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type {NativeEmoji} from "emoji-picker-element/shared"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {sendWrapped} from "@welshman/app"
|
||||
import SmileCircle from "@assets/icons/smile-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import EmojiButton from "@lib/components/EmojiButton.svelte"
|
||||
import {makeReaction} from "@app/core/commands"
|
||||
@@ -18,5 +19,5 @@
|
||||
</script>
|
||||
|
||||
<EmojiButton {onEmoji} class="btn join-item btn-xs">
|
||||
<Icon icon="smile-circle" size={4} />
|
||||
<Icon icon={SmileCircle} size={4} />
|
||||
</EmojiButton>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
import ChatMessageEmojiButton from "@app/components/ChatMessageEmojiButton.svelte"
|
||||
import EventInfo from "@app/components/EventInfo.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import Reply from "@assets/icons/reply-2.svg?dataurl"
|
||||
import Code2 from "@assets/icons/code-2.svg?dataurl"
|
||||
|
||||
const {event, pubkeys, popover, replyTo} = $props()
|
||||
|
||||
@@ -19,10 +21,10 @@
|
||||
<ChatMessageEmojiButton {event} {pubkeys} />
|
||||
{#if replyTo}
|
||||
<Button class="btn join-item btn-xs" onclick={reply}>
|
||||
<Icon size={4} icon="reply" />
|
||||
<Icon size={4} icon={Reply} />
|
||||
</Button>
|
||||
{/if}
|
||||
<Button class="btn join-item btn-xs" onclick={showInfo}>
|
||||
<Icon size={4} icon="code-2" />
|
||||
<Icon size={4} icon={Code2} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
import {makeReaction} from "@app/core/commands"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {clip} from "@app/util/toast"
|
||||
import SmileCircle from "@assets/icons/smile-circle.svg?dataurl"
|
||||
import Reply from "@assets/icons/reply-2.svg?dataurl"
|
||||
import Copy from "@assets/icons/copy.svg?dataurl"
|
||||
import Code2 from "@assets/icons/code-2.svg?dataurl"
|
||||
|
||||
type Props = {
|
||||
pubkeys: string[]
|
||||
@@ -40,19 +44,19 @@
|
||||
|
||||
<div class="col-2">
|
||||
<Button class="btn btn-primary w-full" onclick={showEmojiPicker}>
|
||||
<Icon size={4} icon="smile-circle" />
|
||||
<Icon size={4} icon={SmileCircle} />
|
||||
Send Reaction
|
||||
</Button>
|
||||
<Button class="btn btn-neutral w-full" onclick={sendReply}>
|
||||
<Icon size={4} icon="reply" />
|
||||
<Icon size={4} icon={Reply} />
|
||||
Send Reply
|
||||
</Button>
|
||||
<Button class="btn btn-neutral w-full" onclick={copyText}>
|
||||
<Icon size={4} icon="copy" />
|
||||
<Icon size={4} icon={Copy} />
|
||||
Copy Text
|
||||
</Button>
|
||||
<Button class="btn btn-neutral" onclick={showInfo}>
|
||||
<Icon size={4} icon="code-2" />
|
||||
<Icon size={4} icon={Code2} />
|
||||
Message Details
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
import {preventDefault} from "@lib/html"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
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 ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
@@ -67,12 +69,12 @@
|
||||
</Field>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={pubkeys.length === 0}>
|
||||
Create Chat
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
} from "@welshman/content"
|
||||
import {preventDefault, stopPropagation} from "@lib/html"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ContentToken from "@app/components/ContentToken.svelte"
|
||||
@@ -31,7 +32,7 @@
|
||||
import ContentQuote from "@app/components/ContentQuote.svelte"
|
||||
import ContentTopic from "@app/components/ContentTopic.svelte"
|
||||
import ContentMention from "@app/components/ContentMention.svelte"
|
||||
import {entityLink, userSettingValues} from "@app/core/state"
|
||||
import {entityLink, userSettingsValues} from "@app/core/state"
|
||||
|
||||
interface Props {
|
||||
event: any
|
||||
@@ -68,11 +69,11 @@
|
||||
|
||||
if (!parsed || hideMediaAtDepth <= depth) return false
|
||||
|
||||
if (isLink(parsed) && $userSettingValues.show_media && isStartOrEnd(i)) {
|
||||
if (isLink(parsed) && $userSettingsValues.show_media && isStartAndEnd(i)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ((isEvent(parsed) || isAddress(parsed)) && isStartOrEnd(i)) {
|
||||
if ((isEvent(parsed) || isAddress(parsed)) && isStartAndEnd(i)) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -94,14 +95,12 @@
|
||||
|
||||
const isStartAndEnd = (i: number) => isStart(i) && isEnd(i)
|
||||
|
||||
const isStartOrEnd = (i: number) => isStart(i) || isEnd(i)
|
||||
|
||||
const ignoreWarning = () => {
|
||||
warning = null
|
||||
}
|
||||
|
||||
let warning = $state(
|
||||
$userSettingValues.hide_sensitive && event.tags.find(nthEq(0, "content-warning"))?.[1],
|
||||
$userSettingsValues.hide_sensitive && event.tags.find(nthEq(0, "content-warning"))?.[1],
|
||||
)
|
||||
|
||||
const shortContent = $derived(
|
||||
@@ -122,7 +121,7 @@
|
||||
<div class="relative">
|
||||
{#if warning}
|
||||
<div class="card2 card2-sm bg-alt row-2">
|
||||
<Icon icon="danger" />
|
||||
<Icon icon={Danger} />
|
||||
<p>
|
||||
This note has been flagged by the author as "{warning}".<br />
|
||||
<Button class="link" onclick={ignoreWarning}>Show anyway</Button>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type {ParsedEmojiValue} from "@welshman/content"
|
||||
import {imgproxy} from "@app/core/state"
|
||||
|
||||
export let value: ParsedEmojiValue
|
||||
|
||||
@@ -8,10 +7,7 @@
|
||||
</script>
|
||||
|
||||
{#if value.url}
|
||||
<img
|
||||
{alt}
|
||||
src={imgproxy(value.url, {w: 24, h: 24})}
|
||||
class="-mt-0.5 inline h-[1em] min-w-[1em] align-middle" />
|
||||
<img {alt} src={value.url} class="-mt-0.5 inline h-[1em] min-w-[1em] align-middle" />
|
||||
{:else}
|
||||
{alt}
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {ellipsize, displayUrl, postJson} from "@welshman/lib"
|
||||
import {dufflepud, imgproxy} from "@app/core/state"
|
||||
import {dufflepud} from "@app/core/state"
|
||||
import {preventDefault, stopPropagation} from "@lib/html"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import ContentLinkDetail from "@app/components/ContentLinkDetail.svelte"
|
||||
@@ -51,7 +51,7 @@
|
||||
<img
|
||||
alt="Link preview"
|
||||
onerror={onError}
|
||||
src={imgproxy(preview.image)}
|
||||
src={preview.image}
|
||||
class="bg-alt max-h-72 rounded-t-box object-contain object-center" />
|
||||
{/if}
|
||||
<div class="flex flex-col gap-2 p-4">
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
<script lang="ts">
|
||||
import {onMount, onDestroy} from "svelte"
|
||||
import {displayUrl} from "@welshman/lib"
|
||||
import {getTags, decryptFile, getTagValue, tagsFromIMeta} from "@welshman/util"
|
||||
import {
|
||||
getTags,
|
||||
getBlob,
|
||||
decryptFile,
|
||||
getTagValue,
|
||||
tagsFromIMeta,
|
||||
makeBlossomAuthEvent,
|
||||
} from "@welshman/util"
|
||||
import {signer} from "@welshman/app"
|
||||
import LinkRound from "@assets/icons/link-round.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import {imgproxy} from "@app/core/state"
|
||||
|
||||
const {value, event, ...props} = $props()
|
||||
|
||||
@@ -13,18 +21,34 @@
|
||||
.map(tagsFromIMeta)
|
||||
.find(meta => getTagValue("url", meta) === url) || event.tags
|
||||
|
||||
const hash = getTagValue("x", meta)
|
||||
const key = getTagValue("decryption-key", meta)
|
||||
const nonce = getTagValue("decryption-nonce", meta)
|
||||
const algorithm = getTagValue("encryption-algorithm", meta)
|
||||
|
||||
const onError = () => {
|
||||
hasError = true
|
||||
const onError = async () => {
|
||||
// If the image failed to load, try authenticating
|
||||
if (hash && $signer) {
|
||||
const server = new URL(url).origin
|
||||
const template = makeBlossomAuthEvent({action: "get", server, hashes: [hash]})
|
||||
const authEvent = await $signer.sign(template)
|
||||
const res = await getBlob(server, hash, {authEvent})
|
||||
|
||||
if (res.status === 200) {
|
||||
src = URL.createObjectURL(await res.blob())
|
||||
} else {
|
||||
hasError = true
|
||||
}
|
||||
} else {
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
|
||||
let hasError = $state(false)
|
||||
let src = $state(imgproxy(url))
|
||||
let src = $state("")
|
||||
|
||||
onMount(async () => {
|
||||
// If we have an encryption algorithm, fetch and decrypt
|
||||
if (algorithm === "aes-gcm" && key && nonce) {
|
||||
const response = await fetch(url)
|
||||
|
||||
@@ -32,8 +56,10 @@
|
||||
const ciphertext = new Uint8Array(await response.arrayBuffer())
|
||||
const decryptedData = await decryptFile({ciphertext, key, nonce, algorithm})
|
||||
|
||||
src = URL.createObjectURL(new Blob([decryptedData]))
|
||||
src = URL.createObjectURL(new Blob([new Uint8Array(decryptedData)]))
|
||||
}
|
||||
} else {
|
||||
src = url
|
||||
}
|
||||
})
|
||||
|
||||
@@ -44,9 +70,9 @@
|
||||
|
||||
{#if hasError}
|
||||
<a href={url} class="link-content whitespace-nowrap">
|
||||
<Icon icon="link-round" size={3} class="inline-block" />
|
||||
<Icon icon={LinkRound} size={3} class="inline-block" />
|
||||
{displayUrl(url)}
|
||||
</a>
|
||||
{:else}
|
||||
{:else if src}
|
||||
<img alt="" {src} onerror={onError} {...props} />
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {displayUrl} from "@welshman/lib"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import LinkRound from "@assets/icons/link-round.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import ContentLinkDetail from "@app/components/ContentLinkDetail.svelte"
|
||||
@@ -16,12 +17,12 @@
|
||||
{#if url.match(/\.(jpe?g|png|gif|webp)$/)}
|
||||
<!-- Use a real link so people can copy the href -->
|
||||
<a href={url} class="link-content whitespace-nowrap" onclick={preventDefault(expand)}>
|
||||
<Icon icon="link-round" size={3} class="inline-block" />
|
||||
<Icon icon={LinkRound} size={3} class="inline-block" />
|
||||
{displayUrl(url)}
|
||||
</a>
|
||||
{:else}
|
||||
<Link external href={url} class="link-content whitespace-nowrap">
|
||||
<Icon icon="link-round" size={3} class="inline-block" />
|
||||
<Icon icon={LinkRound} size={3} class="inline-block" />
|
||||
{displayUrl(url)}
|
||||
</Link>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Bolt from "@assets/icons/bolt.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import {clip} from "@app/util/toast"
|
||||
@@ -9,6 +10,6 @@
|
||||
</script>
|
||||
|
||||
<Button onclick={copy} class="link-content">
|
||||
<Icon icon="bolt" size={3} class="inline-block translate-y-px" />
|
||||
<Icon icon={Bolt} size={3} class="inline-block translate-y-px" />
|
||||
{value.slice(0, 16)}...
|
||||
</Button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {formatTimestamp} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
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 Content from "@app/components/Content.svelte"
|
||||
@@ -40,7 +41,7 @@
|
||||
</div>
|
||||
<div class="ml-13 flex items-center justify-between">
|
||||
<div class="flex gap-1">
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
<span class="text-sm opacity-70">
|
||||
{events.length}
|
||||
{events.length === 1 ? "message" : "messages"}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import type {Instance} from "tippy.js"
|
||||
import type {NativeEmoji} from "emoji-picker-element/shared"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import Bolt from "@assets/icons/bolt.svg?dataurl"
|
||||
import SmileCircle from "@assets/icons/smile-circle.svg?dataurl"
|
||||
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Tippy from "@lib/components/Tippy.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -42,11 +45,11 @@
|
||||
<Button class="join rounded-full">
|
||||
{#if ENABLE_ZAPS && !hideZap}
|
||||
<ZapButton {url} {event} class="btn join-item btn-neutral btn-xs">
|
||||
<Icon icon="bolt" size={4} />
|
||||
<Icon icon={Bolt} size={4} />
|
||||
</ZapButton>
|
||||
{/if}
|
||||
<EmojiButton {onEmoji} class="btn join-item btn-neutral btn-xs">
|
||||
<Icon icon="smile-circle" size={4} />
|
||||
<Icon icon={SmileCircle} size={4} />
|
||||
</EmojiButton>
|
||||
<Tippy
|
||||
bind:popover
|
||||
@@ -54,7 +57,7 @@
|
||||
props={{url, noun, event, customActions, onClick: hidePopover}}
|
||||
params={{trigger: "manual", interactive: true}}>
|
||||
<Button class="btn join-item btn-neutral btn-xs" onclick={showPopover}>
|
||||
<Icon icon="menu-dots" size={4} />
|
||||
<Icon icon={MenuDots} size={4} />
|
||||
</Button>
|
||||
</Tippy>
|
||||
</Button>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {repository} from "@welshman/app"
|
||||
import {notifications} from "@app/util/notifications"
|
||||
import Reply from "@assets/icons/reply-2.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
|
||||
const {url, path, event}: {url: string; path: string; event: TrustedEvent} = $props()
|
||||
@@ -21,7 +22,7 @@
|
||||
</script>
|
||||
|
||||
<div class="flex-inline btn btn-neutral btn-xs gap-1 rounded-full">
|
||||
<Icon icon="reply" />
|
||||
<Icon icon={Reply} />
|
||||
<span>{$replies.length} {$replies.length === 1 ? "reply" : "replies"}</span>
|
||||
</div>
|
||||
<div class="btn btn-neutral btn-xs relative hidden rounded-full sm:flex">
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
import {LOCALE, secondsToDate} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {displayRelayUrl} from "@welshman/util"
|
||||
import FileText from "@assets/icons/file-text.svg?dataurl"
|
||||
import Copy from "@assets/icons/copy.svg?dataurl"
|
||||
import UserCircle from "@assets/icons/user-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -56,10 +59,10 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="file" />
|
||||
<Icon icon={FileText} />
|
||||
<input type="text" class="ellipsize min-w-0 grow" value={nevent1} />
|
||||
<Button onclick={copyLink} class="flex items-center">
|
||||
<Icon icon="copy" />
|
||||
<Icon icon={Copy} />
|
||||
</Button>
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -70,10 +73,10 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="user-circle" />
|
||||
<Icon icon={UserCircle} />
|
||||
<input type="text" class="ellipsize min-w-0 grow" value={npub1} />
|
||||
<Button onclick={copyPubkey} class="flex items-center">
|
||||
<Icon icon="copy" />
|
||||
<Icon icon={Copy} />
|
||||
</Button>
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -98,7 +101,7 @@
|
||||
<pre class="card2 card2-sm bg-alt overflow-auto text-xs"><code>{json}</code></pre>
|
||||
<p class="absolute right-2 top-2 flex flex-grow items-center justify-between">
|
||||
<Button onclick={copyJson} class="btn btn-neutral btn-sm flex items-center">
|
||||
<Icon icon="copy" /> Copy
|
||||
<Icon icon={Copy} /> Copy
|
||||
</Button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
import EventShare from "@app/components/EventShare.svelte"
|
||||
import EventDeleteConfirm from "@app/components/EventDeleteConfirm.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import ShareCircle from "@assets/icons/share-circle.svg?dataurl"
|
||||
import Code2 from "@assets/icons/code-2.svg?dataurl"
|
||||
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
||||
import Danger from "@assets/icons/danger.svg?dataurl"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
@@ -43,14 +47,14 @@
|
||||
{#if isRoot}
|
||||
<li>
|
||||
<Button onclick={share}>
|
||||
<Icon size={4} icon="share-circle" />
|
||||
<Icon size={4} icon={ShareCircle} />
|
||||
Share to Chat
|
||||
</Button>
|
||||
</li>
|
||||
{/if}
|
||||
<li>
|
||||
<Button onclick={showInfo}>
|
||||
<Icon size={4} icon="code-2" />
|
||||
<Icon size={4} icon={Code2} />
|
||||
{noun} Details
|
||||
</Button>
|
||||
</li>
|
||||
@@ -58,14 +62,14 @@
|
||||
{#if event.pubkey === $pubkey}
|
||||
<li>
|
||||
<Button onclick={showDelete} class="text-error">
|
||||
<Icon size={4} icon="trash-bin-2" />
|
||||
<Icon size={4} icon={TrashBin2} />
|
||||
Delete {noun}
|
||||
</Button>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<Button class="text-error" onclick={report}>
|
||||
<Icon size={4} icon="danger" />
|
||||
<Icon size={4} icon={Danger} />
|
||||
Report Content
|
||||
</Button>
|
||||
</li>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import {writable} from "svelte/store"
|
||||
import {isMobile, preventDefault} from "@lib/html"
|
||||
import {fly} from "@lib/transition"
|
||||
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
@@ -81,7 +82,7 @@
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="paperclip" size={3} />
|
||||
<Icon icon={Paperclip} size={3} />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
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 ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
@@ -78,12 +80,12 @@
|
||||
</Field>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Send Report</Spinner>
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import {goto} from "$app/navigation"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {preventDefault} from "@lib/html"
|
||||
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 ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
@@ -50,12 +52,12 @@
|
||||
</div>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={!selection}>
|
||||
Share {noun}
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import {makeEvent, ZAP_GOAL} from "@welshman/util"
|
||||
import {publishThunk} from "@welshman/app"
|
||||
import {isMobile, preventDefault} from "@lib/html"
|
||||
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
|
||||
import Bolt from "@assets/icons/bolt.svg?dataurl"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
@@ -114,7 +117,7 @@
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="paperclip" size={3} />
|
||||
<Icon icon={Paperclip} size={3} />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -126,7 +129,7 @@
|
||||
{#snippet input()}
|
||||
<div class="flex flex-grow justify-end">
|
||||
<label class="input input-bordered flex items-center gap-2">
|
||||
<Icon icon="bolt" />
|
||||
<Icon icon={Bolt} />
|
||||
<input bind:value={amount} type="number" class="w-28" />
|
||||
<p class="opacity-50">sats</p>
|
||||
</label>
|
||||
@@ -144,7 +147,7 @@
|
||||
</div>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary">Create Goal</Button>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import {getTagValue, fromMsats, ZAP_RESPONSE} from "@welshman/util"
|
||||
import {deriveEventsMapped} from "@welshman/store"
|
||||
import {repository, getValidZap} from "@welshman/app"
|
||||
import Bolt from "@assets/icons/bolt.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ZapButton from "@app/components/ZapButton.svelte"
|
||||
|
||||
@@ -43,7 +44,7 @@
|
||||
</div>
|
||||
<progress class="progress progress-primary" value={zapAmount} max={goalAmount}></progress>
|
||||
<ZapButton {url} {event} class="btn btn-primary lg:m-auto lg:px-20">
|
||||
<Icon icon="bolt" />
|
||||
<Icon icon={Bolt} />
|
||||
Contribute to this goal
|
||||
</ZapButton>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {session} from "@welshman/app"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import CheckCircle from "@assets/icons/check-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
@@ -27,8 +29,8 @@
|
||||
</p>
|
||||
<p>
|
||||
On <Link external href="https://nostr.com/">Nostr</Link>, <strong>you</strong> control your own
|
||||
identity and social data, through the magic of crytography. The basic idea is that you have a
|
||||
<strong>public key</strong>, which acts as your user id, and a
|
||||
identity and social data, through the magic of cryptography. The basic idea is that you have a
|
||||
<strong>public key</strong>, which acts as your user ID, and a
|
||||
<strong>private key</strong> which allows you to prove your identity.
|
||||
</p>
|
||||
{#if $session?.email}
|
||||
@@ -39,11 +41,11 @@
|
||||
<p>If you'd like to switch to self-custody, please click below to get started.</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="btn btn-primary" onclick={startEject}>
|
||||
<Icon icon="check-circle" />
|
||||
<Icon icon={CheckCircle} />
|
||||
I want to hold my own keys
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
|
||||
const back = () => history.back()
|
||||
</script>
|
||||
|
||||
<div class="column gap-4">
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
<div>What are digital signatures?</div>
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
<p>
|
||||
Most online services ask their users to trust them that they're being honest, and they usually
|
||||
are. However, traditional social media platforms have the ability to <strong
|
||||
>create forged content</strong> that can appear to be genuinely authored, but which are actually
|
||||
counterfeit.
|
||||
</p>
|
||||
<p>
|
||||
On <Link external href="https://nostr.com/">Nostr</Link>, all your content is authenticated
|
||||
using <strong>digital signatures</strong>, which cryptographically tie a particular person to a
|
||||
given post or message.
|
||||
</p>
|
||||
<p>
|
||||
The result is that you don't normally have to trust service providers not to tamper with the
|
||||
information flowing through the network — instead, your client software can prove that a given
|
||||
piece of data is authentic.
|
||||
</p>
|
||||
<Button class="btn btn-primary" onclick={back}>Got it</Button>
|
||||
</div>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {deriveZapperForPubkey} 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 ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
@@ -29,7 +30,7 @@
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
+35
-22
@@ -1,7 +1,11 @@
|
||||
<script lang="ts">
|
||||
import {randomId} from "@welshman/lib"
|
||||
import {preventDefault, stopPropagation} from "@lib/html"
|
||||
import {randomId, call} from "@welshman/lib"
|
||||
import {preventDefault, stopPropagation, compressFile} from "@lib/html"
|
||||
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
||||
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
||||
import GallerySend from "@assets/icons/gallery-send.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import {uploadFile} from "@app/core/commands"
|
||||
|
||||
interface Props {
|
||||
file?: File | undefined
|
||||
@@ -24,14 +28,14 @@
|
||||
active = false
|
||||
}
|
||||
|
||||
const onDrop = (e: any) => {
|
||||
const onDrop = async (e: any) => {
|
||||
active = false
|
||||
|
||||
file = e.dataTransfer.files[0]
|
||||
file = await compressFile(e.dataTransfer.files[0])
|
||||
}
|
||||
|
||||
const onChange = (e: any) => {
|
||||
file = e.target.files[0]
|
||||
const onChange = async (e: any) => {
|
||||
file = await compressFile(e.target.files[0])
|
||||
}
|
||||
|
||||
const onClear = () => {
|
||||
@@ -44,20 +48,29 @@
|
||||
let initialUrl = $state(url)
|
||||
|
||||
$effect(() => {
|
||||
if (file) {
|
||||
const reader = new FileReader()
|
||||
call(async () => {
|
||||
if (file) {
|
||||
const {result} = await uploadFile(file)
|
||||
|
||||
reader.addEventListener(
|
||||
"load",
|
||||
() => {
|
||||
url = reader.result as string
|
||||
},
|
||||
false,
|
||||
)
|
||||
reader.readAsDataURL(file)
|
||||
} else {
|
||||
url = initialUrl
|
||||
}
|
||||
if (result?.url) {
|
||||
url = result.url
|
||||
} else {
|
||||
const reader = new FileReader()
|
||||
|
||||
reader.addEventListener(
|
||||
"load",
|
||||
() => {
|
||||
url = reader.result as string
|
||||
},
|
||||
false,
|
||||
)
|
||||
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
} else {
|
||||
url = initialUrl
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -84,14 +97,14 @@
|
||||
tabindex="-1"
|
||||
onmousedown={stopPropagation(onClear)}
|
||||
ontouchstart={stopPropagation(onClear)}>
|
||||
<Icon icon="close-circle" class="scale-150 !bg-base-300" />
|
||||
<Icon icon={CloseCircle} class="scale-150 !bg-base-300" />
|
||||
</span>
|
||||
{:else}
|
||||
<Icon icon="add-circle" class="scale-150 !bg-base-300" />
|
||||
<Icon icon={AddCircle} class="scale-150 !bg-base-300" />
|
||||
{/if}
|
||||
</div>
|
||||
{#if !url}
|
||||
<Icon icon="gallery-send" size={7} />
|
||||
<Icon icon={GallerySend} size={7} />
|
||||
{/if}
|
||||
</label>
|
||||
</form>
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Login from "@assets/icons/login-3.svg?dataurl"
|
||||
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
@@ -21,9 +23,9 @@
|
||||
<p class="text-center">The chat app built for self-hosted communities.</p>
|
||||
</div>
|
||||
<Button onclick={logIn}>
|
||||
<CardButton class="!btn-primary">
|
||||
<CardButton class="btn-primary">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon="login-2" size={7} /></div>
|
||||
<div><Icon icon={Login} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Log in</div>
|
||||
@@ -33,10 +35,10 @@
|
||||
{/snippet}
|
||||
</CardButton>
|
||||
</Button>
|
||||
<Button onclick={signUp}>
|
||||
<Button onclick={signUp} class="btn-neutral">
|
||||
<CardButton>
|
||||
{#snippet icon()}
|
||||
<div><Icon icon="add-circle" size={7} /></div>
|
||||
<div><Icon icon={AddCircle} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Create an account</div>
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
import {Capacitor} from "@capacitor/core"
|
||||
import {getNip07, getNip55, Nip55Signer} from "@welshman/signer"
|
||||
import {addSession, type Session, makeNip07Session, makeNip55Session} from "@welshman/app"
|
||||
import Widget from "@assets/icons/widget-2.svg?dataurl"
|
||||
import Key from "@assets/icons/key-minimalistic.svg?dataurl"
|
||||
import Cpu from "@assets/icons/cpu-bolt.svg?dataurl"
|
||||
import Compass from "@assets/icons/compass-big.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -96,7 +100,7 @@
|
||||
{#if loading === "nip07"}
|
||||
<span class="loading loading-spinner mr-3"></span>
|
||||
{:else}
|
||||
<Icon icon="widget" />
|
||||
<Icon icon={Widget} />
|
||||
{/if}
|
||||
Log in with Extension
|
||||
</Button>
|
||||
@@ -116,7 +120,7 @@
|
||||
{#if loading === "password"}
|
||||
<span class="loading loading-spinner mr-3"></span>
|
||||
{:else}
|
||||
<Icon icon="key" />
|
||||
<Icon icon={Key} />
|
||||
{/if}
|
||||
Log in with Password
|
||||
</Button>
|
||||
@@ -125,7 +129,7 @@
|
||||
onclick={loginWithBunker}
|
||||
{disabled}
|
||||
class="btn {hasSigner || BURROW_URL ? 'btn-neutral' : 'btn-primary'}">
|
||||
<Icon icon="cpu" />
|
||||
<Icon icon={Cpu} />
|
||||
Log in with Remote Signer
|
||||
</Button>
|
||||
{#if BURROW_URL && hasSigner}
|
||||
@@ -133,7 +137,7 @@
|
||||
{#if loading === "password"}
|
||||
<span class="loading loading-spinner mr-3"></span>
|
||||
{:else}
|
||||
<Icon icon="key" />
|
||||
<Icon icon={Key} />
|
||||
{/if}
|
||||
Log in with Password
|
||||
</Button>
|
||||
@@ -144,7 +148,7 @@
|
||||
{disabled}
|
||||
href="https://nostrapps.com#signers"
|
||||
class="btn {hasSigner || BURROW_URL ? '' : 'btn-neutral'}">
|
||||
<Icon icon="compass" />
|
||||
<Icon icon={Compass} />
|
||||
Browse Signer Apps
|
||||
</Link>
|
||||
{/if}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
import {preventDefault} from "@lib/html"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
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 ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
@@ -30,9 +32,10 @@
|
||||
onNostrConnect: async (response: Nip46ResponseWithResult) => {
|
||||
const pubkey = await controller.broker.getPublicKey()
|
||||
|
||||
loginWithNip46(pubkey, controller.clientSecret, response.event.pubkey, SIGNER_RELAYS)
|
||||
|
||||
await loadUserData(pubkey)
|
||||
|
||||
loginWithNip46(pubkey, controller.clientSecret, response.event.pubkey, SIGNER_RELAYS)
|
||||
setChecked("*")
|
||||
clearModals()
|
||||
},
|
||||
@@ -46,13 +49,20 @@
|
||||
try {
|
||||
const {signerPubkey, connectSecret, relays} = Nip46Broker.parseBunkerUrl($bunker)
|
||||
|
||||
if (!signerPubkey || relays.length === 0) {
|
||||
if (!signerPubkey) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Sorry, it looks like that's an invalid bunker link.",
|
||||
})
|
||||
}
|
||||
|
||||
if (relays.length === 0) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "That bunker link does not include any relays.",
|
||||
})
|
||||
}
|
||||
|
||||
controller.loading.set(true)
|
||||
|
||||
const {clientSecret} = controller
|
||||
@@ -89,6 +99,7 @@
|
||||
}
|
||||
|
||||
const selectConnect = () => {
|
||||
controller.loading.set(false)
|
||||
mode = "connect"
|
||||
}
|
||||
|
||||
@@ -133,13 +144,13 @@
|
||||
{/if}
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back} disabled={$loading}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
{#if mode === "bunker"}
|
||||
<Button type="submit" class="btn btn-primary" disabled={$loading || !$bunker}>
|
||||
<Spinner loading={$loading}>Next</Spinner>
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
{/if}
|
||||
</ModalFooter>
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
||||
import Key from "@assets/icons/key-minimalistic.svg?dataurl"
|
||||
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 ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
@@ -121,7 +125,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="user-rounded" />
|
||||
<Icon icon={UserRounded} />
|
||||
<input bind:value={email} />
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -132,7 +136,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="key" />
|
||||
<Icon icon={Key} />
|
||||
<input bind:value={password} type="password" />
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -144,12 +148,12 @@
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back} disabled={loading}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading || !email || !password}>
|
||||
<Spinner {loading}>Next</Spinner>
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {preventDefault} from "@lib/html"
|
||||
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 Spinner from "@lib/components/Spinner.svelte"
|
||||
@@ -32,7 +33,7 @@
|
||||
<p class="text-center">Your local database will be cleared.</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<script lang="ts">
|
||||
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"
|
||||
import Settings from "@assets/icons/settings-minimalistic.svg?dataurl"
|
||||
import Code2 from "@assets/icons/code-2.svg?dataurl"
|
||||
import Exit from "@assets/icons/logout-3.svg?dataurl"
|
||||
import Bell from "@assets/icons/bell.svg?dataurl"
|
||||
import Wallet from "@assets/icons/wallet.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -6,15 +14,18 @@
|
||||
import LogOut from "@app/components/LogOut.svelte"
|
||||
import {PLATFORM_NAME} from "@app/core/state"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {theme} from "@app/util/theme"
|
||||
|
||||
const logout = () => pushModal(LogOut)
|
||||
|
||||
const toggleTheme = () => theme.set($theme === "dark" ? "light" : "dark")
|
||||
</script>
|
||||
|
||||
<div class="column menu gap-2">
|
||||
<Link replaceState href="/settings/profile">
|
||||
<CardButton>
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon="user-rounded" size={7} /></div>
|
||||
<div><Icon icon={UserRounded} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Profile</div>
|
||||
@@ -24,10 +35,36 @@
|
||||
{/snippet}
|
||||
</CardButton>
|
||||
</Link>
|
||||
<Link replaceState href="/settings/relays">
|
||||
<CardButton>
|
||||
<Link replaceState href="/settings/alerts">
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon="server" size={7} /></div>
|
||||
<div><Icon icon={Bell} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Alerts</div>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
<div>Set up email digests and push notifications</div>
|
||||
{/snippet}
|
||||
</CardButton>
|
||||
</Link>
|
||||
<Link replaceState href="/settings/wallet">
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon={Wallet} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Wallet</div>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
<div>Connect a bitcoin wallet for sending social tips</div>
|
||||
{/snippet}
|
||||
</CardButton>
|
||||
</Link>
|
||||
<Link replaceState href="/settings/relays">
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon={Server} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Relays</div>
|
||||
@@ -37,10 +74,10 @@
|
||||
{/snippet}
|
||||
</CardButton>
|
||||
</Link>
|
||||
<Link replaceState href="/settings">
|
||||
<CardButton>
|
||||
<Link replaceState href="/settings/content">
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon="settings" size={7} /></div>
|
||||
<div><Icon icon={Settings} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Settings</div>
|
||||
@@ -50,10 +87,23 @@
|
||||
{/snippet}
|
||||
</CardButton>
|
||||
</Link>
|
||||
<Link replaceState href="/settings/about">
|
||||
<CardButton>
|
||||
<Button onclick={toggleTheme}>
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon="code-2" size={7} /></div>
|
||||
<div><Icon icon={Moon} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Theme</div>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
<div>Switch between light and dark mode</div>
|
||||
{/snippet}
|
||||
</CardButton>
|
||||
</Button>
|
||||
<Link replaceState href="/settings/about">
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon={Code2} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>About</div>
|
||||
@@ -64,6 +114,6 @@
|
||||
</CardButton>
|
||||
</Link>
|
||||
<Button onclick={logout} class="btn btn-neutral">
|
||||
<Icon icon="exit" /> Log Out
|
||||
<Icon icon={Exit} /> Log Out
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
import {displayRelayUrl, getTagValue} from "@welshman/util"
|
||||
import {deriveRelay} from "@welshman/app"
|
||||
import {fly} from "@lib/transition"
|
||||
import AltArrowDown from "@assets/icons/alt-arrow-down.svg?dataurl"
|
||||
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
||||
import LinkRound from "@assets/icons/link-round.svg?dataurl"
|
||||
import Exit from "@assets/icons/logout-3.svg?dataurl"
|
||||
import Login from "@assets/icons/login-3.svg?dataurl"
|
||||
import HomeSmile from "@assets/icons/home-smile.svg?dataurl"
|
||||
import StarFallMinimalistic from "@assets/icons/star-fall-minimalistic-2.svg?dataurl"
|
||||
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
||||
import CalendarMinimalistic from "@assets/icons/calendar-minimalistic.svg?dataurl"
|
||||
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
||||
import ChatRound from "@assets/icons/chat-round.svg?dataurl"
|
||||
import Bell from "@assets/icons/bell.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Popover from "@lib/components/Popover.svelte"
|
||||
@@ -92,7 +104,7 @@
|
||||
<strong class="ellipsize flex items-center gap-3">
|
||||
{displayRelayUrl(url)}
|
||||
</strong>
|
||||
<Icon icon="alt-arrow-down" />
|
||||
<Icon icon={AltArrowDown} />
|
||||
</SecondaryNavItem>
|
||||
{#if showMenu}
|
||||
<Popover hideOnClick onClose={toggleMenu}>
|
||||
@@ -101,25 +113,25 @@
|
||||
class="menu absolute z-popover mt-2 w-full gap-1 rounded-box bg-base-100 p-2 shadow-xl">
|
||||
<li>
|
||||
<Button onclick={showMembers}>
|
||||
<Icon icon="user-rounded" />
|
||||
<Icon icon={UserRounded} />
|
||||
View Members ({members.length})
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<Button onclick={createInvite}>
|
||||
<Icon icon="link-round" />
|
||||
<Icon icon={LinkRound} />
|
||||
Create Invite
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
{#if $userRoomsByUrl.has(url)}
|
||||
<Button onclick={leaveSpace} class="text-error">
|
||||
<Icon icon="exit" />
|
||||
<Icon icon={Exit} />
|
||||
Leave Space
|
||||
</Button>
|
||||
{:else}
|
||||
<Button onclick={joinSpace} class="bg-primary text-primary-content">
|
||||
<Icon icon="login-2" />
|
||||
<Icon icon={Login} />
|
||||
Join Space
|
||||
</Button>
|
||||
{/if}
|
||||
@@ -130,27 +142,27 @@
|
||||
</div>
|
||||
<div class="flex max-h-[calc(100vh-150px)] min-h-0 flex-col gap-1 overflow-auto">
|
||||
<SecondaryNavItem {replaceState} href={makeSpacePath(url)}>
|
||||
<Icon icon="home-smile" /> Home
|
||||
<Icon icon={HomeSmile} /> Home
|
||||
</SecondaryNavItem>
|
||||
{#if ENABLE_ZAPS}
|
||||
<SecondaryNavItem
|
||||
{replaceState}
|
||||
href={goalsPath}
|
||||
notification={$notifications.has(goalsPath)}>
|
||||
<Icon icon="star-fall-minimalistic-2" /> Goals
|
||||
<Icon icon={StarFallMinimalistic} /> Goals
|
||||
</SecondaryNavItem>
|
||||
{/if}
|
||||
<SecondaryNavItem
|
||||
{replaceState}
|
||||
href={threadsPath}
|
||||
notification={$notifications.has(threadsPath)}>
|
||||
<Icon icon="notes-minimalistic" /> Threads
|
||||
<Icon icon={NotesMinimalistic} /> Threads
|
||||
</SecondaryNavItem>
|
||||
<SecondaryNavItem
|
||||
{replaceState}
|
||||
href={calendarPath}
|
||||
notification={$notifications.has(calendarPath)}>
|
||||
<Icon icon="calendar-minimalistic" /> Calendar
|
||||
<Icon icon={CalendarMinimalistic} /> Calendar
|
||||
</SecondaryNavItem>
|
||||
{#if hasNip29($relay)}
|
||||
{#if $userRooms.length > 0}
|
||||
@@ -174,7 +186,7 @@
|
||||
<MenuSpaceRoomItem {replaceState} {url} {room} />
|
||||
{/each}
|
||||
<SecondaryNavItem {replaceState} onclick={addRoom}>
|
||||
<Icon icon="add-circle" />
|
||||
<Icon icon={AddCircle} />
|
||||
Create room
|
||||
</SecondaryNavItem>
|
||||
{:else}
|
||||
@@ -182,14 +194,14 @@
|
||||
{replaceState}
|
||||
href={chatPath}
|
||||
notification={$notifications.has(chatPath)}>
|
||||
<Icon icon="chat-round" /> Chat
|
||||
<Icon icon={ChatRound} /> Chat
|
||||
</SecondaryNavItem>
|
||||
{/if}
|
||||
</div>
|
||||
</SecondaryNavSection>
|
||||
<div class="p-4">
|
||||
<button class="btn btn-neutral btn-sm w-full" onclick={manageAlerts}>
|
||||
<Icon icon="bell" />
|
||||
<Icon icon={Bell} />
|
||||
Manage Alerts
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import MenuSpace from "@app/components/MenuSpace.svelte"
|
||||
@@ -14,7 +15,7 @@
|
||||
</script>
|
||||
|
||||
<Button onclick={openMenu} class="btn btn-neutral btn-sm relative md:hidden">
|
||||
<Icon icon="menu-dots" />
|
||||
<Icon icon={MenuDots} />
|
||||
{#if $notifications.has(path)}
|
||||
<div class="absolute right-0 top-0 -mr-1 -mt-1 h-2 w-2 rounded-full bg-primary"></div>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Lock from "@assets/icons/lock-keyhole.svg?dataurl"
|
||||
import Hashtag from "@assets/icons/hashtag.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
|
||||
import ChannelName from "@app/components/ChannelName.svelte"
|
||||
@@ -24,9 +26,9 @@
|
||||
{replaceState}
|
||||
notification={notify ? $notifications.has(path) : false}>
|
||||
{#if $channel?.closed || $channel?.private}
|
||||
<Icon icon="lock" size={4} />
|
||||
<Icon icon={Lock} size={4} />
|
||||
{:else}
|
||||
<Icon icon="hashtag" />
|
||||
<Icon icon={Hashtag} />
|
||||
{/if}
|
||||
<div class="min-w-0 overflow-hidden text-ellipsis">
|
||||
<ChannelName {url} {room} />
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Compass from "@assets/icons/compass.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Divider from "@lib/components/Divider.svelte"
|
||||
import CardButton from "@lib/components/CardButton.svelte"
|
||||
import MenuSpacesItem from "@app/components/MenuSpacesItem.svelte"
|
||||
import SpaceAdd from "@app/components/SpaceAdd.svelte"
|
||||
import {userRoomsByUrl, PLATFORM_RELAYS} from "@app/core/state"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
|
||||
const addSpace = () => pushModal(SpaceAdd)
|
||||
</script>
|
||||
|
||||
<div class="column menu gap-2">
|
||||
@@ -21,18 +18,18 @@
|
||||
{/each}
|
||||
<Divider />
|
||||
{/if}
|
||||
<Button onclick={addSpace}>
|
||||
<CardButton>
|
||||
<Link href="/discover">
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><Icon icon="login-2" size={7} /></div>
|
||||
<div><Icon icon={Compass} size={7} /></div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<div>Add a space</div>
|
||||
<div>Explore Spaces</div>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
<div>Join or create a new space</div>
|
||||
<div>Join create, or browse spaces</div>
|
||||
{/snippet}
|
||||
</CardButton>
|
||||
</Button>
|
||||
</Link>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</script>
|
||||
|
||||
<Link replaceState href={path}>
|
||||
<CardButton>
|
||||
<CardButton class="btn-neutral">
|
||||
{#snippet icon()}
|
||||
<div><SpaceAvatar {url} /></div>
|
||||
{/snippet}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {page} from "$app/stores"
|
||||
import Drawer from "@lib/components/Drawer.svelte"
|
||||
import Dialog from "@lib/components/Dialog.svelte"
|
||||
import {modals, clearModals} from "@app/util/modal"
|
||||
import {modal, clearModals} from "@app/util/modal"
|
||||
|
||||
const onKeyDown = (e: any) => {
|
||||
if (e.code === "Escape" && e.target === document.body) {
|
||||
@@ -10,22 +9,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
const hash = $derived($page.url.hash.slice(1))
|
||||
const modal = $derived($modals[hash])
|
||||
const m = $derived($modal)
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={onKeyDown} />
|
||||
|
||||
{#if modal?.options?.drawer}
|
||||
<Drawer onClose={clearModals} {...modal.options}>
|
||||
{#key modal.id}
|
||||
<modal.component {...modal.props} />
|
||||
{#if m?.options?.drawer}
|
||||
<Drawer onClose={clearModals} {...m.options}>
|
||||
{#key m.id}
|
||||
<m.component {...m.props} />
|
||||
{/key}
|
||||
</Drawer>
|
||||
{:else if modal}
|
||||
<Dialog onClose={clearModals} {...modal.options}>
|
||||
{#key modal.id}
|
||||
<modal.component {...modal.props} />
|
||||
{:else if m}
|
||||
<Dialog onClose={clearModals} {...m.options}>
|
||||
{#key m.id}
|
||||
<m.component {...m.props} />
|
||||
{/key}
|
||||
</Dialog>
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {userSettingsValues} from "@app/core/state"
|
||||
import {notifications} from "../util/notifications"
|
||||
|
||||
let audioElement: HTMLAudioElement
|
||||
|
||||
let enabled = $state(false)
|
||||
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.hidden) {
|
||||
enabled = true
|
||||
} else {
|
||||
enabled = false
|
||||
}
|
||||
})
|
||||
let notificationCount = $state($notifications.size)
|
||||
|
||||
const playSound = () => {
|
||||
if (enabled && $userSettingsValues.play_notification_sound) {
|
||||
audioElement.play()
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
audioElement.load()
|
||||
|
||||
notifications.subscribe(notifications => {
|
||||
if (notifications.size > notificationCount) {
|
||||
playSound()
|
||||
}
|
||||
|
||||
notificationCount = notifications.size
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<audio bind:this={audioElement} src="/new-notification-3-398649.mp3"></audio>
|
||||
@@ -8,6 +8,7 @@
|
||||
import {Router} from "@welshman/router"
|
||||
import {userMutes} from "@welshman/app"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
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"
|
||||
@@ -44,7 +45,7 @@
|
||||
{#if muted}
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="row-2 relative">
|
||||
<Icon icon="danger" class="mt-1" />
|
||||
<Icon icon={Danger} class="mt-1" />
|
||||
<p>You have muted this person.</p>
|
||||
</div>
|
||||
<Button class="link ml-8" onclick={ignoreMute}>Show anyway</Button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {NativeEmoji} from "emoji-picker-element/shared"
|
||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import SmileCircle from "@assets/icons/smile-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import EmojiButton from "@lib/components/EmojiButton.svelte"
|
||||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
@@ -32,7 +33,7 @@
|
||||
<div class="flex w-full justify-between gap-2">
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-right">
|
||||
<EmojiButton {onEmoji} class="btn btn-neutral btn-xs h-[26px] rounded-box">
|
||||
<Icon icon="smile-circle" size={4} />
|
||||
<Icon icon={SmileCircle} size={4} />
|
||||
</EmojiButton>
|
||||
</ReactionSummary>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import {preventDefault} from "@lib/html"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
||||
import Key from "@assets/icons/key-minimalistic.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
@@ -49,7 +51,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="user-rounded" />
|
||||
<Icon icon={UserRounded} />
|
||||
<input readonly value={email} class="grow" />
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -60,7 +62,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="key" />
|
||||
<Icon icon={Key} />
|
||||
<input bind:value={password} class="grow" type="password" />
|
||||
</label>
|
||||
{/snippet}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {postJson, sleep} from "@welshman/lib"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
||||
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 FieldInline from "@lib/components/FieldInline.svelte"
|
||||
@@ -55,7 +57,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="user-rounded" />
|
||||
<Icon icon={UserRounded} />
|
||||
<input bind:value={email} class="grow" />
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -65,7 +67,7 @@
|
||||
</FieldInline>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import UserCircle from "@assets/icons/user-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Profile from "@app/components/Profile.svelte"
|
||||
@@ -21,14 +22,14 @@
|
||||
<div class="flex justify-between">
|
||||
<Profile {pubkey} {url} />
|
||||
<Button onclick={openProfile} class="btn btn-primary hidden sm:flex">
|
||||
<Icon icon="user-circle" />
|
||||
<Icon icon={UserCircle} />
|
||||
View Profile
|
||||
</Button>
|
||||
</div>
|
||||
<ProfileInfo {pubkey} {url} />
|
||||
<ProfileBadges {pubkey} {url} />
|
||||
<Button onclick={openProfile} class="btn btn-primary sm:hidden">
|
||||
<Icon icon="user-circle" />
|
||||
<Icon icon={UserCircle} />
|
||||
View Profile
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {makeSpacePath} from "@app/util/routes"
|
||||
import {notifications} from "@app/util/notifications"
|
||||
import Widget from "@assets/icons/widget.svg?dataurl"
|
||||
import Compass from "@assets/icons/compass.svg?dataurl"
|
||||
import Letter from "@assets/icons/letter.svg?dataurl"
|
||||
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
||||
import HomeSmile from "@assets/icons/home-smile.svg?dataurl"
|
||||
import SettingsMinimalistic from "@assets/icons/settings-minimalistic.svg?dataurl"
|
||||
import Settings from "@assets/icons/settings.svg?dataurl"
|
||||
|
||||
type Props = {
|
||||
children?: Snippet
|
||||
@@ -24,8 +31,6 @@
|
||||
|
||||
const {children}: Props = $props()
|
||||
|
||||
const addSpace = () => pushModal(SpaceAdd)
|
||||
|
||||
const showSpacesMenu = () => (spaceUrls.length > 0 ? pushModal(MenuSpaces) : pushModal(SpaceAdd))
|
||||
|
||||
const showOtherSpacesMenu = () => pushModal(MenuOtherSpaces, {urls: secondarySpaceUrls})
|
||||
@@ -55,7 +60,7 @@
|
||||
|
||||
<div
|
||||
class="ml-sai mt-sai mb-sai relative z-nav hidden w-14 flex-shrink-0 bg-base-200 pt-4 md:block">
|
||||
<div class="flex h-full flex-col justify-between">
|
||||
<div class="flex h-full flex-col" class:justify-between={PLATFORM_RELAYS.length === 0}>
|
||||
<div>
|
||||
{#each PLATFORM_RELAYS as url (url)}
|
||||
<PrimaryNavItemSpace {url} />
|
||||
@@ -73,14 +78,17 @@
|
||||
class="tooltip-right"
|
||||
onclick={showOtherSpacesMenu}
|
||||
notification={otherSpaceNotifications}>
|
||||
<Avatar icon="widget" class="!h-10 !w-10" />
|
||||
<Avatar icon={Widget} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
{/if}
|
||||
<PrimaryNavItem title="Add Space" onclick={addSpace} class="tooltip-right">
|
||||
<Avatar icon="add-square" class="!h-10 !w-10" />
|
||||
<PrimaryNavItem title="Add a Space" href="/discover" class="tooltip-right">
|
||||
<Avatar icon={Compass} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
{/each}
|
||||
</div>
|
||||
{#if PLATFORM_RELAYS.length > 0}
|
||||
<Divider />
|
||||
{/if}
|
||||
<div>
|
||||
<PrimaryNavItem
|
||||
title="Settings"
|
||||
@@ -94,10 +102,10 @@
|
||||
onclick={openChat}
|
||||
class="tooltip-right"
|
||||
notification={$notifications.has("/chat")}>
|
||||
<Avatar icon="letter" class="!h-10 !w-10" />
|
||||
<Avatar icon={Letter} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
<PrimaryNavItem title="Search" href="/people" class="tooltip-right">
|
||||
<Avatar icon="magnifer" class="!h-10 !w-10" />
|
||||
<Avatar icon={Magnifier} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,25 +120,25 @@
|
||||
<div class="content-padding-x content-sizing flex justify-between px-2">
|
||||
<div class="flex gap-2 sm:gap-8">
|
||||
<PrimaryNavItem title="Home" href="/home">
|
||||
<Avatar icon="home-smile" class="!h-10 !w-10" />
|
||||
<Avatar icon={HomeSmile} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
<PrimaryNavItem
|
||||
title="Messages"
|
||||
onclick={openChat}
|
||||
notification={$notifications.has("/chat")}>
|
||||
<Avatar icon="letter" class="!h-10 !w-10" />
|
||||
<Avatar icon={Letter} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
{#if PLATFORM_RELAYS.length !== 1}
|
||||
<PrimaryNavItem
|
||||
title="Spaces"
|
||||
onclick={showSpacesMenu}
|
||||
notification={anySpaceNotifications}>
|
||||
<Avatar icon="settings-minimalistic" class="!h-10 !w-10" />
|
||||
<Avatar icon={SettingsMinimalistic} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
{/if}
|
||||
</div>
|
||||
<PrimaryNavItem title="Settings" onclick={showSettingsMenu}>
|
||||
<Avatar icon="settings" src={$userProfile?.picture} class="!h-10 !w-10" />
|
||||
<Avatar icon={Settings} src={$userProfile?.picture} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import ProfileDetail from "@app/components/ProfileDetail.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {clip} from "@app/util/toast"
|
||||
import Copy from "@assets/icons/copy.svg?dataurl"
|
||||
|
||||
type Props = {
|
||||
pubkey: string
|
||||
@@ -55,7 +56,7 @@
|
||||
<div class="flex items-center gap-1 overflow-hidden text-ellipsis text-xs opacity-60">
|
||||
{displayPubkey(pubkey)}
|
||||
<Button onclick={copyPubkey} class="pt-1">
|
||||
<Icon size={3} icon="copy" />
|
||||
<Icon size={3} icon={Copy} />
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import Avatar from "@lib/components/Avatar.svelte"
|
||||
import {removeNil} from "@welshman/lib"
|
||||
import {deriveProfile} from "@welshman/app"
|
||||
import UserCircle from "@assets/icons/user-circle.svg?dataurl"
|
||||
|
||||
type Props = {
|
||||
pubkey: string
|
||||
@@ -13,4 +14,4 @@
|
||||
const profile = deriveProfile(pubkey, removeNil([url]))
|
||||
</script>
|
||||
|
||||
<Avatar src={$profile?.picture} icon="user-circle" {...props} />
|
||||
<Avatar src={$profile?.picture} icon={UserCircle} {...props} />
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
DELETE,
|
||||
isReplaceable,
|
||||
getAddress,
|
||||
getRelaysFromList,
|
||||
} from "@welshman/util"
|
||||
import {pubkey, userRelaySelections, publishThunk, repository} from "@welshman/app"
|
||||
import {pubkey, publishThunk, repository} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
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"
|
||||
@@ -18,7 +19,13 @@
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {logout} from "@app/core/commands"
|
||||
import {INDEXER_RELAYS, PLATFORM_NAME, userMembership, getMembershipUrls} from "@app/core/state"
|
||||
import {
|
||||
INDEXER_RELAYS,
|
||||
PLATFORM_NAME,
|
||||
userMembership,
|
||||
getMembershipUrls,
|
||||
userWriteRelays,
|
||||
} from "@app/core/state"
|
||||
|
||||
let progress: number | undefined = $state(undefined)
|
||||
let confirmText = $state("")
|
||||
@@ -41,7 +48,7 @@
|
||||
const denominator = chunks.length + 2
|
||||
const relays = uniq([
|
||||
...INDEXER_RELAYS,
|
||||
...getRelaysFromList($userRelaySelections),
|
||||
...$userWriteRelays,
|
||||
...getMembershipUrls($userMembership),
|
||||
])
|
||||
|
||||
@@ -136,12 +143,12 @@
|
||||
{/if}
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-error" disabled={showProgress || !confirmOk}>
|
||||
<Spinner loading={progress !== undefined}>Confirm</Spinner>
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {goto} from "$app/navigation"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import Letter from "@assets/icons/letter-opened.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Avatar from "@lib/components/Avatar.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
@@ -33,7 +35,7 @@
|
||||
<ProfileBadges {pubkey} {url} />
|
||||
<ModalFooter>
|
||||
<Button onclick={back} class="hidden md:btn md:btn-link">
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<div class="flex gap-2">
|
||||
@@ -42,7 +44,7 @@
|
||||
Open in Coracle
|
||||
</Link>
|
||||
<Button onclick={openChat} class="btn btn-primary">
|
||||
<Icon icon="letter" />
|
||||
<Icon icon={Letter} />
|
||||
Open Chat
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
<script lang="ts">
|
||||
import {nthNe} from "@welshman/lib"
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {
|
||||
getTag,
|
||||
makeEvent,
|
||||
makeProfile,
|
||||
editProfile,
|
||||
createProfile,
|
||||
isPublishedProfile,
|
||||
uniqTags,
|
||||
} from "@welshman/util"
|
||||
import {Router} from "@welshman/router"
|
||||
import {pubkey, profilesByPubkey, publishThunk} from "@welshman/app"
|
||||
import {getTag, makeProfile} from "@welshman/util"
|
||||
import {pubkey, profilesByPubkey} from "@welshman/app"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ProfileEditForm from "@app/components/ProfileEditForm.svelte"
|
||||
import {clearModals} from "@app/util/modal"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {PROTECTED, getMembershipUrls, userMembership} from "@app/core/state"
|
||||
import {PROTECTED} from "@app/core/state"
|
||||
import {updateProfile} from "../core/commands"
|
||||
|
||||
const profile = $profilesByPubkey.get($pubkey!) || makeProfile()
|
||||
const shouldBroadcast = !getTag(PROTECTED, profile.event?.tags || [])
|
||||
@@ -25,21 +16,7 @@
|
||||
const back = () => history.back()
|
||||
|
||||
const onsubmit = ({profile, shouldBroadcast}: {profile: Profile; shouldBroadcast: boolean}) => {
|
||||
const router = Router.get()
|
||||
const template = isPublishedProfile(profile) ? editProfile(profile) : createProfile(profile)
|
||||
const scenarios = [router.FromRelays(getMembershipUrls($userMembership))]
|
||||
|
||||
if (shouldBroadcast) {
|
||||
scenarios.push(router.FromUser(), router.Index())
|
||||
template.tags = template.tags.filter(nthNe(0, "-"))
|
||||
} else {
|
||||
template.tags = uniqTags([...template.tags, PROTECTED])
|
||||
}
|
||||
|
||||
const event = makeEvent(template.kind, template)
|
||||
const relays = router.merge(scenarios).getUrls()
|
||||
|
||||
publishThunk({event, relays})
|
||||
updateProfile({profile, shouldBroadcast})
|
||||
pushToast({message: "Your profile has been updated!"})
|
||||
clearModals()
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
import type {Snippet} from "svelte"
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import UserCircle from "@assets/icons/user-circle.svg?dataurl"
|
||||
import MapPoint from "@assets/icons/map-point.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import InputProfilePicture from "@lib/components/InputProfilePicture.svelte"
|
||||
import InputProfilePicture from "@app/components/InputProfilePicture.svelte"
|
||||
import InfoHandle from "@app/components/InfoHandle.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
|
||||
@@ -53,11 +55,11 @@
|
||||
{/if}
|
||||
<Field>
|
||||
{#snippet label()}
|
||||
<p>Username</p>
|
||||
<p>Nickname</p>
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="user-circle" />
|
||||
<Icon icon={UserCircle} />
|
||||
<input bind:value={values.profile.name} class="grow" type="text" />
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -86,7 +88,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="map-point" />
|
||||
<Icon icon={MapPoint} />
|
||||
<input bind:value={values.profile.nip05} class="grow" type="text" />
|
||||
</label>
|
||||
{/snippet}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import {session} from "@welshman/app"
|
||||
import {slideAndFade} from "@lib/transition"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Key from "@assets/icons/key-minimalistic.svg?dataurl"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import CheckCircle from "@assets/icons/check-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
@@ -81,7 +84,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="key" />
|
||||
<Icon icon={Key} />
|
||||
<input type="password" disabled={loading} bind:value={password} class="grow" />
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -90,17 +93,17 @@
|
||||
{/if}
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" disabled={loading || success} onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
{#if success}
|
||||
<Button class="btn btn-primary" disabled={loading} onclick={reload}>
|
||||
<Icon icon="check-circle" />
|
||||
<Icon icon={CheckCircle} />
|
||||
<Spinner {loading}>Refresh the page</Spinner>
|
||||
</Button>
|
||||
{:else}
|
||||
<Button class="btn btn-error" disabled={loading} onclick={confirm}>
|
||||
<Icon icon="check-circle" />
|
||||
<Icon icon={CheckCircle} />
|
||||
<Spinner {loading}>I understand, send me my private key</Spinner>
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
import {append, remove, uniq} from "@welshman/lib"
|
||||
import {profileSearch} from "@welshman/app"
|
||||
import Suggestions from "@lib/components/Suggestions.svelte"
|
||||
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
||||
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Tippy from "@lib/components/Tippy.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -61,7 +63,7 @@
|
||||
{@const onClick = () => pushModal(ProfileDetail, {pubkey})}
|
||||
<div class="flex-inline badge badge-neutral mr-1 gap-1">
|
||||
<Button class="flex items-center" onclick={() => removePubkey(pubkey)}>
|
||||
<Icon icon="close-circle" size={4} class="-ml-1 mt-px" />
|
||||
<Icon icon={CloseCircle} size={4} class="-ml-1 mt-px" />
|
||||
</Button>
|
||||
<Button onclick={onClick}>
|
||||
<ProfileName {pubkey} />
|
||||
@@ -70,7 +72,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
<label class="input input-bordered flex w-full items-center gap-2" bind:this={input}>
|
||||
<Icon icon="magnifer" />
|
||||
<Icon icon={Magnifier} />
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<input
|
||||
{autofocus}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -33,7 +35,7 @@
|
||||
</div>
|
||||
<Link class="btn btn-primary" href={makeSpacePath(url)}>
|
||||
Go to space
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Link>
|
||||
</div>
|
||||
{:else}
|
||||
@@ -43,7 +45,7 @@
|
||||
{/each}
|
||||
<ModalFooter>
|
||||
<Button onclick={back} class="hidden md:btn md:btn-link">
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<Button class="max-w-full {props.class}" onclick={copy}>
|
||||
<div bind:this={wrapper} style={`height: ${height}px`}>
|
||||
<Button class="flex w-full justify-center {props.class}" onclick={copy}>
|
||||
<div bind:this={wrapper} class="w-md" style={`height: ${height}px`}>
|
||||
<canvas
|
||||
class="rounded-box"
|
||||
bind:this={canvas}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {parse, isEmoji, renderAsHtml} from "@welshman/content"
|
||||
import Heart from "@assets/icons/heart-angle.svg?dataurl"
|
||||
import ThumbsDown from "@assets/icons/dislike.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ContentEmoji from "@app/components/ContentEmoji.svelte"
|
||||
|
||||
@@ -7,9 +9,9 @@
|
||||
</script>
|
||||
|
||||
{#if event.content === "+" || event.content === ""}
|
||||
<Icon icon="heart" />
|
||||
<Icon icon={Heart} />
|
||||
{:else if event.content === "-"}
|
||||
<Icon icon="thumbs-down" />
|
||||
<Icon icon={ThumbsDown} />
|
||||
{:else}
|
||||
{#each parse(event) as parsed}
|
||||
{#if isEmoji(parsed)}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import {load} from "@welshman/net"
|
||||
import {pubkey, repository, getValidZap, displayProfileByPubkey} from "@welshman/app"
|
||||
import {isMobile, preventDefault, stopPropagation} from "@lib/html"
|
||||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Reaction from "@app/components/Reaction.svelte"
|
||||
import EventReportDetails from "@app/components/EventReportDetails.svelte"
|
||||
@@ -120,7 +121,7 @@
|
||||
class="btn btn-error btn-xs tooltip-right flex items-center gap-1 rounded-full"
|
||||
class:tooltip={!noTooltip && !isMobile}
|
||||
onclick={stopPropagation(preventDefault(onReportClick))}>
|
||||
<Icon icon="danger" />
|
||||
<Icon icon={Danger} />
|
||||
<span>{$reports.length}</span>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
import {isShareableRelayUrl, normalizeRelayUrl} from "@welshman/util"
|
||||
import {relaySearch} from "@welshman/app"
|
||||
import {createScroller} from "@lib/html"
|
||||
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
||||
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import RelayItem from "@app/components/RelayItem.svelte"
|
||||
@@ -38,14 +40,14 @@
|
||||
</script>
|
||||
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="magnifer" />
|
||||
<Icon icon={Magnifier} />
|
||||
<input bind:value={term} class="grow" type="text" placeholder="Search for relays..." />
|
||||
</label>
|
||||
<div class="column -m-6 mt-0 h-[50vh] gap-2 overflow-auto p-6 pt-2" bind:this={element}>
|
||||
{#if customUrl && isShareableRelayUrl(customUrl) && !$relays.includes(normalizeRelayUrl(customUrl))}
|
||||
<RelayItem url={term}>
|
||||
<Button class="btn btn-outline btn-sm flex items-center" onclick={() => addRelay(customUrl)}>
|
||||
<Icon icon="add-circle" />
|
||||
<Icon icon={AddCircle} />
|
||||
Add Relay
|
||||
</Button>
|
||||
</RelayItem>
|
||||
@@ -56,7 +58,7 @@
|
||||
.slice(0, limit) as url (url)}
|
||||
<RelayItem {url}>
|
||||
<Button class="btn btn-outline btn-sm flex items-center" onclick={() => addRelay(url)}>
|
||||
<Icon icon="add-circle" />
|
||||
<Icon icon={AddCircle} />
|
||||
Add Relay
|
||||
</Button>
|
||||
</RelayItem>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Server from "@assets/icons/server.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import {displayUrl} from "@welshman/lib"
|
||||
@@ -15,7 +16,7 @@
|
||||
<div class="card2 card2-sm bg-alt column gap-2">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="ellipsize flex items-center gap-2">
|
||||
<Icon icon="server" />
|
||||
<Icon icon={Server} />
|
||||
<p class="ellipsize">{displayRelayUrl(url)}</p>
|
||||
</div>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
const {url} = $props()
|
||||
|
||||
const display = deriveRelayDisplay(url)
|
||||
const display = $derived(deriveRelayDisplay(url))
|
||||
</script>
|
||||
|
||||
{$display}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import {gt} from "@welshman/lib"
|
||||
import {deriveRelay} from "@welshman/app"
|
||||
import Ghost from "@assets/icons/ghost-smile.svg?dataurl"
|
||||
import CheckCircle from "@assets/icons/check-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import RelayName from "@app/components/RelayName.svelte"
|
||||
import RelayDescription from "@app/components/RelayDescription.svelte"
|
||||
import ProfileCircles from "@app/components/ProfileCircles.svelte"
|
||||
import {membersByUrl, userRoomsByUrl} from "@app/core/state"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
}
|
||||
|
||||
const {url}: Props = $props()
|
||||
const relay = deriveRelay(url)
|
||||
</script>
|
||||
|
||||
<div class="col-4 text-left">
|
||||
<div class="col-2">
|
||||
<div class="relative flex gap-4">
|
||||
<div class="relative">
|
||||
<div class="avatar relative">
|
||||
<div
|
||||
class="center !flex h-12 w-12 min-w-12 rounded-full border-2 border-solid border-base-300 bg-base-300">
|
||||
{#if $relay?.profile?.icon}
|
||||
<img alt="" src={$relay.profile.icon} />
|
||||
{:else}
|
||||
<Icon icon={Ghost} size={5} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if $userRoomsByUrl.has(url)}
|
||||
<div
|
||||
class="tooltip absolute -right-1 -top-1 h-5 w-5 rounded-full bg-primary"
|
||||
data-tip="You are already a member of this space.">
|
||||
<Icon icon={CheckCircle} class="scale-110" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="ellipsize whitespace-nowrap text-xl">
|
||||
<RelayName {url} />
|
||||
</h2>
|
||||
<p class="text-sm opacity-75">{url}</p>
|
||||
</div>
|
||||
</div>
|
||||
<RelayDescription {url} />
|
||||
</div>
|
||||
{#if gt($membersByUrl.get(url)?.size, 0)}
|
||||
<div class="row-2 card2 card2-sm bg-alt">
|
||||
Members:
|
||||
<ProfileCircles pubkeys={Array.from($membersByUrl.get(url) || [])} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -2,11 +2,15 @@
|
||||
import {goto} from "$app/navigation"
|
||||
import {uniqBy, nth} from "@welshman/lib"
|
||||
import {displayRelayUrl, makeRoomMeta} from "@welshman/util"
|
||||
import {deriveRelay, getThunkError, createRoom, editRoom, joinRoom} from "@welshman/app"
|
||||
import {deriveRelay, waitForThunkError, createRoom, editRoom, joinRoom} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Hashtag from "@assets/icons/hashtag.svg?dataurl"
|
||||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||
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 ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
@@ -24,19 +28,19 @@
|
||||
const tryCreate = async () => {
|
||||
room.tags = uniqBy(nth(0), [...room.tags, ["name", name]])
|
||||
|
||||
const createMessage = await getThunkError(createRoom(url, room))
|
||||
const createMessage = await waitForThunkError(createRoom(url, room))
|
||||
|
||||
if (createMessage && !createMessage.match(/^duplicate:|already a member/)) {
|
||||
return pushToast({theme: "error", message: createMessage})
|
||||
}
|
||||
|
||||
const editMessage = await getThunkError(editRoom(url, room))
|
||||
const editMessage = await waitForThunkError(editRoom(url, room))
|
||||
|
||||
if (editMessage) {
|
||||
return pushToast({theme: "error", message: editMessage})
|
||||
}
|
||||
|
||||
const joinMessage = await getThunkError(joinRoom(url, room))
|
||||
const joinMessage = await waitForThunkError(joinRoom(url, room))
|
||||
|
||||
if (joinMessage && !joinMessage.includes("already")) {
|
||||
return pushToast({theme: "error", message: joinMessage})
|
||||
@@ -79,25 +83,25 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="hashtag" />
|
||||
<Icon icon={Hashtag} />
|
||||
<input bind:value={name} class="grow" type="text" />
|
||||
</label>
|
||||
{/snippet}
|
||||
</Field>
|
||||
{:else}
|
||||
<p class="bg-alt card2 row-2">
|
||||
<Icon icon="danger" />
|
||||
<Icon icon={Danger} />
|
||||
This relay does not support creating rooms.
|
||||
</p>
|
||||
{/if}
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={!name || loading || !hasNip29($relay)}>
|
||||
<Spinner {loading}>Create Room</Spinner>
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<script lang="ts">
|
||||
import {postJson} from "@welshman/lib"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
||||
import Key from "@assets/icons/key-minimalistic.svg?dataurl"
|
||||
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -59,7 +62,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="user-rounded" />
|
||||
<Icon icon={UserRounded} />
|
||||
<input bind:value={email} />
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -70,14 +73,14 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="key" />
|
||||
<Icon icon={Key} />
|
||||
<input bind:value={password} type="password" />
|
||||
</label>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading || !email || !password}>
|
||||
<Spinner {loading}>Sign Up</Spinner>
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
<p class="text-sm opacity-75">
|
||||
Note that your email and password will only work to log in to {PLATFORM_NAME}. To use your key
|
||||
@@ -87,7 +90,7 @@
|
||||
<Divider>Or</Divider>
|
||||
{/if}
|
||||
<Button onclick={next} class="btn {email || password ? 'btn-neutral' : 'btn-primary'}">
|
||||
<Icon icon="key" />
|
||||
<Icon icon={Key} />
|
||||
Generate a key
|
||||
</Button>
|
||||
<div class="text-sm">
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
import {createProfile, PROFILE, makeEvent} from "@welshman/util"
|
||||
import {publishThunk, loginWithNip01} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import HomeSmile from "@assets/icons/home-smile.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {clearModals} from "@app/util/modal"
|
||||
import {PROTECTED} from "@app/core/state"
|
||||
|
||||
type Props = {
|
||||
@@ -31,6 +34,8 @@
|
||||
|
||||
// Don't publish anywhere yet, wait until they join a space
|
||||
publishThunk({event, relays: []})
|
||||
|
||||
clearModals()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -50,11 +55,11 @@
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="btn btn-primary" type="submit">
|
||||
<Icon icon="home-smile" />
|
||||
<Icon icon={HomeSmile} />
|
||||
Go to Dashboard
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
import {makeSecret} from "@welshman/signer"
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {preventDefault, downloadText} from "@lib/html"
|
||||
import Key from "@assets/icons/key-minimalistic.svg?dataurl"
|
||||
import ArrowDown from "@assets/icons/arrow-down.svg?dataurl"
|
||||
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 Field from "@lib/components/Field.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -13,6 +17,7 @@
|
||||
import SignUpComplete from "@app/components/SignUpComplete.svelte"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {PLATFORM_NAME} from "@app/core/state"
|
||||
|
||||
type Props = {
|
||||
profile: Profile
|
||||
@@ -24,7 +29,26 @@
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const cleanupCopy = (copy: string) =>
|
||||
copy
|
||||
.replace(/\n\s*\n\s*/g, "NEWLINE")
|
||||
.replace(/\s+/g, " ")
|
||||
.replace(/NEWLINE/g, "\n\n")
|
||||
.trim()
|
||||
|
||||
const downloadKey = () => {
|
||||
const sharedCopy = `
|
||||
Most online services keep track of users by giving them a username and password. This gives the
|
||||
service total control over their users, allowing them to ban them at any time, or sell their activity.
|
||||
|
||||
On Nostr, you control your own identity and social data, through the magic of cryptography. The basic
|
||||
idea is that you have a public key, which acts as your user ID, and a private key which allows you to
|
||||
prove your identity.
|
||||
|
||||
It's very important to keep your private key secret because it grants permanent and complete access to your
|
||||
account.
|
||||
`
|
||||
|
||||
if (usePassword) {
|
||||
if (password.length < 12) {
|
||||
return pushToast({
|
||||
@@ -34,12 +58,37 @@
|
||||
}
|
||||
|
||||
const ncryptsec = encrypt(hexToBytes(secret), password)
|
||||
const instructions = `
|
||||
This file contains a backup of your Nostr secret key, downloaded from ${PLATFORM_NAME} and encrypted using
|
||||
a password you chose when you signed up.
|
||||
|
||||
downloadText("Nostr Secret Key.txt", ncryptsec)
|
||||
${sharedCopy}
|
||||
|
||||
Your encrypted private key is:
|
||||
|
||||
${ncryptsec}
|
||||
|
||||
To use it to log in to other Nostr apps, find a Nostr Signer app (https://nostrapps.com/#signers is a good
|
||||
place to look), and import your key.
|
||||
`
|
||||
|
||||
downloadText("Nostr Secret Key.txt", cleanupCopy(instructions))
|
||||
} else {
|
||||
const nsec = nsecEncode(hexToBytes(secret))
|
||||
const instructions = `
|
||||
This file contains a backup of your Nostr secret key, downloaded from ${PLATFORM_NAME}.
|
||||
|
||||
downloadText("Nostr Secret Key.txt", nsec)
|
||||
${sharedCopy}
|
||||
|
||||
Your private key is:
|
||||
|
||||
${nsec}
|
||||
|
||||
To use it to log in to other Nostr apps, find a Nostr Signer app (https://nostrapps.com/#signers is a good
|
||||
place to look), and import your key.
|
||||
`
|
||||
|
||||
downloadText("Nostr Secret Key.txt", cleanupCopy(instructions))
|
||||
}
|
||||
|
||||
didDownload = true
|
||||
@@ -84,7 +133,7 @@
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="key" />
|
||||
<Icon icon={Key} />
|
||||
<input bind:value={password} onchange={onPasswordChange} class="grow" type="password" />
|
||||
</label>
|
||||
{/snippet}
|
||||
@@ -96,7 +145,7 @@
|
||||
<div class="flex flex-col">
|
||||
<Button class="btn {didDownload ? 'btn-neutral' : 'btn-primary'}" onclick={downloadKey}>
|
||||
Download my key
|
||||
<Icon icon="arrow-down" />
|
||||
<Icon icon={ArrowDown} />
|
||||
</Button>
|
||||
<Button class="btn btn-link no-underline" onclick={toggleUsePassword}>
|
||||
{#if usePassword}
|
||||
@@ -108,12 +157,12 @@
|
||||
</div>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button disabled={!didDownload} class="btn btn-primary" type="submit">
|
||||
Continue
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {makeProfile} from "@welshman/util"
|
||||
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 ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
@@ -23,12 +25,12 @@
|
||||
{#snippet footer()}
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="btn btn-primary" type="submit">
|
||||
Create Account
|
||||
<Icon icon="alt-arrow-right" />
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
{/snippet}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import {spec, prop, avg} from "@welshman/lib"
|
||||
import {signerLog, SignerLogEntryStatus} from "@welshman/app"
|
||||
import {session, SessionMethod, signerLog, SignerLogEntryStatus} from "@welshman/app"
|
||||
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
||||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||
import ClockCircle from "@assets/icons/clock-circle.svg?dataurl"
|
||||
import CheckCircle from "@assets/icons/check-circle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import LogOut from "@app/components/LogOut.svelte"
|
||||
@@ -22,27 +26,45 @@
|
||||
const logout = () => pushModal(LogOut)
|
||||
</script>
|
||||
|
||||
<div class="card2 bg-alt flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xl font-bold">Signer Status</span>
|
||||
<span class="flex items-center gap-2">
|
||||
{#if isDisconnected}
|
||||
<Icon icon="close-circle" class="text-error" size={4} /> Disconnected
|
||||
{:else if recentFailure > 3}
|
||||
<Icon icon="danger" class="text-warning" size={4} /> Partial Failure
|
||||
{:else if recentAvg > 1000 || recentPending > 3}
|
||||
<Icon icon="clock-circle" class="text-warning" size={4} /> Slow connection
|
||||
{:else if recentSuccess === 0 && recentFailure > 0}{:else}
|
||||
<Icon icon="check-circle" class="text-success" size={4} /> Ok
|
||||
{/if}
|
||||
</span>
|
||||
{#if $session && $session.method !== SessionMethod.Anonymous}
|
||||
<div class="card2 bg-alt flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xl font-bold">Signer Status</span>
|
||||
<span class="flex items-center gap-2">
|
||||
{#if isDisconnected}
|
||||
<Icon icon={CloseCircle} class="text-error" size={4} /> Disconnected
|
||||
{:else if recentFailure > 3}
|
||||
<Icon icon={Danger} class="text-warning" size={4} /> Partial Failure
|
||||
{:else if recentAvg > 1000 || recentPending > 3}
|
||||
<Icon icon={ClockCircle} class="text-warning" size={4} /> Slow connection
|
||||
{:else if recentSuccess === 0 && recentFailure > 0}{:else}
|
||||
<Icon icon={CheckCircle} class="text-success" size={4} /> Ok
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-sm opacity-75">
|
||||
<p>
|
||||
Logged in with
|
||||
{#if $session.method === SessionMethod.Nip01}
|
||||
private key
|
||||
{:else if $session.method === SessionMethod.Nip07}
|
||||
browser extension
|
||||
{:else if $session.method === SessionMethod.Nip46}
|
||||
remote signer
|
||||
{:else if $session.method === SessionMethod.Nip55}
|
||||
{$session.signer}
|
||||
{:else if $session.method === SessionMethod.Pubkey}
|
||||
public key (readonly)
|
||||
{/if}
|
||||
</p>
|
||||
<p>
|
||||
{success} requests succeeded, {failure} failed, {pending} pending
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm opacity-75">
|
||||
{success} requests succeeded, {failure} failed, {pending} pending
|
||||
</p>
|
||||
{#if isDisconnected}
|
||||
<Button class="btn btn-outline btn-error" onclick={logout}>Logout to Reconnect</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if isDisconnected}
|
||||
<Button class="btn btn-outline btn-error" onclick={logout}>Logout to Reconnect</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user