From 98dda99c5c4c69e34bd4d6097193c6c2c9e50a2d Mon Sep 17 00:00:00 2001 From: h Date: Mon, 19 Oct 2026 03:25:48 +0000 Subject: [PATCH] feat(frontend): profiles chip in the feed shows name, username, photo and title changes --- frontend/src/lib/api/types.ts | 3 +- .../lib/components/feed/ChatChanges.svelte | 10 ++- .../src/lib/components/feed/FeedEntry.svelte | 64 +++++++++++++++++-- .../lib/components/feed/FeedFilters.svelte | 3 +- .../src/lib/components/feed/FeedList.svelte | 1 + .../src/lib/components/feed/FeedPhoto.svelte | 64 +++++++++++++++++++ .../src/lib/components/feed/FeedView.svelte | 8 ++- frontend/src/lib/i18n/messages/feed.ts | 34 ++++++++++ frontend/src/lib/stores/feed.svelte.ts | 8 ++- 9 files changed, 181 insertions(+), 14 deletions(-) create mode 100644 frontend/src/lib/components/feed/FeedPhoto.svelte diff --git a/frontend/src/lib/api/types.ts b/frontend/src/lib/api/types.ts index 16af511..c29182d 100644 --- a/frontend/src/lib/api/types.ts +++ b/frontend/src/lib/api/types.ts @@ -222,7 +222,7 @@ export interface MessageVersion { text: string | null; } -export type FeedKind = "all" | "deleted" | "edited"; +export type FeedKind = "all" | "deleted" | "edited" | "profile"; export type FeedChatKind = "dm" | "group" | "channel"; export interface FeedChat { @@ -237,6 +237,7 @@ export interface FeedItem { at: string; before: string | null; chat: FeedChat; + detail: Record | null; edit_date: string | null; has_before: boolean; id: number; diff --git a/frontend/src/lib/components/feed/ChatChanges.svelte b/frontend/src/lib/components/feed/ChatChanges.svelte index 8336680..94871b1 100644 --- a/frontend/src/lib/components/feed/ChatChanges.svelte +++ b/frontend/src/lib/components/feed/ChatChanges.svelte @@ -1,5 +1,5 @@ + + + {#if url} + + {:else} + + {/if} + + + diff --git a/frontend/src/lib/components/feed/FeedView.svelte b/frontend/src/lib/components/feed/FeedView.svelte index 2e05590..c7030f9 100644 --- a/frontend/src/lib/components/feed/FeedView.svelte +++ b/frontend/src/lib/components/feed/FeedView.svelte @@ -11,6 +11,7 @@ feed, messageHref, } from "#lib/stores/feed.svelte.js"; + import { ui } from "#lib/stores/ui.svelte.js"; import { goto } from "$app/navigation"; import { page } from "$app/state"; @@ -20,8 +21,11 @@ let { filter }: Props = $props(); - function open(item: FeedItem, messageId: number) { - goto(messageHref(page.url, item.chat.chat_id, messageId)); + async function open(item: FeedItem, messageId: number) { + await goto(messageHref(page.url, item.chat.chat_id, messageId)); + if (item.kind === "profile" && !messageId) { + ui.openPanel("profile"); + } } onMount(() => { diff --git a/frontend/src/lib/i18n/messages/feed.ts b/frontend/src/lib/i18n/messages/feed.ts index d8ba9b1..e8d0d8c 100644 --- a/frontend/src/lib/i18n/messages/feed.ts +++ b/frontend/src/lib/i18n/messages/feed.ts @@ -76,13 +76,22 @@ export const feed = { en: "No edited messages yet", ru: "Изменённых пока нет", }, + "feed.empty.profile": { + en: "No profile changes yet", + ru: "Профили пока не менялись", + }, "feed.error": { en: "Couldn't load the feed", ru: "Не удалось загрузить ленту", }, + "feed.field.name": { en: "Name", ru: "Имя" }, + "feed.field.photo": { en: "Photo", ru: "Фото" }, + "feed.field.title": { en: "Title", ru: "Название" }, + "feed.field.username": { en: "Username", ru: "Юзернейм" }, "feed.filter.all": { en: "All", ru: "Все" }, "feed.filter.deleted": { en: "Deleted", ru: "Удалённые" }, "feed.filter.edited": { en: "Edited", ru: "Изменённые" }, + "feed.filter.profile": { en: "Profiles", ru: "Профили" }, "feed.lostText": { en: "The earlier text wasn't saved", ru: "Прежний текст не сохранился", @@ -91,6 +100,15 @@ export const feed = { "feed.mediaChanged": { en: "Media replaced", ru: "Медиа заменено" }, "feed.more": { en: "Show more", ru: "Показать ещё" }, "feed.noText": { en: "No text", ru: "Без текста" }, + "feed.profileCount": { + en: { one: "{count} change", other: "{count} changes" }, + ru: { + few: "{count} изменения", + many: "{count} изменений", + one: "{count} изменение", + other: "{count} изменения", + }, + }, "feed.retry": { en: "Retry", ru: "Повторить" }, "feed.title": { en: "Feed", ru: "Лента" }, "feed.unseen": { @@ -127,6 +145,20 @@ export const feed = { "panel.search": { en: "Search", ru: "Поиск" }, "panel.trash": { en: "Trash", ru: "Корзина" }, "panel.versions": { en: "Versions", ru: "Версии" }, + "thread.copiedId": { en: "ID copied", ru: "ID скопирован" }, + "thread.copiedText": { en: "Text copied", ru: "Текст скопирован" }, + "thread.copyId": { en: "Copy ID", ru: "Копировать ID" }, + "thread.copyText": { en: "Copy text", ru: "Копировать текст" }, + "thread.deleted": { en: "deleted", ru: "удалено" }, + "thread.links": { + en: { one: "{count} link", other: "{count} links" }, + ru: { + few: "{count} ссылки", + many: "{count} ссылок", + one: "{count} ссылка", + other: "{count} ссылки", + }, + }, "thread.loadFailed": { en: "Couldn't load messages", ru: "Не удалось загрузить сообщения", @@ -135,5 +167,7 @@ export const feed = { en: "This message isn't in the archive", ru: "Этого сообщения нет в архиве", }, + "thread.senderProfile": { en: "Sender's profile", ru: "Профиль автора" }, "thread.toLatest": { en: "Jump to latest", ru: "К последним сообщениям" }, + "thread.toReply": { en: "Go to the reply", ru: "Перейти к ответу" }, } satisfies Messages; diff --git a/frontend/src/lib/stores/feed.svelte.ts b/frontend/src/lib/stores/feed.svelte.ts index 3dfd2fb..98f3b3e 100644 --- a/frontend/src/lib/stores/feed.svelte.ts +++ b/frontend/src/lib/stores/feed.svelte.ts @@ -10,7 +10,7 @@ const KIND_PARAM = "feed"; const CHATS_PARAM = "chats"; const MESSAGE_PARAM = "message"; -export const FEED_KINDS: FeedKind[] = ["all", "deleted", "edited"]; +export const FEED_KINDS: FeedKind[] = ["all", "deleted", "edited", "profile"]; export const FEED_CHAT_KINDS: FeedChatKind[] = ["dm", "group", "channel"]; export interface FeedFilter { @@ -59,11 +59,13 @@ function href(url: UrlLike): string { export function messageHref( url: UrlLike, chatId: number, - messageId: number + messageId?: number ): string { const next = withFilter(url, feedFilterOf(url)); next.pathname = `/app/${chatId}`; - next.searchParams.set(MESSAGE_PARAM, String(messageId)); + if (messageId) { + next.searchParams.set(MESSAGE_PARAM, String(messageId)); + } return href(next); }