|
3 | 3 | import { horizontalSlide } from '$util/horizontalSlide'; |
4 | 4 | import { fade } from 'svelte/transition'; |
5 | 5 | import { expoOut } from 'svelte/easing'; |
| 6 | + import { createQuery } from '@tanstack/svelte-query'; |
6 | 7 |
|
7 | 8 | import Navigation from './NavButton.svelte'; |
8 | 9 | import Modal from '$lib/components/Dialogue.svelte'; |
9 | 10 | import Button from '$lib/components/Button.svelte'; |
| 11 | + import Banner from '$lib/components/Banner.svelte'; |
| 12 | + import Query from '$lib/components/Query.svelte'; |
10 | 13 |
|
11 | 14 | import Cog from 'svelte-material-icons/Cog.svelte'; |
12 | 15 | import Replay from 'svelte-material-icons/Replay.svelte'; |
13 | 16 |
|
14 | | - import { api_base_url, set_api_base_url, default_api_url } from '$data/api/settings'; |
| 17 | + import { status_url, api_base_url, set_api_base_url, default_api_url } from '$data/api/settings'; |
15 | 18 | import RouterEvents from '$data/RouterEvents'; |
| 19 | + import { queries } from '$data/api'; |
16 | 20 |
|
17 | 21 | import { useQueryClient } from '@tanstack/svelte-query'; |
18 | 22 |
|
|
31 | 35 | } |
32 | 36 |
|
33 | 37 | let url = api_base_url(); |
| 38 | + const statusUrl = status_url(); |
34 | 39 |
|
35 | 40 | function save() { |
36 | 41 | set_api_base_url(url); |
|
44 | 49 | let menuOpen = false; |
45 | 50 | let modalOpen = false; |
46 | 51 | let y: number; |
| 52 | + const pingQuery = () => createQuery(['ping'], queries.ping); |
47 | 53 |
|
48 | 54 | onMount(() => { |
49 | 55 | return RouterEvents.subscribe((event) => { |
|
56 | 62 |
|
57 | 63 | <svelte:window bind:scrollY={y} /> |
58 | 64 |
|
59 | | -<nav class:scrolled={y > 10}> |
60 | | - <a class="menu-btn skiptab-btn" href="#skiptab">Skip navigation</a> |
61 | | - |
62 | | - <button |
63 | | - class="menu-btn mobile-only" |
64 | | - on:click={() => (menuOpen = !menuOpen)} |
65 | | - class:open={menuOpen} |
66 | | - aria-label="Menu" |
67 | | - > |
68 | | - <span class="menu-btn__burger" /> |
69 | | - </button> |
70 | | - <a href="/" id="logo"><img src="/logo.svg" alt="ReVanced Logo" /></a> |
71 | | - |
72 | | - {#key menuOpen} |
73 | | - <div |
74 | | - class="nav-wrapper" |
75 | | - class:desktop-only={!menuOpen} |
76 | | - transition:horizontalSlide={{ direction: 'inline', easing: expoOut, duration: 400 }} |
| 65 | +<div id="nav-container"> |
| 66 | + <Query query={pingQuery()} let:data> |
| 67 | + {#if !data} |
| 68 | + <span class="banner"> |
| 69 | + <Banner level="caution" permanent> |
| 70 | + The API is currently unresponsive and some services may not work correctly. {#if statusUrl} |
| 71 | + Check the <a href={statusUrl} target="_blank" rel="noopener noreferrer">status page</a> for |
| 72 | + updates. |
| 73 | + {/if} |
| 74 | + </Banner> |
| 75 | + </span> |
| 76 | + {/if} |
| 77 | + </Query> |
| 78 | + |
| 79 | + <nav class:scrolled={y > 10}> |
| 80 | + <a class="menu-btn skiptab-btn" href="#skiptab">Skip navigation</a> |
| 81 | + |
| 82 | + <button |
| 83 | + class="menu-btn mobile-only" |
| 84 | + on:click={() => (menuOpen = !menuOpen)} |
| 85 | + class:open={menuOpen} |
| 86 | + aria-label="Menu" |
77 | 87 | > |
78 | | - <div id="main-navigation"> |
79 | | - <ul class="nav-buttons"> |
80 | | - <Navigation href="/" label="Home">Home</Navigation> |
81 | | - <Navigation queryKey="manager" href="/download" label="Download">Download</Navigation> |
82 | | - <Navigation queryKey="patches" href="/patches" label="Patches">Patches</Navigation> |
83 | | - <Navigation queryKey="contributors" href="/contributors" label="Contributors"> |
84 | | - Contributors |
85 | | - </Navigation> |
86 | | - <Navigation queryKey={['about', 'team']} href="/donate" label="Donate">Donate</Navigation> |
87 | | - </ul> |
88 | | - </div> |
89 | | - <div id="secondary-navigation"> |
90 | | - <button on:click={() => (modalOpen = !modalOpen)} aria-label="Settings"> |
91 | | - <Cog size="20px" color="var(--surface-six)" /> |
92 | | - </button> |
| 88 | + <span class="menu-btn__burger" /> |
| 89 | + </button> |
| 90 | + <a href="/" id="logo"><img src="/logo.svg" alt="ReVanced Logo" /></a> |
| 91 | + |
| 92 | + {#key menuOpen} |
| 93 | + <div |
| 94 | + id="nav-wrapper-container" |
| 95 | + class:desktop-only={!menuOpen} |
| 96 | + transition:horizontalSlide={{ direction: 'inline', easing: expoOut, duration: 400 }} |
| 97 | + > |
| 98 | + <div id="banner-pad"> |
| 99 | + <Query query={pingQuery()} let:data> |
| 100 | + {#if !data} |
| 101 | + <span class="banner"> |
| 102 | + <Banner level="caution" permanent> |
| 103 | + The API is currently unresponsive and some services may not work correctly. {#if statusUrl} |
| 104 | + Check the |
| 105 | + <a href={statusUrl} target="_blank" rel="noopener noreferrer">status page</a> for |
| 106 | + updates. |
| 107 | + {/if} |
| 108 | + </Banner> |
| 109 | + </span> |
| 110 | + {/if} |
| 111 | + </Query> |
| 112 | + </div> |
| 113 | + |
| 114 | + <div class="nav-wrapper"> |
| 115 | + <div id="main-navigation"> |
| 116 | + <ul class="nav-buttons"> |
| 117 | + <Navigation href="/" label="Home">Home</Navigation> |
| 118 | + <Navigation queryKey="manager" href="/download" label="Download">Download</Navigation> |
| 119 | + <Navigation queryKey="patches" href="/patches" label="Patches">Patches</Navigation> |
| 120 | + <Navigation queryKey="contributors" href="/contributors" label="Contributors"> |
| 121 | + Contributors |
| 122 | + </Navigation> |
| 123 | + <Navigation queryKey={['about', 'team']} href="/donate" label="Donate" |
| 124 | + >Donate</Navigation |
| 125 | + > |
| 126 | + </ul> |
| 127 | + </div> |
| 128 | + <div id="secondary-navigation"> |
| 129 | + <button on:click={() => (modalOpen = !modalOpen)} aria-label="Settings"> |
| 130 | + <Cog size="20px" color="var(--surface-six)" /> |
| 131 | + </button> |
| 132 | + </div> |
| 133 | + </div> |
93 | 134 | </div> |
94 | | - </div> |
95 | | - {/key} |
96 | | - |
97 | | - {#if menuOpen} |
98 | | - <div |
99 | | - class="overlay mobile-only" |
100 | | - transition:fade={{ duration: 350 }} |
101 | | - on:click={() => (menuOpen = !menuOpen)} |
102 | | - on:keypress={() => (menuOpen = !menuOpen)} |
103 | | - /> |
104 | | - {/if} |
105 | | -</nav> |
| 135 | + {/key} |
| 136 | + |
| 137 | + {#if menuOpen} |
| 138 | + <div |
| 139 | + class="overlay mobile-only" |
| 140 | + transition:fade={{ duration: 350 }} |
| 141 | + on:click={() => (menuOpen = !menuOpen)} |
| 142 | + on:keypress={() => (menuOpen = !menuOpen)} |
| 143 | + /> |
| 144 | + {/if} |
| 145 | + </nav> |
| 146 | +</div> |
106 | 147 |
|
107 | 148 | <!-- settings --> |
108 | 149 | <Modal bind:modalOpen> |
|
126 | 167 | </svelte:fragment> |
127 | 168 | </Modal> |
128 | 169 |
|
129 | | -<style> |
| 170 | +<style lang="scss"> |
130 | 171 | #logo { |
131 | 172 | padding: 0.5rem; |
132 | 173 | } |
|
160 | 201 | top: 30px; |
161 | 202 | } |
162 | 203 |
|
| 204 | + #nav-container { |
| 205 | + position: sticky; |
| 206 | + z-index: 666; |
| 207 | + width: 100%; |
| 208 | +
|
| 209 | + &:has(.nav-buttons > li:first-child.selected) { |
| 210 | + margin-bottom: 2.65rem; |
| 211 | +
|
| 212 | + &:has(.banner) { |
| 213 | + margin-bottom: 1.5rem; |
| 214 | + } |
| 215 | + } |
| 216 | + } |
| 217 | +
|
163 | 218 | nav { |
164 | | - position: fixed; |
165 | | - top: 0; |
166 | 219 | display: flex; |
167 | 220 | gap: 2rem; |
168 | 221 | justify-content: space-between; |
169 | 222 | align-items: center; |
170 | 223 | padding: 1rem 2rem; |
171 | | - z-index: 666; |
172 | 224 | height: 70px; |
173 | 225 | background-color: var(--surface-eight); |
174 | 226 | width: 100%; |
|
181 | 233 | gap: 2rem; |
182 | 234 | } |
183 | 235 |
|
184 | | - a { |
185 | | - display: flex; |
186 | | - } |
187 | | -
|
188 | 236 | img { |
189 | 237 | height: 22px; |
190 | 238 | } |
|
220 | 268 | } |
221 | 269 | } |
222 | 270 |
|
| 271 | + #banner-pad { |
| 272 | + display: none; |
| 273 | + } |
| 274 | +
|
| 275 | + #nav-wrapper-container { |
| 276 | + width: 100%; |
| 277 | + } |
| 278 | +
|
223 | 279 | @media (max-width: 767px) { |
| 280 | + #banner-pad { |
| 281 | + display: block; |
| 282 | + width: 100vw; |
| 283 | + visibility: hidden; |
| 284 | + } |
| 285 | +
|
| 286 | + #nav-container:has(.nav-buttons > li:first-child.selected):has(.banner) { |
| 287 | + margin-bottom: 0rem; |
| 288 | + } |
| 289 | +
|
| 290 | + #nav-wrapper-container { |
| 291 | + overflow: hidden; |
| 292 | + position: fixed; |
| 293 | + width: 20rem; |
| 294 | + top: 0; |
| 295 | + left: 0; |
| 296 | + height: 100%; |
| 297 | + background-color: var(--surface-eight); |
| 298 | + z-index: 100; |
| 299 | + } |
| 300 | +
|
224 | 301 | .nav-wrapper { |
225 | 302 | flex-direction: column; |
226 | 303 | gap: 0.5rem; |
227 | 304 | height: 100%; |
228 | 305 | margin: 0 auto; |
229 | | - position: fixed; |
230 | 306 | width: 20rem; |
231 | | - top: 0px; |
232 | 307 | border-radius: 0px 24px 24px 0px; |
233 | | - left: 0px; |
234 | | - background-color: var(--surface-eight); |
235 | 308 | padding: 1rem; |
236 | 309 | padding-top: 6rem; |
237 | | - z-index: 100; |
238 | 310 | } |
239 | 311 |
|
240 | 312 | .desktop-only { |
|
0 commit comments