mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-03 03:33:16 +03:00
Extract header and footer into their own components
This commit is contained in:
parent
1d983516ed
commit
c5c5152bf2
@ -8,53 +8,22 @@
|
||||
import { Toaster } from 'svelte-french-toast';
|
||||
import { userStore } from '$lib/stores/user';
|
||||
import type { LayoutData } from './$types';
|
||||
import { Link, Tooltip } from '$lib/components';
|
||||
import { IconEmail } from '$lib/icons';
|
||||
import { page } from '$app/stores';
|
||||
import { derived } from '@square/svelte-store';
|
||||
import { onMount, setContext } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { unsubscribe } from '$lib/utils';
|
||||
import LinkProjectModal from './LinkProjectModal.svelte';
|
||||
import Breadcrumbs from './Breadcrumbs.svelte';
|
||||
import ShareIssueModal from './ShareIssueModal.svelte';
|
||||
import { SETTINGS_CONTEXT, loadUserSettings } from '$lib/userSettings';
|
||||
import { initTheme } from '$lib/theme';
|
||||
import { install as installUpdate } from '$lib/updater';
|
||||
import { relaunch } from '@tauri-apps/api/process';
|
||||
import { onUpdaterEvent } from '@tauri-apps/api/updater';
|
||||
import Header from './Header.svelte';
|
||||
import Footer from './Footer.svelte';
|
||||
|
||||
export let data: LayoutData;
|
||||
const { posthog, projects, sentry, cloud, update } = data;
|
||||
|
||||
const user = userStore;
|
||||
|
||||
let updateStatus: {
|
||||
error?: string;
|
||||
status: 'PENDING' | 'DOWNLOADED' | 'ERROR' | 'DONE' | 'UPTODATE';
|
||||
};
|
||||
onMount(() => {
|
||||
const unsubscribe = onUpdaterEvent((status) => {
|
||||
updateStatus = status;
|
||||
if (updateStatus.error) {
|
||||
toasts.error(updateStatus.error);
|
||||
}
|
||||
});
|
||||
return () => unsubscribe.then((unsubscribe) => unsubscribe());
|
||||
});
|
||||
|
||||
let updateTimer: ReturnType<typeof setInterval>;
|
||||
onMount(() => {
|
||||
update.load();
|
||||
const tenMinutes = 1000 * 60 * 10;
|
||||
updateTimer = setInterval(() => {
|
||||
update.reload?.();
|
||||
}, tenMinutes);
|
||||
return () => {
|
||||
() => clearInterval(updateTimer);
|
||||
};
|
||||
});
|
||||
|
||||
const userSettings = loadUserSettings();
|
||||
initTheme(userSettings);
|
||||
setContext(SETTINGS_CONTEXT, userSettings);
|
||||
@ -104,93 +73,19 @@
|
||||
}));
|
||||
}),
|
||||
|
||||
user.subscribe(posthog.identify),
|
||||
user.subscribe(sentry.identify)
|
||||
userStore.subscribe(posthog.identify),
|
||||
userStore.subscribe(sentry.identify)
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="flex h-full flex-col">
|
||||
<header
|
||||
data-tauri-drag-region
|
||||
class="bg-color-3 border-color-4 flex h-8 flex-shrink-0 flex-row items-center gap-x-6 border-b"
|
||||
style="z-index: 9999;"
|
||||
>
|
||||
<div class="breadcrumb-project-container ml-[80px]">
|
||||
<Breadcrumbs project={$project} />
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
<div class="mr-6">
|
||||
{#await user.load() then}
|
||||
<Link href="/user/">
|
||||
{#if $user?.picture}
|
||||
<img class="mr-1 inline-block h-5 w-5 rounded-full" src={$user.picture} alt="Avatar" />
|
||||
{/if}
|
||||
{$user?.name ?? 'Account'}
|
||||
</Link>
|
||||
{/await}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<Header project={$project} user={$userStore}></Header>
|
||||
<div class="flex flex-grow overflow-y-auto overscroll-none">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<Toaster />
|
||||
|
||||
<LinkProjectModal bind:this={linkProjectModal} {cloud} {projects} />
|
||||
|
||||
<ShareIssueModal bind:this={shareIssueModal} user={$user} {cloud} />
|
||||
<footer class="text-color-3 w-full text-sm font-medium">
|
||||
<div
|
||||
class="bg-color-3 border-color-4 flex h-[1.375rem] flex-shrink-0 select-none items-center border-t"
|
||||
>
|
||||
<div class="mx-4 flex w-full flex-row items-center justify-between space-x-2 pb-[1px]">
|
||||
<div>
|
||||
{#if $project}
|
||||
<Link href="/repo/{$project?.id}/settings">
|
||||
<div class="flex flex-row items-center space-x-2">
|
||||
{#if $project?.api?.sync}
|
||||
<div class="h-2 w-2 rounded-full bg-green-700" />
|
||||
<span>online</span>
|
||||
{:else}
|
||||
<div class="h-2 w-2 rounded-full bg-red-700" />
|
||||
<span class="text-light-600 dark:text-dark-200">offline</span>
|
||||
{/if}
|
||||
</div>
|
||||
</Link>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
{#if $update?.enabled && $update?.shouldUpdate}
|
||||
<div class="flex items-center gap-1">
|
||||
{#if !updateStatus}
|
||||
<div
|
||||
class="mr-1 flex h-4 w-4 items-center justify-center rounded-full bg-red-500 text-xs font-bold text-white"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
<button on:click={() => installUpdate()}>
|
||||
version {$update.version} available
|
||||
</button>
|
||||
{:else if updateStatus.status === 'PENDING'}
|
||||
<span>downloading update...</span>
|
||||
{:else if updateStatus.status === 'DOWNLOADED'}
|
||||
<span>installing update...</span>
|
||||
{:else if updateStatus.status === 'DONE'}
|
||||
<button on:click={() => relaunch()}>restart to update</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<Tooltip label="Send feedback">
|
||||
<IconEmail class="h-4 w-4" on:click={() => events.emit('openSendIssueModal')} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<ShareIssueModal bind:this={shareIssueModal} user={$userStore} {cloud} />
|
||||
<Footer project={$project} {update}></Footer>
|
||||
</div>
|
||||
|
95
packages/ui/src/routes/Footer.svelte
Normal file
95
packages/ui/src/routes/Footer.svelte
Normal file
@ -0,0 +1,95 @@
|
||||
<script lang="ts">
|
||||
import type { Project } from '$lib/api/ipc/projects';
|
||||
import Link from '$lib/components/Link/Link.svelte';
|
||||
import Tooltip from '$lib/components/Tooltip/Tooltip.svelte';
|
||||
import IconEmail from '$lib/icons/IconEmail.svelte';
|
||||
import type { Loadable } from '@square/svelte-store';
|
||||
import { installUpdate, onUpdaterEvent } from '@tauri-apps/api/updater';
|
||||
import * as toasts from '$lib/toasts';
|
||||
import { onMount } from 'svelte';
|
||||
import { relaunch } from '@tauri-apps/api/process';
|
||||
import * as events from '$lib/events';
|
||||
|
||||
export let project: Project | undefined;
|
||||
export let update: Loadable<any>;
|
||||
|
||||
let updateStatus: {
|
||||
error?: string;
|
||||
status: 'PENDING' | 'DOWNLOADED' | 'ERROR' | 'DONE' | 'UPTODATE';
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
const unsubscribe = onUpdaterEvent((status) => {
|
||||
updateStatus = status;
|
||||
if (updateStatus.error) {
|
||||
toasts.error(updateStatus.error);
|
||||
}
|
||||
});
|
||||
return () => unsubscribe.then((unsubscribe) => unsubscribe());
|
||||
});
|
||||
|
||||
let updateTimer: ReturnType<typeof setInterval>;
|
||||
onMount(() => {
|
||||
update.load();
|
||||
const tenMinutes = 1000 * 60 * 10;
|
||||
updateTimer = setInterval(() => {
|
||||
update.reload?.();
|
||||
}, tenMinutes);
|
||||
return () => {
|
||||
() => clearInterval(updateTimer);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<footer class="text-color-3 w-full text-sm font-medium">
|
||||
<div
|
||||
class="bg-color-3 border-color-4 flex h-[1.375rem] flex-shrink-0 select-none items-center border-t"
|
||||
>
|
||||
<div class="mx-4 flex w-full flex-row items-center justify-between space-x-2 pb-[1px]">
|
||||
<div>
|
||||
{#if project}
|
||||
<Link href="/repo/{project?.id}/settings">
|
||||
<div class="flex flex-row items-center space-x-2">
|
||||
{#if project?.api?.sync}
|
||||
<div class="h-2 w-2 rounded-full bg-green-700" />
|
||||
<span>online</span>
|
||||
{:else}
|
||||
<div class="h-2 w-2 rounded-full bg-red-700" />
|
||||
<span class="text-light-600 dark:text-dark-200">offline</span>
|
||||
{/if}
|
||||
</div>
|
||||
</Link>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
{#if $update?.enabled && $update?.shouldUpdate}
|
||||
<div class="flex items-center gap-1">
|
||||
{#if !updateStatus}
|
||||
<div
|
||||
class="mr-1 flex h-4 w-4 items-center justify-center rounded-full bg-red-500 text-xs font-bold text-white"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
<button on:click={() => installUpdate()}>
|
||||
version {$update.version} available
|
||||
</button>
|
||||
{:else if updateStatus.status === 'PENDING'}
|
||||
<span>downloading update...</span>
|
||||
{:else if updateStatus.status === 'DOWNLOADED'}
|
||||
<span>installing update...</span>
|
||||
{:else if updateStatus.status === 'DONE'}
|
||||
<button on:click={() => relaunch()}>restart to update</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<Tooltip label="Send feedback">
|
||||
<IconEmail class="h-4 w-4" on:click={() => events.emit('openSendIssueModal')} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
28
packages/ui/src/routes/Header.svelte
Normal file
28
packages/ui/src/routes/Header.svelte
Normal file
@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import type { User } from '$lib/api/cloud/api';
|
||||
import type { Project } from '$lib/api/ipc/projects';
|
||||
import Link from '$lib/components/Link/Link.svelte';
|
||||
import Breadcrumbs from './Breadcrumbs.svelte';
|
||||
|
||||
export let user: User | undefined;
|
||||
export let project: Project | undefined;
|
||||
</script>
|
||||
|
||||
<header
|
||||
data-tauri-drag-region
|
||||
class="bg-color-3 border-color-4 flex h-8 flex-shrink-0 flex-row items-center gap-x-6 border-b"
|
||||
style="z-index: 9999;"
|
||||
>
|
||||
<div class="breadcrumb-project-container ml-[80px]">
|
||||
<Breadcrumbs {project} />
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
<div class="mr-6">
|
||||
<Link href="/user/">
|
||||
{#if user?.picture}
|
||||
<img class="mr-1 inline-block h-5 w-5 rounded-full" src={user.picture} alt="Avatar" />
|
||||
{/if}
|
||||
{user?.name ?? 'Account'}
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
Loading…
Reference in New Issue
Block a user