fix(front): clean startup console noise (#10965)

* fix(front): clean startup console noise

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* fix(love-resources): make content-type check strict-boolean safe

The recording availability probe used a negated optional chain
(!res.headers.get(...)?.includes(...)) whose type is boolean | undefined.
The repo ESLint config rejects this under
@typescript-eslint/strict-boolean-expressions and it breaks the
formatting CI job. Compare against true explicitly instead; behaviour
is unchanged (a missing or non-JSON content-type still means not JSON).

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

---------

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Co-authored-by: Michael Uray <michaeluray@users.noreply.github.com>
Co-authored-by: Artyom Savchenko <armisav@gmail.com>
This commit is contained in:
Michael Uray
2026-09-25 17:51:12 +02:00
committed by GitHub
co-authored by Michael Uray Artyom Savchenko
parent bda10fa45e
commit 67c3ebfaf7
3 changed files with 42 additions and 5 deletions
+11 -2
View File
@@ -4,7 +4,12 @@ import { getClient } from '.'
import notification from '@hcengineering/notification'
const sounds = new Map<Asset, AudioBuffer>()
const context = new AudioContext()
let context: AudioContext | undefined
function getAudioContext (): AudioContext {
context ??= new AudioContext()
return context
}
export async function isNotificationAllowed (_class?: Ref<Class<Doc>>): Promise<boolean> {
if (_class === undefined) return false
@@ -24,7 +29,7 @@ export async function prepareSound (key: string): Promise<void> {
const soundUrl = getMetadata(key as Asset) as string
const rawAudio = await fetch(soundUrl)
const rawBuffer = await rawAudio.arrayBuffer()
const decodedBuffer = await context.decodeAudioData(rawBuffer)
const decodedBuffer = await getAudioContext().decodeAudioData(rawBuffer)
sounds.set(key as Asset, decodedBuffer)
} catch (err) {
@@ -46,6 +51,10 @@ export async function playSound (soundKey: string, loop = false): Promise<(() =>
}
try {
const context = getAudioContext()
if (context.state === 'suspended') {
await context.resume()
}
const audio = context.createBufferSource()
audio.buffer = sound
audio.loop = loop
+4
View File
@@ -296,6 +296,10 @@ async function checkRecordAvailable (): Promise<void> {
}, 500)
} else if (endpoint !== '') {
const res = await fetch(concatLink(endpoint, '/checkRecordAvailable'))
if (!res.ok || res.headers.get('content-type')?.includes('application/json') !== true) {
isRecordingAvailable.set(false)
return
}
const result = await res.json()
isRecordingAvailable.set(result)
} else {
+27 -3
View File
@@ -735,7 +735,7 @@ export function pushAvailable (): boolean {
return (
'serviceWorker' in navigator &&
'PushManager' in window &&
publicKey !== undefined &&
isValidPushPublicKey(publicKey) &&
'Notification' in window &&
Notification.permission !== 'denied'
)
@@ -748,7 +748,7 @@ export async function subscribePush (): Promise<boolean> {
}
const client = getClient()
const publicKey = getPushPublicKey()
if ('serviceWorker' in navigator && 'PushManager' in window && publicKey !== undefined) {
if ('serviceWorker' in navigator && 'PushManager' in window && isValidPushPublicKey(publicKey)) {
try {
const loc = getCurrentLocation()
let registration = await navigator.serviceWorker.getRegistration(`/${loc.path[0]}/${loc.path[1]}`)
@@ -763,7 +763,7 @@ export async function subscribePush (): Promise<boolean> {
if (current == null) {
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: publicKey
applicationServerKey: urlBase64ToUint8Array(publicKey)
})
await client.createDoc(notification.class.PushSubscription, core.space.Workspace, {
user: getCurrentAccount().uuid,
@@ -808,6 +808,30 @@ function getPushPublicKey (): string | undefined {
return publicKey.trim() !== '' ? publicKey : undefined
}
function isValidPushPublicKey (publicKey: string | undefined): publicKey is string {
if (publicKey === undefined || publicKey.trim() === '') return false
try {
const key = urlBase64ToUint8Array(publicKey)
return key.length === 65 && key[0] === 4
} catch {
return false
}
}
function urlBase64ToUint8Array (value: string): Uint8Array<ArrayBuffer> {
const padding = '='.repeat((4 - (value.length % 4)) % 4)
const base64 = `${value}${padding}`.replace(/-/g, '+').replace(/_/g, '/')
const rawData = atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}
async function cleanTag (_id: Ref<Doc>): Promise<void> {
const client = getClient()
const notifications = await client.findAll(notification.class.BrowserNotification, {