Files
decePubClient/wwwroot/main.js
T
thepraandClaude Opus 5.5 7b1bfa71c0 On touch, a tap on a post opens its thread
On a phone the only way into a post's thread was a small Expand button (and now an entry in More), while a tap on the
post pinned it to a globe that is usually hidden there, outlining the card. Where the pointer can't hover, a tap on a
post that isn't on its links, controls or menus and doesn't end a text selection opens its thread through the card's
own thread link (main.js, data-thread on the card); cards are pinned to the globe with a mouse only. The globe's corner
stats have 12px text on touch. Phone flow: a tap in a post's margin opens its thread and pins nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 23:39:10 +02:00

216 lines
5.8 KiB
JavaScript

window.cascadingStateInstanceReference = (dotNetReference) => {
try {
window.cascadingStateInstance = dotNetReference
} catch (e) {
console.error(e)
}
}
window.logFromJs = (message, where) => {
try {
if (!window.cascadingStateInstance ||
typeof window.cascadingStateInstance.invokeMethodAsync != "function")
return
window.cascadingStateInstance.invokeMethodAsync('LogFromJs', message, where)
} catch (e) {
console.error(e)
}
}
window.isServiceWorkerAvailable = () => {
try {
return navigator.serviceWorker.valueOf().controller != null
} catch (e) {
console.error(e)
window.logFromJs(e.message, "isServiceWorkerAvailable")
}
}
window.getLanguage = () => {
try {
return navigator.language.substr(0, 2)
} catch (e) {
console.error(e)
window.logFromJs(e.message, "getLanguage")
}
}
window.setLanguage = (lang) => {
try {
navigator.language = lang
} catch (e) {
console.error(e)
window.logFromJs(e.message, "setLanguage")
}
}
window.isOnline = async () => {
try {
const isOnline = navigator.onLine
//const url = "https://contents.nuvola.xyz/api/assets/6d9f5e55-2034-456f-92e7-d9ec51066443"
//let isOnlineStatusCode = true
//try {
// const online = await fetch(url)
// isOnlineStatusCode = online.status >= 200 && online.status < 399
//} catch (_) {
// isOnlineStatusCode = false
//}
if (navigator.connection !== undefined &&
navigator.connection.effectiveType !== undefined &&
navigator.connection.type !== undefined) {
if (!isOnline)
return isOnline
return navigator.connection.type === "cellular" ?
navigator.connection.effectiveType.indexOf("2g") === -1 /*&& isOnlineStatusCode*/ :
true
} else
return isOnline //&& isOnlineStatusCode
} catch (e) {
console.error(e)
window.logFromJs(e.message, "isOnline")
}
}
window.clearCache = async () => {
try {
const cachesNames = await caches.keys()
for (let i = 0; i < cachesNames.length; i++)
await caches.delete(cachesNames[i])
} catch (e) {
console.error(e)
window.logFromJs(e.message, "clearCache")
}
}
window.clearLocalStorage = () => {
try {
localStorage.removeItem("PrivateCacheData")
localStorage.removeItem("AuthData")
} catch (e) {
console.error(e)
window.logFromJs(e.message, "clearLocalStorage")
}
}
window.reloadPage = () => {
try {
location.reload(true)
} catch (e) {
console.error(e)
window.logFromJs(e.message, "reloadPage")
}
}
window.getHeight = (classId) => {
try {
const elements = document.querySelectorAll(classId)
let px = 0
if (!elements) return 0
elements.forEach(v => px += v.offsetHeight)
return px
} catch (e) {
console.error(e)
window.logFromJs(e.message, "getHeight")
}
}
window.setAppBadge = (counter) => {
try {
navigator.setAppBadge(counter)
} catch (e) {
console.error(e)
window.logFromJs(e.message, "setAppBadge")
}
}
window.clearAppBadge = () => {
try {
navigator.clearAppBadge()
} catch (e) {
console.error(e)
window.logFromJs(e.message, "clearAppBadge")
}
}
// On a touch screen a tap on a post (not on its links, controls or menus, and not ending a text selection) opens its
// thread, through the card's own thread link (Content.razor, data-thread-link), which Blazor's router takes.
document.addEventListener("click", event => {
try {
if (window.matchMedia("(hover: hover)").matches || !(event.target instanceof Element))
return
const card = event.target.closest("[data-thread]")
if (!card || event.target.closest("a, button, input, textarea, select, label, summary, video, audio, [role=button], .dropdown, .modal, [data-sheet]"))
return
if (!window.getSelection()?.isCollapsed)
return
card.querySelector("a[data-thread-link]")?.click()
} catch (e) {
console.error(e)
window.logFromJs(e.message, "openThread")
}
})
window.canHover = () => {
try {
return window.matchMedia("(hover: hover)").matches
} catch (e) {
console.error(e)
window.logFromJs(e.message, "canHover")
return true
}
}
// the selected tab of a sideways-scrolling strip, brought into its middle
window.revealActive = strip => {
try {
strip?.querySelector("[aria-selected=true]")?.scrollIntoView({ inline: "center", block: "nearest" })
} catch (e) {
console.error(e)
window.logFromJs(e.message, "revealActive")
}
}
// a phone: narrower than Tailwind's md, or held sideways (as short as the short: variant)
window.isMobileMedia = () => {
try {
return window.matchMedia("(max-width: 767.98px), (max-height: 30rem)").matches
} catch (e) {
console.error(e)
window.logFromJs(e.message, "isMobileMedia")
}
}
window.canShareStuff = () => {
try {
return navigator.share !== undefined
} catch (e) {
console.error(e)
window.logFromJs(e.message, "canShareStuff")
}
}
window.shareTextUrl = (title, text, url) => {
try {
navigator.share({
title: title,
text: text,
url: url
})
} catch (e) {
console.error(e)
window.logFromJs(e.message, "shareTextUrl")
}
}
window.shareText = (title, text) => {
try {
navigator.share({
title: title,
text: text
})
} catch (e) {
console.error(e)
window.logFromJs(e.message, "shareText")
}
}// This device's position for located posts and the Nearby feed, asked only when the reader chooses one of them. It
// goes to PrivaPub only, which keeps located posts on the server and rounds what it stores.
window.feedPosition = () => new Promise(resolve => {
try {
if (!navigator.geolocation)
return resolve({ error: "unsupported" })
navigator.geolocation.getCurrentPosition(
position => resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude, accuracy: position.coords.accuracy }),
error => resolve({ error: error.message || String(error.code) }),
{ enableHighAccuracy: false, timeout: 15000, maximumAge: 600000 })
} catch (e) {
console.error(e)
window.logFromJs(e.message, "feedPosition")
resolve({ error: e.message })
}
})