Refactor project page & timeline session components

- Update project page layout to a `div` with a bottom border
- Adjust display width of session timeline based on session duration
- Add timeline session components to project page
- Add logic to determine if project exists
- Update authentication API calls and parse JSON responses
- Add new methods for creating login token and getting user object

[src/routes/+layout.svelte]
- Change div container from `flex` to `grid`
- Increase sidebar height from `10` to `12`
- Change sidebar width from `1/4` to `flex`
- Change main content width from `flex-grow` to `col-span-5`
- Add `dark` class to sidebar and main content
[src/lib/components/timeline/index.ts]
- Change the exported component from `TimelineDay` to `TimelineDaySession`
[src/routes/+layout.ts]
- Add an `add` method to the `projects` object
- Add `user` object to the `load` function
- Update the `projects` object to be initialized from an external source if not building
[src/routes/projects/[projectId]/+layout.svelte]
- Change the layout of the project page from a `div` to a `div` with a bottom border
[src/lib/components/timeline/TimelineDay.svelte]
- Delete the TimelineDay component
- Remove the code related to the component, including the `sessionDisplayWidth` function
[src/routes/projects/[projectId]/sessions/[sessionId]/+page.svelte]
- Replace the `border-y` class with `border-b` in the `+page.svelte` file
[src/routes/projects/[projectId]/+page.svelte]
- Add logic to adjust the display width of the session timeline based on session duration
- Add logic to determine if the project exists
- Add timeline session components to the project page
[src/lib/users.ts]
- Move imports to the top of the file
- Add a check to see if a user is logged in
- Set the store to the user if they are logged in
- Log the user when they are set
- Write the user to the file when they are set
[src/lib/authentication.ts]
- Change the URL for the API calls
- Parse JSON responses from the API
- Add a new method for creating a login token
- Add a new method for getting a user object
- Fix typo in error message
- Update the content-type header for API calls
[src/routes/projects/[projectId]/sessions/[sessionId]/+page.ts]
- Change the logic for finding the current session
- Make the code more precise in finding the current session
- Change the logic for finding the previous session
This commit is contained in:
Kiril Videlov 2023-02-15 15:36:20 +01:00
parent c5c46e8a25
commit c19e374476
10 changed files with 116 additions and 102 deletions

View File

@ -1,59 +1,68 @@
import { dev } from '$app/environment'
import { dev } from "$app/environment";
const apiUrl = dev ? new URL('https://test.app.gitbutler.com/api/') : new URL('https://app.gitbutler.com/api/');
const apiUrl = dev
? new URL("https://test.app.gitbutler.com/api/")
: new URL("https://app.gitbutler.com/api/");
const getUrl = (path: string) => new URL(path, apiUrl).toString();
export type LoginToken = {
token: string,
expires: string,
url: string
}
token: string;
expires: string;
url: string;
};
export type User = {
id: number,
name: string,
email: string,
picture: string,
locale: string,
created_at: string,
updated_at: string,
access_token: string,
}
id: number;
name: string;
email: string;
picture: string;
locale: string;
created_at: string;
updated_at: string;
access_token: string;
};
const parseJSON = async (response: Response) => {
if (response.status === 204 || response.status === 205) {
return null;
}
if (response.status >= 400) {
throw new Error(`HTTP Error ${response.statusText}: ${await response.text()}`);
throw new Error(
`HTTP Error ${response.statusText}: ${await response.text()}`
);
}
return await response.json();
}
};
export default ({ fetch }: { fetch: typeof window.fetch } = { fetch: window.fetch }) => ({
export default (
{ fetch }: { fetch: typeof window.fetch } = { fetch: window.fetch }
) => ({
login: {
token: {
create: (params: {} = {}): Promise<LoginToken> => fetch(getUrl('login/token.json'), {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params),
}).then(parseJSON).then(token => {
const url = new URL(token.url);
url.host = apiUrl.host;
return {
...token,
url: url.toString(),
}
}),
create: (params: {} = {}): Promise<LoginToken> =>
fetch(getUrl("login/token.json"), {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
})
.then(parseJSON)
.then((token) => {
const url = new URL(token.url);
url.host = apiUrl.host;
return {
...token,
url: url.toString(),
};
}),
},
user: {
get: (token: string): Promise<User> => fetch(getUrl(`login/user/${token}.json`), {
method: 'GET',
}).then(parseJSON),
}
}
})
get: (token: string): Promise<User> =>
fetch(getUrl(`login/user/${token}.json`), {
method: "GET",
}).then(parseJSON),
},
},
});

View File

