mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
feat(web): add composer enter behavior setting (#586)
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
DEFAULT_COMPOSER_ENTER_BEHAVIOR,
|
||||
getComposerEnterBehaviorOptions,
|
||||
getInitialComposerEnterBehavior,
|
||||
} from './useComposerEnterBehavior'
|
||||
|
||||
describe('useComposerEnterBehavior helpers', () => {
|
||||
beforeEach(() => {
|
||||
window.localStorage.clear()
|
||||
})
|
||||
|
||||
it('returns the allowed enter behavior options', () => {
|
||||
expect(getComposerEnterBehaviorOptions()).toEqual([
|
||||
{ value: 'send', labelKey: 'settings.chat.enterBehavior.send' },
|
||||
{ value: 'newline', labelKey: 'settings.chat.enterBehavior.newline' },
|
||||
])
|
||||
})
|
||||
|
||||
it('falls back to the default behavior for missing or invalid storage values', () => {
|
||||
expect(getInitialComposerEnterBehavior()).toBe(DEFAULT_COMPOSER_ENTER_BEHAVIOR)
|
||||
|
||||
window.localStorage.setItem('hapi-composer-enter-behavior', 'invalid')
|
||||
expect(getInitialComposerEnterBehavior()).toBe(DEFAULT_COMPOSER_ENTER_BEHAVIOR)
|
||||
})
|
||||
|
||||
it('reads a valid stored enter behavior', () => {
|
||||
window.localStorage.setItem('hapi-composer-enter-behavior', 'newline')
|
||||
|
||||
expect(getInitialComposerEnterBehavior()).toBe('newline')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,99 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
export type ComposerEnterBehavior = 'send' | 'newline'
|
||||
|
||||
export const DEFAULT_COMPOSER_ENTER_BEHAVIOR: ComposerEnterBehavior = 'send'
|
||||
|
||||
export function getComposerEnterBehaviorOptions(): ReadonlyArray<{ value: ComposerEnterBehavior; labelKey: string }> {
|
||||
return [
|
||||
{ value: 'send', labelKey: 'settings.chat.enterBehavior.send' },
|
||||
{ value: 'newline', labelKey: 'settings.chat.enterBehavior.newline' },
|
||||
]
|
||||
}
|
||||
|
||||
function getComposerEnterBehaviorStorageKey(): string {
|
||||
return 'hapi-composer-enter-behavior'
|
||||
}
|
||||
|
||||
function isBrowser(): boolean {
|
||||
return typeof window !== 'undefined' && typeof document !== 'undefined'
|
||||
}
|
||||
|
||||
function safeGetItem(key: string): string | null {
|
||||
if (!isBrowser()) {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
return localStorage.getItem(key)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function safeSetItem(key: string, value: string): void {
|
||||
if (!isBrowser()) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
localStorage.setItem(key, value)
|
||||
} catch {
|
||||
// Ignore storage errors
|
||||
}
|
||||
}
|
||||
|
||||
function safeRemoveItem(key: string): void {
|
||||
if (!isBrowser()) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
localStorage.removeItem(key)
|
||||
} catch {
|
||||
// Ignore storage errors
|
||||
}
|
||||
}
|
||||
|
||||
function parseComposerEnterBehavior(raw: string | null): ComposerEnterBehavior {
|
||||
if (raw === 'send' || raw === 'newline') {
|
||||
return raw
|
||||
}
|
||||
return DEFAULT_COMPOSER_ENTER_BEHAVIOR
|
||||
}
|
||||
|
||||
export function getInitialComposerEnterBehavior(): ComposerEnterBehavior {
|
||||
return parseComposerEnterBehavior(safeGetItem(getComposerEnterBehaviorStorageKey()))
|
||||
}
|
||||
|
||||
export function useComposerEnterBehavior(): {
|
||||
composerEnterBehavior: ComposerEnterBehavior
|
||||
setComposerEnterBehavior: (behavior: ComposerEnterBehavior) => void
|
||||
} {
|
||||
const [composerEnterBehavior, setComposerEnterBehaviorState] = useState<ComposerEnterBehavior>(getInitialComposerEnterBehavior)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBrowser()) {
|
||||
return
|
||||
}
|
||||
|
||||
const onStorage = (event: StorageEvent) => {
|
||||
if (event.key !== getComposerEnterBehaviorStorageKey()) {
|
||||
return
|
||||
}
|
||||
setComposerEnterBehaviorState(parseComposerEnterBehavior(event.newValue))
|
||||
}
|
||||
|
||||
window.addEventListener('storage', onStorage)
|
||||
return () => window.removeEventListener('storage', onStorage)
|
||||
}, [])
|
||||
|
||||
const setComposerEnterBehavior = useCallback((behavior: ComposerEnterBehavior) => {
|
||||
setComposerEnterBehaviorState(behavior)
|
||||
|
||||
if (behavior === DEFAULT_COMPOSER_ENTER_BEHAVIOR) {
|
||||
safeRemoveItem(getComposerEnterBehaviorStorageKey())
|
||||
} else {
|
||||
safeSetItem(getComposerEnterBehaviorStorageKey(), behavior)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return { composerEnterBehavior, setComposerEnterBehavior }
|
||||
}
|
||||
Reference in New Issue
Block a user