mirror of
https://git.hmsn.ink/kospo/svcm/dmz.git
synced 2026-03-20 10:13:41 +09:00
first
This commit is contained in:
178
src/stores/chat.ts
Normal file
178
src/stores/chat.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* This is a store that hold the messaging-v1 state
|
||||
* It uses the useFetch composition component to make the api calls
|
||||
*
|
||||
* @see /src/pages/messaging-v1.vue
|
||||
* @see /src/composable/useFetch.ts
|
||||
* @see /src/components/partials/chat/*.vue
|
||||
* @see /src/utils/api/chat
|
||||
*/
|
||||
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import type { $Fetch } from 'ofetch'
|
||||
|
||||
export interface Conversation {
|
||||
id: number
|
||||
name: string
|
||||
lastMessage: string
|
||||
unreadMessages: boolean
|
||||
avatar: string
|
||||
}
|
||||
export interface Message {
|
||||
id: number
|
||||
conversationId: number
|
||||
messageId: number
|
||||
type: 'msg' | 'image' | 'imagelink' | 'system'
|
||||
sender: string | null
|
||||
avatar: string | null
|
||||
content: {
|
||||
time: string | null
|
||||
text?: string
|
||||
subtext?: string
|
||||
image_url?: string
|
||||
link_image?: string
|
||||
link_badge?: string
|
||||
}
|
||||
}
|
||||
|
||||
const defaultConversation: Conversation = {
|
||||
id: 0,
|
||||
name: '',
|
||||
lastMessage: '',
|
||||
unreadMessages: false,
|
||||
avatar: '/images/avatars/placeholder.jpg',
|
||||
}
|
||||
|
||||
export const useChat = defineStore('chat', () => {
|
||||
const $fetch = useApiFetch()
|
||||
const conversations = ref<Conversation[]>([])
|
||||
const messages = ref<Message[]>([])
|
||||
const selectedConversationId = ref(0)
|
||||
const addConversationOpen = ref(false)
|
||||
const mobileConversationDetailsOpen = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const selectedConversation = computed(() => {
|
||||
const conversation = conversations.value?.find(
|
||||
item => item.id === selectedConversationId.value,
|
||||
)
|
||||
|
||||
if (!conversation) {
|
||||
return defaultConversation
|
||||
}
|
||||
else {
|
||||
return conversation
|
||||
}
|
||||
})
|
||||
|
||||
async function loadConversations(start = 0, limit = 10) {
|
||||
if (loading.value) return
|
||||
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const response = await fetchConversations($fetch, start, limit)
|
||||
conversations.value = response.conversations ?? []
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function selectConversastion(conversationId: number) {
|
||||
if (loading.value) return
|
||||
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const response = await fetchMessages($fetch, conversationId)
|
||||
selectedConversationId.value = conversationId
|
||||
messages.value = response.messages
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function unselectConversation() {
|
||||
selectedConversationId.value = 0
|
||||
messages.value = []
|
||||
}
|
||||
|
||||
function setAddConversationOpen(value: boolean) {
|
||||
addConversationOpen.value = value
|
||||
}
|
||||
|
||||
function setMobileConversationDetailsOpen(value: boolean) {
|
||||
mobileConversationDetailsOpen.value = value
|
||||
}
|
||||
|
||||
return {
|
||||
conversations,
|
||||
messages,
|
||||
selectedConversation,
|
||||
selectedConversationId,
|
||||
addConversationOpen,
|
||||
mobileConversationDetailsOpen,
|
||||
loading,
|
||||
loadConversations,
|
||||
setAddConversationOpen,
|
||||
setMobileConversationDetailsOpen,
|
||||
selectConversastion,
|
||||
unselectConversation,
|
||||
} as const
|
||||
})
|
||||
|
||||
async function fetchConversations(
|
||||
$fetch: $Fetch,
|
||||
start = 0,
|
||||
limit = 20,
|
||||
): Promise<{ conversations: Conversation[], count: number }> {
|
||||
let count = 0
|
||||
|
||||
const { _data: conversations = [], headers } = await $fetch.raw<Conversation[]>(
|
||||
`/api/conversations`,
|
||||
{
|
||||
query: {
|
||||
_start: start,
|
||||
_limit: limit,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if (headers.has('X-Total-Count')) {
|
||||
count = parseInt(headers.get('X-Total-Count') ?? '0')
|
||||
}
|
||||
|
||||
return { conversations, count }
|
||||
}
|
||||
|
||||
async function fetchMessages(
|
||||
$fetch: $Fetch,
|
||||
conversationId: number,
|
||||
start = 0,
|
||||
limit = 20,
|
||||
): Promise<{ messages: Message[], count: number }> {
|
||||
let count = 0
|
||||
|
||||
const { _data: messages = [], headers } = await $fetch.raw<Message[]>(
|
||||
`/api/conversations/${conversationId}/messages?_start=${start}&_limit=${limit}`,
|
||||
)
|
||||
|
||||
if (headers.has('X-Total-Count')) {
|
||||
count = parseInt(headers.get('X-Total-Count') ?? '0')
|
||||
}
|
||||
|
||||
return { messages, count }
|
||||
}
|
||||
|
||||
/**
|
||||
* Pinia supports Hot Module replacement so you can edit your stores and
|
||||
* interact with them directly in your app without reloading the page.
|
||||
*
|
||||
* @see https://pinia.esm.dev/cookbook/hot-module-replacement.html
|
||||
* @see https://vitejs.dev/guide/api-hmr.html
|
||||
*/
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useChat, import.meta.hot))
|
||||
}
|
||||
223
src/stores/layout-switcher.ts
Normal file
223
src/stores/layout-switcher.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* This store is used for the layout switcher.
|
||||
* It's used on the demo page to allow user to change which component
|
||||
* is used for the layout.
|
||||
*
|
||||
* We can import and set activeSidebar (or use toggleSidebar) anywhere in our project
|
||||
* @see /src/pages/components.vue
|
||||
* @see /src/pages/sidebar/dashboards.vue
|
||||
* @see /src/layouts/layout-switcher/LayoutSwitcher.vue
|
||||
*/
|
||||
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
|
||||
export const useLayoutSwitcher = defineStore('layoutSwitcher', () => {
|
||||
const route = useRoute()
|
||||
|
||||
// utils
|
||||
|
||||
const isNavbarRoute = computed(() => route?.fullPath?.startsWith?.('/navbar/'))
|
||||
const isSidebarRoute = computed(() => route?.fullPath?.startsWith?.('/sidebar/'))
|
||||
const hasDynamicLayout = computed(() => isNavbarRoute.value || isSidebarRoute.value)
|
||||
const navbarLayoutLink = computed(
|
||||
() => route?.fullPath?.replace?.('sidebar', 'navbar') ?? '',
|
||||
)
|
||||
const sidebarLayoutLink = computed(
|
||||
() => route?.fullPath?.replace?.('navbar', 'sidebar') ?? '',
|
||||
)
|
||||
|
||||
// navbar
|
||||
const NavbarLayout = defineAsyncComponent({
|
||||
loader: () => import('/@src/layouts/navbar.vue'),
|
||||
delay: 0,
|
||||
suspensible: false,
|
||||
})
|
||||
const NavsearchLayout = defineAsyncComponent({
|
||||
loader: () => import('/@src/layouts/navsearch.vue'),
|
||||
delay: 0,
|
||||
suspensible: false,
|
||||
})
|
||||
|
||||
const navbarComponents = {
|
||||
'navbar-default': NavbarLayout,
|
||||
'navbar-fade': NavbarLayout,
|
||||
'navbar-colored': NavbarLayout,
|
||||
|
||||
'navsearch-fixed': NavsearchLayout,
|
||||
'navsearch-fixed-fade': NavsearchLayout,
|
||||
'navsearch-shrink': NavsearchLayout,
|
||||
'navsearch-reveal': NavsearchLayout,
|
||||
} as const
|
||||
|
||||
type NavbarComponentsId = keyof typeof navbarComponents
|
||||
const navbarComponentsIds = Object.keys(navbarComponents)
|
||||
|
||||
const navbarLayoutId = ref<NavbarComponentsId>('navbar-default')
|
||||
const navbarLayoutComponent = computed(() => {
|
||||
return navbarComponents[navbarLayoutId.value] || NavbarLayout
|
||||
})
|
||||
|
||||
const navbarLayoutTheme = computed(() => {
|
||||
switch (navbarLayoutId.value) {
|
||||
case 'navbar-fade':
|
||||
case 'navsearch-fixed-fade':
|
||||
return 'fade'
|
||||
case 'navbar-colored':
|
||||
return 'colored'
|
||||
case 'navsearch-fixed':
|
||||
case 'navsearch-shrink':
|
||||
case 'navsearch-reveal':
|
||||
default:
|
||||
return 'default'
|
||||
}
|
||||
})
|
||||
|
||||
// sidebar
|
||||
const SidebarLayout = defineAsyncComponent({
|
||||
loader: () => import('/@src/layouts/sidebar.vue'),
|
||||
delay: 0,
|
||||
suspensible: false,
|
||||
})
|
||||
const SideblockLayout = defineAsyncComponent({
|
||||
loader: () => import('/@src/layouts/sideblock.vue'),
|
||||
delay: 0,
|
||||
suspensible: false,
|
||||
})
|
||||
|
||||
const sidebarComponents = {
|
||||
'sidebar-default': SidebarLayout,
|
||||
'sidebar-color': SidebarLayout,
|
||||
'sidebar-color-curved': SidebarLayout,
|
||||
'sidebar-curved': SidebarLayout,
|
||||
'sidebar-float': SidebarLayout,
|
||||
'sidebar-labels': SidebarLayout,
|
||||
'sidebar-labels-hover': SidebarLayout,
|
||||
|
||||
'sideblock-default': SideblockLayout,
|
||||
'sideblock-color': SideblockLayout,
|
||||
'sideblock-color-curved': SideblockLayout,
|
||||
'sideblock-curved': SideblockLayout,
|
||||
} as const
|
||||
|
||||
type SidebarComponentsId = keyof typeof sidebarComponents
|
||||
const sidebarComponentsIds = Object.keys(sidebarComponents)
|
||||
|
||||
const sidebarLayoutId = ref<SidebarComponentsId>('sideblock-default')
|
||||
const sidebarLayoutComponent = computed(() => {
|
||||
return sidebarComponents[sidebarLayoutId.value] || SidebarLayout
|
||||
})
|
||||
|
||||
const sidebarLayoutTheme = computed(() => {
|
||||
switch (sidebarLayoutId.value) {
|
||||
case 'sidebar-float':
|
||||
return 'float'
|
||||
case 'sidebar-labels':
|
||||
return 'labels'
|
||||
case 'sidebar-labels-hover':
|
||||
return 'labels-hover'
|
||||
case 'sidebar-color':
|
||||
case 'sideblock-color':
|
||||
return 'color'
|
||||
case 'sidebar-curved':
|
||||
case 'sideblock-curved':
|
||||
return 'curved'
|
||||
case 'sideblock-color-curved':
|
||||
case 'sidebar-color-curved':
|
||||
return 'color-curved'
|
||||
case 'sidebar-default':
|
||||
case 'sideblock-default':
|
||||
default:
|
||||
return 'default'
|
||||
}
|
||||
})
|
||||
|
||||
// dynamic layout
|
||||
const dynamicLayoutId = computed<NavbarComponentsId | SidebarComponentsId>({
|
||||
get: () => {
|
||||
if (isNavbarRoute.value) {
|
||||
return navbarLayoutId.value
|
||||
}
|
||||
else {
|
||||
return sidebarLayoutId.value
|
||||
}
|
||||
},
|
||||
set: (value) => {
|
||||
if (navbarComponentsIds.includes(value)) {
|
||||
navbarLayoutId.value = value as NavbarComponentsId
|
||||
return
|
||||
}
|
||||
|
||||
if (sidebarComponentsIds.includes(value)) {
|
||||
sidebarLayoutId.value = value as SidebarComponentsId
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const dynamicLayoutComponent = computed(() => {
|
||||
if (isNavbarRoute.value) {
|
||||
return navbarLayoutComponent.value
|
||||
}
|
||||
else {
|
||||
return sidebarLayoutComponent.value
|
||||
}
|
||||
})
|
||||
|
||||
const contentSize = ref<'default' | 'large' | 'wide' | 'full'>('large')
|
||||
|
||||
const dynamicLayoutProps = computed(() => {
|
||||
if (isNavbarRoute.value) {
|
||||
return {
|
||||
theme: navbarLayoutTheme.value,
|
||||
key: navbarLayoutId.value,
|
||||
size: contentSize.value,
|
||||
scrollBehavior:
|
||||
navbarLayoutId.value === 'navsearch-shrink'
|
||||
? 'shrink'
|
||||
: navbarLayoutId.value === 'navsearch-reveal'
|
||||
? 'reveal'
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
else {
|
||||
return {
|
||||
theme: sidebarLayoutTheme.value,
|
||||
size: contentSize.value,
|
||||
key: sidebarLayoutId.value,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function setDynamicLayoutId(theme: NavbarComponentsId | SidebarComponentsId) {
|
||||
dynamicLayoutId.value = theme
|
||||
}
|
||||
|
||||
return {
|
||||
contentSize,
|
||||
dynamicLayoutComponent,
|
||||
dynamicLayoutProps,
|
||||
dynamicLayoutId,
|
||||
setDynamicLayoutId,
|
||||
sidebarLayoutId,
|
||||
sidebarLayoutComponent,
|
||||
sidebarLayoutTheme,
|
||||
navbarLayoutId,
|
||||
navbarLayoutComponent,
|
||||
navbarLayoutTheme,
|
||||
isNavbarRoute,
|
||||
isSidebarRoute,
|
||||
navbarLayoutLink,
|
||||
sidebarLayoutLink,
|
||||
hasDynamicLayout,
|
||||
} as const
|
||||
})
|
||||
|
||||
/**
|
||||
* Pinia supports Hot Module replacement so you can edit your stores and
|
||||
* interact with them directly in your app without reloading the page.
|
||||
*
|
||||
* @see https://pinia.esm.dev/cookbook/hot-module-replacement.html
|
||||
* @see https://vitejs.dev/guide/api-hmr.html
|
||||
*/
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useLayoutSwitcher, import.meta.hot))
|
||||
}
|
||||
44
src/stores/panels.ts
Normal file
44
src/stores/panels.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* This is a store that hold left panel state
|
||||
* It could be one of the ActivePanelId
|
||||
*
|
||||
* Using useStorage from @vueuse/core allow persistance storage accross tabs/sessions
|
||||
*
|
||||
* We can import and set activePanel anywhere in our project
|
||||
* @see /src/components/panels/PanelSearch.vue
|
||||
* @see /src/components/panels/PanelActivity.vue
|
||||
*/
|
||||
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
|
||||
export type ActivePanelId = 'none' | 'search' | 'languages' | 'activity' | 'task'
|
||||
|
||||
export const usePanels = defineStore('panels', () => {
|
||||
const active = useStorage<ActivePanelId>('active-panel', 'none')
|
||||
|
||||
function setActive(panelId: ActivePanelId) {
|
||||
active.value = panelId
|
||||
}
|
||||
|
||||
function close() {
|
||||
active.value = 'none'
|
||||
}
|
||||
|
||||
return {
|
||||
active,
|
||||
setActive,
|
||||
close,
|
||||
} as const
|
||||
})
|
||||
|
||||
/**
|
||||
* Pinia supports Hot Module replacement so you can edit your stores and
|
||||
* interact with them directly in your app without reloading the page.
|
||||
*
|
||||
* @see https://pinia.esm.dev/cookbook/hot-module-replacement.html
|
||||
* @see https://vitejs.dev/guide/api-hmr.html
|
||||
*/
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(usePanels, import.meta.hot))
|
||||
}
|
||||
37
src/stores/user-session.ts
Normal file
37
src/stores/user-session.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import type { UserData } from '/@src/utils/types'
|
||||
|
||||
export const useUserSession = defineStore('userSession', () => {
|
||||
const user = ref<UserData>()
|
||||
|
||||
const isLoggedIn = computed(() => user.value !== undefined)
|
||||
|
||||
function setUser(newUser: UserData) {
|
||||
user.value = newUser
|
||||
}
|
||||
|
||||
async function logoutUser() {
|
||||
const token = useUserToken()
|
||||
token.value = undefined
|
||||
user.value = undefined
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
isLoggedIn,
|
||||
logoutUser,
|
||||
setUser,
|
||||
} as const
|
||||
})
|
||||
|
||||
/**
|
||||
* Pinia supports Hot Module replacement so you can edit your stores and
|
||||
* interact with them directly in your app without reloading the page.
|
||||
*
|
||||
* @see https://pinia.esm.dev/cookbook/hot-module-replacement.html
|
||||
* @see https://vitejs.dev/guide/api-hmr.html
|
||||
*/
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useUserSession, import.meta.hot))
|
||||
}
|
||||
Reference in New Issue
Block a user