Files
decePubClient/wwwroot/main.js
T
thepraandClaude Opus 5.5 fd2f2e7c65
Build / Build (push) Successful in 51s
R1 cleanup: collAnon and dead code gone, no IndexedDB, honest PWA files
Starts the completion program (SocialPub docs/ROADMAP.md, "Owner decisions on finishing everything", 2026-10-17).

- The localizer's namespace is decePubClient.Services; collAnon is gone from the repo.
- Dead code: PagesBase, VConstants, SettingsOptions, ViewLanguage, unused APIs routes, InstanceService.Self,
  RequestStats.LastOf/AverageMs, PostAnon/DeleteTotallyAnon, unused AppStatusService methods and their main.js helpers
  (the app badge stays for Web Push), ExtensionMethods/SUtility leftovers (Markdig and BlazorZXingJs packages go),
  unused PublicCacheData/PageSettings/AppConfiguration fields, commented-out code.
- The client log in IndexedDB, which nothing ever read, goes with the whole IndexedDB layer (DnetIndexedDb, IStorage):
  logs go to the browser console; main.js deletes the old "data" database once.
- The solution names the real ClientModels project. Unused vendor assets (fontawesome, solid, open-iconic, rxjs) go.
- robots.txt disallows everything; manifest.json and index.html say decePub; the service worker's cache name follows
  each publish's assets version, so old caches are purged.
- MessageType is Visibility (member names kept: they are resx keys); ExpandMessage.razor is Thread.razor (same route).
- i18n: dead keys removed, Italian "camera" is "punto di vista", DInputBase/DCalendar validation messages come from
  ErrorMessages, DataTransfer's spinner has its label.
- CLAUDE.md follows: client storage, JS interop, PWA, the D fields' messages.

Verified: dotnet build decePubClient.csproj and decePubClient.sln -c Release (0 errors, no new warnings); a Pasture
publish served by the pasture's decepub peer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
2026-10-17 02:56:57 +02:00

148 lines
4.7 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)
}
}
// The client once kept its error log in IndexedDB (DnetIndexedDb's database "data"). Nothing uses IndexedDB now, so a
// browser that still holds that database drops it.
try {
window.indexedDB?.deleteDatabase("data")
} catch (e) {
console.error(e)
}
window.getLanguage = () => {
try {
return navigator.language.substr(0, 2)
} catch (e) {
console.error(e)
window.logFromJs(e.message, "getLanguage")
}
}
window.isOnline = async () => {
try {
const isOnline = navigator.onLine
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 :
true
} else
return isOnline
} catch (e) {
console.error(e)
window.logFromJs(e.message, "isOnline")
}
}
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")
}
})
// The Back button (a popstate) as opposed to the app's own navigation: Blazor's location-changing handlers can't tell
// them apart, and MainLayout lets Back close an open overlay but lets the app's navigation through. Registered for the
// capture phase, so it runs before Blazor's own listener.
let backAt = 0
window.addEventListener("popstate", () => backAt = Date.now(), { capture: true })
window.tookBack = () => {
const back = Date.now() - backAt < 1000
backAt = 0
return back
}
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")
}
}
// the reader asked for less motion: what would move by itself (a gifv) waits for a tap
window.prefersReducedMotion = () => {
try {
return window.matchMedia("(prefers-reduced-motion: reduce)").matches
} catch (e) {
console.error(e)
window.logFromJs(e.message, "prefersReducedMotion")
return false
}
}
// 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")
}
}
// 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 })
}
})