Modify the checkout widget to support subscriptions. Add usage-based billing toggle to sharing settings.
This commit is contained in:
@@ -57,42 +57,39 @@ let pages: Tab[] = [
|
||||
]
|
||||
|
||||
onMount(async () => {
|
||||
await load_page(window.location.pathname, false)
|
||||
await load_page(window.location.href, false)
|
||||
})
|
||||
|
||||
let current_page: Tab = $state(null)
|
||||
|
||||
const load_page = async (pathname: string, history: boolean): Promise<boolean> => {
|
||||
// find_page resolves a URL path to the page which renders it. It returns null
|
||||
// when no page matches, in which case the browser has to handle the navigation
|
||||
const find_page = (pathname: string): Tab => {
|
||||
const path = decodeURIComponent(pathname)
|
||||
let by_prefix: Tab = null
|
||||
for (const page of pages) {
|
||||
if (path === page.path) {
|
||||
return page
|
||||
}
|
||||
if (page.prefix !== undefined && path.startsWith(page.prefix)) {
|
||||
by_prefix = page
|
||||
}
|
||||
}
|
||||
return by_prefix
|
||||
}
|
||||
|
||||
// href can be a full URL or a path, and may contain a query string and hash.
|
||||
// Those are not used for routing, but they do need to survive the navigation
|
||||
const load_page = async (href: string, history: boolean): Promise<boolean> => {
|
||||
const url = new URL(href, window.location.href)
|
||||
console.debug(
|
||||
"Page router: Navigating to page", pathname,
|
||||
"Page router: Navigating to page", url.pathname,
|
||||
"current path", window.location.pathname,
|
||||
"log history:", history,
|
||||
)
|
||||
|
||||
let user: User
|
||||
try {
|
||||
user = await get_user()
|
||||
} catch (err) {
|
||||
alert("Failed to get user details: " + err)
|
||||
}
|
||||
|
||||
const path_decoded = decodeURIComponent(pathname)
|
||||
let page_by_path: Tab = null
|
||||
let page_by_prefix: Tab = null
|
||||
for (const page of pages) {
|
||||
if (path_decoded === page.path) {
|
||||
page_by_path = page
|
||||
}
|
||||
if (page.prefix !== undefined && path_decoded.startsWith(page.prefix)) {
|
||||
page_by_prefix = page
|
||||
}
|
||||
}
|
||||
|
||||
if (page_by_path !== null) {
|
||||
current_page = page_by_path
|
||||
} else if (page_by_prefix !== null) {
|
||||
current_page = page_by_prefix
|
||||
} else {
|
||||
const page = find_page(url.pathname)
|
||||
if (page === null) {
|
||||
current_page = {
|
||||
path: "",
|
||||
title: "Not Found",
|
||||
@@ -101,19 +98,37 @@ const load_page = async (pathname: string, history: boolean): Promise<boolean> =
|
||||
return false
|
||||
}
|
||||
|
||||
let user: User
|
||||
try {
|
||||
user = await get_user()
|
||||
} catch (err) {
|
||||
alert("Failed to get user details: " + err)
|
||||
}
|
||||
|
||||
// If this page requires login, and the user is not logged in, then we
|
||||
// redirect the user to the login page
|
||||
if (current_page.login === true && (user === null || user === undefined)) {
|
||||
// redirect the user to the login page. The page is deliberately not mounted
|
||||
// first, it would fire authenticated requests which are all bound to fail
|
||||
if (page.login === true && (user === null || user === undefined)) {
|
||||
console.debug("User is not logged in, redirecting to login page", user)
|
||||
return load_page("/login", true)
|
||||
return load_page(
|
||||
"/login?redirect=" + encodeURIComponent(url.pathname + url.search),
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
window.document.title = current_page.title+" / Nova"
|
||||
window.document.title = page.title+" / Nova"
|
||||
|
||||
// The address bar has to be updated before the page is mounted. Pages read
|
||||
// their parameters from it when they initialize, the login page picks up
|
||||
// the redirect target this way
|
||||
if (history) {
|
||||
window.history.pushState({}, window.document.title, pathname)
|
||||
window.history.pushState(
|
||||
{}, window.document.title, url.pathname + url.search + url.hash,
|
||||
)
|
||||
}
|
||||
|
||||
current_page = page
|
||||
|
||||
if (current_page.custom_breadcrumbs !== true) {
|
||||
breadcrumbs_store.set(breadcrumbs)
|
||||
}
|
||||
@@ -129,29 +144,53 @@ const load_page = async (pathname: string, history: boolean): Promise<boolean> =
|
||||
return true
|
||||
}
|
||||
|
||||
const click = async (e: MouseEvent) => {
|
||||
const origin = (e.target as Element).closest("a");
|
||||
const click = (e: MouseEvent) => {
|
||||
// Middle clicks, right clicks and modified clicks open the link in a new
|
||||
// tab or window. That's the browser's job, not ours
|
||||
if (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) {
|
||||
return
|
||||
}
|
||||
|
||||
const origin = (e.target as Element).closest("a")
|
||||
if (origin === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const url = URL.parse(origin.href)
|
||||
if (window.location.host !== url.host) {
|
||||
// Links which explicitly ask for browser behaviour: a new tab, a download,
|
||||
// or an external destination
|
||||
if (origin.target !== "" || origin.hasAttribute("download") ||
|
||||
origin.rel === "external") {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Caught link click to", url.pathname);
|
||||
|
||||
// Try to load the page, if the page was found we cancel the browser
|
||||
// navigation event so we can handle it ourselves
|
||||
if (await load_page(url.pathname, true) === true) {
|
||||
e.preventDefault()
|
||||
// Other hosts, and protocols like mailto: and magnet:, have no host match
|
||||
const url = URL.parse(origin.href)
|
||||
if (url === null || url.host !== window.location.host) {
|
||||
return
|
||||
}
|
||||
|
||||
// In-page anchors are left to the browser, it knows how to scroll to them
|
||||
if (url.pathname === window.location.pathname && url.hash !== "") {
|
||||
return
|
||||
}
|
||||
|
||||
// Paths without a page of their own are served by the backend, like the API
|
||||
// and file downloads. Loading them here would replace the page the user is
|
||||
// looking at with a Not Found error while the browser navigates away
|
||||
if (find_page(url.pathname) === null) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Caught link click to", url.pathname)
|
||||
|
||||
// The event has to be cancelled before the first await, after that the
|
||||
// browser has already committed to the navigation
|
||||
e.preventDefault()
|
||||
load_page(origin.href, true)
|
||||
}
|
||||
|
||||
const popstate = (e: PopStateEvent) => {
|
||||
load_page(window.location.pathname, false)
|
||||
load_page(window.location.href, false)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user