Unmount history component when hidden

- history service resets as history component is unmounted
- restores functionality I changed in my previous commit
This commit is contained in:
Mattias Granlund 2024-05-17 19:34:32 +02:00
parent ec24ffe979
commit 425289e10a
3 changed files with 151 additions and 138 deletions

View File

@ -7,23 +7,20 @@
import ScrollableContainer from './ScrollableContainer.svelte';
import SnapshotCard from './SnapshotCard.svelte';
import emptyFolderSvg from '$lib/assets/empty-state/empty-folder.svg?raw';
import { listen } from '$lib/backend/ipc';
import { Project } from '$lib/backend/projects';
import { clickOutside } from '$lib/clickOutside';
import { HistoryService, createdOnDay } from '$lib/history/history';
import { persisted } from '$lib/persisted/persisted';
import { getContext } from '$lib/utils/context';
import * as hotkeys from '$lib/utils/hotkeys';
import { RemoteFile } from '$lib/vbranches/types';
import { plainToInstance } from 'class-transformer';
import { onMount } from 'svelte';
import { createEventDispatcher } from 'svelte';
import type { Snapshot, SnapshotDiff } from '$lib/history/types';
const project = getContext(Project);
const historyService = getContext(HistoryService);
const snapshots = historyService.snapshots;
const dispatch = createEventDispatcher<{ hide: any }>();
const showHistoryView = persisted(false, 'showHistoryView');
const loading = historyService.loading;
let currentFilePreview: RemoteFile | undefined = undefined;
@ -49,142 +46,122 @@
});
}
onMount(() => {
const unsubscribe = listen<string>('menu://view/history/clicked', () => {
$showHistoryView = !$showHistoryView;
});
// TODO: Refactor somehow
const unsubscribeHotkeys = hotkeys.on('$mod+Shift+H', () => {
$showHistoryView = !$showHistoryView;
});
return async () => {
unsubscribe();
unsubscribeHotkeys();
};
});
let snapshotFilesTempStore:
| { entryId: string; diffs: { [key: string]: SnapshotDiff } }
| undefined = undefined;
let selectedFile: { entryId: string; path: string } | undefined = undefined;
</script>
{#if $showHistoryView}
<aside class="sideview-container" class:show-view={$showHistoryView}>
<div
class="sideview-content-wrap"
use:clickOutside={{
handler: () => {
$showHistoryView = false;
}
}}
>
{#if currentFilePreview}
<div class="file-preview" class:show-file-view={currentFilePreview}>
<FileCard
isCard={false}
conflicted={false}
file={currentFilePreview}
isUnapplied={false}
readonly={true}
on:close={() => {
currentFilePreview = undefined;
selectedFile = undefined;
}}
/>
</div>
<aside class="sideview-container show-view">
<div
class="sideview-content-wrap"
use:clickOutside={{
handler: () => dispatch('hide')
}}
>
{#if currentFilePreview}
<div class="file-preview" class:show-file-view={currentFilePreview}>
<FileCard
isCard={false}
conflicted={false}
file={currentFilePreview}
isUnapplied={false}
readonly={true}
on:close={() => {
currentFilePreview = undefined;
selectedFile = undefined;
}}
/>
</div>
{/if}
<div class="sideview">
<div class="sideview__header" data-tauri-drag-region>
<i class="clock-icon">
<div class="clock-pointers" />
</i>
<h3 class="sideview__header-title text-base-15 text-bold">Project history</h3>
<Button
style="ghost"
icon="cross"
on:click={() => {
dispatch('hide');
}}
/>
</div>
<!-- EMPTY STATE -->
{#if $snapshots.length == 0}
<EmptyStatePlaceholder image={emptyFolderSvg}>
<svelte:fragment slot="title">No snapshots yet</svelte:fragment>
<svelte:fragment slot="caption">
Gitbutler saves your work, including file changes, so your progress is always secure.
Adjust snapshot settings in project settings.
</svelte:fragment>
</EmptyStatePlaceholder>
{/if}
<div class="sideview">
<div class="sideview__header" data-tauri-drag-region>
<i class="clock-icon">
<div class="clock-pointers" />
</i>
<h3 class="sideview__header-title text-base-15 text-bold">Project history</h3>
<Button
style="ghost"
icon="cross"
on:click={() => {
$showHistoryView = false;
}}
/>
</div>
{#if $snapshots.length == 0 && $loading}
<FullviewLoading />
{/if}
<!-- EMPTY STATE -->
{#if $snapshots.length == 0}
<EmptyStatePlaceholder image={emptyFolderSvg}>
<svelte:fragment slot="title">No snapshots yet</svelte:fragment>
<svelte:fragment slot="caption">
Gitbutler saves your work, including file changes, so your progress is always secure.
Adjust snapshot settings in project settings.
</svelte:fragment>
</EmptyStatePlaceholder>
{/if}
{#if $snapshots.length == 0 && $loading}
<FullviewLoading />
{/if}
<!-- SNAPSHOTS -->
{#if $snapshots.length > 0}
<ScrollableContainer on:bottomReached={onLastInView}>
<div class="container">
<!-- SNAPSHOTS FEED -->
{#each $snapshots as entry, idx (entry.id)}
{#if idx === 0 || createdOnDay(entry.createdAt) != createdOnDay($snapshots[idx - 1].createdAt)}
<div class="sideview__date-header">
<h4 class="text-base-12 text-semibold">
{createdOnDay(entry.createdAt)}
</h4>
</div>
{/if}
{#if entry.details}
<SnapshotCard
{entry}
isCurrent={idx == 0}
on:restoreClick={() => {
historyService.restoreSnapshot(project.id, entry.id);
}}
{selectedFile}
on:diffClick={async (filePath) => {
const path = filePath.detail;
if (snapshotFilesTempStore?.entryId == entry.id) {
updateFilePreview(entry, path);
} else {
snapshotFilesTempStore = {
entryId: entry.id,
diffs: await historyService.getSnapshotDiff(project.id, entry.id)
};
updateFilePreview(entry, path);
}
}}
/>
{/if}
{/each}
<div class="welcome-point">
<div class="welcome-point__icon">
<Icon name="finish" />
</div>
<div class="welcome-point__content">
<p class="text-base-13 text-semibold">Welcome to history!</p>
<p class="welcome-point__caption text-base-body-12">
Gitbutler saves your work, including file changes, so your progress is always
secure. Adjust snapshot settings in project settings.
</p>
<!-- SNAPSHOTS -->
{#if $snapshots.length > 0}
<ScrollableContainer on:bottomReached={onLastInView}>
<div class="container">
<!-- SNAPSHOTS FEED -->
{#each $snapshots as entry, idx (entry.id)}
{#if idx === 0 || createdOnDay(entry.createdAt) != createdOnDay($snapshots[idx - 1].createdAt)}
<div class="sideview__date-header">
<h4 class="text-base-12 text-semibold">
{createdOnDay(entry.createdAt)}
</h4>
</div>
{/if}
{#if entry.details}
<SnapshotCard
{entry}
isCurrent={idx == 0}
on:restoreClick={() => {
historyService.restoreSnapshot(project.id, entry.id);
}}
{selectedFile}
on:diffClick={async (filePath) => {
const path = filePath.detail;
if (snapshotFilesTempStore?.entryId == entry.id) {
updateFilePreview(entry, path);
} else {
snapshotFilesTempStore = {
entryId: entry.id,
diffs: await historyService.getSnapshotDiff(project.id, entry.id)
};
updateFilePreview(entry, path);
}
}}
/>
{/if}
{/each}
<div class="welcome-point">
<div class="welcome-point__icon">
<Icon name="finish" />
</div>
<div class="welcome-point__content">
<p class="text-base-13 text-semibold">Welcome to history!</p>
<p class="welcome-point__caption text-base-body-12">
Gitbutler saves your work, including file changes, so your progress is always
secure. Adjust snapshot settings in project settings.
</p>
</div>
</div>
</ScrollableContainer>
{/if}
</div>
</div>
</ScrollableContainer>
{/if}
</div>
</aside>
{/if}
</div>
</aside>
<!-- TODO: HANDLE LOADING STATE -->

View File

@ -6,18 +6,33 @@ import { writable } from 'svelte/store';
export class HistoryService {
cursor: string | undefined = undefined;
snapshots = writable<Snapshot[]>([], (set) => {
this.loadSnapshots().then((x) => set(x));
readonly loading = writable(false);
readonly snapshots = writable<Snapshot[]>([], (set) => {
// Load snapshots when going from 0 -> 1 subscriber.
this.fetch().then((x) => set(x));
return () => {
// Clear store when component last subscriber unsubscribes.
set([]);
this.cursor = undefined;
};
});
loading = writable(false);
constructor(private projectId: string) {}
async loadSnapshots(after?: string) {
async load() {
if (this.cursor) this.cursor = undefined;
this.snapshots.set(await this.fetch());
this.loading.set(false);
}
async loadMore() {
if (!this.cursor) throw new Error('Unable to load more without a cursor');
const more = await this.fetch(this.cursor);
// TODO: Update API so we don't have to .slice()
this.snapshots.update((snapshots) => [...snapshots, ...more.slice(1)]);
}
private async fetch(after?: string) {
this.loading.set(true);
const resp = await invoke<Snapshot[]>('list_snapshots', {
projectId: this.projectId,
@ -29,10 +44,8 @@ export class HistoryService {
return plainToInstance(Snapshot, resp);
}
async loadMore() {
if (!this.cursor) throw new Error('Unable to load more without a cursor');
const more = await this.loadSnapshots(this.cursor);
this.snapshots.update((snapshots) => [...snapshots, ...more.slice(1)]);
clear() {
this.snapshots.set([]);
}
async getSnapshotDiff(projectId: string, sha: string) {

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { listen } from '$lib/backend/ipc';
import { Project } from '$lib/backend/projects';
import { BranchService } from '$lib/branches/service';
import History from '$lib/components/History.svelte';
@ -8,11 +9,13 @@
import ProblemLoadingRepo from '$lib/components/ProblemLoadingRepo.svelte';
import ProjectSettingsMenuAction from '$lib/components/ProjectSettingsMenuAction.svelte';
import { HistoryService } from '$lib/history/history';
import { persisted } from '$lib/persisted/persisted';
import * as hotkeys from '$lib/utils/hotkeys';
import { BaseBranchService, NoDefaultTarget } from '$lib/vbranches/baseBranch';
import { BranchController } from '$lib/vbranches/branchController';
import { BaseBranch } from '$lib/vbranches/types';
import { VirtualBranchService } from '$lib/vbranches/virtualBranch';
import { onDestroy, setContext } from 'svelte';
import { onDestroy, onMount, setContext } from 'svelte';
import type { LayoutData } from './$types';
export let data: LayoutData;
@ -42,6 +45,8 @@
$: setContext(BaseBranch, baseBranch);
$: setContext(Project, project);
const showHistoryView = persisted(false, 'showHistoryView');
let intervalId: any;
// Once on load and every time the project id changes
@ -58,6 +63,22 @@
if (intervalId) clearInterval(intervalId);
}
onMount(() => {
const unsubscribe = listen<string>('menu://view/history/clicked', () => {
$showHistoryView = !$showHistoryView;
});
// TODO: Refactor somehow
const unsubscribeHotkeys = hotkeys.on('$mod+Shift+H', () => {
$showHistoryView = !$showHistoryView;
});
return async () => {
unsubscribe();
unsubscribeHotkeys();
};
});
onDestroy(() => clearFetchInterval());
</script>
@ -81,8 +102,10 @@
{:else if $baseBranch}
<div class="view-wrap" role="group" on:dragover|preventDefault>
<Navigation />
{#if $showHistoryView}
<History on:hide={() => ($showHistoryView = false)} />
{/if}
<slot />
<History />
</div>
{/if}
{/key}