From e3b814687058ee07c29f5aa5ef5692855afe1c1c Mon Sep 17 00:00:00 2001 From: Yuuki Wesp Date: Thu, 3 Sep 2026 23:52:47 +0300 Subject: [PATCH 1/5] empty states animations, fixes design bugs, fixes theme applying --- index.html | 27 ++ package.json | 1 + packages/i18n/src/core/en.json | 3 + packages/i18n/src/core/ru.json | 3 + scripts/theme-empty-states.ts | 179 ++++++++ src/components/ChatList.vue | 17 +- src/components/LeftSideUserList.vue | 8 +- src/components/MediaChannelView.vue | 17 +- src/components/ServerSettingsWindow.vue | 16 +- src/components/SettingsWindow.vue | 2 +- src/components/TextChannelView.vue | 7 +- src/components/chats/ChatMessageList.vue | 7 +- src/components/chats/GifPicker.vue | 10 +- src/components/home/HomeSideShell.vue | 4 +- src/components/home/views/InventoryView.vue | 5 +- .../home/views/NotificationShell.vue | 5 +- .../home/views/friends/FriendList.vue | 256 +++++++++-- .../home/views/friends/FriendListItem.vue | 240 ++++++++-- .../home/views/friends/FriendsShell.vue | 412 ++++++++++++------ .../home/widgets/ActiveNowWidget.vue | 7 +- src/components/home/widgets/LevelWidget.vue | 2 +- .../home/widgets/RecentSpacesWidget.vue | 7 +- src/components/overlays/LoadingOverlay.vue | 7 +- .../popovers/UserProfilePopover.vue | 4 +- src/components/settings/Invites.vue | 8 +- .../settings/spaces/BotRolesTab.vue | 3 +- .../settings/spaces/BotsSettings.vue | 6 +- .../settings/spaces/RolesSettings.vue | 3 +- .../settings/spaces/ServerProfile.vue | 75 +++- src/components/shared/EmptyStateArt.vue | 134 ++++++ src/components/shared/ErrorBoundary.vue | 14 +- src/store/data/profileCacheStore.ts | 27 +- src/styles/empty-states/inventory.svg | 31 ++ src/styles/empty-states/no-bots.svg | 38 ++ src/styles/empty-states/no-channels.svg | 34 ++ src/styles/empty-states/no-friends-online.svg | 31 ++ src/styles/empty-states/no-friends-sad.svg | 34 ++ src/styles/empty-states/no-invites.svg | 33 ++ src/styles/empty-states/no-messages.svg | 30 ++ src/styles/empty-states/no-one-here.svg | 34 ++ src/styles/empty-states/no-text-channel.svg | 25 ++ src/styles/empty-states/not-found.svg | 37 ++ src/styles/empty-states/select-role.svg | 33 ++ 43 files changed, 1592 insertions(+), 284 deletions(-) create mode 100644 scripts/theme-empty-states.ts create mode 100644 src/components/shared/EmptyStateArt.vue create mode 100644 src/styles/empty-states/inventory.svg create mode 100644 src/styles/empty-states/no-bots.svg create mode 100644 src/styles/empty-states/no-channels.svg create mode 100644 src/styles/empty-states/no-friends-online.svg create mode 100644 src/styles/empty-states/no-friends-sad.svg create mode 100644 src/styles/empty-states/no-invites.svg create mode 100644 src/styles/empty-states/no-messages.svg create mode 100644 src/styles/empty-states/no-one-here.svg create mode 100644 src/styles/empty-states/no-text-channel.svg create mode 100644 src/styles/empty-states/not-found.svg create mode 100644 src/styles/empty-states/select-role.svg diff --git a/index.html b/index.html index dc845b35..026aae0a 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,33 @@ + + diff --git a/package.json b/package.json index 06ad3c8d..0ac07e25 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "build:games:watch": "bun run scripts/build-games.ts --watch", "build:packages": "bun run --filter './packages/*' build", "sync:legal": "bun run scripts/sync-legal.ts", + "theme:empty-states": "bun run scripts/theme-empty-states.ts", "sentry:dashboards": "bun run scripts/sentry/dashboards.ts", "validate": "vue-tsc --noEmit -p tsconfig.app.json", "test": "bun run test:app && bun run test:packages", diff --git a/packages/i18n/src/core/en.json b/packages/i18n/src/core/en.json index 96aad473..38770ebe 100644 --- a/packages/i18n/src/core/en.json +++ b/packages/i18n/src/core/en.json @@ -850,6 +850,9 @@ "outgoing_request": "Outgoing", "pending": "Pending", "blocked_users": "Blocked Users", + "blocked_status": "Blocked", + "show_all": "Show all", + "show_less": "Show less", "empty_section": "Nothing here yet", "no_items_found": "No items found", "join": "Join", diff --git a/packages/i18n/src/core/ru.json b/packages/i18n/src/core/ru.json index c19727d4..0d503b1f 100644 --- a/packages/i18n/src/core/ru.json +++ b/packages/i18n/src/core/ru.json @@ -730,6 +730,9 @@ "outgoing_request": "Исходящий", "pending": "Ожидают", "blocked_users": "Заблокированные пользователи", + "blocked_status": "Заблокирован", + "show_all": "Показать все", + "show_less": "Свернуть", "empty_section": "Пока здесь пусто", "no_items_found": "Ничего не найдено", "join": "Присоединиться", diff --git a/scripts/theme-empty-states.ts b/scripts/theme-empty-states.ts new file mode 100644 index 00000000..da0d04be --- /dev/null +++ b/scripts/theme-empty-states.ts @@ -0,0 +1,179 @@ +/** + * Makes a freshly delivered empty-state SVG usable inside the app. + * + * The artwork ships as a self-contained animated SVG with a hard-coded purple palette. Two things + * have to change before it can be inlined into the client (see components/shared/EmptyStateArt.vue): + * + * 1. Scoping. A `, + ); + + const styleStart = src.indexOf("") + "".length; + src = + transformMarkup(src.slice(0, styleStart)) + + src.slice(styleStart, styleEnd) + + transformMarkup(src.slice(styleEnd)); + + src = src.replace(/]*)>/, (all, attrs: string) => + attrs.includes("class=") ? all : ``, + ); + + fs.writeFileSync(full, src); + console.log(`✓ ${file}`); +} diff --git a/src/components/ChatList.vue b/src/components/ChatList.vue index d482ab7b..e1b64642 100644 --- a/src/components/ChatList.vue +++ b/src/components/ChatList.vue @@ -12,9 +12,7 @@
-
- -
+

{{ t("no_channels") }}

- {{ searchQuery ? t("no_results") : t("no_members_online") }} + + {{ searchQuery ? t("no_results") : t("no_members_online") }}
@@ -78,6 +79,7 @@ import { useLocale } from "@/store/system/localeStore"; import { usePoolStore } from "@/store/data/poolStore"; import UserInListSideElement from "./UserInListSideElement.vue"; import Skeleton from "./shared/Skeleton.vue"; +import EmptyStateArt from "./shared/EmptyStateArt.vue"; import { computed, ref, nextTick } from "vue"; import { persistedValue } from "@argon/storage"; import { useListLoading } from "@/composables/useListLoading"; @@ -438,9 +440,11 @@ const formatColour = (argb: number) => { .empty-state { display: flex; + flex-direction: column; align-items: center; justify-content: center; - padding: 2rem 1rem; + gap: 0.25rem; + padding: 1.25rem 1rem; color: hsl(var(--muted-foreground) / 0.5); font-size: 0.78rem; text-align: center; diff --git a/src/components/MediaChannelView.vue b/src/components/MediaChannelView.vue index 54e4fe98..d8c55d89 100644 --- a/src/components/MediaChannelView.vue +++ b/src/components/MediaChannelView.vue @@ -66,9 +66,7 @@
-
- -
+ {{ t("empty_channel") }} {{ t("empty_channel_hint") }}
@@ -190,6 +188,7 @@ import PlayFramePanel from "./playframe/PlayFramePanel.vue"; import ActivityCard from "./playframe/ActivityCard.vue"; import PingDetailsPopup from "./PingDetailsPopup.vue"; import MediaControls from "./MediaControls.vue"; +import EmptyStateArt from "./shared/EmptyStateArt.vue"; import { Signal, Users2, Volume2, Gamepad2, } from "lucide-vue-next"; @@ -369,18 +368,6 @@ onUnmounted(() => { user-select: none; } -.empty-state-icon { - display: flex; - align-items: center; - justify-content: center; - width: 64px; - height: 64px; - border-radius: 16px; - background: hsl(var(--muted) / 0.5); - color: hsl(var(--muted-foreground) / 0.5); - margin-bottom: 4px; -} - .empty-state-title { font-size: 15px; font-weight: 600; diff --git a/src/components/ServerSettingsWindow.vue b/src/components/ServerSettingsWindow.vue index f9128d53..08647286 100644 --- a/src/components/ServerSettingsWindow.vue +++ b/src/components/ServerSettingsWindow.vue @@ -13,7 +13,7 @@
-