mirror of
https://github.com/wukko/cobalt.git
synced 2024-11-15 12:50:01 +00:00
web/save: add supported services popover
This commit is contained in:
parent
7524d202f7
commit
42410f7b20
5 changed files with 222 additions and 1 deletions
|
@ -6,5 +6,7 @@
|
||||||
"mute": "mute",
|
"mute": "mute",
|
||||||
"input.placeholder": "paste the link here",
|
"input.placeholder": "paste the link here",
|
||||||
"terms.note.agreement": "by continuing, you agree to",
|
"terms.note.agreement": "by continuing, you agree to",
|
||||||
"terms.note.link": "terms and ethics of use"
|
"terms.note.link": "terms and ethics of use",
|
||||||
|
"services.title": "supported services",
|
||||||
|
"services.disclaimer": "cobalt is not affiliated with any of the supported services listed above."
|
||||||
}
|
}
|
||||||
|
|
155
web/src/components/save/SupportedServices.svelte
Normal file
155
web/src/components/save/SupportedServices.svelte
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { t } from "$lib/i18n/translations";
|
||||||
|
import { getServerInfo, cachedInfo } from "$lib/api/server-info";
|
||||||
|
|
||||||
|
import Skeleton from "$components/misc/Skeleton.svelte";
|
||||||
|
import IconPlus from "@tabler/icons-svelte/IconPlus.svelte";
|
||||||
|
|
||||||
|
let services: string[] = [];
|
||||||
|
|
||||||
|
$: expanded = false;
|
||||||
|
$: loaded = false;
|
||||||
|
|
||||||
|
const loadInfo = async () => {
|
||||||
|
await getServerInfo();
|
||||||
|
|
||||||
|
if ($cachedInfo) {
|
||||||
|
loaded = true;
|
||||||
|
services = $cachedInfo.cobalt.services;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="supported-services">
|
||||||
|
<button
|
||||||
|
id="services-button"
|
||||||
|
class:expanded
|
||||||
|
on:click={async () => {
|
||||||
|
expanded = !expanded;
|
||||||
|
if (expanded && services.length === 0) {
|
||||||
|
await loadInfo();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="expand-icon">
|
||||||
|
<IconPlus />
|
||||||
|
</div>
|
||||||
|
<span class="title">{$t("save.services.title")}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div id="services-popover" class:expanded>
|
||||||
|
<div id="services-container">
|
||||||
|
{#if loaded}
|
||||||
|
{#each services as service}
|
||||||
|
<div class="service-item">{service}</div>
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
{#each { length: 17 } as _}
|
||||||
|
<Skeleton
|
||||||
|
class="elevated"
|
||||||
|
width={Math.random() * 44 + 50 + "px"}
|
||||||
|
height="24.5px"
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div id="services-disclaimer" class="subtext">
|
||||||
|
{$t("save.services.disclaimer")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#supported-services {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
max-width: 400px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#services-popover {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: transform 0.2s cubic-bezier(0.53, 0.05, 0.23, 0.99);
|
||||||
|
border-radius: 18px;
|
||||||
|
background: var(--button);
|
||||||
|
box-shadow:
|
||||||
|
var(--button-box-shadow),
|
||||||
|
0 0 10px 10px var(--button-stroke);
|
||||||
|
|
||||||
|
transform: scale(0);
|
||||||
|
transform-origin: top center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
padding: 12px;
|
||||||
|
gap: 6px;
|
||||||
|
top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#services-popover.expanded {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#services-button {
|
||||||
|
gap: 9px;
|
||||||
|
padding: 7px 13px 7px 10px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
border-radius: 18px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-icon {
|
||||||
|
height: 21px;
|
||||||
|
width: 21px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 18px;
|
||||||
|
background: var(--button-elevated);
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#services-button:hover .expand-icon {
|
||||||
|
background: var(--button-elevated-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-icon :global(svg) {
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
stroke-width: 2px;
|
||||||
|
color: var(--secondary);
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanded .expand-icon {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#services-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: calc(var(--border-radius) / 2);
|
||||||
|
background: var(--button-elevated);
|
||||||
|
font-size: 12.5px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
#services-disclaimer {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
46
web/src/lib/api/server-info.ts
Normal file
46
web/src/lib/api/server-info.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { get, writable } from "svelte/store";
|
||||||
|
import { currentApiURL } from "$lib/api/api-url";
|
||||||
|
|
||||||
|
import type { CobaltServerInfoResponse, CobaltErrorResponse, CobaltServerInfo } from "$lib/types/api";
|
||||||
|
|
||||||
|
export const cachedInfo = writable<CobaltServerInfo | undefined>();
|
||||||
|
|
||||||
|
const request = async () => {
|
||||||
|
const apiEndpoint = `${currentApiURL()}/`;
|
||||||
|
|
||||||
|
const response: CobaltServerInfoResponse = await fetch(apiEndpoint, {
|
||||||
|
redirect: "manual",
|
||||||
|
signal: AbortSignal.timeout(10000),
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.catch((e) => {
|
||||||
|
if (e?.message?.includes("timed out")) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
error: {
|
||||||
|
code: "error.api.timed_out"
|
||||||
|
}
|
||||||
|
} as CobaltErrorResponse
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getServerInfo = async () => {
|
||||||
|
const cache = get(cachedInfo);
|
||||||
|
if (cache) return true;
|
||||||
|
|
||||||
|
const freshInfo = await request();
|
||||||
|
|
||||||
|
if (!freshInfo || !("cobalt" in freshInfo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("status" in freshInfo)) {
|
||||||
|
cachedInfo.set(freshInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -43,7 +43,23 @@ export type CobaltSession = {
|
||||||
exp: number,
|
exp: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CobaltServerInfo = {
|
||||||
|
cobalt: {
|
||||||
|
version: string,
|
||||||
|
url: string,
|
||||||
|
startTime: string,
|
||||||
|
durationLimit: number,
|
||||||
|
services: string[]
|
||||||
|
},
|
||||||
|
git: {
|
||||||
|
branch: string,
|
||||||
|
commit: string,
|
||||||
|
remote: string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type CobaltSessionResponse = CobaltSession | CobaltErrorResponse;
|
export type CobaltSessionResponse = CobaltSession | CobaltErrorResponse;
|
||||||
|
export type CobaltServerInfoResponse = CobaltServerInfo | CobaltErrorResponse;
|
||||||
|
|
||||||
export type CobaltAPIResponse = CobaltErrorResponse
|
export type CobaltAPIResponse = CobaltErrorResponse
|
||||||
| CobaltPickerResponse
|
| CobaltPickerResponse
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import Omnibox from "$components/save/Omnibox.svelte";
|
import Omnibox from "$components/save/Omnibox.svelte";
|
||||||
import Meowbalt from "$components/misc/Meowbalt.svelte";
|
import Meowbalt from "$components/misc/Meowbalt.svelte";
|
||||||
|
import SupportedServices from "$components/save/SupportedServices.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -10,6 +11,7 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div id="cobalt-save-container" class="center-column-container">
|
<div id="cobalt-save-container" class="center-column-container">
|
||||||
|
<SupportedServices />
|
||||||
<main
|
<main
|
||||||
id="cobalt-save"
|
id="cobalt-save"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
|
Loading…
Reference in a new issue