remove "display name" from adding new friends
This commit is contained in:
+13
-5
@@ -5,13 +5,13 @@
|
||||
|
||||
|
||||
export const commands = {
|
||||
async createFriend(friend: User) : Promise<User> {
|
||||
return await TAURI_INVOKE("create_friend", { friend });
|
||||
async createFriend(id: string) : Promise<Friend> {
|
||||
return await TAURI_INVOKE("create_friend", { id });
|
||||
},
|
||||
async listFriends() : Promise<User[]> {
|
||||
async listFriends() : Promise<Friend[]> {
|
||||
return await TAURI_INVOKE("list_friends");
|
||||
},
|
||||
async getFriend(id: string) : Promise<User | null> {
|
||||
async getFriend(id: string) : Promise<Friend | null> {
|
||||
return await TAURI_INVOKE("get_friend", { id });
|
||||
},
|
||||
async deleteFriend(id: string) : Promise<boolean> {
|
||||
@@ -47,6 +47,9 @@ async listStatuses() : Promise<ConnectionStatus[]> {
|
||||
async listFriendStatuses() : Promise<string[]> {
|
||||
return await TAURI_INVOKE("list_friend_statuses");
|
||||
},
|
||||
async resolveFriendDisplayName(userId: string) : Promise<string | null> {
|
||||
return await TAURI_INVOKE("resolve_friend_display_name", { userId });
|
||||
},
|
||||
async pickAndSendImage(recipientId: string) : Promise<boolean> {
|
||||
return await TAURI_INVOKE("pick_and_send_image", { recipientId });
|
||||
},
|
||||
@@ -113,10 +116,15 @@ raw: CursorPosition;
|
||||
*/
|
||||
mapped: CursorPosition }
|
||||
export type ForegroundAppChanged = { meta: AppMeta }
|
||||
/**
|
||||
* A configured public-key relationship with optional cached remote metadata.
|
||||
* The display name remains absent until learned from a signed remote profile.
|
||||
*/
|
||||
export type Friend = { id: string; displayName: string | null }
|
||||
export type FriendForegroundAppChanged = { friendId: string; meta: AppMeta }
|
||||
export type FriendInteractionReceived = { interactionId: string; friendId: string; content: InteractionContent }
|
||||
export type FriendStatusesChanged = { friendIds: string[] }
|
||||
export type FriendsChanged = { friends: User[] }
|
||||
export type FriendsChanged = { friends: Friend[] }
|
||||
export type InteractionContent = { type: "text"; text: string } | { type: "wave" } | { type: "image"; mediaType: string; data: string }
|
||||
export type NetworkStatusChanged = { statuses: ConnectionStatus[] }
|
||||
export type ProfileChanged = { profile: User }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { friends } from "$lib/listeners/friends";
|
||||
import { friendName, friends } from "$lib/listeners/friends";
|
||||
import {
|
||||
liveMetadata,
|
||||
liveMetadataListenerError,
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
function nameFor(id: string) {
|
||||
if (id === $liveMetadata.localId) return "You";
|
||||
return (
|
||||
$friends.find((friend) => friend.id === id)?.displayName ??
|
||||
"Unknown friend"
|
||||
return friendName(
|
||||
$friends.find((friend) => friend.id === id),
|
||||
"Unknown friend",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,15 @@
|
||||
friendStatusesListenerError,
|
||||
onlineFriendIds,
|
||||
} from "$lib/listeners/friend-statuses";
|
||||
import { friends, friendsListenerError } from "$lib/listeners/friends";
|
||||
import {
|
||||
friendName,
|
||||
friends,
|
||||
friendsListenerError,
|
||||
} from "$lib/listeners/friends";
|
||||
import { onMount } from "svelte";
|
||||
import PanelMessage from "./panel-message.svelte";
|
||||
import type { RegisterPanel } from "./types";
|
||||
import Info from "$lib/icons/info.svelte";
|
||||
|
||||
let { register }: { register: RegisterPanel } = $props();
|
||||
|
||||
@@ -85,8 +90,9 @@
|
||||
|
||||
{#if mode === "remove" && selected}
|
||||
<PanelMessage kind="warning">
|
||||
Applying will remove <strong>{selected.displayName}</strong>. They will
|
||||
immediately go offline and stop receiving your activity.
|
||||
Applying will remove <strong
|
||||
>{friendName(selected, "Unknown friend")}</strong
|
||||
>. They will immediately go offline and stop receiving your activity.
|
||||
</PanelMessage>
|
||||
<button class="btn w-fit" type="button" onclick={() => (mode = "browse")}
|
||||
>Keep friend</button
|
||||
@@ -126,7 +132,23 @@
|
||||
ondblclick={() => (selectedId = friend.id)}
|
||||
>
|
||||
<span class="min-w-0">
|
||||
<strong class="block truncate">{friend.displayName}</strong>
|
||||
<div class="flex flex-row gap-1.5">
|
||||
<strong
|
||||
class:italic={!friend.displayName}
|
||||
class="block truncate"
|
||||
>{friendName(friend, "Unknown friend")}</strong
|
||||
>
|
||||
{#if !friend.displayName}
|
||||
<div
|
||||
class="tooltip tooltip-primary"
|
||||
data-tip={"Will be resolved when they're online"}
|
||||
>
|
||||
<div class="*:size-3 opacity-50">
|
||||
<Info />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<span class="block truncate font-mono text-[9px] opacity-65"
|
||||
>{friend.id}</span
|
||||
>
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
friendStatusesListenerError,
|
||||
onlineFriendIds,
|
||||
} from "$lib/listeners/friend-statuses";
|
||||
import { friends, friendsListenerError } from "$lib/listeners/friends";
|
||||
import {
|
||||
friendName,
|
||||
friends,
|
||||
friendsListenerError,
|
||||
} from "$lib/listeners/friends";
|
||||
</script>
|
||||
|
||||
<section>
|
||||
@@ -20,7 +24,7 @@
|
||||
{#each $friends as friend (friend.id)}
|
||||
{@const online = $onlineFriendIds.has(friend.id)}
|
||||
<li>
|
||||
<strong>{friend.displayName}</strong>: {online ? "Online" : "Offline"}
|
||||
<strong>{friendName(friend, "Unknown friend")}</strong>: {online ? "Online" : "Offline"}
|
||||
<small>({friend.id})</small>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { commands } from "$lib/bindings";
|
||||
import { friends, friendsListenerError } from "$lib/listeners/friends";
|
||||
import {
|
||||
friendName,
|
||||
friends,
|
||||
friendsListenerError,
|
||||
} from "$lib/listeners/friends";
|
||||
|
||||
let error = "";
|
||||
|
||||
@@ -11,10 +15,7 @@
|
||||
error = "";
|
||||
|
||||
try {
|
||||
await commands.createFriend({
|
||||
id: String(data.get("id") ?? ""),
|
||||
displayName: String(data.get("displayName") ?? ""),
|
||||
});
|
||||
await commands.createFriend(String(data.get("id") ?? ""));
|
||||
form.reset();
|
||||
} catch (err) {
|
||||
error = String(err);
|
||||
@@ -43,13 +44,6 @@
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
Display name
|
||||
<input name="displayName" required />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<button type="submit">Create friend</button>
|
||||
</form>
|
||||
|
||||
@@ -63,7 +57,7 @@
|
||||
<ul>
|
||||
{#each $friends as friend (friend.id)}
|
||||
<li>
|
||||
<span>{friend.displayName} ({friend.id})</span>
|
||||
<span>{friendName(friend, "Unknown friend")} ({friend.id})</span>
|
||||
<button type="button" onclick={() => deleteFriend(friend.id)}>Delete</button>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { writable } from "svelte/store";
|
||||
import { commands, events, type User } from "$lib/bindings";
|
||||
import { commands, events, type Friend } from "$lib/bindings";
|
||||
|
||||
export const friends = writable<User[]>([]);
|
||||
export const UNRESOLVED_FRIEND_NAME = "Name pending resolution";
|
||||
|
||||
export function friendName(friend: Friend | undefined, fallback: string) {
|
||||
return friend?.displayName ?? (friend ? UNRESOLVED_FRIEND_NAME : fallback);
|
||||
}
|
||||
|
||||
export const friends = writable<Friend[]>([]);
|
||||
export const friendsListenerError = writable("");
|
||||
|
||||
export async function initFriendsListener() {
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { onDestroy } from "svelte";
|
||||
import { commands } from "$lib/bindings";
|
||||
import PanelMessage from "$lib/components/control-panel/panel-message.svelte";
|
||||
import Info from "$lib/icons/info.svelte";
|
||||
|
||||
let displayName = $state("");
|
||||
let friendId = $state("");
|
||||
let busy = $state(false);
|
||||
let error = $state("");
|
||||
let preview = $state<{
|
||||
status: "idle" | "loading" | "resolved" | "unresolved" | "unavailable";
|
||||
text: string;
|
||||
}>({ status: "idle", text: "" });
|
||||
let lookupTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let lookupGeneration = 0;
|
||||
|
||||
onDestroy(() => {
|
||||
lookupGeneration += 1;
|
||||
if (lookupTimer) clearTimeout(lookupTimer);
|
||||
});
|
||||
|
||||
async function closeWindow() {
|
||||
await getCurrentWindow().close();
|
||||
@@ -15,23 +26,52 @@
|
||||
|
||||
async function submit(event: SubmitEvent) {
|
||||
event.preventDefault();
|
||||
const name = displayName.trim();
|
||||
const id = friendId.trim();
|
||||
if (!name || !id) {
|
||||
error = "Both display name and public key are required.";
|
||||
if (!id) {
|
||||
error = "Identification key is required.";
|
||||
return;
|
||||
}
|
||||
|
||||
busy = true;
|
||||
error = "";
|
||||
try {
|
||||
await commands.createFriend({ id, displayName: name });
|
||||
await commands.createFriend(id);
|
||||
await closeWindow();
|
||||
} catch (cause) {
|
||||
error = String(cause);
|
||||
busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleProfilePreview(event: Event) {
|
||||
const id = (event.currentTarget as HTMLTextAreaElement).value.trim();
|
||||
const generation = ++lookupGeneration;
|
||||
if (lookupTimer) clearTimeout(lookupTimer);
|
||||
if (!id) {
|
||||
preview = { status: "idle", text: "" };
|
||||
return;
|
||||
}
|
||||
|
||||
preview = { status: "loading", text: "Resolving…" };
|
||||
lookupTimer = setTimeout(
|
||||
() => void resolveProfilePreview(id, generation),
|
||||
250,
|
||||
);
|
||||
}
|
||||
|
||||
async function resolveProfilePreview(id: string, generation: number) {
|
||||
try {
|
||||
const displayName = await commands.resolveFriendDisplayName(id);
|
||||
if (generation !== lookupGeneration) return;
|
||||
preview = displayName
|
||||
? { status: "resolved", text: displayName }
|
||||
: { status: "unresolved", text: "Unresolved user" };
|
||||
} catch {
|
||||
if (generation === lookupGeneration) {
|
||||
preview = { status: "unavailable", text: "Unresolved" };
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head><title>Add Friend</title></svelte:head>
|
||||
@@ -46,18 +86,14 @@
|
||||
<div class="min-h-0 flex-1 space-y-2">
|
||||
{#if error}<PanelMessage kind="error">{error}</PanelMessage>{/if}
|
||||
|
||||
<PanelMessage>
|
||||
Their display name will appear after Wyd learns their profile from a
|
||||
shared server.
|
||||
</PanelMessage>
|
||||
|
||||
<fieldset class="fieldset border border-base-300 bg-base-100 p-3">
|
||||
<legend class="fieldset-legend px-1">Friend identity</legend>
|
||||
<label class="fieldset-label" for="friend-name">Display name</label>
|
||||
<input
|
||||
id="friend-name"
|
||||
class="input w-full"
|
||||
bind:value={displayName}
|
||||
maxlength="64"
|
||||
autocomplete="off"
|
||||
required
|
||||
/>
|
||||
<label class="fieldset-label mt-1" for="friend-key"
|
||||
<label class="fieldset-label" for="friend-key"
|
||||
>Identification Key <div
|
||||
class="tooltip tooltip-primary"
|
||||
data-tip="Verify with your friend before adding."
|
||||
@@ -71,7 +107,20 @@
|
||||
id="friend-key"
|
||||
class="textarea h-20 w-full resize-none font-mono text-[10px]"
|
||||
bind:value={friendId}
|
||||
autocomplete="off"
|
||||
oninput={scheduleProfilePreview}
|
||||
required></textarea>
|
||||
<label class="fieldset-label mt-1" for="friend-display-name"
|
||||
>Display name</label
|
||||
>
|
||||
<input
|
||||
id="friend-display-name"
|
||||
class:italic={preview.status !== "resolved"}
|
||||
class="input w-full"
|
||||
value={preview.text}
|
||||
aria-busy={preview.status === "loading"}
|
||||
readonly
|
||||
/>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import Popover from "$lib/components/popover.svelte";
|
||||
import { friends } from "$lib/listeners/friends";
|
||||
import { friendName, friends } from "$lib/listeners/friends";
|
||||
import { onlineFriendIds } from "$lib/listeners/friend-statuses";
|
||||
import { incomingInteraction } from "$lib/listeners/interactions";
|
||||
import { liveMetadata } from "$lib/listeners/live-metadata";
|
||||
@@ -44,8 +44,10 @@
|
||||
function displayName(userId: string) {
|
||||
return $profile?.id === userId
|
||||
? $profile.displayName
|
||||
: ($friends.find((friend) => friend.id === userId)?.displayName ??
|
||||
"Unknown user");
|
||||
: friendName(
|
||||
$friends.find((friend) => friend.id === userId),
|
||||
"Unknown user",
|
||||
);
|
||||
}
|
||||
|
||||
function dismissInteraction(interactionId: string) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
type AppMeta,
|
||||
type InteractionContent,
|
||||
} from "$lib/bindings";
|
||||
import { friends } from "$lib/listeners/friends";
|
||||
import { friendName, friends } from "$lib/listeners/friends";
|
||||
import { profile } from "$lib/listeners/profile";
|
||||
|
||||
type Props = {
|
||||
@@ -33,8 +33,10 @@
|
||||
let username = $derived(
|
||||
$profile?.id === userId
|
||||
? $profile.displayName
|
||||
: ($friends.find((friend) => friend.id === userId)?.displayName ??
|
||||
"Unknown user"),
|
||||
: friendName(
|
||||
$friends.find((friend) => friend.id === userId),
|
||||
"Unknown user",
|
||||
),
|
||||
);
|
||||
|
||||
function selectMode(nextMode: "message" | "image") {
|
||||
|
||||
Reference in New Issue
Block a user