feat(frontend): forwarded header, pinned bar, folder tabs, inline buttons, edit history and deleted accounts speak russian and english
This commit is contained in:
1 parent
0cd34f3037
commit
25364f4523
8 files changed
+50
-18
No files matched your search
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ContextMenu from "#lib/components/ui/ContextMenu.svelte";
|
import ContextMenu from "#lib/components/ui/ContextMenu.svelte";
|
||||||
import ContextMenuItem from "#lib/components/ui/ContextMenuItem.svelte";
|
import ContextMenuItem from "#lib/components/ui/ContextMenuItem.svelte";
|
||||||
|
import { t } from "#lib/i18n/locale.svelte.js";
|
||||||
import { folders } from "#lib/stores/folders.svelte.js";
|
import { folders } from "#lib/stores/folders.svelte.js";
|
||||||
import { policyUi } from "#lib/stores/policy.svelte.js";
|
import { policyUi } from "#lib/stores/policy.svelte.js";
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tabs = $derived<Tab[]>([
|
const tabs = $derived<Tab[]>([
|
||||||
{ id: null, title: "All Chats" },
|
{ id: null, title: t("folders.all") },
|
||||||
...folders.list.map((folder) => ({
|
...folders.list.map((folder) => ({
|
||||||
id: folder.folder_id,
|
id: folder.folder_id,
|
||||||
title: folder.title,
|
title: folder.title,
|
||||||
@@ -93,7 +94,7 @@
|
|||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet menu()}
|
{#snippet menu()}
|
||||||
<ContextMenuItem icon="settings" onselect={() => editPolicy(tab)}>
|
<ContextMenuItem icon="settings" onselect={() => editPolicy(tab)}>
|
||||||
Политика захвата
|
{t("settings.policy")}
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ForwardView } from "#lib/api/types.js";
|
import type { ForwardView } from "#lib/api/types.js";
|
||||||
import { peerName } from "#lib/format/peer.js";
|
import { peerName } from "#lib/format/peer.js";
|
||||||
|
import { t } from "#lib/i18n/locale.svelte.js";
|
||||||
import { peers } from "#lib/stores/peers.svelte.js";
|
import { peers } from "#lib/stores/peers.svelte.js";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -14,20 +15,20 @@
|
|||||||
);
|
);
|
||||||
const name = $derived.by(() => {
|
const name = $derived.by(() => {
|
||||||
if (forward.kind === "channel") {
|
if (forward.kind === "channel") {
|
||||||
return forward.chat_title ?? "Channel";
|
return forward.chat_title ?? t("forward.channel");
|
||||||
}
|
}
|
||||||
if (forward.kind === "hidden") {
|
if (forward.kind === "hidden") {
|
||||||
return forward.from_name ?? "Hidden account";
|
return forward.from_name ?? t("forward.hidden");
|
||||||
}
|
}
|
||||||
if (peer) {
|
if (peer) {
|
||||||
return peerName(peer);
|
return peerName(peer);
|
||||||
}
|
}
|
||||||
return forward.from_name ?? "Unknown";
|
return forward.from_name ?? t("forward.unknown");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="ForwardHeader">
|
<div class="ForwardHeader">
|
||||||
<span class="label">Forwarded from</span>
|
<span class="label">{t("forward.from")}</span>
|
||||||
<span class="name">{name}</span>
|
<span class="name">{name}</span>
|
||||||
{#if forward.signature}
|
{#if forward.signature}
|
||||||
<span class="signature">({forward.signature})</span>
|
<span class="signature">({forward.signature})</span>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { InlineButton } from "#lib/api/types.js";
|
import type { InlineButton } from "#lib/api/types.js";
|
||||||
import { safeHref } from "#lib/format/url.js";
|
import { safeHref } from "#lib/format/url.js";
|
||||||
|
import { t } from "#lib/i18n/locale.svelte.js";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
rows: InlineButton[][];
|
rows: InlineButton[][];
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<span class="label"
|
<span class="label"
|
||||||
>{copied === key ? "Скопировано" : button.text}</span
|
>{copied === key ? t("inline.copied") : button.text}</span
|
||||||
>
|
>
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import Icon from "#lib/components/ui/Icon.svelte";
|
import Icon from "#lib/components/ui/Icon.svelte";
|
||||||
import Spinner from "#lib/components/ui/Spinner.svelte";
|
import Spinner from "#lib/components/ui/Spinner.svelte";
|
||||||
import { formatFull } from "#lib/format/datetime.js";
|
import { formatFull } from "#lib/format/datetime.js";
|
||||||
|
import { t } from "#lib/i18n/locale.svelte.js";
|
||||||
import { toasts } from "#lib/stores/toasts.svelte.js";
|
import { toasts } from "#lib/stores/toasts.svelte.js";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
versions = text;
|
versions = text;
|
||||||
mediaVersions = media;
|
mediaVersions = media;
|
||||||
})
|
})
|
||||||
.catch(() => toasts.error("Failed to load edit history"))
|
.catch(() => toasts.error(t("versions.failed")))
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
loading = false;
|
loading = false;
|
||||||
});
|
});
|
||||||
@@ -46,8 +47,8 @@
|
|||||||
<Dialog.Overlay class="dialog-overlay" />
|
<Dialog.Overlay class="dialog-overlay" />
|
||||||
<Dialog.Content class="dialog-content">
|
<Dialog.Content class="dialog-content">
|
||||||
<header class="dialog-head">
|
<header class="dialog-head">
|
||||||
<Dialog.Title class="dialog-title">Edit history</Dialog.Title>
|
<Dialog.Title class="dialog-title">{t("versions.title")}</Dialog.Title>
|
||||||
<Dialog.Close aria-label="Close" class="dialog-close">
|
<Dialog.Close aria-label={t("common.close")} class="dialog-close">
|
||||||
<Icon name="close" size="1.25rem" />
|
<Icon name="close" size="1.25rem" />
|
||||||
</Dialog.Close>
|
</Dialog.Close>
|
||||||
</header>
|
</header>
|
||||||
@@ -55,11 +56,11 @@
|
|||||||
{#if loading}
|
{#if loading}
|
||||||
<div class="centered"><Spinner /></div>
|
<div class="centered"><Spinner /></div>
|
||||||
{:else if versions.length === 0 && mediaVersions.length === 0}
|
{:else if versions.length === 0 && mediaVersions.length === 0}
|
||||||
<p class="empty">No versions recorded.</p>
|
<p class="empty">{t("versions.none")}</p>
|
||||||
{:else}
|
{:else}
|
||||||
{#if mediaVersions.length > 0}
|
{#if mediaVersions.length > 0}
|
||||||
<div class="media-versions">
|
<div class="media-versions">
|
||||||
<span class="media-label">Media versions</span>
|
<span class="media-label">{t("versions.media")}</span>
|
||||||
<div class="media-strip">
|
<div class="media-strip">
|
||||||
{#each mediaVersions as media (media.id)}
|
{#each mediaVersions as media (media.id)}
|
||||||
<MediaVersionThumb version={media} />
|
<MediaVersionThumb version={media} />
|
||||||
@@ -70,12 +71,14 @@
|
|||||||
{#each versions as version, index (version.observed_at)}
|
{#each versions as version, index (version.observed_at)}
|
||||||
<div class="version">
|
<div class="version">
|
||||||
<div class="version-meta">
|
<div class="version-meta">
|
||||||
<span class="version-label">Version {index + 1}</span>
|
<span class="version-label"
|
||||||
|
>{t("versions.version", { number: index + 1 })}</span
|
||||||
|
>
|
||||||
<span class="version-date"
|
<span class="version-date"
|
||||||
>{formatFull(version.observed_at)}</span
|
>{formatFull(version.observed_at)}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-text">{version.text ?? "(no text)"}</div>
|
<div class="version-text">{version.text ?? t("feed.noText")}</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import type { PinnedView } from "#lib/api/types.js";
|
import type { PinnedView } from "#lib/api/types.js";
|
||||||
import Icon from "#lib/components/ui/Icon.svelte";
|
import Icon from "#lib/components/ui/Icon.svelte";
|
||||||
import { mediaKindLabel } from "#lib/format/media.js";
|
import { mediaKindLabel } from "#lib/format/media.js";
|
||||||
|
import { t } from "#lib/i18n/locale.svelte.js";
|
||||||
import { accounts } from "#lib/stores/accounts.svelte.js";
|
import { accounts } from "#lib/stores/accounts.svelte.js";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
? (pinned.text ??
|
? (pinned.text ??
|
||||||
(pinned.media_kind
|
(pinned.media_kind
|
||||||
? mediaKindLabel(pinned.media_kind)
|
? mediaKindLabel(pinned.media_kind)
|
||||||
: "Pinned message"))
|
: t("pinned.message")))
|
||||||
: ""
|
: ""
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
>
|
>
|
||||||
<Icon name="pin" size="1.125rem" />
|
<Icon name="pin" size="1.125rem" />
|
||||||
<span class="body">
|
<span class="body">
|
||||||
<span class="label">Pinned message</span>
|
<span class="label">{t("pinned.message")}</span>
|
||||||
<span class="preview">{preview}</span>
|
<span class="preview">{preview}</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Account, PeerView } from "#lib/api/types.js";
|
import type { Account, PeerView } from "#lib/api/types.js";
|
||||||
|
import { t } from "#lib/i18n/locale.svelte.js";
|
||||||
|
|
||||||
const PEER_COLOR_COUNT = 7;
|
const PEER_COLOR_COUNT = 7;
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ export function peerName(peer: PeerView | null): string {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (peer.is_deleted_account) {
|
if (peer.is_deleted_account) {
|
||||||
return "Удалённый аккаунт";
|
return t("chat.deletedAccount");
|
||||||
}
|
}
|
||||||
const parts = [peer.first_name, peer.last_name].filter(Boolean);
|
const parts = [peer.first_name, peer.last_name].filter(Boolean);
|
||||||
if (parts.length > 0) {
|
if (parts.length > 0) {
|
||||||
@@ -20,7 +21,7 @@ export function peerName(peer: PeerView | null): string {
|
|||||||
if (peer.username) {
|
if (peer.username) {
|
||||||
return `@${peer.username}`;
|
return `@${peer.username}`;
|
||||||
}
|
}
|
||||||
return "Удалённый аккаунт";
|
return t("chat.deletedAccount");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function accountName(account: Account): string {
|
export function accountName(account: Account): string {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { login } from "#lib/i18n/messages/login.js";
|
|||||||
import { media } from "#lib/i18n/messages/media.js";
|
import { media } from "#lib/i18n/messages/media.js";
|
||||||
import { menu } from "#lib/i18n/messages/menu.js";
|
import { menu } from "#lib/i18n/messages/menu.js";
|
||||||
import { notes } from "#lib/i18n/messages/notes.js";
|
import { notes } from "#lib/i18n/messages/notes.js";
|
||||||
|
import { parts } from "#lib/i18n/messages/parts.js";
|
||||||
import { policy } from "#lib/i18n/messages/policy.js";
|
import { policy } from "#lib/i18n/messages/policy.js";
|
||||||
import { presence } from "#lib/i18n/messages/presence.js";
|
import { presence } from "#lib/i18n/messages/presence.js";
|
||||||
import { profile } from "#lib/i18n/messages/profile.js";
|
import { profile } from "#lib/i18n/messages/profile.js";
|
||||||
@@ -39,6 +40,7 @@ export const messages = {
|
|||||||
...media,
|
...media,
|
||||||
...menu,
|
...menu,
|
||||||
...notes,
|
...notes,
|
||||||
|
...parts,
|
||||||
...policy,
|
...policy,
|
||||||
...presence,
|
...presence,
|
||||||
...profile,
|
...profile,
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import type { Messages } from "#lib/i18n/types.js";
|
||||||
|
|
||||||
|
export const parts = {
|
||||||
|
"folders.all": { en: "All Chats", ru: "Все чаты" },
|
||||||
|
"forward.channel": { en: "Channel", ru: "Канал" },
|
||||||
|
"forward.from": { en: "Forwarded from", ru: "Переслано от" },
|
||||||
|
"forward.hidden": { en: "Hidden account", ru: "Скрытый аккаунт" },
|
||||||
|
"forward.unknown": { en: "Unknown sender", ru: "Неизвестный отправитель" },
|
||||||
|
"inline.copied": { en: "Copied", ru: "Скопировано" },
|
||||||
|
"pinned.message": { en: "Pinned Message", ru: "Закреплённое сообщение" },
|
||||||
|
"versions.failed": {
|
||||||
|
en: "Couldn't load the edit history",
|
||||||
|
ru: "Не удалось загрузить историю изменений",
|
||||||
|
},
|
||||||
|
"versions.media": { en: "Media versions", ru: "Версии медиа" },
|
||||||
|
"versions.none": {
|
||||||
|
en: "No versions saved.",
|
||||||
|
ru: "Версии не сохранились.",
|
||||||
|
},
|
||||||
|
"versions.title": { en: "Edit history", ru: "История изменений" },
|
||||||
|
"versions.version": { en: "Version {number}", ru: "Версия {number}" },
|
||||||
|
} satisfies Messages;
|
||||||
Reference in new issue
Block a user