Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions desktop/src-tauri/src/commands/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ pub fn sign_event(
Ok(event.as_json())
}

#[tauri::command]
pub fn get_nsec(state: State<'_, AppState>) -> Result<String, String> {
let keys = state.keys.lock().map_err(|error| error.to_string())?;
keys.secret_key()
.to_bech32()
.map_err(|error| format!("encode nsec: {error}"))
}

#[tauri::command]
pub fn create_auth_event(
challenge: String,
Expand Down
1 change: 1 addition & 0 deletions desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ pub fn run() {
})
.invoke_handler(tauri::generate_handler![
get_identity,
get_nsec,
get_profile,
update_profile,
get_user_profile,
Expand Down
7 changes: 5 additions & 2 deletions desktop/src/features/settings/ui/MobilePairingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { QRCodeSVG } from "qrcode.react";
import { Check, Copy, Loader2, Smartphone, TriangleAlert } from "lucide-react";

import { useMintTokenMutation } from "@/features/tokens/hooks";
import { getRelayHttpUrl } from "@/shared/api/tauri";
import { getNsec, getRelayHttpUrl } from "@/shared/api/tauri";
import type { TokenScope } from "@/shared/api/types";
import { Button } from "@/shared/ui/button";
import {
Expand Down Expand Up @@ -31,6 +31,7 @@ type PairingPayload = {
relayUrl: string;
token: string;
pubkey: string;
nsec: string;
};

function PairingDialog({
Expand All @@ -51,19 +52,21 @@ function PairingDialog({
const [copied, setCopied] = useState(false);

const generate = useCallback(async (pubkey: string) => {
const [tokenResult, relayUrl] = await Promise.all([
const [tokenResult, relayUrl, nsec] = await Promise.all([
mintRef.current({
name: `mobile-${Date.now()}`,
scopes: [...MOBILE_SCOPES],
expiresInDays: EXPIRES_IN_DAYS,
}),
getRelayHttpUrl(),
getNsec(),
]);

const payload: PairingPayload = {
relayUrl,
token: tokenResult.token,
pubkey,
nsec,
};
return `sprout://${toBase64Url(JSON.stringify(payload))}`;
}, []);
Expand Down
4 changes: 4 additions & 0 deletions desktop/src/shared/api/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,10 @@ export async function getIdentity(): Promise<Identity> {
};
}

export async function getNsec(): Promise<string> {
return invokeTauri<string>("get_nsec");
}

export async function getProfile(): Promise<Profile> {
const profile = await invokeTauri<RawProfile>("get_profile");
return fromRawProfile(profile);
Expand Down
6 changes: 6 additions & 0 deletions mobile/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
PODS:
- connectivity_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_secure_storage (6.0.0):
- Flutter
Expand Down Expand Up @@ -56,6 +58,7 @@ PODS:
- FlutterMacOS

DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- Flutter (from `Flutter`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
Expand All @@ -76,6 +79,8 @@ SPEC REPOS:
- PromisesObjC

EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
Flutter:
:path: Flutter
flutter_secure_storage:
Expand All @@ -86,6 +91,7 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"

SPEC CHECKSUMS:
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
Expand Down
8 changes: 8 additions & 0 deletions mobile/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'features/home/home_page.dart';
import 'features/pairing/pairing_page.dart';
import 'shared/auth/auth.dart';
import 'shared/relay/relay.dart';
import 'shared/theme/theme.dart';

class App extends HookConsumerWidget {
Expand All @@ -19,6 +20,13 @@ class App extends HookConsumerWidget {
final lightScheme = applyAccent(lightColorScheme, accentIndex);
final darkScheme = applyAccent(darkColorScheme, accentIndex);

// Eagerly initialize websocket session and lifecycle observer when
// authenticated. These providers connect and manage the websocket.
if (authState.value?.status == AuthStatus.authenticated) {
ref.watch(relaySessionProvider);
ref.watch(appLifecycleProvider);
}

return MaterialApp(
title: 'Sprout',
theme: AppTheme.light(colorScheme: lightScheme),
Expand Down
17 changes: 17 additions & 0 deletions mobile/lib/features/channels/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,21 @@ class Channel {
bool get isForum => channelType == 'forum';
bool get isDm => channelType == 'dm';
bool get isPrivate => visibility == 'private';

Channel copyWith({DateTime? lastMessageAt}) => Channel(
id: id,
name: name,
channelType: channelType,
visibility: visibility,
description: description,
topic: topic,
purpose: purpose,
createdBy: createdBy,
createdAt: createdAt,
memberCount: memberCount,
lastMessageAt: lastMessageAt ?? this.lastMessageAt,
isMember: isMember,
ttlSeconds: ttlSeconds,
ttlDeadline: ttlDeadline,
);
}
Loading