mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-24 18:12:48 +03:00
commit
928262a09b
@ -2,3 +2,4 @@ PUBLIC_API_BASE_URL=https://test.app.gitbutler.com/
|
||||
PUBLIC_POSTHOG_API_KEY=phc_t7VDC9pQELnYep9IiDTxrq2HLseY5wyT7pn0EpHM7rr
|
||||
PUBLIC_CHAIN_API=https://data-test.gitbutler.com/chain/
|
||||
PUBLIC_SENTRY_ENVIRONMENT=development
|
||||
PUBLIC_CLOUD_BASE_URL=https://cloud.gitbutler.com/
|
||||
|
@ -2,3 +2,4 @@ PUBLIC_API_BASE_URL=https://app.gitbutler.com/
|
||||
PUBLIC_POSTHOG_API_KEY=phc_yJx46mXv6kA5KTuM2eEQ6IwNTgl5YW3feKV5gi7mfGG
|
||||
PUBLIC_CHAIN_API=https://data.gitbutler.com/chain/
|
||||
PUBLIC_SENTRY_ENVIRONMENT=nightly
|
||||
PUBLIC_CLOUD_BASE_URL=https://cloud.gitbutler.com/
|
||||
|
@ -2,3 +2,4 @@ PUBLIC_API_BASE_URL=https://app.gitbutler.com/
|
||||
PUBLIC_POSTHOG_API_KEY=phc_yJx46mXv6kA5KTuM2eEQ6IwNTgl5YW3feKV5gi7mfGG
|
||||
PUBLIC_CHAIN_API=https://data.gitbutler.com/chain/
|
||||
PUBLIC_SENTRY_ENVIRONMENT=production
|
||||
PUBLIC_CLOUD_BASE_URL=https://cloud.gitbutler.com/
|
||||
|
@ -3,3 +3,4 @@ PUBLIC_POSTHOG_API_KEY=
|
||||
PUBLIC_CHAIN_API=https://data-test.gitbutler.com/chain/
|
||||
PUBLIC_SENTRY_ENVIRONMENT=
|
||||
PUBLIC_TESTING=true
|
||||
PUBLIC_CLOUD_BASE_URL=https://cloud.gitbutler.com/
|
||||
|
@ -35,6 +35,11 @@
|
||||
import * as events from '$lib/utils/events';
|
||||
import { unsubscribe } from '$lib/utils/unsubscribe';
|
||||
import { HttpClient } from '@gitbutler/shared/httpClient';
|
||||
import {
|
||||
DesktopRoutesService,
|
||||
setRoutesService,
|
||||
WebRoutesService
|
||||
} from '@gitbutler/shared/sharedRoutes';
|
||||
import { LineManagerFactory } from '@gitbutler/ui/commitLines/lineManager';
|
||||
import { LineManagerFactory as StackingLineManagerFactory } from '@gitbutler/ui/commitLinesStacking/lineManager';
|
||||
import { onMount, setContext, type Snippet } from 'svelte';
|
||||
@ -42,6 +47,7 @@
|
||||
import type { LayoutData } from './$types';
|
||||
import { dev } from '$app/environment';
|
||||
import { goto } from '$app/navigation';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
const { data, children }: { data: LayoutData; children: Snippet } = $props();
|
||||
|
||||
@ -65,6 +71,10 @@
|
||||
setContext(LineManagerFactory, data.lineManagerFactory);
|
||||
setContext(StackingLineManagerFactory, data.stackingLineManagerFactory);
|
||||
|
||||
const webRoutesService = new WebRoutesService(true, env.PUBLIC_CLOUD_BASE_URL);
|
||||
const desktopRoutesService = new DesktopRoutesService(webRoutesService);
|
||||
setRoutesService(desktopRoutesService);
|
||||
|
||||
setNameNormalizationServiceContext(new IpcNameNormalizationService(invoke));
|
||||
|
||||
const user = data.userService.user;
|
||||
|
@ -36,6 +36,7 @@
|
||||
import { UpstreamIntegrationService } from '$lib/vbranches/upstreamIntegrationService';
|
||||
import { VirtualBranchService } from '$lib/vbranches/virtualBranch';
|
||||
import { CloudPatchStacksService } from '@gitbutler/shared/cloud/stacks/service';
|
||||
import { DesktopRoutesService, getRoutesService } from '@gitbutler/shared/sharedRoutes';
|
||||
import { onDestroy, setContext, type Snippet } from 'svelte';
|
||||
import { derived as storeDerived } from 'svelte/store';
|
||||
import type { LayoutData } from './$types';
|
||||
@ -89,6 +90,13 @@
|
||||
setContext(PatchStackCreationService, data.patchStackCreationService);
|
||||
});
|
||||
|
||||
const routesService = getRoutesService();
|
||||
$effect(() => {
|
||||
if (routesService instanceof DesktopRoutesService) {
|
||||
routesService.currentProjectId.set(projectId);
|
||||
}
|
||||
});
|
||||
|
||||
let intervalId: any;
|
||||
|
||||
const octokit = $derived(accessToken ? octokitFromAccessToken(accessToken) : undefined);
|
||||
|
@ -2,4 +2,23 @@
|
||||
import CloudPatchStackIndex from '@gitbutler/shared/cloud/stacks/CloudPatchStackIndex.svelte';
|
||||
</script>
|
||||
|
||||
<div class="series-container">
|
||||
<h2 class="text-head-24 heading">Your patch stacks:</h2>
|
||||
<CloudPatchStackIndex />
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.heading {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.series-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
|
||||
margin: 24px auto;
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import CloudPatchStackShow from '@gitbutler/shared/cloud/stacks/CloudPatchStackShow.svelte';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
const patchStackId = $derived($page.params.patchStackId);
|
||||
</script>
|
||||
|
||||
<div class="patch-stack-container">
|
||||
{#if patchStackId}
|
||||
<CloudPatchStackShow {patchStackId} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.patch-stack-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
|
||||
margin: 24px auto;
|
||||
}
|
||||
</style>
|
@ -28,6 +28,8 @@
|
||||
|
|
||||
<a href="/projects">Projects</a>
|
||||
|
|
||||
<a href="/repositories">Repositories</a>
|
||||
|
|
||||
<a href="/user">User</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -3,7 +3,12 @@
|
||||
import { AuthService } from '$lib/auth/authService';
|
||||
import Navigation from '$lib/components/Navigation.svelte';
|
||||
import { UserService } from '$lib/user/userService';
|
||||
import {
|
||||
CloudRepositoriesService,
|
||||
RepositoriesApiService
|
||||
} from '@gitbutler/shared/cloud/repositories/service';
|
||||
import { HttpClient } from '@gitbutler/shared/httpClient';
|
||||
import { WebRoutesService, setRoutesService } from '@gitbutler/shared/sharedRoutes';
|
||||
import { setContext, type Snippet } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
@ -15,6 +20,9 @@
|
||||
|
||||
const { children }: Props = $props();
|
||||
|
||||
const webRoutesService = new WebRoutesService();
|
||||
setRoutesService(webRoutesService);
|
||||
|
||||
const authService = new AuthService();
|
||||
setContext(AuthService, authService);
|
||||
|
||||
@ -24,11 +32,15 @@
|
||||
const userService = new UserService(httpClient);
|
||||
setContext(UserService, userService);
|
||||
|
||||
const repositoriesApiService = new RepositoriesApiService(httpClient);
|
||||
const cloudRepositoriesService = new CloudRepositoriesService(repositoriesApiService);
|
||||
setContext(CloudRepositoriesService, cloudRepositoriesService);
|
||||
|
||||
$effect(() => {
|
||||
if ($page.url.searchParams.get('gb_access_token')) {
|
||||
const token = $page.url.searchParams.get('gb_access_token');
|
||||
if (token && token.length > 0) {
|
||||
$page.data.authService.setToken(token);
|
||||
authService.setToken(token);
|
||||
|
||||
$page.url.searchParams.delete('gb_access_token');
|
||||
goto(`?${$page.url.searchParams.toString()}`);
|
||||
|
@ -1 +1,2 @@
|
||||
export const ssr = false;
|
||||
export const csr = true;
|
||||
|
53
apps/web/src/routes/repositories/+page.svelte
Normal file
53
apps/web/src/routes/repositories/+page.svelte
Normal file
@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { CloudRepositoriesService } from '@gitbutler/shared/cloud/repositories/service';
|
||||
import { getContext } from '@gitbutler/shared/context';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
const cloudRepositoriesService = getContext(CloudRepositoriesService);
|
||||
const repositories = $derived(cloudRepositoriesService.repositories);
|
||||
|
||||
$inspect($repositories);
|
||||
</script>
|
||||
|
||||
<h2>Your projects:</h2>
|
||||
|
||||
{#if !$repositories}
|
||||
<p>Loading...</p>
|
||||
{:else if $repositories.length === 0}
|
||||
<p>
|
||||
You've not got any projects added yet. Enable project syncing in GitButler in order to see
|
||||
projects.
|
||||
</p>
|
||||
{:else}
|
||||
<div class="card">
|
||||
{#each $repositories as repository}
|
||||
<div class="line-item">
|
||||
<div>
|
||||
<h5 class="text-head-22">{repository.name}</h5>
|
||||
<p>{repository.name}</p>
|
||||
</div>
|
||||
<Button
|
||||
style="pop"
|
||||
kind="solid"
|
||||
onclick={() => {
|
||||
goto(`/repositories/${repository.repositoryId}`);
|
||||
}}>Visit</Button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.line-item {
|
||||
padding: 8px;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid var(--clr-border-1);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
CloudPatchStacksService,
|
||||
PatchStacksApiService
|
||||
} from '@gitbutler/shared/cloud/stacks/service';
|
||||
import { getContext } from '@gitbutler/shared/context';
|
||||
import { HttpClient } from '@gitbutler/shared/httpClient';
|
||||
import { setContext, type Snippet } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
const { children }: { children: Snippet } = $props();
|
||||
|
||||
const repositoryId = writable<string | undefined>();
|
||||
$effect(() => repositoryId.set($page.params.repositoryId));
|
||||
|
||||
const httpClient = getContext(HttpClient);
|
||||
|
||||
const patchStacksApiService = new PatchStacksApiService(httpClient);
|
||||
const cloudPatchStacksService = new CloudPatchStacksService(repositoryId, patchStacksApiService);
|
||||
setContext(CloudPatchStacksService, cloudPatchStacksService);
|
||||
</script>
|
||||
|
||||
{@render children()}
|
@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import CloudPatchStackIndex from '@gitbutler/shared/cloud/stacks/CloudPatchStackIndex.svelte';
|
||||
</script>
|
||||
|
||||
<h2>Your patch stacks:</h2>
|
||||
|
||||
<CloudPatchStackIndex />
|
@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import CloudPatchStackShow from '@gitbutler/shared/cloud/stacks/CloudPatchStackShow.svelte';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
const patchStackId = $derived($page.params.patchStackId);
|
||||
</script>
|
||||
|
||||
{#if patchStackId}
|
||||
<CloudPatchStackShow {patchStackId} />
|
||||
{/if}
|
@ -62,7 +62,8 @@
|
||||
"svelte": "catalog:svelte",
|
||||
"svelte-check": "catalog:svelte",
|
||||
"vite": "catalog:",
|
||||
"vitest": "^2.0.5"
|
||||
"vitest": "^2.0.5",
|
||||
"moment": "^2.30.1"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
105
packages/shared/src/lib/cloud/repositories/service.ts
Normal file
105
packages/shared/src/lib/cloud/repositories/service.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { writableDerived } from '$lib/storeUtils';
|
||||
import { derived, get, type Readable, type Writable } from 'svelte/store';
|
||||
import type { HttpClient } from '$lib/httpClient';
|
||||
|
||||
interface ApiRepository {
|
||||
name: string;
|
||||
description: string | null;
|
||||
repository_id: string;
|
||||
git_url: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export class CloudRepository {
|
||||
readonly name: string;
|
||||
readonly description: string | null;
|
||||
readonly repositoryId: string;
|
||||
readonly gitUrl: string;
|
||||
readonly createdAt: Date;
|
||||
readonly updatedAt: Date;
|
||||
|
||||
constructor(apiRepository: ApiRepository) {
|
||||
this.name = apiRepository.name;
|
||||
this.description = apiRepository.description;
|
||||
this.repositoryId = apiRepository.repository_id;
|
||||
this.gitUrl = apiRepository.git_url;
|
||||
this.createdAt = new Date(apiRepository.created_at);
|
||||
this.updatedAt = new Date(apiRepository.updated_at);
|
||||
}
|
||||
}
|
||||
|
||||
export class RepositoriesApiService {
|
||||
readonly canGetRepositories: Readable<boolean>;
|
||||
|
||||
constructor(private readonly httpClient: HttpClient) {
|
||||
this.canGetRepositories = httpClient.authenticationAvailable;
|
||||
}
|
||||
|
||||
async getRepositories(): Promise<ApiRepository[] | undefined> {
|
||||
try {
|
||||
return await this.httpClient.get<ApiRepository[]>('projects');
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError) {
|
||||
return undefined;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MINUTES_15 = 15 * 60 * 1000;
|
||||
|
||||
export class CloudRepositoriesService {
|
||||
readonly #apiRepositories: Writable<ApiRepository[] | undefined>;
|
||||
|
||||
readonly repositories: Readable<CloudRepository[] | undefined>;
|
||||
|
||||
constructor(private readonly repositoriesApiService: RepositoriesApiService) {
|
||||
this.#apiRepositories = writableDerived(
|
||||
repositoriesApiService.canGetRepositories,
|
||||
undefined as ApiRepository[] | undefined,
|
||||
(canGetRepositories, set) => {
|
||||
if (!canGetRepositories) {
|
||||
set(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
let canceled = false;
|
||||
|
||||
const callback = (() => {
|
||||
this.repositoriesApiService.getRepositories().then((apiRepositories) => {
|
||||
if (!canceled) {
|
||||
set(apiRepositories);
|
||||
}
|
||||
});
|
||||
}).bind(this);
|
||||
|
||||
callback();
|
||||
const timeout = setInterval(callback, MINUTES_15);
|
||||
|
||||
return () => {
|
||||
canceled = true;
|
||||
clearInterval(timeout);
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
this.repositories = derived(this.#apiRepositories, (apiRepositories) => {
|
||||
return apiRepositories?.map((apiRepository) => new CloudRepository(apiRepository));
|
||||
});
|
||||
}
|
||||
|
||||
/** Refresh the list of patch stacks */
|
||||
async refresh(): Promise<void> {
|
||||
const canGetRepositories = get(this.repositoriesApiService.canGetRepositories);
|
||||
|
||||
if (canGetRepositories) {
|
||||
const repositories = await this.repositoriesApiService.getRepositories();
|
||||
this.#apiRepositories.set(repositories);
|
||||
} else {
|
||||
this.#apiRepositories.set(undefined);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,57 @@
|
||||
<script lang="ts">
|
||||
import { CloudPatchStacksService } from '$lib/cloud/stacks/service';
|
||||
import { getContext } from '$lib/context';
|
||||
import { getRoutesService } from '$lib/sharedRoutes';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import moment from 'moment';
|
||||
import { get } from 'svelte/store';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
/**
|
||||
* Expects the following contexts:
|
||||
* - PatchStacksService
|
||||
* - CloudPatchStacksService
|
||||
* - RoutesService
|
||||
*/
|
||||
|
||||
const patchStacksService = getContext(CloudPatchStacksService);
|
||||
const routesService = getRoutesService();
|
||||
const patchStacks = $derived(patchStacksService.patchStacks);
|
||||
</script>
|
||||
|
||||
{#if $patchStacks}
|
||||
<div class="card">
|
||||
{#each $patchStacks as patchStack}
|
||||
<div>{patchStack.title}</div>
|
||||
<div class="line-item">
|
||||
<div>
|
||||
<p class="text-15 text-bold">{patchStack.title}</p>
|
||||
<p>Version: v{patchStack.version}</p>
|
||||
<p>Status: {patchStack.status}</p>
|
||||
<p>Created: {moment(patchStack.createdAt).fromNow()}</p>
|
||||
</div>
|
||||
<Button
|
||||
style="pop"
|
||||
kind="solid"
|
||||
onclick={() => {
|
||||
const repositoryId = get(patchStacksService.repositoryId);
|
||||
if (repositoryId) {
|
||||
goto(routesService.patchStack(repositoryId, patchStack.uuid));
|
||||
}
|
||||
}}>Visit</Button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.line-item {
|
||||
padding: 8px;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid var(--clr-border-1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import { CloudPatchStacksService } from '$lib/cloud/stacks/service';
|
||||
import { getContext } from '$lib/context';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import moment from 'moment';
|
||||
|
||||
interface Props {
|
||||
patchStackId: string;
|
||||
}
|
||||
|
||||
const { patchStackId }: Props = $props();
|
||||
|
||||
const cloudPatchStacksService = getContext(CloudPatchStacksService);
|
||||
const optionalPatchStack = $derived(cloudPatchStacksService.patchStackForId(patchStackId));
|
||||
</script>
|
||||
|
||||
{#if $optionalPatchStack.state === 'uninitialized'}
|
||||
<p>Loading...</p>
|
||||
{:else if $optionalPatchStack.state === 'not-found'}
|
||||
<p>Error: Stack not found</p>
|
||||
{:else if $optionalPatchStack.state === 'found'}
|
||||
{@const patchStack = $optionalPatchStack.value}
|
||||
|
||||
<h1 class="text-head-24 padding-bottom">{patchStack.title}</h1>
|
||||
<div class="two-by-two padding-bottom">
|
||||
<div class="card">
|
||||
<div class="card__content">
|
||||
<p>Version: {patchStack.version}</p>
|
||||
<p>Status: {patchStack.status}</p>
|
||||
<p>Created at: {moment(patchStack.createdAt).fromNow()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<p class="card__header text-15 text-bold">Contributors:</p>
|
||||
<div class="card__content">
|
||||
<ul>
|
||||
{#each patchStack.contributors as contributor}
|
||||
<li>{contributor}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="text-head-20 padding-bottom">Patches: ({patchStack.patches.length})</h2>
|
||||
|
||||
<div class="card">
|
||||
{#each patchStack.patches as patch}
|
||||
<div class="line-item">
|
||||
<div>
|
||||
<p class="text-15 text-bold">{patch.title || 'Unnamed'}</p>
|
||||
<p>Commit: {patch.commitSha.slice(0, 7)} - Change: {patch.changeId.slice(0, 7)}</p>
|
||||
<p>Version: {patch.version}</p>
|
||||
</div>
|
||||
<Button style="pop" kind="solid">Visit</Button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
.padding-bottom {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.two-by-two {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.line-item {
|
||||
padding: 8px;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid var(--clr-border-1);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -55,7 +55,7 @@ interface ApiPatch {
|
||||
contributors: string[];
|
||||
statistics: ApiPatchStatstics;
|
||||
review: ApiPatchReview;
|
||||
review_all: ApiPatchReview[];
|
||||
review_all: ApiPatchReview;
|
||||
}
|
||||
|
||||
export class CloudPatch {
|
||||
@ -68,7 +68,7 @@ export class CloudPatch {
|
||||
contributors: string[];
|
||||
statistics: CloudPatchStatsitics;
|
||||
review: CloudPatchReview;
|
||||
reviewAll: CloudPatchReview[];
|
||||
reviewAll: CloudPatchReview;
|
||||
|
||||
constructor(apiPatch: ApiPatch) {
|
||||
this.changeId = apiPatch.change_id;
|
||||
@ -80,7 +80,7 @@ export class CloudPatch {
|
||||
this.contributors = apiPatch.contributors;
|
||||
this.statistics = new CloudPatchStatsitics(apiPatch.statistics);
|
||||
this.review = new CloudPatchReview(apiPatch.review);
|
||||
this.reviewAll = apiPatch.review_all.map((review) => new CloudPatchReview(review));
|
||||
this.reviewAll = new CloudPatchReview(apiPatch.review_all);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ export class CloudPatchStack {
|
||||
contributors: string[];
|
||||
// TODO(CTO): Determine the best way to talk about these nested objects.
|
||||
// Should they be in their own reactive service?
|
||||
// patches: Patch[];
|
||||
patches: CloudPatch[];
|
||||
|
||||
constructor(apiPatchStack: ApiPatchStack) {
|
||||
this.branchId = apiPatchStack.branch_id;
|
||||
@ -132,7 +132,7 @@ export class CloudPatchStack {
|
||||
this.createdAt = apiPatchStack.created_at;
|
||||
this.stackSize = apiPatchStack.stack_size || 0;
|
||||
this.contributors = apiPatchStack.contributors;
|
||||
// this.patches = apiPatchStack.patches?.map((patch) => new Patch(patch));
|
||||
this.patches = apiPatchStack.patches?.map((patch) => new CloudPatch(patch));
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,7 +224,7 @@ export class CloudPatchStacksService {
|
||||
readonly patchStacks: Readable<CloudPatchStack[] | undefined>;
|
||||
|
||||
constructor(
|
||||
private readonly repositoryId: Readable<string | undefined>,
|
||||
readonly repositoryId: Readable<string | undefined>,
|
||||
private readonly patchStacksApiService: PatchStacksApiService
|
||||
) {
|
||||
const values = derived(
|
||||
@ -303,9 +303,9 @@ export class CloudPatchStacksService {
|
||||
}
|
||||
}
|
||||
|
||||
#patchStacksForBranchIds = new Map<string, Readable<LoadableOptional<CloudPatchStack>>>();
|
||||
#patchStacksByBranchIds = new Map<string, Readable<LoadableOptional<CloudPatchStack>>>();
|
||||
patchStackForBranchId(branchId: string): Readable<LoadableOptional<CloudPatchStack>> {
|
||||
let store = this.#patchStacksForBranchIds.get(branchId);
|
||||
let store = this.#patchStacksByBranchIds.get(branchId);
|
||||
if (store) return store;
|
||||
|
||||
store = derived(this.patchStacks, (patchStacks): LoadableOptional<CloudPatchStack> => {
|
||||
@ -317,7 +317,25 @@ export class CloudPatchStacksService {
|
||||
return { state: 'not-found' };
|
||||
}
|
||||
});
|
||||
this.#patchStacksForBranchIds.set(branchId, store);
|
||||
this.#patchStacksByBranchIds.set(branchId, store);
|
||||
return store;
|
||||
}
|
||||
|
||||
#patchStacksByIds = new Map<string, Readable<LoadableOptional<CloudPatchStack>>>();
|
||||
patchStackForId(patchStackId: string): Readable<LoadableOptional<CloudPatchStack>> {
|
||||
let store = this.#patchStacksByIds.get(patchStackId);
|
||||
if (store) return store;
|
||||
|
||||
store = derived(this.patchStacks, (patchStacks): LoadableOptional<CloudPatchStack> => {
|
||||
if (!patchStacks) return { state: 'uninitialized' };
|
||||
const patchStack = patchStacks.find((patchStack) => patchStack.uuid === patchStackId);
|
||||
if (patchStack) {
|
||||
return { state: 'found', value: patchStack };
|
||||
} else {
|
||||
return { state: 'not-found' };
|
||||
}
|
||||
});
|
||||
this.#patchStacksByIds.set(patchStackId, store);
|
||||
return store;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
// Reexport your entry components here
|
||||
export function foo() {
|
||||
return 'foo';
|
||||
}
|
63
packages/shared/src/lib/sharedRoutes.ts
Normal file
63
packages/shared/src/lib/sharedRoutes.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { buildContext } from '$lib/context';
|
||||
import { get, writable, type Writable } from 'svelte/store';
|
||||
|
||||
export interface RoutesService {
|
||||
repositories(): string;
|
||||
repository(repositoryId: string): string;
|
||||
patchStack(repositoryId: string, patchStackId: string): string;
|
||||
}
|
||||
|
||||
export class WebRoutesService implements RoutesService {
|
||||
constructor(
|
||||
private readonly externalizePaths: boolean = false,
|
||||
private readonly webBaseUrl?: string
|
||||
) {}
|
||||
|
||||
repositories() {
|
||||
return this.externalizePath('/repositories');
|
||||
}
|
||||
|
||||
repository(repositoryId: string): string {
|
||||
return this.externalizePath(`/repositories/${repositoryId}`);
|
||||
}
|
||||
|
||||
patchStack(repositoryId: string, patchStackId: string): string {
|
||||
return this.externalizePath(`/repositories/${repositoryId}/patchStacks/${patchStackId}`);
|
||||
}
|
||||
|
||||
private externalizePath(path: string) {
|
||||
if (this.externalizePaths) {
|
||||
return new URL(path, this.webBaseUrl).href;
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DesktopRoutesService implements RoutesService {
|
||||
currentProjectId: Writable<string | undefined> = writable<string | undefined>();
|
||||
|
||||
constructor(private readonly webRoutesService: WebRoutesService) {}
|
||||
|
||||
repositories() {
|
||||
return this.webRoutesService.repositories();
|
||||
}
|
||||
|
||||
repository(repositoryId: string): string {
|
||||
const projectId = get(this.currentProjectId);
|
||||
if (projectId) {
|
||||
return `/${projectId}/series`;
|
||||
}
|
||||
return this.webRoutesService.repository(repositoryId);
|
||||
}
|
||||
|
||||
patchStack(repositoryId: string, patchStackId: string): string {
|
||||
const projectId = get(this.currentProjectId);
|
||||
if (projectId) {
|
||||
return `/${projectId}/series/patchStacks/${patchStackId}`;
|
||||
}
|
||||
return this.webRoutesService.patchStack(repositoryId, patchStackId);
|
||||
}
|
||||
}
|
||||
|
||||
export const [getRoutesService, setRoutesService] = buildContext<RoutesService>('routes-service');
|
@ -4,7 +4,7 @@
|
||||
* application code.
|
||||
*/
|
||||
|
||||
import { writable, type Readable } from 'svelte/store';
|
||||
import { writable, type Readable, type Writable } from 'svelte/store';
|
||||
|
||||
export function combineLatest<T>(stores: Readable<T>[], initialValue: T): Readable<T> {
|
||||
const store = writable(initialValue, (set) => {
|
||||
@ -39,7 +39,7 @@ export function writableDerived<A, B>(
|
||||
store: Readable<B>,
|
||||
initialValue: A,
|
||||
startStopNotifier: (derivedValue: B, set: (value: A) => void) => (() => void) | undefined
|
||||
) {
|
||||
): Writable<A> {
|
||||
const derivedStore = writable(initialValue, (set) => {
|
||||
let startStopUnsubscribe: (() => void) | undefined = undefined;
|
||||
const unsubscribeStore = store.subscribe((value) => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"target": "ES2022",
|
||||
"lib": ["dom", "dom.iterable", "ES2021"],
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
@ -11,7 +11,8 @@
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"experimentalDecorators": true
|
||||
"experimentalDecorators": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": [
|
||||
".svelte-kit/ambient.d.ts",
|
||||
|
@ -383,6 +383,9 @@ importers:
|
||||
date-fns:
|
||||
specifier: ^2.30.0
|
||||
version: 2.30.0
|
||||
moment:
|
||||
specifier: ^2.30.1
|
||||
version: 2.30.1
|
||||
playwright:
|
||||
specifier: ^1.46.1
|
||||
version: 1.46.1
|
||||
|
Loading…
Reference in New Issue
Block a user