diff --git a/apps/frontend/auto-imports.d.ts b/apps/frontend/auto-imports.d.ts new file mode 100644 index 0000000..2d9a0c2 --- /dev/null +++ b/apps/frontend/auto-imports.d.ts @@ -0,0 +1,83 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +// biome-ignore lint: disable +export {} +declare global { + const EffectScope: typeof import('vue')['EffectScope'] + const computed: typeof import('vue')['computed'] + const createApp: typeof import('vue')['createApp'] + const customRef: typeof import('vue')['customRef'] + const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] + const defineComponent: typeof import('vue')['defineComponent'] + const effectScope: typeof import('vue')['effectScope'] + const getActiveHead: typeof import('unhead')['getActiveHead'] + const getCurrentInstance: typeof import('vue')['getCurrentInstance'] + const getCurrentScope: typeof import('vue')['getCurrentScope'] + const h: typeof import('vue')['h'] + const inject: typeof import('vue')['inject'] + const isProxy: typeof import('vue')['isProxy'] + const isReactive: typeof import('vue')['isReactive'] + const isReadonly: typeof import('vue')['isReadonly'] + const isRef: typeof import('vue')['isRef'] + const markRaw: typeof import('vue')['markRaw'] + const nextTick: typeof import('vue')['nextTick'] + const onActivated: typeof import('vue')['onActivated'] + const onBeforeMount: typeof import('vue')['onBeforeMount'] + const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] + const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] + const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] + const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] + const onDeactivated: typeof import('vue')['onDeactivated'] + const onErrorCaptured: typeof import('vue')['onErrorCaptured'] + const onMounted: typeof import('vue')['onMounted'] + const onRenderTracked: typeof import('vue')['onRenderTracked'] + const onRenderTriggered: typeof import('vue')['onRenderTriggered'] + const onScopeDispose: typeof import('vue')['onScopeDispose'] + const onServerPrefetch: typeof import('vue')['onServerPrefetch'] + const onUnmounted: typeof import('vue')['onUnmounted'] + const onUpdated: typeof import('vue')['onUpdated'] + const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] + const provide: typeof import('vue')['provide'] + const reactive: typeof import('vue')['reactive'] + const readonly: typeof import('vue')['readonly'] + const ref: typeof import('vue')['ref'] + const resolveComponent: typeof import('vue')['resolveComponent'] + const shallowReactive: typeof import('vue')['shallowReactive'] + const shallowReadonly: typeof import('vue')['shallowReadonly'] + const shallowRef: typeof import('vue')['shallowRef'] + const toRaw: typeof import('vue')['toRaw'] + const toRef: typeof import('vue')['toRef'] + const toRefs: typeof import('vue')['toRefs'] + const toValue: typeof import('vue')['toValue'] + const triggerRef: typeof import('vue')['triggerRef'] + const unref: typeof import('vue')['unref'] + const useAttrs: typeof import('vue')['useAttrs'] + const useCssModule: typeof import('vue')['useCssModule'] + const useCssVars: typeof import('vue')['useCssVars'] + const useHead: typeof import('unhead')['useHead'] + const useHeadSafe: typeof import('unhead')['useHeadSafe'] + const useId: typeof import('vue')['useId'] + const useLink: typeof import('vue-router')['useLink'] + const useModel: typeof import('vue')['useModel'] + const useRoute: typeof import('vue-router')['useRoute'] + const useRouter: typeof import('vue-router')['useRouter'] + const useSeoMeta: typeof import('unhead')['useSeoMeta'] + const useServerHead: typeof import('unhead')['useServerHead'] + const useServerHeadSafe: typeof import('unhead')['useServerHeadSafe'] + const useServerSeoMeta: typeof import('unhead')['useServerSeoMeta'] + const useSlots: typeof import('vue')['useSlots'] + const useTemplateRef: typeof import('vue')['useTemplateRef'] + const watch: typeof import('vue')['watch'] + const watchEffect: typeof import('vue')['watchEffect'] + const watchPostEffect: typeof import('vue')['watchPostEffect'] + const watchSyncEffect: typeof import('vue')['watchSyncEffect'] +} +// for type re-export +declare global { + // @ts-ignore + export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' + import('vue') +} diff --git a/apps/frontend/components.d.ts b/apps/frontend/components.d.ts index 4778e4d..814b592 100644 --- a/apps/frontend/components.d.ts +++ b/apps/frontend/components.d.ts @@ -10,6 +10,7 @@ declare module 'vue' { Button: typeof import('primevue/button')['default'] Card: typeof import('primevue/card')['default'] ChatComponent: typeof import('./src/components/ChatComponent.vue')['default'] + ChatSubscribeWebPush: typeof import('./src/components/ChatSubscribeWebPush.vue')['default'] Checkbox: typeof import('primevue/checkbox')['default'] CreateActionsSection: typeof import('./src/components/create/CreateActionsSection.vue')['default'] CreateMessageDeliveryMethodSection: typeof import('./src/components/create/CreateMessageDeliveryMethodSection.vue')['default'] diff --git a/apps/frontend/package.json b/apps/frontend/package.json index a43c3bd..0e093d8 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -21,6 +21,7 @@ "@primevue/forms": "^4.2.1", "@primevue/themes": "^4.2.1", "@tanstack/vue-query": "^5.60.6", + "@unhead/vue": "^1.11.13", "events": "^3.3.0", "md5": "^2.3.0", "pinia": "^2.2.6", @@ -33,6 +34,7 @@ "svgo": "^3.3.2", "tailwind-scrollbar": "^3.1.0", "tailwindcss-primeui": "^0.3.4", + "unhead": "^1.11.13", "vue": "^3.5.12", "vue-query": "^1.26.0", "vue-router": "^4.4.5" @@ -62,6 +64,7 @@ "prettier": "^3.3.3", "tailwindcss": "^3.4.14", "typescript": "~5.6.3", + "unplugin-auto-import": "^0.18.6", "unplugin-vue-components": "^0.27.4", "vite": "^5.4.10", "vite-plugin-vue-devtools": "^7.5.4", diff --git a/apps/frontend/src/api/dbs.ts b/apps/frontend/src/api/dbs.ts index cd4a3fd..b320cec 100644 --- a/apps/frontend/src/api/dbs.ts +++ b/apps/frontend/src/api/dbs.ts @@ -5,6 +5,7 @@ import type { MessageDocument, WebPushSubscriptionDocument, PublicSettingsDocument, + UserChatSettingsDocument, } from '@hereconnect/types' export const qrCodeDbRemote = new PouchDB(`${import.meta.env.VITE_API_URL}/qr`, { @@ -15,12 +16,11 @@ export const qrCodeDbLocal = new PouchDB('qr') export const messagesDbLocal = new PouchDB('messages') -export const webPushSubscriptionsDbRemote = new PouchDB( - `${import.meta.env.VITE_API_URL}/web_push_subscriptions`, - { - skip_setup: true, - }, -) +export const webPushSubscriptionsDbRemote = new PouchDB< + WebPushSubscriptionDocument +>(`${import.meta.env.VITE_API_URL}/web_push_subscriptions`, { + skip_setup: true, +}) export const publicSettingsDbRemote = new PouchDB( `${import.meta.env.VITE_API_URL}/public_settings`, @@ -35,3 +35,10 @@ export const messagesDbRemote = new PouchDB( skip_setup: true, }, ) + +export const userChatSettingsDbRemote = new PouchDB( + `${import.meta.env.VITE_API_URL}/user_chat_settings`, + { + skip_setup: true, + }, +) diff --git a/apps/frontend/src/api/userChatSettings.ts b/apps/frontend/src/api/userChatSettings.ts new file mode 100644 index 0000000..69937ab --- /dev/null +++ b/apps/frontend/src/api/userChatSettings.ts @@ -0,0 +1,64 @@ +import { getUserUuid } from '@/utils/getUserUuid' +import { getBrowserUuid } from '@/utils/getBrowserUuid' +import { userChatSettingsDbRemote } from '@/api/dbs' +import type { UserChatSettingsDocument } from '@hereconnect/types' + +export const getUserChatSettingsDocument = ({ + chat, + qr_code_uri, + user_chat_role, +}: { + chat: string + qr_code_uri: string + user_chat_role: UserChatSettingsDocument['user_chat_role'] +}) => { + return userChatSettingsDbRemote.get( + `user_chat_settings:${qr_code_uri}:${chat}:${user_chat_role}:${getUserUuid()}`, + ) +} + +export const saveUserChatSettings = async ({ + chat, + qr_code_uri, + user_chat_role, + is_push_notification_enabled, +}: { + chat: string + qr_code_uri: string + user_chat_role: UserChatSettingsDocument['user_chat_role'] + is_push_notification_enabled: boolean +}) => { + try { + const user_uuid = getUserUuid() + const browser_uuid = getBrowserUuid() + const _id = `user_chat_settings:${qr_code_uri}:${chat}:${user_chat_role}:${user_uuid}` + const result = await userChatSettingsDbRemote.upsert( + _id, + (doc): UserChatSettingsDocument | false => { + const isNotChanged = doc.is_push_notification_enabled === is_push_notification_enabled + if (isNotChanged) { + return false + } + + const updated_at = new Date().toISOString() + + return Object.assign(doc, { + _id, + type: 'user_chat_settings', + qr_code_uri, + browser_uuid, + user_uuid, + user_chat_role, + chat, + is_push_notification_enabled, + created_at: doc.created_at ?? updated_at, + updated_at, + } satisfies UserChatSettingsDocument) + }, + ) + console.log('Настройки успешно сохранены', result) + } catch (error) { + console.error('Ошибка сохранения настроек:', error) + throw error + } +} diff --git a/apps/frontend/src/api/webPush.ts b/apps/frontend/src/api/webPush.ts index 8882f90..55504fd 100644 --- a/apps/frontend/src/api/webPush.ts +++ b/apps/frontend/src/api/webPush.ts @@ -3,13 +3,21 @@ import { publicSettingsDbRemote, webPushSubscriptionsDbRemote } from '@/api/dbs' import { getUserUuid } from '@/utils/getUserUuid' import { getBrowserUuid } from '@/utils/getBrowserUuid' +export const getWebPushSubscriptionDocument = async () => { + const type = 'web_push_subscription' + const user_uuid = getUserUuid() + const browser_uuid = getBrowserUuid() + const _id = `${type}:${user_uuid}:${browser_uuid}` + return webPushSubscriptionsDbRemote.get(_id) +} + export const saveWebPushSubscription = async (subscription: PushSubscription) => { const type = 'web_push_subscription' const user_uuid = getUserUuid() const browser_uuid = getBrowserUuid() const _id = `${type}:${user_uuid}:${browser_uuid}` const created_at = new Date().toISOString() - const webPushSubscriptionDoc: WebPushSubscriptionDocument = { + const webPushSubscriptionDoc: WebPushSubscriptionDocument = { _id, type, user_uuid, @@ -20,7 +28,7 @@ export const saveWebPushSubscription = async (subscription: PushSubscription) => try { const { rev } = await webPushSubscriptionsDbRemote.upsert( _id, - (doc): WebPushSubscriptionDocument | false => { + (doc): WebPushSubscriptionDocument | false => { webPushSubscriptionDoc._rev = doc._rev const isNotChanged = JSON.stringify(webPushSubscriptionDoc.subscription) === JSON.stringify(doc.subscription) @@ -37,6 +45,7 @@ export const saveWebPushSubscription = async (subscription: PushSubscription) => return webPushSubscriptionDoc } catch (error) { console.error('Ошибка сохранения подписки:', error) + throw error } } diff --git a/apps/frontend/src/components/ChatComponent.vue b/apps/frontend/src/components/ChatComponent.vue index 07d4fa0..01503d6 100644 --- a/apps/frontend/src/components/ChatComponent.vue +++ b/apps/frontend/src/components/ChatComponent.vue @@ -24,16 +24,25 @@ - -
- -