openpencil/src/components/CollabPanel/context.ts
rcoenen 04ad2db00b
fix(collab): copy public desktop share links
Squashed PR #343 with maintainer rebase on current master.
2026-06-30 12:04:35 +03:00

113 lines
3.2 KiB
TypeScript

import { useClipboard } from '@vueuse/core'
import { computed, inject, provide, proxyRefs, ref, watch } from 'vue'
import type { InjectionKey, ShallowUnwrapRef } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from '@open-pencil/vue'
import { DEFAULT_COLLAB_STATE, useCollabInjected } from '@/app/collab/use'
import { toast } from '@/app/shell/ui'
import { getShareUrl } from '@/constants'
function createCollabPanelContext() {
const route = useRoute()
const router = useRouter()
const collab = useCollabInjected()
const { copy, copied } = useClipboard({ copiedDuring: 2000 })
const { dialogs } = useI18n()
const joinInput = ref('')
const nameDraft = ref(collab?.state.value.localName ?? '')
const pendingRoomId = computed(() =>
typeof route.params.roomId === 'string' ? route.params.roomId : null
)
const popoverOpen = ref(!!pendingRoomId.value)
const state = computed(() => collab?.state.value ?? DEFAULT_COLLAB_STATE)
const peers = computed(() => collab?.remotePeers.value ?? [])
const followingPeer = computed(() => collab?.followingPeer.value ?? null)
const shareUrl = computed(() => {
if (!state.value.roomId) return ''
return getShareUrl(state.value.roomId)
})
const isJoining = computed(() => !!pendingRoomId.value && !state.value.connected)
watch(
pendingRoomId,
(roomId) => {
if (!state.value.connected) popoverOpen.value = !!roomId
},
{ immediate: true }
)
function copyLink() {
if (!shareUrl.value) return
void copy(shareUrl.value)
toast.info('Link copied to clipboard')
}
function share() {
if (!collab || !nameDraft.value.trim()) return
collab.setLocalName(nameDraft.value.trim())
const roomId = collab.shareCurrentDoc()
void router.push(`/share/${roomId}`)
void copy(getShareUrl(roomId))
toast.info('Link copied to clipboard')
popoverOpen.value = false
}
function join() {
if (!collab) return
const roomId = pendingRoomId.value || joinInput.value.trim().replace(/.*\/share\//, '')
if (!roomId || !nameDraft.value.trim()) return
collab.setLocalName(nameDraft.value.trim())
collab.connect(roomId)
void router.push(`/share/${roomId}`)
popoverOpen.value = false
}
function disconnect() {
if (!collab) return
collab.disconnect()
popoverOpen.value = false
void router.push('/')
}
function toggleFollowPeer(clientId: number) {
collab?.followPeer(followingPeer.value === clientId ? null : clientId)
}
return {
dialogs,
copied,
joinInput,
nameDraft,
popoverOpen,
state,
peers,
followingPeer,
shareUrl,
isJoining,
copyLink,
share,
join,
disconnect,
toggleFollowPeer
}
}
export type CollabPanelContext = ShallowUnwrapRef<ReturnType<typeof createCollabPanelContext>>
const COLLAB_PANEL_KEY: InjectionKey<CollabPanelContext> = Symbol('CollabPanelContext')
export function provideCollabPanel() {
const ctx = proxyRefs(createCollabPanelContext())
provide(COLLAB_PANEL_KEY, ctx)
return ctx
}
export function useCollabPanelContext(): CollabPanelContext {
const ctx = inject(COLLAB_PANEL_KEY)
if (!ctx) throw new Error('Collab panel controls must be used within CollabPanel')
return ctx
}