Add profile detail modal

This commit is contained in:
Jon Staab
2024-12-16 12:54:17 -08:00
parent 3b202b31cb
commit cd8d8b548f
11 changed files with 150 additions and 55 deletions
+8 -10
View File
@@ -11,16 +11,16 @@
import {isMobile} from "@lib/html"
import LongPress from "@lib/components/LongPress.svelte"
import Avatar from "@lib/components/Avatar.svelte"
import Link from "@lib/components/Link.svelte"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Content from "@app/components/Content.svelte"
import ThunkStatus from "@app/components/ThunkStatus.svelte"
import ProfileDetail from "@app/components/ProfileDetail.svelte"
import ReactionSummary from "@app/components/ReactionSummary.svelte"
import ChannelMessageEmojiButton from "@app/components/ChannelMessageEmojiButton.svelte"
import ChannelMessageMenuButton from "@app/components/ChannelMessageMenuButton.svelte"
import ChannelMessageMenuMobile from "@app/components/ChannelMessageMenuMobile.svelte"
import {colors, tagRoom, pubkeyLink, PROTECTED} from "@app/state"
import {colors, tagRoom, PROTECTED} from "@app/state"
import {publishDelete, publishReaction} from "@app/commands"
import {pushModal} from "@app/modal"
@@ -39,6 +39,8 @@
const onLongPress = () => pushModal(ChannelMessageMenuMobile, {url, event})
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey})
const onReactionClick = (content: string, events: TrustedEvent[]) => {
const reaction = events.find(e => e.pubkey === $pubkey)
@@ -61,22 +63,18 @@
class="group relative flex w-full cursor-default flex-col p-2 pb-3 text-left">
<div class="flex w-full gap-3 overflow-auto">
{#if showPubkey}
<Link external href={pubkeyLink(event.pubkey)} class="flex items-start">
<Button on:click={openProfile} class="flex items-start">
<Avatar src={$profile?.picture} class="border border-solid border-base-content" size={8} />
</Link>
</Button>
{:else}
<div class="w-8 min-w-8 max-w-8" />
{/if}
<div class="min-w-0 flex-grow pr-1">
{#if showPubkey}
<div class="flex items-center gap-2">
<Link
external
href={pubkeyLink(event.pubkey)}
class="text-sm font-bold"
style="color: {colorValue}">
<Button on:click={openProfile} class="text-sm font-bold" style="color: {colorValue}">
{$profileDisplay}
</Link>
</Button>
<span class="text-xs opacity-50">{formatTimestampAsTime(event.created_at)}</span>
</div>
{/if}
+5 -3
View File
@@ -32,10 +32,11 @@
import ProfileName from "@app/components/ProfileName.svelte"
import ProfileCircle from "@app/components/ProfileCircle.svelte"
import ProfileCircles from "@app/components/ProfileCircles.svelte"
import ProfileDetail from "@app/components/ProfileDetail.svelte"
import ProfileList from "@app/components/ProfileList.svelte"
import ChatMessage from "@app/components/ChatMessage.svelte"
import ChatCompose from "@app/components/ChannelCompose.svelte"
import {userSettingValues, deriveChat, splitChatId, PLATFORM_NAME, pubkeyLink} from "@app/state"
import {userSettingValues, deriveChat, splitChatId, PLATFORM_NAME} from "@app/state"
import {pushModal} from "@app/modal"
import {sendWrapped} from "@app/commands"
@@ -125,10 +126,11 @@
<div slot="title" class="flex flex-col gap-1 sm:flex-row sm:gap-2">
{#if others.length === 1}
{@const pubkey = others[0]}
<Link external href={pubkeyLink(pubkey)} class="row-2">
{@const onClick = () => pushModal(ProfileDetail, {pubkey})}
<Button on:click={onClick} class="row-2">
<ProfileCircle {pubkey} size={5} />
<ProfileName {pubkey} />
</Link>
</Button>
{:else}
<div class="flex items-center gap-2">
<ProfileCircles pubkeys={others} size={5} />
+10 -8
View File
@@ -11,16 +11,17 @@
} from "@welshman/app"
import {isMobile} from "@lib/html"
import Icon from "@lib/components/Icon.svelte"
import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte"
import Tippy from "@lib/components/Tippy.svelte"
import LongPress from "@lib/components/LongPress.svelte"
import Avatar from "@lib/components/Avatar.svelte"
import Content from "@app/components/Content.svelte"
import ReactionSummary from "@app/components/ReactionSummary.svelte"
import ThunkStatus from "@app/components/ThunkStatus.svelte"
import ProfileDetail from "@app/components/ProfileDetail.svelte"
import ChatMessageMenu from "@app/components/ChatMessageMenu.svelte"
import ChatMessageMenuMobile from "@app/components/ChatMessageMenuMobile.svelte"
import {colors, pubkeyLink} from "@app/state"
import {colors} from "@app/state"
import {makeDelete, makeReaction, sendWrapped} from "@app/commands"
import {pushModal} from "@app/modal"
@@ -42,6 +43,8 @@
await sendWrapped({template, pubkeys})
}
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey})
const showMobileMenu = () => pushModal(ChatMessageMenuMobile, {event, pubkeys})
const togglePopover = () => {
@@ -94,21 +97,20 @@
{#if showPubkey}
<div class="flex items-center gap-2">
{#if !isOwn}
<Link external href={pubkeyLink(event.pubkey)} class="flex items-center gap-1">
<Button on:click={openProfile} class="flex items-center gap-1">
<Avatar
src={$profile?.picture}
class="border border-solid border-base-content"
size={4} />
<div class="flex items-center gap-2">
<Link
external
href={pubkeyLink(event.pubkey)}
<Button
on:click={openProfile}
class="text-sm font-bold"
style="color: {colorValue}">
{$profileDisplay}
</Link>
</Button>
</div>
</Link>
</Button>
{/if}
<span class="text-xs opacity-50">{formatTimestampAsTime(event.created_at)}</span>
</div>
+7 -4
View File
@@ -1,14 +1,17 @@
<script lang="ts">
import {displayProfile} from "@welshman/util"
import {deriveProfile} from "@welshman/app"
import Link from "@lib/components/Link.svelte"
import {pubkeyLink} from "@app/state"
import Button from "@lib/components/Button.svelte"
import ProfileDetail from "@app/components/ProfileDetail.svelte"
import {pushModal} from "@app/modal"
export let value
const profile = deriveProfile(value.pubkey)
const openProfile = () => pushModal(ProfileDetail, {pubkey: value.pubkey})
</script>
<Link external href={pubkeyLink(value.pubkey)} class="link-content">
<Button on:click={openProfile} class="link-content">
@{displayProfile($profile)}
</Link>
</Button>
+1 -1
View File
@@ -90,7 +90,7 @@
<PrimaryNavItem title="Search" href="/people">
<Avatar icon="magnifer" class="!h-10 !w-10" />
</PrimaryNavItem>
<PrimaryNavItem title="Notes" href="/notes">
<PrimaryNavItem title="Notes" on:click={openNotes}>
<Avatar icon="notes-minimalistic" class="!h-10 !w-10" />
</PrimaryNavItem>
<PrimaryNavItem
+9 -6
View File
@@ -9,10 +9,11 @@
displayHandle,
deriveProfileDisplay,
} from "@welshman/app"
import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte"
import Avatar from "@lib/components/Avatar.svelte"
import WotScore from "@lib/components/WotScore.svelte"
import {pubkeyLink} from "@app/state"
import ProfileDetail from "@app/components/ProfileDetail.svelte"
import {pushModal} from "@app/modal"
export let pubkey
@@ -21,19 +22,21 @@
const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey)
const openProfile = () => pushModal(ProfileDetail, {pubkey})
$: following =
pubkey === $session!.pubkey || getPubkeyTagValues(getListTags($userFollows)).includes(pubkey)
</script>
<div class="flex max-w-full gap-3">
<Link external href={pubkeyLink(pubkey)} class="py-1">
<Button on:click={openProfile} class="py-1">
<Avatar src={$profile?.picture} size={10} />
</Link>
</Button>
<div class="flex min-w-0 flex-col">
<div class="flex items-center gap-2">
<Link external href={pubkeyLink(pubkey)} class="text-bold overflow-hidden text-ellipsis">
<Button on:click={openProfile} class="text-bold overflow-hidden text-ellipsis">
{$profileDisplay}
</Link>
</Button>
<WotScore score={$score} active={following} />
</div>
<div class="overflow-hidden text-ellipsis text-sm opacity-75">
+76
View File
@@ -0,0 +1,76 @@
<script lang="ts">
import {goto} from "$app/navigation"
import {displayPubkey, getPubkeyTagValues, getListTags} from "@welshman/util"
import {
session,
userFollows,
deriveUserWotScore,
deriveProfile,
deriveHandleForPubkey,
displayHandle,
deriveProfileDisplay,
} from "@welshman/app"
import Icon from "@lib/components/Icon.svelte"
import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte"
import Avatar from "@lib/components/Avatar.svelte"
import WotScore from "@lib/components/WotScore.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import ProfileInfo from "@app/components/ProfileInfo.svelte"
import ChatEnable from "@app/components/ChatEnable.svelte"
import {canDecrypt, pubkeyLink} from "@app/state"
import {pushModal} from "@app/modal"
import {makeChatPath} from "@app/routes"
export let pubkey
const profile = deriveProfile(pubkey)
const profileDisplay = deriveProfileDisplay(pubkey)
const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey)
const back = () => history.back()
const chatPath = makeChatPath([pubkey])
const openChat = () => ($canDecrypt ? goto(chatPath) : pushModal(ChatEnable, {next: chatPath}))
$: following =
pubkey === $session!.pubkey || getPubkeyTagValues(getListTags($userFollows)).includes(pubkey)
</script>
<div class="column gap-4">
<div class="flex max-w-full gap-3">
<span class="py-1">
<Avatar src={$profile?.picture} size={10} />
</span>
<div class="flex min-w-0 flex-col">
<div class="flex items-center gap-2">
<span class="text-bold overflow-hidden text-ellipsis">
{$profileDisplay}
</span>
<WotScore score={$score} active={following} />
</div>
<div class="overflow-hidden text-ellipsis text-sm opacity-75">
{$handle ? displayHandle($handle) : displayPubkey(pubkey)}
</div>
</div>
</div>
<ProfileInfo {pubkey} />
<ModalFooter>
<Button on:click={back} class="btn btn-link">
<Icon icon="alt-arrow-left" />
Go back
</Button>
<div class="flex gap-2">
<Link external href={pubkeyLink(pubkey)} class="btn btn-neutral">
<Icon icon="user-circle" />
See Complete Profile
</Link>
<Button on:click={openChat} class="btn btn-primary">
<Icon icon="letter" />
Open Chat
</Button>
</div>
</ModalFooter>
</div>
+5 -4
View File
@@ -5,12 +5,12 @@
import {profileSearch} from "@welshman/app"
import Icon from "@lib/components/Icon.svelte"
import Tippy from "@lib/components/Tippy.svelte"
import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte"
import Suggestions from "@lib/editor/Suggestions.svelte"
import SuggestionProfile from "@lib/editor/SuggestionProfile.svelte"
import ProfileName from "@app/components/ProfileName.svelte"
import {pubkeyLink} from "@app/state"
import ProfileDetail from "@app/components/ProfileDetail.svelte"
import {pushModal} from "@app/modal"
export let value: string[]
export let autofocus = false
@@ -48,13 +48,14 @@
<div class="flex flex-col gap-2">
<div>
{#each value as pubkey (pubkey)}
{@const onClick = () => pushModal(ProfileDetail, {pubkey})}
<div class="flex-inline badge badge-neutral mr-1 gap-1">
<Button class="flex items-center" on:click={() => removePubkey(pubkey)}>
<Icon icon="close-circle" size={4} class="-ml-1 mt-px" />
</Button>
<Link external href={pubkeyLink(pubkey)}>
<Button on:click={onClick}>
<ProfileName {pubkey} />
</Link>
</Button>
</div>
{/each}
</div>
+6 -3
View File
@@ -4,15 +4,18 @@
import Link from "@lib/components/Link.svelte"
import Content from "@app/components/Content.svelte"
import ProfileName from "@app/components/ProfileName.svelte"
import ProfileDetail from "@app/components/ProfileDetail.svelte"
import ThreadActions from "@app/components/ThreadActions.svelte"
import {makeThreadPath} from "@app/routes"
import {pubkeyLink} from "@app/state"
import {pushModal} from "@app/modal"
export let url
export let event
export let hideActions = false
const title = event.tags.find(nthEq(0, "title"))?.[1]
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey})
</script>
<Link class="col-2 card2 bg-alt w-full cursor-pointer" href={makeThreadPath(url, event.id)}>
@@ -32,9 +35,9 @@
<div class="flex w-full flex-col items-end justify-between gap-2 sm:flex-row">
<span class="whitespace-nowrap py-1 text-sm opacity-75">
Posted by
<Link external href={pubkeyLink(event.pubkey)} class="link-content">
<button type="button" on:click|preventDefault={openProfile} class="link-content">
@<ProfileName pubkey={event.pubkey} />
</Link>
</button>
</span>
{#if !hideActions}
<ThreadActions showActivity {url} {event} />
+7 -2
View File
@@ -1,9 +1,14 @@
<script lang="ts">
import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte"
import Icon from "@lib/components/Icon.svelte"
import {PLATFORM_NAME, pubkeyLink} from "@app/state"
import ProfileDetail from "@app/components/ProfileDetail.svelte"
import {PLATFORM_NAME} from "@app/state"
import {pushModal} from "@app/modal"
const pubkey = "97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322"
const openProfile = () => pushModal(ProfileDetail, {pubkey})
</script>
<div class="mt-8 min-h-screen bg-base-200 sm:hero">
@@ -34,7 +39,7 @@
<p class="text-sm">
Built with 💜 by
<span class="text-primary">
@<Link external href={pubkeyLink(pubkey)} class="link">hodlbod</Link>
@<Button on:click={openProfile} class="link">hodlbod</Button>
</span>
</p>
<p class="text-xs">
+16 -14
View File
@@ -120,8 +120,8 @@
</div>
<div class="grid grid-cols-3 gap-2">
<Link href={threadsPath} class="btn btn-primary">
<Icon icon="notes-minimalistic" />
<div class="relative">
<div class="relative flex items-center gap-2">
<Icon icon="notes-minimalistic" />
Threads
{#if $notifications.has(threadsPath)}
<div
@@ -133,12 +133,12 @@
{#each $userRooms as room (room)}
{@const roomPath = makeRoomPath(url, room)}
<Link href={roomPath} class="btn btn-neutral">
{#if channelIsLocked($channelsById.get(makeChannelId(url, room)))}
<Icon icon="lock" size={4} />
{:else}
<Icon icon="hashtag" />
{/if}
<div class="relative">
<div class="relative flex min-w-0 items-center gap-2 overflow-hidden">
{#if channelIsLocked($channelsById.get(makeChannelId(url, room)))}
<Icon icon="lock" size={4} />
{:else}
<Icon icon="hashtag" />
{/if}
<ChannelName {url} {room} />
{#if $notifications.has(roomPath)}
<div
@@ -150,12 +150,14 @@
{/each}
{#each $otherRooms as room (room)}
<Link href={makeRoomPath(url, room)} class="btn btn-neutral">
{#if channelIsLocked($channelsById.get(makeChannelId(url, room)))}
<Icon icon="lock" size={4} />
{:else}
<Icon icon="hashtag" />
{/if}
<ChannelName {url} {room} />
<div class="relative flex min-w-0 items-center gap-2 overflow-hidden">
{#if channelIsLocked($channelsById.get(makeChannelId(url, room)))}
<Icon icon="lock" size={4} />
{:else}
<Icon icon="hashtag" />
{/if}
<ChannelName {url} {room} />
</div>
</Link>
{/each}
<Button on:click={addRoom} class="btn btn-neutral">