@ -1,28 +0,0 @@
<script lang="ts">
import type { Session } from "$lib/sessions";
import TimelineDaySession from "./TimelineDaySession.svelte";
export let sessions: Session[];
export let projectId: string;
const sessionDisplayWidth = (session: Session) => {
let sessionDurationHours =
(session.meta.lastTs - session.meta.startTs) / 1000 / 60 / 60;
if (sessionDurationHours <= 1) {
return "w-40";
} else {
return "w-60";
}
};
</script>
<div class="mt-12">
<div class="-mb-5 border border-slate-400 h-1.5 bg-slate-200 z-0" />
<div class="flex flex-row space-x-2 z-10">
{#each sessions as session}
<div class={sessionDisplayWidth(session)}>
<TimelineDaySession {projectId} {session} />
</div>
{/each}
</div>
</div>

View File

@ -1 +1 @@
export { default as TimelineDay } from "./TimelineDay.svelte";
export { default as TimelineDaySession } from "./TimelineDaySession.svelte";

View File

@ -1,20 +1,28 @@
import { BaseDirectory, exists, readTextFile, writeTextFile } from '@tauri-apps/api/fs';
import type { User } from '$lib/authentication';
import { writable } from 'svelte/store';
import {
BaseDirectory,
exists,
readTextFile,
writeTextFile,
} from "@tauri-apps/api/fs";
import type { User } from "$lib/authentication";
import { writable } from "svelte/store";
const userFile = 'user.json';
const userFile = "user.json";
const isLoggedIn = () => exists(userFile, {
dir: BaseDirectory.AppLocalData
})
const isLoggedIn = () =>
exists(userFile, {
dir: BaseDirectory.AppLocalData,
});
export default async () => {
const store = writable<User | undefined>(undefined);
if (await isLoggedIn()) {
const user = JSON.parse(await readTextFile(userFile, {
dir: BaseDirectory.AppLocalData
})) as User;
const user = JSON.parse(
await readTextFile(userFile, {
dir: BaseDirectory.AppLocalData,
})
) as User;
store.set(user);
}
@ -22,11 +30,10 @@ export default async () => {
if (user) {
console.log({ user });
await writeTextFile(userFile, JSON.stringify(user), {
dir: BaseDirectory.AppLocalData
dir: BaseDirectory.AppLocalData,
});
}
})
});
return store;
}
};

View File

@ -67,19 +67,19 @@
<a href="/users/" class="mr-4 cursor-default font-bold">{$user ? $user.name : 'User'}</a>
</header>
<div class="flex h-screen flex-grow flex-row text-zinc-400 overflow-hidden">
<div class="grid grid-cols-6 h-screen flex-grow flex-row text-zinc-400 overflow-hidden">
<div
id="sidebar"
class="
overflow-auto
flex
w-1/4 flex-col bg-zinc-50
flex-col bg-zinc-50
border-r
border-zinc-700
dark:bg-zinc-900"
>
<div
class=" flex h-10 items-center border-b border-zinc-700 hover:bg-zinc-800"
class=" flex h-12 items-center border-b border-zinc-700 hover:bg-zinc-800"
>
<div class="flex-grow">
<DropDown projects={$projects} />
@ -108,7 +108,7 @@
</div>
<div
class="flex-grow h-full border-ldark:border-zinc-700 dark:bg-zinc-800 overflow-hidden"
class="h-full col-span-5 border-ldark:border-zinc-700 dark:bg-zinc-800 overflow-hidden"
>
<slot />
</div>

View File

@ -8,12 +8,16 @@ export const prerender = true;
export const csr = true;
export const load: LayoutLoad = async () => {
const projects = building ? ({
...readable<Project[]>([]),
add: () => {
throw new Error("not implemented");
}
}) : await (await import("$lib/projects")).default();
const user = building ? writable<undefined>(undefined) : await (await import("$lib/users")).default();
return { projects, user }
const projects = building
? {
...readable<Project[]>([]),
add: () => {
throw new Error("not implemented");
},
}
: await (await import("$lib/projects")).default();
const user = building
? writable<undefined>(undefined)
: await (await import("$lib/users")).default();
return { projects, user };
};

View File

@ -7,7 +7,7 @@
$: lastSessionId = $sessions[$sessions.length - 1]?.id;
</script>
<div class="p-3 flex flex-row space-x-3 text-zinc-500 text-lg select-none">
<div class="h-12 p-3 flex flex-row space-x-3 text-zinc-500 text-lg select-none border-b border-zinc-700">
<div>Week</div>
<a href="/projects/{$project?.id}" class="hover:text-zinc-300">Day</a>
{#if lastSessionId}

View File

@ -1,14 +1,35 @@
<script lang="ts">
import type { PageData } from "./$types";
import { TimelineDay } from "$lib/components/timeline";
import type { Session } from "$lib/sessions";
import { TimelineDaySession } from "$lib/components/timeline";
export let data: PageData;
const { project, sessions } = data;
const sessionDisplayWidth = (session: Session) => {
let sessionDurationHours =
(session.meta.lastTs - session.meta.startTs) / 1000 / 60 / 60;
if (sessionDurationHours <= 1) {
return "w-40";
} else {
return "w-60";
}
};
</script>
<div>
<div class="w-full h-full mx-2 flex">
{#if $project}
<TimelineDay projectId={$project?.id} sessions={$sessions} />
<div class="flex-grow items-center justify-center mt-4">
<div
class="justify-center overflow-auto flex flex-row space-x-2 pt-2"
>
{#each $sessions as session}
<div class={sessionDisplayWidth(session)}>
<TimelineDaySession projectId={$project.id} {session} />
</div>
{/each}
</div>
</div>
{:else}
<p>Project not found</p>
{/if}

View File

@ -56,7 +56,7 @@
</script>
<div class="flex flex-col w-full h-full overflow-hidden space-y-2">
<div class="flex justify-center border-y border-zinc-700 p-2">
<div class="flex justify-center border-b border-zinc-700 p-2">
<SessionNav
project={$project}
session={$session}

View File

@ -20,10 +20,11 @@ export const load: PageLoad = async ({ parent, params }) => {
});
return {
session: derived(sessions, (sessions) => {
const result = sessions.find((session) => session.id === params.sessionId)
const result = sessions.find(
(session) => session.id === params.sessionId
);
return result ? result : sessions[0];
}
),
}),
previousSesssion: derived(sessions, (sessions) => {
const currentSessionIndex = sessions.findIndex(
(session) => session.id === params.sessionId