On a phone the globe is a banner above the feed; a tap on it now opens it full screen over everything, with a close button (Escape closes it too), the focus moving to that button and back, and the feed underneath kept as it was but out of reach (inert). The camera refits the new shape. Held sideways, a phone (a window at most 30rem tall, the new short: variant) has no room for the banner: the banner and its toggle give way to a button that opens the globe full screen. isMobileMedia, which picks the first visit's default, agrees with Tailwind's md now (it was off by one) and counts a phone held sideways. GlobePanel tells its state (data-globe-state). Phone flows: the globe opens full screen and closes, and a sideways phone keeps a feed at least 180px tall with no sideways scroll. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
181 lines
4.5 KiB
JavaScript
181 lines
4.5 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")
|
|
}
|
|
}
|
|
// 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 })
|
|
}
|
|
})
|