mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 01:09:56 -05:00
fix: make weel and touch passive
This commit is contained in:
+31
-17
@@ -1,4 +1,5 @@
|
||||
import { platformService } from '$lib/platform-service'
|
||||
import { settingsState } from '$lib/state/settings.svelte'
|
||||
import type { Manga } from '$lib/types/manga'
|
||||
import type { Chapter } from '$lib/types/chapter'
|
||||
|
||||
@@ -9,11 +10,8 @@ const APP_BUTTONS = [
|
||||
|
||||
const FALLBACK_IMAGE = 'moku_logo'
|
||||
|
||||
let sessionStart: number | null = null
|
||||
|
||||
function isPublicUrl(url: string | null | undefined): boolean {
|
||||
return typeof url === 'string' && url.startsWith('https://')
|
||||
}
|
||||
let sessionStart: number | null = null
|
||||
let activeMangaId: number | null = null
|
||||
|
||||
function trunc(s: string, max = 128): string {
|
||||
return s.length <= max ? s : `${s.slice(0, max - 1)}…`
|
||||
@@ -24,6 +22,31 @@ function formatChapter(chapter: Chapter): string {
|
||||
return `Chapter ${Number.isInteger(n) ? n : n.toFixed(1)}`
|
||||
}
|
||||
|
||||
// Suwayomi always returns the proxy path (/api/v1/manga/{id}/thumbnail), never the raw CDN URL.
|
||||
// The proxy URL is only useful to Discord when the server is publicly reachable over HTTPS.
|
||||
// For localhost setups cover art falls back to the app logo until Suwayomi exposes rawThumbnailUrl.
|
||||
function resolveCoverUrl(manga: Manga): string {
|
||||
const serverBase = (settingsState.settings.serverUrl ?? '').replace(/\/$/, '')
|
||||
if (!serverBase.startsWith('https://')) return FALLBACK_IMAGE
|
||||
const path = manga.thumbnailUrl?.startsWith('/') ? manga.thumbnailUrl : `/api/v1/manga/${manga.id}/thumbnail`
|
||||
return `${serverBase}${path}`
|
||||
}
|
||||
|
||||
function buildPresence(manga: Manga, chapter: Chapter, coverUrl: string) {
|
||||
return {
|
||||
details: trunc(manga.title),
|
||||
state: `${formatChapter(chapter)} · Reading`,
|
||||
timestamps: { start: sessionStart ?? Date.now() },
|
||||
assets: {
|
||||
largeImage: coverUrl,
|
||||
largeText: trunc(manga.title),
|
||||
smallImage: FALLBACK_IMAGE,
|
||||
smallText: 'Moku',
|
||||
},
|
||||
buttons: APP_BUTTONS,
|
||||
}
|
||||
}
|
||||
|
||||
export async function initRpc(): Promise<void> {
|
||||
if (!platformService.isSupported('discord-rpc')) return
|
||||
sessionStart = Date.now()
|
||||
@@ -36,18 +59,9 @@ export async function destroyRpc(): Promise<void> {
|
||||
|
||||
export async function setReading(manga: Manga, chapter: Chapter): Promise<void> {
|
||||
if (!platformService.isSupported('discord-rpc')) return
|
||||
await platformService.setDiscordPresence({
|
||||
details: trunc(manga.title),
|
||||
state: `${formatChapter(chapter)} · Reading`,
|
||||
timestamps: { start: sessionStart ?? Date.now() },
|
||||
assets: {
|
||||
largeImage: isPublicUrl(manga.thumbnailUrl) ? manga.thumbnailUrl : FALLBACK_IMAGE,
|
||||
largeText: trunc(manga.title),
|
||||
smallImage: FALLBACK_IMAGE,
|
||||
smallText: 'Moku',
|
||||
},
|
||||
buttons: APP_BUTTONS,
|
||||
})
|
||||
|
||||
activeMangaId = manga.id
|
||||
await platformService.setDiscordPresence(buildPresence(manga, chapter, resolveCoverUrl(manga)))
|
||||
}
|
||||
|
||||
export async function setIdle(): Promise<void> {
|
||||
|
||||
+19
-16
@@ -158,30 +158,33 @@
|
||||
if (appState.status !== 'ready') return
|
||||
// capture phase so events from any component — including modals — reset the timer
|
||||
const onActivity = () => resetIdleTimer()
|
||||
document.addEventListener('mousemove', onActivity, true)
|
||||
document.addEventListener('keydown', onActivity, true)
|
||||
document.addEventListener('touchstart', onActivity, true)
|
||||
document.addEventListener('touchmove', onActivity, true) // sustained touch-scroll in reader
|
||||
document.addEventListener('wheel', onActivity, true) // mouse-wheel / trackpad scroll in reader
|
||||
document.addEventListener('click', onActivity, true)
|
||||
document.addEventListener('mousemove', onActivity, true)
|
||||
document.addEventListener('keydown', onActivity, true)
|
||||
document.addEventListener('touchstart', onActivity, true)
|
||||
// passive:true tells the browser it can render scroll frames without waiting for the handler
|
||||
document.addEventListener('touchmove', onActivity, { capture: true, passive: true })
|
||||
document.addEventListener('wheel', onActivity, { capture: true, passive: true })
|
||||
document.addEventListener('click', onActivity, true)
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', onActivity, true)
|
||||
document.removeEventListener('keydown', onActivity, true)
|
||||
document.removeEventListener('touchstart', onActivity, true)
|
||||
document.removeEventListener('touchmove', onActivity, true)
|
||||
document.removeEventListener('wheel', onActivity, true)
|
||||
document.removeEventListener('click', onActivity, true)
|
||||
document.removeEventListener('mousemove', onActivity, true)
|
||||
document.removeEventListener('keydown', onActivity, true)
|
||||
document.removeEventListener('touchstart', onActivity, true)
|
||||
document.removeEventListener('touchmove', onActivity, { capture: true })
|
||||
document.removeEventListener('wheel', onActivity, { capture: true })
|
||||
document.removeEventListener('click', onActivity, true)
|
||||
}
|
||||
})
|
||||
|
||||
function resetIdleTimer() {
|
||||
if (idleTimer) clearTimeout(idleTimer)
|
||||
appState.idleSplash = false
|
||||
if (idleTimer) { clearTimeout(idleTimer); idleTimer = null }
|
||||
if (appState.idleSplash) appState.idleSplash = false
|
||||
// read the setting live so changes take effect without a restart
|
||||
const timeoutMs = (settingsState.settings.idleTimeoutMin ?? 5) * 60_000
|
||||
// 0 means "Never" — skip the timer entirely
|
||||
const mins = settingsState.settings.idleTimeoutMin ?? 5
|
||||
if (mins === 0) return
|
||||
idleTimer = setTimeout(() => {
|
||||
if (appState.status === 'ready') appState.idleSplash = true
|
||||
}, timeoutMs)
|
||||
}, mins * 60_000)
|
||||
}
|
||||
|
||||
function onIdleDismiss() { appState.idleSplash = false; resetIdleTimer() }
|
||||
|
||||
Reference in New Issue
Block a user