@chisquare-tech/yan-ngx-apps-messages 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{chisquare-tech-yan-ngx-apps-messages-chisquare-tech-yan-ngx-apps-messages-BtQC73sF.mjs → chisquare-tech-yan-ngx-apps-messages-chisquare-tech-yan-ngx-apps-messages-BBsDA0CG.mjs} +350 -41
- package/fesm2022/chisquare-tech-yan-ngx-apps-messages-chisquare-tech-yan-ngx-apps-messages-BBsDA0CG.mjs.map +1 -0
- package/fesm2022/{chisquare-tech-yan-ngx-apps-messages-messages.page-L0fDp0gv.mjs → chisquare-tech-yan-ngx-apps-messages-messages.page-Dc4cpq4L.mjs} +88 -10
- package/fesm2022/chisquare-tech-yan-ngx-apps-messages-messages.page-Dc4cpq4L.mjs.map +1 -0
- package/fesm2022/{chisquare-tech-yan-ngx-apps-messages-start.page-gJ1cJDfy.mjs → chisquare-tech-yan-ngx-apps-messages-start.page-Dnv52Der.mjs} +2 -2
- package/fesm2022/{chisquare-tech-yan-ngx-apps-messages-start.page-gJ1cJDfy.mjs.map → chisquare-tech-yan-ngx-apps-messages-start.page-Dnv52Der.mjs.map} +1 -1
- package/fesm2022/chisquare-tech-yan-ngx-apps-messages.mjs +1 -1
- package/package.json +2 -2
- package/types/chisquare-tech-yan-ngx-apps-messages.d.ts +62 -2
- package/fesm2022/chisquare-tech-yan-ngx-apps-messages-chisquare-tech-yan-ngx-apps-messages-BtQC73sF.mjs.map +0 -1
- package/fesm2022/chisquare-tech-yan-ngx-apps-messages-messages.page-L0fDp0gv.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chisquare-tech-yan-ngx-apps-messages-messages.page-L0fDp0gv.mjs","sources":["../../../projects/messages/src/lib/styles.ts","../../../projects/messages/src/lib/messages.page.ts"],"sourcesContent":["/** The prototype's Messages layout (prototype.css .msg-list, .pane, .thread-head, .composer) on yan-design. */\nexport const MESSAGES_STYLES = `\n /* Messages fills the shell's main area: no content column, and the thread scrolls inside it */\n .yan-shell__scroller:has(> .yan-shell__col > yan-messages-page) { overflow: hidden; }\n .yan-shell__col:has(> yan-messages-page) { max-width: none; padding: 0; height: 100%; }\n yan-messages-page { display: block; height: 100%; }\n\n .yan-msgs { display: flex; height: 100%; }\n .yan-msgs__list { flex: none; width: 100%; overflow-y: auto; }\n .yan-msgs__pane { flex: 1; min-width: 0; display: none; flex-direction: column; }\n .yan-msgs--open .yan-msgs__list { display: none; }\n .yan-msgs--open .yan-msgs__pane { display: flex; }\n @media (min-width: 820px) {\n .yan-msgs__list { width: 340px; border-right: 1px solid var(--yan-color-border-default); }\n .yan-msgs--open .yan-msgs__list { display: block; }\n .yan-msgs__pane { display: flex; }\n .yan-msgs__back { display: none; }\n }\n\n .yan-msgs__title { font: var(--yan-text-title-lg); letter-spacing: var(--yan-text-title-lg-tracking); font-size: 22px; padding: 18px 16px 10px; margin: 0; }\n .yan-msgs__row { padding: 12px 16px; font: inherit; }\n .yan-msgs__line { display: flex; align-items: center; gap: 6px; min-width: 0; }\n .yan-msgs__name { font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .yan-msgs__last { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .yan-msgs__empty, .yan-msgs__err { margin: 16px; }\n .yan-msgs__sk1 { display: block; height: 12px; width: 40%; border-radius: 6px; margin-bottom: 6px; }\n .yan-msgs__sk2 { display: block; height: 10px; width: 70%; border-radius: 6px; }\n\n .yan-msgs__head { flex: none; display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--yan-color-border-default); }\n .yan-msgs__headname { font-weight: 600; }\n .yan-msgs__grow { flex: 1; min-width: 0; }\n .yan-msgs__scroll { flex: 1; min-height: 0; overflow-y: auto; }\n .yan-msgs__placeholder { margin: auto; max-width: 360px; padding: 24px; }\n .yan-msgs__older { align-self: center; }\n .yan-msgs__start { align-self: center; text-align: center; max-width: 360px; }\n .yan-msgs__lost { align-self: center; text-align: center; max-width: 420px; }\n .yan-msgs__from { font: var(--yan-text-micro); color: var(--yan-color-ink-muted); margin: 4px 0 -6px 4px; }\n .yan-msgs__unreadable { font-style: italic; color: var(--yan-color-ink-muted); }\n .yan-bubble--me.yan-msgs__unreadable { color: var(--yan-color-accent-on-accent); opacity: 0.8; }\n .yan-msgs__pending { opacity: 0.6; }\n .yan-msgs__failed { align-self: flex-end; font: var(--yan-text-caption); color: var(--yan-color-danger-default); margin-top: -6px; }\n .yan-msgs__seen { align-self: flex-end; font: var(--yan-text-micro); color: var(--yan-color-ink-muted); margin-top: -6px; }\n .yan-msgs__typing { align-self: flex-start; }\n .yan-msgs__skb { width: 50%; height: 36px; }\n .yan-msgs__cap { font: var(--yan-text-caption); color: var(--yan-color-ink-muted); }\n .yan-msgs__note { flex: none; padding: 8px 16px 0; }\n .yan-msgs__banner { flex: none; margin: 8px 16px 0; }\n .yan-msgs__reqbar { flex: none; display: flex; flex-direction: column; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--yan-color-border-default); }\n .yan-msgs__reqtext { display: block; width: 100%; margin: 10px 0; padding: 0; border: 0; background: none; text-align: left; font: inherit; color: inherit; cursor: pointer; overflow-wrap: anywhere; }\n .yan-msgs__reqacts { margin-top: 10px; }\n .yan-msgs__composer { flex: none; display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--yan-color-border-default); }\n .yan-msgs__composer .yan-input { flex: 1; min-width: 0; border-radius: var(--yan-radius-pill); }\n`;\n","import { ChangeDetectionStrategy, Component, ElementRef, OnDestroy, OnInit, ViewEncapsulation, computed, effect, inject, signal, viewChild } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { ToastService, hueOf, initials, timeAgo } from '@chisquare-tech/yan-ngx-ui';\nimport { Subscription } from 'rxjs';\nimport { E2EE_LABEL, conversationName, expiresIn, kindLabel, others, pendingFor, personName, requestsLine, seen } from './labels';\nimport { errorText, type Conversation } from './messaging-api';\nimport { MessagesStore, type Bubble } from './messages.store';\nimport { MESSAGES_HOST } from './tokens';\nimport { MESSAGES_STYLES } from './styles';\n\n/**\n * `/messages`, `/messages/requests`, `/messages/:id`: the prototype's Messages screen. The list (Requests first, then\n * conversations) and the open thread side by side from 820 px; below that, push navigation. Every DM and group is\n * end-to-end encrypted on this device.\n */\n@Component({\n selector: 'yan-messages-page',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n styles: MESSAGES_STYLES,\n template: `\n <div class=\"yan-msgs\" [class.yan-msgs--open]=\"!!sel()\">\n <div class=\"yan-msgs__list\" [attr.aria-hidden]=\"null\">\n <h1 class=\"yan-msgs__title\">Messages</h1>\n @if (!signedIn()) {\n <div class=\"yan-empty yan-msgs__empty\">\n <span class=\"yi yi-messages\" style=\"--yi:28px\" aria-hidden=\"true\"></span>\n <div class=\"yan-empty__title\">Sign in to message people</div>\n <div>Direct messages and groups are end-to-end encrypted.</div>\n <button type=\"button\" class=\"yan-btn\" (click)=\"signIn()\">Sign in</button>\n </div>\n } @else if (store.failed() && !store.loaded()) {\n <div class=\"yan-error yan-msgs__err\" role=\"alert\"><span class=\"yi yi-alert-circle\" style=\"--yi:14px\" aria-hidden=\"true\"></span>We couldn't load your messages. <button type=\"button\" class=\"yan-btn yan-btn--sm yan-btn--ghost\" (click)=\"store.loadLists()\">Try again</button></div>\n } @else if (!store.loaded()) {\n @for (i of [1, 2, 3, 4]; track i) {\n <div class=\"yan-row yan-msgs__row\" aria-hidden=\"true\"><span class=\"yan-avatar yan-avatar--lg yan-skeleton\"></span><span class=\"yan-row__body\"><span class=\"yan-skeleton yan-msgs__sk1\"></span><span class=\"yan-skeleton yan-msgs__sk2\"></span></span></div>\n }\n } @else {\n <button type=\"button\" class=\"yan-row yan-row--interactive yan-msgs__row\" [class.yan-row--selected]=\"sel() === 'requests'\" (click)=\"go(['requests'])\">\n <span class=\"yan-avatar yan-avatar--lg yan-avatar--square\" style=\"--yan-avatar-hue:80\"><span class=\"yi yi-inbox\" aria-hidden=\"true\"></span></span>\n <span class=\"yan-row__body\">\n <span class=\"yan-msgs__line\"><b class=\"yan-msgs__name\">Requests</b><span class=\"yan-eyebrow\">Outside your network</span>@if (store.requests().length) {<span class=\"yan-badge\">{{ store.requests().length }}</span>}</span>\n <span class=\"yan-row__sub yan-msgs__last\">{{ requestsSub() }}</span>\n </span>\n </button>\n @for (c of store.inbox(); track c.id) {\n <button type=\"button\" class=\"yan-row yan-row--interactive yan-msgs__row\" [class.yan-row--selected]=\"sel() === c.id\" (click)=\"go([c.id])\">\n <span class=\"yan-avatar yan-avatar--lg\" [class.yan-avatar--square]=\"c.kind === 'group'\" [style.--yan-avatar-hue]=\"hueFor(c)\">{{ initialFor(c) }}</span>\n <span class=\"yan-row__body\">\n <span class=\"yan-msgs__line\"><b class=\"yan-msgs__name\">{{ name(c) }}</b><span class=\"yan-eyebrow\">{{ kind(c) }}</span>@if (c.unread) {<span class=\"yan-badge\" [attr.aria-label]=\"c.unread + ' unread'\">{{ c.unread }}</span>}</span>\n <span class=\"yan-row__sub yan-msgs__last\">{{ typingIn(c) || store.preview(c) }}</span>\n </span>\n </button>\n } @empty {\n <div class=\"yan-empty yan-msgs__empty\">\n <span class=\"yi yi-messages\" style=\"--yi:28px\" aria-hidden=\"true\"></span>\n <div class=\"yan-empty__title\">No messages yet</div>\n <div>Message someone from their profile. People outside your network see it under Requests first.</div>\n </div>\n }\n }\n </div>\n\n <div class=\"yan-msgs__pane\">\n @if (!sel()) {\n <div class=\"yan-empty yan-msgs__placeholder\">\n <span class=\"yi yi-shield-lock\" style=\"--yi:28px\" aria-hidden=\"true\"></span>\n <div class=\"yan-empty__title\">Your messages</div>\n <div>Pick a conversation. Only the people in it can read it, on their devices.</div>\n </div>\n } @else if (sel() === 'requests') {\n <div class=\"yan-msgs__head\">\n <button type=\"button\" class=\"yan-icon-btn yan-icon-btn--plain yan-msgs__back\" (click)=\"go([])\" aria-label=\"Back to messages\"><span class=\"yi yi-arrow-left\" aria-hidden=\"true\"></span></button>\n <div class=\"yan-msgs__grow\"><div class=\"yan-msgs__headname\">Requests</div><span class=\"yan-security yan-security--server\"><span class=\"yi yi-inbox\" style=\"--yi:14px\" aria-hidden=\"true\"></span>Requests from outside your network</span></div>\n </div>\n <div class=\"yan-thread yan-msgs__scroll\">\n @for (r of store.requests(); track r.id) {\n <div class=\"yan-card yan-card--tight\">\n <div class=\"yan-msgs__line\"><span class=\"yan-avatar\" [style.--yan-avatar-hue]=\"hueFor(r)\">{{ initialFor(r) }}</span><div class=\"yan-msgs__grow\"><b>{{ name(r) }}</b><div class=\"yan-msgs__cap\">Not in your network · {{ ago(r.lastMessageAt || r.createdAt) }} · {{ expires(r) }}</div></div></div>\n <button type=\"button\" class=\"yan-msgs__reqtext\" (click)=\"go([r.id])\">{{ store.preview(r) || 'Open to read' }}</button>\n <div class=\"yan-cluster yan-msgs__reqacts\">\n <button type=\"button\" class=\"yan-btn yan-btn--sm\" [attr.aria-disabled]=\"busy()\" (click)=\"accept(r)\">Accept</button>\n <button type=\"button\" class=\"yan-btn yan-btn--sm yan-btn--secondary\" [attr.aria-disabled]=\"busy()\" (click)=\"decline(r)\">Decline</button>\n </div>\n </div>\n } @empty {\n <div class=\"yan-empty\"><span class=\"yi yi-inbox\" style=\"--yi:28px\" aria-hidden=\"true\"></span><div class=\"yan-empty__title\">No requests</div><div>Messages from people outside your network land here first.</div></div>\n }\n </div>\n } @else {\n @let t = thread();\n <div class=\"yan-msgs__head\">\n <button type=\"button\" class=\"yan-icon-btn yan-icon-btn--plain yan-msgs__back\" (click)=\"go(t.conv?.myState === 'request' ? ['requests'] : [])\" aria-label=\"Back to messages\"><span class=\"yi yi-arrow-left\" aria-hidden=\"true\"></span></button>\n <div class=\"yan-msgs__grow\">\n <div class=\"yan-msgs__headname\">{{ t.conv ? name(t.conv) : '' }}</div>\n <span class=\"yan-security {{ e2ee.cls }}\"><span class=\"yi yi-{{ e2ee.icon }}\" style=\"--yi:14px\" aria-hidden=\"true\"></span>{{ e2ee.text }}</span>\n </div>\n </div>\n @if (t.missing) {\n <div class=\"yan-empty yan-msgs__placeholder\"><span class=\"yi yi-messages\" style=\"--yi:28px\" aria-hidden=\"true\"></span><div class=\"yan-empty__title\">This conversation isn't available</div><div>It may have been declined, or you left it.</div></div>\n } @else {\n <div class=\"yan-thread yan-msgs__scroll\" #scroller (scroll)=\"onScroll()\">\n @if (t.next) {\n <button type=\"button\" class=\"yan-btn yan-btn--sm yan-btn--ghost yan-msgs__older\" [attr.aria-disabled]=\"t.loading\" (click)=\"store.loadOlder(sel()!)\">Earlier messages</button>\n }\n @if (t.failed) {\n <div class=\"yan-error\" role=\"alert\"><span class=\"yi yi-alert-circle\" style=\"--yi:14px\" aria-hidden=\"true\"></span>We couldn't load this conversation. <button type=\"button\" class=\"yan-btn yan-btn--sm yan-btn--ghost\" (click)=\"reload()\">Try again</button></div>\n } @else if (t.loading && !t.items.length) {\n <div class=\"yan-bubble yan-skeleton yan-msgs__skb\" aria-hidden=\"true\"></div>\n <div class=\"yan-bubble yan-bubble--me yan-skeleton yan-msgs__skb\" aria-hidden=\"true\"></div>\n } @else if (!t.items.length) {\n <div class=\"yan-msgs__cap yan-msgs__start\">Messages here are end-to-end encrypted. Only the people in this conversation can read them.</div>\n } @else if (hasUnreadable()) {\n <div class=\"yan-msgs__cap yan-msgs__lost\">Messages sent before you set up this device can't be read here. They're still on the devices you had then.</div>\n }\n @for (m of t.items; track m.clientMsgId || m.id) {\n @if (showSender(m, $index)) {<div class=\"yan-msgs__from\">{{ who(m.senderId) }}</div>}\n <div class=\"yan-bubble\" [class.yan-bubble--me]=\"m.mine\" [class.yan-msgs__unreadable]=\"m.state === 'unreadable'\" [class.yan-msgs__pending]=\"m.state === 'pending'\">{{ m.text }}</div>\n @if (m.state === 'failed') {\n <div class=\"yan-msgs__failed\" role=\"alert\">{{ m.error || 'Not sent.' }} <button type=\"button\" class=\"yan-btn yan-btn--link yan-btn--sm\" (click)=\"store.retry(sel()!, m)\">Try again</button></div>\n } @else if (m.id === seenOn()) {\n <div class=\"yan-msgs__seen\">Seen</div>\n }\n }\n @if (typingName(); as tn) {<div class=\"yan-msgs__cap yan-msgs__typing\" aria-live=\"polite\">{{ tn }} is typing…</div>}\n </div>\n @if (store.crypto() === 'failed') {\n <div class=\"yan-banner yan-banner--warning yan-msgs__banner\" role=\"alert\"><span class=\"yi yi-alert-triangle\" aria-hidden=\"true\"></span>Messages can't be set up in this browser: it needs storage and WebAssembly, which some private windows block. <button type=\"button\" class=\"yan-btn yan-btn--sm\" (click)=\"store.retryCrypto()\">Try again</button></div>\n }\n @if (t.conv?.myState === 'request') {\n <div class=\"yan-msgs__reqbar\">\n <div class=\"yan-msgs__cap\">{{ name(t.conv!) }} isn't in your network. Accept to reply; decline and it's removed. {{ expires(t.conv!) }}.</div>\n <div class=\"yan-cluster\"><button type=\"button\" class=\"yan-btn yan-btn--sm\" [attr.aria-disabled]=\"busy()\" (click)=\"accept(t.conv!)\">Accept</button><button type=\"button\" class=\"yan-btn yan-btn--sm yan-btn--secondary\" [attr.aria-disabled]=\"busy()\" (click)=\"decline(t.conv!)\">Decline</button></div>\n </div>\n } @else if (t.conv) {\n @if (pending(); as note) {<div class=\"yan-msgs__cap yan-msgs__note\">{{ note }}</div>}\n <form class=\"yan-msgs__composer\" (submit)=\"$event.preventDefault(); send()\">\n <input class=\"yan-input\" [value]=\"draft()\" (input)=\"onDraft($event)\" [placeholder]=\"placeholder()\" [attr.aria-label]=\"placeholder()\" maxlength=\"4000\" autocomplete=\"off\" />\n <button type=\"submit\" class=\"yan-btn yan-btn--accent\" aria-label=\"Send\" [attr.aria-disabled]=\"!draft().trim() || store.crypto() === 'starting'\"><span class=\"yi yi-send\" style=\"--yi:18px\" aria-hidden=\"true\"></span></button>\n </form>\n }\n }\n }\n </div>\n </div>\n `,\n})\nexport class MessagesPage implements OnInit, OnDestroy {\n protected readonly store = inject(MessagesStore);\n private readonly host = inject(MESSAGES_HOST);\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly toast = inject(ToastService);\n private sub?: Subscription;\n private readonly scroller = viewChild<ElementRef<HTMLElement>>('scroller');\n private atBottom = true;\n\n protected readonly e2ee = E2EE_LABEL;\n readonly sel = signal<string | null>(null);\n readonly draft = signal('');\n readonly busy = signal(false);\n readonly signedIn = signal(false);\n\n readonly thread = computed(() => {\n const id = this.sel();\n return id && id !== 'requests' ? this.store.thread(id)() : { conv: null, items: [] as Bubble[], loading: false, failed: false, missing: false };\n });\n\n readonly requestsSub = computed(() => {\n const r = this.store.requests();\n return requestsLine(r.length, r[0] ? this.name(r[0]) : undefined);\n });\n\n /** The message that carries \"Seen\": your last one, once everyone else has read it. */\n readonly seenOn = computed(() => {\n const t = this.thread();\n const me = this.store.me();\n if (!t.conv || !me) return null;\n const ok = t.items.filter((b) => b.state === 'ok' || b.state === 'unreadable');\n const lastMine = [...ok].reverse().find((b) => b.mine)?.id;\n return seen(t.conv, me, ok.map((b) => b.id), lastMine) ? lastMine! : null;\n });\n\n readonly hasUnreadable = computed(() => this.thread().items.some((b) => b.state === 'unreadable' && b.unreadable === 'missing-key'));\n\n readonly pending = computed(() => {\n const t = this.thread();\n const me = this.store.me();\n return t.conv && me ? pendingFor(t.conv, me, this.store.people()) : null;\n });\n\n readonly typingName = computed(() => {\n const id = this.sel();\n const who = id ? this.store.typing()[id] : undefined;\n return who ? personName(this.store.people()[who]) : null;\n });\n\n readonly placeholder = computed(() => {\n const c = this.thread().conv;\n if (!c) return 'Message';\n return c.kind === 'group' ? 'Message group' : `Message ${firstName(this.name(c))}`;\n });\n\n constructor() {\n // keep the newest message in view as messages arrive, unless you've scrolled up to read\n effect(() => {\n const n = this.thread().items.length;\n if (!n) return;\n queueMicrotask(() => requestAnimationFrame(() => this.toBottom()));\n });\n }\n\n ngOnInit(): void {\n this.signedIn.set(!!this.store.me());\n if (this.signedIn()) void this.store.start();\n this.sub = this.route.paramMap.subscribe((p) => {\n const id = p.get('id') ?? (this.route.snapshot.url.at(-1)?.path === 'requests' ? 'requests' : null);\n this.sel.set(id);\n this.draft.set('');\n this.atBottom = true;\n if (id && id !== 'requests' && this.signedIn()) void this.store.openThread(id);\n });\n }\n\n ngOnDestroy(): void {\n this.sub?.unsubscribe();\n }\n\n go(path: string[]): void {\n void this.router.navigate(['/messages', ...path]);\n }\n\n signIn(): void {\n this.host.requestSignIn(this.router.url);\n }\n\n reload(): void {\n const id = this.sel();\n if (id && id !== 'requests') void this.store.openThread(id);\n }\n\n name(c: Conversation): string {\n return conversationName(c, this.store.me() ?? '', this.store.people());\n }\n\n kind(c: Conversation): string {\n return kindLabel(c);\n }\n\n who(userId: string): string {\n return personName(this.store.people()[userId]);\n }\n\n hueFor(c: Conversation): number {\n const o = others(c, this.store.me() ?? '');\n return c.kind === 'direct' && o[0] ? hueOf(o[0].userId) : hueOf(c.id);\n }\n\n initialFor(c: Conversation): string {\n return initials(this.name(c));\n }\n\n ago(iso: string): string {\n return timeAgo(iso);\n }\n\n expires(c: Conversation): string {\n return expiresIn(c.requestUntil);\n }\n\n typingIn(c: Conversation): string {\n const who = this.store.typing()[c.id];\n return who ? `${firstName(personName(this.store.people()[who]))} is typing…` : '';\n }\n\n /** Groups name the sender above a run of their messages. */\n showSender(m: Bubble, i: number): boolean {\n const t = this.thread();\n if (t.conv?.kind !== 'group' || m.mine) return false;\n const prev = t.items[i - 1];\n return !prev || prev.senderId !== m.senderId;\n }\n\n onDraft(e: Event): void {\n this.draft.set((e.target as HTMLInputElement).value);\n const id = this.sel();\n if (id && id !== 'requests' && this.draft().trim()) this.store.typed(id);\n }\n\n async send(): Promise<void> {\n const id = this.sel();\n const text = this.draft().trim();\n if (!id || id === 'requests' || !text || this.store.crypto() === 'starting') return;\n this.draft.set('');\n this.atBottom = true;\n await this.store.send(id, text);\n }\n\n async accept(c: Conversation): Promise<void> {\n if (this.busy()) return;\n this.busy.set(true);\n try {\n await this.store.accept(c.id);\n this.go([c.id]);\n } catch (e) {\n this.toast.show(errorText(e, \"Couldn't accept. Try again.\"));\n } finally {\n this.busy.set(false);\n }\n }\n\n async decline(c: Conversation): Promise<void> {\n if (this.busy()) return;\n this.busy.set(true);\n try {\n await this.store.decline(c.id);\n this.go(['requests']);\n } catch (e) {\n this.toast.show(errorText(e, \"Couldn't decline. Try again.\"));\n } finally {\n this.busy.set(false);\n }\n }\n\n onScroll(): void {\n const el = this.scroller()?.nativeElement;\n if (el) this.atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 80;\n }\n\n private toBottom(): void {\n const el = this.scroller()?.nativeElement;\n if (el && this.atBottom) el.scrollTop = el.scrollHeight;\n }\n}\n\nfunction firstName(n: string): string {\n return n.startsWith('@') ? n : n.split(' ')[0];\n}\n"],"names":[],"mappings":"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"}
|