Skip to content

Commit e4e96a1

Browse files
committed
feat: implement useListEvents composable and refactor related components
1 parent b5a9ef1 commit e4e96a1

6 files changed

Lines changed: 66 additions & 27 deletions

File tree

frontend/app/shared/ui/template/composables/useTemplateListEvents.ts renamed to frontend/app/shared/composables/useListEvents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export type AppListEventsConfig<T> = {
33
key: keyof T
44
}
55

6-
export const useTemplateListEvents = <T>(config: AppListEventsConfig<T>) => {
6+
export const useListEvents = <T>(config: AppListEventsConfig<T>) => {
77
const items = computed(() => (isRef(config.items) ? config.items.value : config.items))
88

99
const findListItemIndex = (value: T) => {

frontend/app/shared/composables/useWebSocket.ts

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,67 @@ export interface UseWebSocketOptions {
1616
immediate?: boolean
1717
}
1818

19-
export function useWebSocketService(channel: MaybeRefOrGetter<string | null>, options: UseWebSocketOptions = {}) {
19+
const resolveChannelValue = (channel: MaybeRefOrGetter<string | null>): string | null => {
20+
if (typeof channel === 'function') return channel()
21+
if (isRef(channel)) return channel.value
22+
return channel
23+
}
24+
25+
const resolveWsBaseUrl = (): string | undefined => {
2026
const config = useRuntimeConfig()
21-
const baseUrl = config.public.wsURL as string | undefined
27+
const explicit = config.public.wsURL as string | undefined
28+
if (explicit?.trim()) {
29+
return explicit.replace(/\/+$/, '')
30+
}
31+
32+
const baseUrl = config.public.baseUrl as string | undefined
33+
if (!baseUrl?.trim()) return undefined
34+
35+
try {
36+
const url = new URL(baseUrl)
37+
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
38+
url.pathname = '/ws'
39+
url.search = ''
40+
url.hash = ''
41+
return `${url.origin}/ws`
42+
} catch {
43+
return undefined
44+
}
45+
}
46+
47+
export const useWebSocketService = (
48+
channel: MaybeRefOrGetter<string | null>,
49+
options: UseWebSocketOptions = {},
50+
) => {
51+
const wsBaseUrl = resolveWsBaseUrl()
2252

2353
const wsUrl = computed(() => {
24-
let channelValue: string | null
25-
if (typeof channel === 'function') {
26-
channelValue = channel()
27-
} else if (isRef(channel)) {
28-
channelValue = channel.value
29-
} else {
30-
channelValue = channel
31-
}
32-
if (!baseUrl || !channelValue) return ''
33-
return `${baseUrl}/${channelValue}`
54+
const channelValue = resolveChannelValue(channel)
55+
if (!wsBaseUrl || !channelValue) return undefined
56+
return `${wsBaseUrl}/${channelValue.replace(/^\/+/, '')}`
3457
})
3558

59+
const closedStatus = ref<'OPEN' | 'CONNECTING' | 'CLOSED'>('CLOSED')
60+
61+
if (import.meta.server) {
62+
return {
63+
open: () => {},
64+
close: () => {},
65+
status: closedStatus,
66+
}
67+
}
68+
3669
const { open, close, status } = useWebSocket(wsUrl, {
37-
immediate: options.immediate ?? true,
70+
immediate: false,
71+
autoConnect: false,
72+
autoClose: true,
3873
autoReconnect: options.autoReconnect ?? {
3974
delay: 1000,
40-
onFailed() {
75+
onFailed: () => {
4176
console.error('WebSocket connection failed')
4277
},
4378
},
44-
onMessage(_ws: WebSocket, event: MessageEvent) {
79+
onMessage: (_ws: WebSocket, event: MessageEvent) => {
4580
try {
4681
const parsed = JSON.parse(event.data) as WebSocketMessage
4782
if (parsed.type && options.onMessage) {
@@ -54,17 +89,21 @@ export function useWebSocketService(channel: MaybeRefOrGetter<string | null>, op
5489
})
5590

5691
watch(
57-
() => wsUrl.value,
92+
wsUrl,
5893
(url) => {
5994
if (url) {
6095
open()
6196
} else {
6297
close()
6398
}
6499
},
65-
{ immediate: true }
100+
{ immediate: options.immediate ?? true },
66101
)
67102

103+
onBeforeUnmount(() => {
104+
close()
105+
})
106+
68107
return {
69108
open,
70109
close,

frontend/app/shared/ui/app/AppAvatar.vue

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
shape="circle"
44
:size="size"
55
:style="_style"
6+
style="overflow: hidden"
67
>
78
<Image
89
v-if="src"
@@ -19,15 +20,15 @@
1920
</template>
2021

2122
<script lang="ts" setup>
22-
export type CAvatarSize = 'normal' | 'large' | 'xlarge' | 'small' | 'xsmall'
23-
export interface CAvatarProps {
23+
export type AppAvatarSize = 'normal' | 'large' | 'xlarge' | 'small' | 'xsmall'
24+
export interface AppAvatarProps {
2425
color?: string | null
2526
name?: string | null
2627
src?: string | null
27-
size?: CAvatarSize
28+
size?: AppAvatarSize
2829
}
2930
30-
const props = withDefaults(defineProps<CAvatarProps>(), {
31+
const props = withDefaults(defineProps<AppAvatarProps>(), {
3132
size: 'normal',
3233
})
3334

frontend/app/shared/ui/app/AppMultiSelect.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ import type { MultiSelectProps } from 'primevue'
5858
import AppInput from './AppInput.vue'
5959
import { useSelectData, type UseSelectDataProps } from '../../composables/useSelectData'
6060
61-
export type CMultiSelectProps<T> = Omit<MultiSelectProps, 'filterFields' | 'options'> &
61+
export type AppMultiSelectProps<T> = Omit<MultiSelectProps, 'filterFields' | 'options'> &
6262
UseSelectDataProps<T> & {
6363
label?: string
6464
class?: string
6565
}
6666
67-
const props = withDefaults(defineProps<CMultiSelectProps<T>>(), {
67+
const props = withDefaults(defineProps<AppMultiSelectProps<T>>(), {
6868
showToggleAll: false,
6969
autoLoad: false,
7070
filter: true,

frontend/app/shared/ui/template/AppListTemplate.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ const listBind = computed(
152152
}) as AppListProps<T>,
153153
)
154154
155-
const { updateListItem, deleteListItem } = useTemplateListEvents({
155+
const { updateListItem, deleteListItem } = useListEvents({
156156
items: computed(() => repoData.items.value),
157157
key: 'id' as keyof T,
158158
})

frontend/app/shared/ui/template/composables/useDetailActions.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { last } from 'lodash-es'
2-
import { useTemplateListEvents } from './useTemplateListEvents'
32

43
export type AppDetailActionsConfig = {
54
disableNotification?: boolean
@@ -15,7 +14,7 @@ export const useDetailActions = <T extends AppModel>(repo: AppRepoStore<T>) => {
1514
const deleteLoading = ref(false)
1615
const _items = toRef(repo, 'items')
1716

18-
const { updateListItem, deleteListItem } = useTemplateListEvents({
17+
const { updateListItem, deleteListItem } = useListEvents({
1918
items: _items,
2019
key: 'id',
2120
})

0 commit comments

Comments
 (0)