1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
<script lang="ts">
import { onMount } from 'svelte';
import { BUILD } from '~/config/build';
import { getJet } from '~/jet';
import { makeErrorPageIntent } from '~/jet/intents/error-page-intent-controller';
import { getLocale } from '~/utils/locale';
// Types
import type { Page } from './jet/models/page';
// Components
import Fonts from '~/components/structure/Fonts.svelte';
import Footer from '~/components/structure/Footer.svelte';
import Navigation from '~/components/navigation/Navigation.svelte';
import NavigationSkeleton from '~/components/navigation/Skeleton.svelte';
import PageResolver from '~/components/PageResolver.svelte';
const locale = getLocale();
const jet = getJet();
$: language = locale.language;
export let page: Promise<Page> | Page = new Promise(() => {});
export let isFirstPage: boolean = true;
$: pageWithRejectionErrorPage = transformRejectionIntoErrorPage(page);
// Critically, this function is not async. We want to preserve the behavior
// where if page is not a promise than neither is
// pageWithRejectionErrorPage.
function transformRejectionIntoErrorPage(
page: Promise<Page> | Page,
): Promise<Page> | Page {
if (!(page instanceof Promise)) {
return page;
}
// The async IIFE allows this function to return synchronously.
return (async (): Promise<Page> => {
try {
return await page;
} catch (error) {
return jet.dispatch(
makeErrorPageIntent({
// This allows the error page to pick the right platform
// and display the correct mesage (ex. "Page not found" for
// a 404)
error: error instanceof Error ? error : null,
}),
);
}
})();
}
// NOTE: The use of page instead of pageWithRejectionErrorPage here is very
// intentional. Since pageWithRejectionErrorPage is reactive, it will
// be undefined in this initializer. This is intentionally not
// not derived (eg. defined as $: webNavigation = ...), since we only
// want to update it _after_ the page promise resolves (so the nav
// doesn't disappear on navigation). But then for SSR, there are no
// promises, so we need a sync value here so the nav renders, which
// is why we have the initializer.
let webNavigation = page instanceof Promise ? null : page.webNavigation;
$: {
if (pageWithRejectionErrorPage instanceof Promise) {
// Clientside once the new page resolves, update the navigation
// (in case it changed)
pageWithRejectionErrorPage.then((page: Page) => {
webNavigation = page.webNavigation;
});
} else {
// Sometimes clientside a promise is not passed to updateApp, so
// we need to handle a WebRenderablePage (possible with a
// different webNavigation).
webNavigation = pageWithRejectionErrorPage.webNavigation;
}
}
onMount(() => {
//@ts-ignore
window.__ASOTW = {
version: BUILD,
};
});
</script>
<svelte:head>
<meta name="version" content={BUILD} />
</svelte:head>
<Fonts {language} />
{#if import.meta.env.DEV}
{#await import('~/components/ArtworkBreakpointLogger.svelte') then { default: ArtworkBreakpointLogger }}
<ArtworkBreakpointLogger />
{/await}
{/if}
<div class="app-container" data-testid="app-container">
<div class="navigation-container">
{#if webNavigation}
<Navigation {webNavigation} />
{:else}
<NavigationSkeleton />
{/if}
</div>
<div
style="display: flex;
position: relative;
flex-direction: column;
min-height: 100vh;
"
>
<main class="page-container">
<PageResolver page={pageWithRejectionErrorPage} {isFirstPage} />
</main>
<Footer />
</div>
</div>
<style lang="scss">
@use '@amp/web-shared-styles/sasskit-stylekit/ac-sasskit-config';
@use '@amp/web-shared-styles/app/core/viewports' as *;
@use '@amp/web-shared-styles/app/core/globalvars' as *;
.app-container {
min-height: 100vh;
min-height: 100dvh;
display: grid;
grid-template-areas:
'structure-header'
'structure-main-section';
grid-template-columns: minmax(0, 1fr);
grid-gap: 0;
grid-template-rows: 44px auto;
@media (--sidebar-visible) {
grid-template-rows: auto;
grid-template-columns: 260px minmax(0, 1fr);
}
@media (--sidebar-large-visible) {
grid-template-columns: $global-sidebar-width-large minmax(0, 1fr);
}
}
.navigation-container {
@media (--range-small-up) {
height: 100vh;
position: sticky;
top: 0;
}
}
.page-container {
flex-grow: 1;
}
</style>
|