From f941ba7192b21f1dd11001cadc0665e9994d22a1 Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:36:25 +0700 Subject: [PATCH] feat(profile): show real telegram avatars in messenger chips --- app/composables/graphql/generated.ts | 11 ++- .../useMessengerConnectionPresentation.ts | 51 ++++++++++++ app/pages/notifications.vue | 61 ++++++++++---- app/pages/profile/index.vue | 75 ++++++++++++----- app/pages/profile/notifications.vue | 81 ++++++++++++++++--- graphql/operations/auth/me.graphql | 1 + .../my-messenger-connections.graphql | 3 + graphql/schema.graphql | 3 + .../messenger-avatar/[connectionId].get.ts | 32 ++++++++ 9 files changed, 267 insertions(+), 51 deletions(-) create mode 100644 app/composables/useMessengerConnectionPresentation.ts create mode 100644 server/api/messenger-avatar/[connectionId].get.ts diff --git a/app/composables/graphql/generated.ts b/app/composables/graphql/generated.ts index 73a2810..be10041 100644 --- a/app/composables/graphql/generated.ts +++ b/app/composables/graphql/generated.ts @@ -152,11 +152,14 @@ export enum LoginChannel { export type MessengerConnection = { __typename?: 'MessengerConnection'; + avatarAvailable: Scalars['Boolean']['output']; channelId: Scalars['String']['output']; + displayName?: Maybe; id: Scalars['ID']['output']; isActive: Scalars['Boolean']['output']; type: MessengerType; userId: Scalars['ID']['output']; + username?: Maybe; }; export type MessengerDispatchResult = { @@ -619,7 +622,7 @@ export type ConsumeLoginTokenMutation = { __typename?: 'Mutation', consumeLoginT export type MeQueryVariables = Exact<{ [key: string]: never; }>; -export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string, email: string } | null }; +export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string, email: string, fullName: string } | null }; export type RegisterSelfMutationVariables = Exact<{ input: RegisterSelfInput; @@ -650,7 +653,7 @@ export type ClientProductsQuery = { __typename?: 'Query', clientProducts: Array< export type MyMessengerConnectionsQueryVariables = Exact<{ [key: string]: never; }>; -export type MyMessengerConnectionsQuery = { __typename?: 'Query', myMessengerConnections: Array<{ __typename?: 'MessengerConnection', id: string, type: MessengerType, channelId: string, isActive: boolean }> }; +export type MyMessengerConnectionsQuery = { __typename?: 'Query', myMessengerConnections: Array<{ __typename?: 'MessengerConnection', id: string, type: MessengerType, channelId: string, displayName?: string | null, username?: string | null, avatarAvailable: boolean, isActive: boolean }> }; export type MyNotificationHistoryQueryVariables = Exact<{ channel: MessengerType; @@ -791,6 +794,7 @@ export const MeDocument = gql` me { id email + fullName } } `; @@ -970,6 +974,9 @@ export const MyMessengerConnectionsDocument = gql` id type channelId + displayName + username + avatarAvailable isActive } } diff --git a/app/composables/useMessengerConnectionPresentation.ts b/app/composables/useMessengerConnectionPresentation.ts new file mode 100644 index 0000000..8f9002f --- /dev/null +++ b/app/composables/useMessengerConnectionPresentation.ts @@ -0,0 +1,51 @@ +type MessengerConnectionView = { + id: string; + type: 'TELEGRAM' | 'MAX'; + channelId: string; + displayName?: string | null; + username?: string | null; + avatarAvailable?: boolean | null; +}; + +export function messengerConnectionName(connection: MessengerConnectionView | null | undefined) { + const displayName = String(connection?.displayName || '').trim(); + if (displayName) { + return displayName; + } + + const username = String(connection?.username || '').trim(); + if (username) { + return `@${username.replace(/^@+/, '')}`; + } + + return connection?.channelId || 'Не подключен'; +} + +export function messengerConnectionHandle(connection: MessengerConnectionView | null | undefined) { + const username = String(connection?.username || '').trim().replace(/^@+/, ''); + if (username) { + return `@${username}`; + } + + return connection?.channelId || ''; +} + +export function messengerConnectionInitials(connection: MessengerConnectionView | null | undefined, fallback: string) { + const base = messengerConnectionName(connection); + const initials = base + .split(' ') + .filter(Boolean) + .slice(0, 2) + .map((part) => part.charAt(0).toUpperCase()) + .join(''); + + return initials || fallback; +} + +export function messengerConnectionAvatarSrc(connection: MessengerConnectionView | null | undefined) { + if (!connection?.avatarAvailable || connection.type !== 'TELEGRAM') { + return ''; + } + + return `/api/messenger-avatar/${encodeURIComponent(connection.id)}`; +} diff --git a/app/pages/notifications.vue b/app/pages/notifications.vue index adf3ee4..efbf88b 100644 --- a/app/pages/notifications.vue +++ b/app/pages/notifications.vue @@ -6,6 +6,12 @@ import { MyNotificationHistoryDocument, SendTestMessengerMessageDocument, } from '~/composables/graphql/generated'; +import { + messengerConnectionAvatarSrc, + messengerConnectionHandle, + messengerConnectionInitials, + messengerConnectionName, +} from '~/composables/useMessengerConnectionPresentation'; import { useMessengerStart } from '~/composables/useMessengerStart'; const selectedChannel = ref<'TELEGRAM' | 'MAX'>('TELEGRAM'); @@ -117,15 +123,29 @@ async function sendTest() {
-
+

Telegram

-

- {{ - telegramConnection - ? `Подключен: ${telegramConnection.channelId}` - : 'Не подключен' - }} -

+
+
+
+ +
+
+
+
+ {{ messengerConnectionInitials(telegramConnection, 'TG') }} +
+
+
+

+ {{ messengerConnectionName(telegramConnection) }} +

+

+ {{ messengerConnectionHandle(telegramConnection) || 'Подключен' }} +

+
+
+

Не подключен