1
This commit is contained in:
commit
6fdf336b9e
128 files changed
+36789
No files matched your search
@@ -0,0 +1,180 @@
|
||||
const PROJECT_EVENTS_CHANNEL = 'jgjs-project-events'
|
||||
const PROJECT_DELETED_STORAGE_KEY = 'jgjs-project-event:project-deleted'
|
||||
const RESET_ALL_STORAGE_KEY = 'jgjs-project-event:reset-all'
|
||||
const PROJECT_EVENT_SESSION_ID_KEY = 'jgjs-project-event-session-id'
|
||||
|
||||
type ProjectDeletedPayload = {
|
||||
type: 'project-deleted'
|
||||
projectId: string
|
||||
sourceSessionId: string
|
||||
at: number
|
||||
}
|
||||
|
||||
type ResetAllPayload = {
|
||||
type: 'reset-all'
|
||||
sourceSessionId: string
|
||||
at: number
|
||||
}
|
||||
|
||||
const randomSessionId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`
|
||||
|
||||
const getOrCreateSessionId = () => {
|
||||
try {
|
||||
const existing = String(window.sessionStorage.getItem(PROJECT_EVENT_SESSION_ID_KEY) || '').trim()
|
||||
if (existing) return existing
|
||||
const next = randomSessionId()
|
||||
window.sessionStorage.setItem(PROJECT_EVENT_SESSION_ID_KEY, next)
|
||||
return next
|
||||
} catch {
|
||||
return randomSessionId()
|
||||
}
|
||||
}
|
||||
|
||||
const parseProjectDeletedPayload = (raw: string | null): ProjectDeletedPayload | null => {
|
||||
if (!raw) return null
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as Partial<ProjectDeletedPayload>
|
||||
if (!parsed || typeof parsed !== 'object') return null
|
||||
if (parsed.type !== 'project-deleted') return null
|
||||
if (typeof parsed.projectId !== 'string' || !parsed.projectId.trim()) return null
|
||||
if (typeof parsed.sourceSessionId !== 'string' || !parsed.sourceSessionId.trim()) return null
|
||||
if (typeof parsed.at !== 'number' || !Number.isFinite(parsed.at)) return null
|
||||
return {
|
||||
type: 'project-deleted',
|
||||
projectId: parsed.projectId.trim(),
|
||||
sourceSessionId: parsed.sourceSessionId.trim(),
|
||||
at: parsed.at
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const parseResetAllPayload = (raw: string | null): ResetAllPayload | null => {
|
||||
if (!raw) return null
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as Partial<ResetAllPayload>
|
||||
if (!parsed || typeof parsed !== 'object') return null
|
||||
if (parsed.type !== 'reset-all') return null
|
||||
if (typeof parsed.sourceSessionId !== 'string' || !parsed.sourceSessionId.trim()) return null
|
||||
if (typeof parsed.at !== 'number' || !Number.isFinite(parsed.at)) return null
|
||||
return {
|
||||
type: 'reset-all',
|
||||
sourceSessionId: parsed.sourceSessionId.trim(),
|
||||
at: parsed.at
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const emitProjectDeleted = (projectIdRaw: string) => {
|
||||
const projectId = String(projectIdRaw || '').trim()
|
||||
if (!projectId) return
|
||||
const payload: ProjectDeletedPayload = {
|
||||
type: 'project-deleted',
|
||||
projectId,
|
||||
sourceSessionId: getOrCreateSessionId(),
|
||||
at: Date.now()
|
||||
}
|
||||
try {
|
||||
if (typeof BroadcastChannel !== 'undefined') {
|
||||
const bc = new BroadcastChannel(PROJECT_EVENTS_CHANNEL)
|
||||
bc.postMessage(payload)
|
||||
bc.close()
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
localStorage.setItem(PROJECT_DELETED_STORAGE_KEY, JSON.stringify(payload))
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
export const listenProjectDeleted = (onDeleted: (projectId: string) => void) => {
|
||||
const localSessionId = getOrCreateSessionId()
|
||||
let bc: BroadcastChannel | null = null
|
||||
|
||||
const handlePayload = (payload: ProjectDeletedPayload | null) => {
|
||||
if (!payload) return
|
||||
if (payload.sourceSessionId === localSessionId) return
|
||||
onDeleted(payload.projectId)
|
||||
}
|
||||
|
||||
const onStorage = (event: StorageEvent) => {
|
||||
if (event.key !== PROJECT_DELETED_STORAGE_KEY) return
|
||||
handlePayload(parseProjectDeletedPayload(event.newValue))
|
||||
}
|
||||
|
||||
if (typeof BroadcastChannel !== 'undefined') {
|
||||
bc = new BroadcastChannel(PROJECT_EVENTS_CHANNEL)
|
||||
bc.onmessage = (event: MessageEvent<ProjectDeletedPayload>) => {
|
||||
handlePayload(parseProjectDeletedPayload(JSON.stringify(event.data)))
|
||||
}
|
||||
}
|
||||
window.addEventListener('storage', onStorage)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('storage', onStorage)
|
||||
if (bc) {
|
||||
bc.close()
|
||||
bc = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const emitResetAll = () => {
|
||||
const payload: ResetAllPayload = {
|
||||
type: 'reset-all',
|
||||
sourceSessionId: getOrCreateSessionId(),
|
||||
at: Date.now()
|
||||
}
|
||||
try {
|
||||
if (typeof BroadcastChannel !== 'undefined') {
|
||||
const bc = new BroadcastChannel(PROJECT_EVENTS_CHANNEL)
|
||||
bc.postMessage(payload)
|
||||
bc.close()
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
localStorage.setItem(RESET_ALL_STORAGE_KEY, JSON.stringify(payload))
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
export const listenResetAll = (onResetAll: () => void) => {
|
||||
const localSessionId = getOrCreateSessionId()
|
||||
let bc: BroadcastChannel | null = null
|
||||
|
||||
const handlePayload = (payload: ResetAllPayload | null) => {
|
||||
if (!payload) return
|
||||
if (payload.sourceSessionId === localSessionId) return
|
||||
onResetAll()
|
||||
}
|
||||
|
||||
const onStorage = (event: StorageEvent) => {
|
||||
if (event.key !== RESET_ALL_STORAGE_KEY) return
|
||||
handlePayload(parseResetAllPayload(event.newValue))
|
||||
}
|
||||
|
||||
if (typeof BroadcastChannel !== 'undefined') {
|
||||
bc = new BroadcastChannel(PROJECT_EVENTS_CHANNEL)
|
||||
bc.onmessage = (event: MessageEvent<ResetAllPayload>) => {
|
||||
handlePayload(parseResetAllPayload(JSON.stringify(event.data)))
|
||||
}
|
||||
}
|
||||
window.addEventListener('storage', onStorage)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('storage', onStorage)
|
||||
if (bc) {
|
||||
bc.close()
|
||||
bc = null
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user