mirror of
https://github.com/open-goal/launcher.git
synced 2024-10-20 04:57:38 -04:00
Toast updates (#405)
Toast improvements: - Icon and color based on toast level (info, warn, error) - Animates out - Made it a component, removing logic from `app.svelte` - handles multiple toast messages better using a queue Sidebar styling logic: This was kinda gross so I revised it a bit. I'm considering doing an overhaul of the sidebar in the future.
This commit is contained in:
parent
d85a4af9fa
commit
0cfbd3609a
|
@ -8,11 +8,9 @@
|
||||||
import Background from "./components/background/Background.svelte";
|
import Background from "./components/background/Background.svelte";
|
||||||
import Header from "./components/header/Header.svelte";
|
import Header from "./components/header/Header.svelte";
|
||||||
import Update from "./routes/Update.svelte";
|
import Update from "./routes/Update.svelte";
|
||||||
import GameInProgress from "./components/games/GameInProgress.svelte";
|
|
||||||
import { isInDebugMode } from "$lib/utils/common";
|
import { isInDebugMode } from "$lib/utils/common";
|
||||||
import { Toast } from "flowbite-svelte";
|
import Toast from "./components/toast/Toast.svelte";
|
||||||
import Help from "./routes/Help.svelte";
|
import Help from "./routes/Help.svelte";
|
||||||
import { toastStore } from "$lib/stores/ToastStore";
|
|
||||||
import { isLoading } from "svelte-i18n";
|
import { isLoading } from "svelte-i18n";
|
||||||
import { getLocale, setLocale } from "$lib/rpc/config";
|
import { getLocale, setLocale } from "$lib/rpc/config";
|
||||||
import GameFeature from "./routes/GameFeature.svelte";
|
import GameFeature from "./routes/GameFeature.svelte";
|
||||||
|
@ -95,16 +93,7 @@
|
||||||
<Route path="/update" component={Update} primary={false} />
|
<Route path="/update" component={Update} primary={false} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if $toastStore.msg !== undefined}
|
<Toast />
|
||||||
<!-- TODO - make these look nice for info/warn/error levels -->
|
|
||||||
<Toast
|
|
||||||
color="green"
|
|
||||||
position="top-right"
|
|
||||||
class="w-full max-w-xs p-2 pl-4 z-50 top-20"
|
|
||||||
>
|
|
||||||
{$toastStore.msg}
|
|
||||||
</Toast>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
import { VersionStore } from "$lib/stores/VersionStore";
|
import { VersionStore } from "$lib/stores/VersionStore";
|
||||||
import { exceptionLog, infoLog } from "$lib/rpc/logging";
|
import { exceptionLog, infoLog } from "$lib/rpc/logging";
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
|
import { toastStore } from "$lib/stores/ToastStore";
|
||||||
|
|
||||||
let launcherVerison = null;
|
let launcherVerison = null;
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
changeLog: changeLog,
|
changeLog: changeLog,
|
||||||
};
|
};
|
||||||
infoLog(`Launcher Update Available`);
|
infoLog(`Launcher Update Available`);
|
||||||
|
toastStore.makeToast("Launcher update available!", "info");
|
||||||
} else {
|
} else {
|
||||||
$UpdateStore.launcher = {
|
$UpdateStore.launcher = {
|
||||||
updateAvailable: false,
|
updateAvailable: false,
|
||||||
|
|
|
@ -12,37 +12,24 @@
|
||||||
$: $location.pathname;
|
$: $location.pathname;
|
||||||
|
|
||||||
function getNavStyle(pathname: string): string {
|
function getNavStyle(pathname: string): string {
|
||||||
let style = "grow-0 shrink-0 basis-1/10 h-full bg-[#101010] px-1 z-10";
|
const baseStyle =
|
||||||
if (
|
"grow-0 shrink-0 basis-1/10 h-full bg-[#101010] px-1 z-10";
|
||||||
!pathname.startsWith("/settings") &&
|
const isOpaque =
|
||||||
!pathname.startsWith("/faq") &&
|
pathname.startsWith("/settings") ||
|
||||||
!pathname.startsWith("/update")
|
pathname.startsWith("/faq") ||
|
||||||
) {
|
pathname.startsWith("/update");
|
||||||
style += " opacity-50 hover:opacity-100 duration-500";
|
return isOpaque
|
||||||
}
|
? baseStyle
|
||||||
return style;
|
: `${baseStyle} opacity-50 hover:opacity-100 duration-500`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNavItemStyle(itemName: string, pathName: string): string {
|
function getNavItemStyle(itemName: string, pathName: string): string {
|
||||||
let style =
|
const baseStyle =
|
||||||
"flex items-center hover:grayscale-0 hover:opacity-100 duration-500 text-orange-400 duration-500";
|
"flex items-center hover:grayscale-0 hover:opacity-100 duration-500 text-orange-400";
|
||||||
if (
|
const isActive =
|
||||||
itemName === "jak1" &&
|
pathName.startsWith(`/${itemName}`) ||
|
||||||
(pathName.startsWith("/jak1") || pathName === "/")
|
(itemName === "jak1" && pathName === "/");
|
||||||
) {
|
return isActive ? baseStyle : `${baseStyle} grayscale`;
|
||||||
return style;
|
|
||||||
} else if (itemName === "jak2" && pathName.startsWith("/jak2")) {
|
|
||||||
return style;
|
|
||||||
} else if (itemName === "jak3" && pathName.startsWith("/jak3")) {
|
|
||||||
return style;
|
|
||||||
} else if (itemName === "jakx" && pathName.startsWith("/jakx")) {
|
|
||||||
return style;
|
|
||||||
} else if (itemName === "settings" && pathName.startsWith("/settings")) {
|
|
||||||
return style;
|
|
||||||
} else if (itemName === "faq" && pathName === "/faq") {
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
return style + " grayscale";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function modifyGameTitleName(gameName: string): string {
|
function modifyGameTitleName(gameName: string): string {
|
||||||
|
|
50
src/components/toast/Toast.svelte
Normal file
50
src/components/toast/Toast.svelte
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<script>
|
||||||
|
import { Toast } from "flowbite-svelte";
|
||||||
|
import { toastStore } from "$lib/stores/ToastStore";
|
||||||
|
import { fly } from "svelte/transition";
|
||||||
|
import IconCheck from "~icons/mdi/check";
|
||||||
|
import IconAlert from "~icons/mdi/stop-alert";
|
||||||
|
import IconAlertCircle from "~icons/mdi/alert-circle";
|
||||||
|
|
||||||
|
let open = false;
|
||||||
|
let counter;
|
||||||
|
let currentToast = null;
|
||||||
|
|
||||||
|
$: if ($toastStore.length > 0 && !currentToast) {
|
||||||
|
currentToast = $toastStore[0];
|
||||||
|
open = true;
|
||||||
|
counter = 6;
|
||||||
|
timeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeout() {
|
||||||
|
if (--counter > 0) return setTimeout(timeout, 1000);
|
||||||
|
open = false;
|
||||||
|
toastStore.removeToast();
|
||||||
|
currentToast = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if currentToast}
|
||||||
|
<Toast
|
||||||
|
{open}
|
||||||
|
dismissable={false}
|
||||||
|
position="top-right"
|
||||||
|
class="z-50 top-20"
|
||||||
|
transition={fly}
|
||||||
|
params={{ y: 200 }}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="icon">
|
||||||
|
{#if currentToast.level == "info"}
|
||||||
|
<IconCheck class="text-green-500 text-5xl" />
|
||||||
|
{:else if currentToast.level == "warn"}
|
||||||
|
<IconAlertCircle class="text-orange-500 text-5xl" />
|
||||||
|
{:else if currentToast.level == "error"}
|
||||||
|
<IconAlert class="text-red-500 text-5xl" />
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
<div class="ps-4 text-sm font-semibold">
|
||||||
|
{currentToast.msg}
|
||||||
|
</div>
|
||||||
|
</Toast>
|
||||||
|
{/if}
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { toastStore } from "$lib/stores/ToastStore";
|
||||||
import { invoke_rpc } from "./rpc";
|
import { invoke_rpc } from "./rpc";
|
||||||
|
|
||||||
// TODO - toasts
|
|
||||||
// TODO - just make this a generic interface for both binaries/feature jobs
|
// TODO - just make this a generic interface for both binaries/feature jobs
|
||||||
interface FeatureJobOutput {
|
interface FeatureJobOutput {
|
||||||
msg: string | null;
|
msg: string | null;
|
||||||
|
@ -8,6 +8,7 @@ interface FeatureJobOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
function failed(msg: string): FeatureJobOutput {
|
function failed(msg: string): FeatureJobOutput {
|
||||||
|
toastStore.makeToast(msg, "error");
|
||||||
return { success: false, msg };
|
return { success: false, msg };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,43 +2,28 @@ import { writable } from "svelte/store";
|
||||||
|
|
||||||
export type ToastLevel = "error" | "warn" | "info" | undefined;
|
export type ToastLevel = "error" | "warn" | "info" | undefined;
|
||||||
|
|
||||||
interface ToastStore {
|
interface ToastMessage {
|
||||||
msg: string | undefined;
|
msg: string;
|
||||||
level: ToastLevel;
|
level: ToastLevel;
|
||||||
interval: any;
|
interval?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const storeValue: ToastStore = {
|
type ToastArray = ToastMessage[];
|
||||||
msg: undefined,
|
const messageArray: ToastArray = [];
|
||||||
level: undefined,
|
|
||||||
interval: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
function createToastStore() {
|
function createToastStore() {
|
||||||
// TODO - the TTL isn't correct still, look into it
|
const { subscribe, update } = writable<ToastArray>(messageArray);
|
||||||
const { subscribe, set, update } = writable<ToastStore>(storeValue);
|
|
||||||
|
|
||||||
const ttl = 5000;
|
|
||||||
let timeoutId: NodeJS.Timer;
|
|
||||||
|
|
||||||
function ttlCheck() {
|
|
||||||
return setTimeout(() => {
|
|
||||||
update((val) => {
|
|
||||||
val.msg = undefined;
|
|
||||||
val.level = undefined;
|
|
||||||
timeoutId = undefined;
|
|
||||||
return val;
|
|
||||||
});
|
|
||||||
}, ttl);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
|
removeToast: () =>
|
||||||
|
update((val) => {
|
||||||
|
val = val.slice(1);
|
||||||
|
return val;
|
||||||
|
}),
|
||||||
makeToast: (msg: string, level: ToastLevel) =>
|
makeToast: (msg: string, level: ToastLevel) =>
|
||||||
update((val) => {
|
update((val) => {
|
||||||
val.msg = msg;
|
val.push({ msg, level });
|
||||||
val.level = level;
|
|
||||||
timeoutId = ttlCheck();
|
|
||||||
return val;
|
return val;
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
import { VersionStore } from "$lib/stores/VersionStore";
|
import { VersionStore } from "$lib/stores/VersionStore";
|
||||||
import { saveActiveVersionChange } from "$lib/rpc/config";
|
import { saveActiveVersionChange } from "$lib/rpc/config";
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
|
import { toastStore } from "$lib/stores/ToastStore";
|
||||||
|
|
||||||
let versionsLoaded = false;
|
let versionsLoaded = false;
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@
|
||||||
$VersionStore.activeVersionName = $VersionStore.selectedVersions.devel;
|
$VersionStore.activeVersionName = $VersionStore.selectedVersions.devel;
|
||||||
$VersionStore.selectedVersions.official = null;
|
$VersionStore.selectedVersions.official = null;
|
||||||
$VersionStore.selectedVersions.unofficial = null;
|
$VersionStore.selectedVersions.unofficial = null;
|
||||||
|
toastStore.makeToast("Saved game version!", "info");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
import { VersionStore } from "$lib/stores/VersionStore";
|
import { VersionStore } from "$lib/stores/VersionStore";
|
||||||
import { saveActiveVersionChange } from "$lib/rpc/config";
|
import { saveActiveVersionChange } from "$lib/rpc/config";
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
|
import { toastStore } from "$lib/stores/ToastStore";
|
||||||
|
|
||||||
let versionsLoaded = false;
|
let versionsLoaded = false;
|
||||||
|
|
||||||
|
@ -64,6 +65,7 @@
|
||||||
$VersionStore.selectedVersions.unofficial;
|
$VersionStore.selectedVersions.unofficial;
|
||||||
$VersionStore.selectedVersions.official = null;
|
$VersionStore.selectedVersions.official = null;
|
||||||
$VersionStore.selectedVersions.devel = null;
|
$VersionStore.selectedVersions.devel = null;
|
||||||
|
toastStore.makeToast("Saved game version!", "info");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue