mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-19 23:52:05 +03:00
Merge pull request #3261 from gitbutlerapp/Convert-cloud-api-object-to-class
Convert cloud api object to class
This commit is contained in:
commit
41aecc8121
@ -1,10 +1,10 @@
|
||||
import { MessageRole, type AIClient, type PromptMessage } from '$lib/backend/aiClient';
|
||||
import type { ModelKind } from '$lib/backend/aiService';
|
||||
import type { getCloudApiClient } from '$lib/backend/cloud';
|
||||
import type { CloudClient } from '$lib/backend/cloud';
|
||||
|
||||
export class ButlerAIClient implements AIClient {
|
||||
constructor(
|
||||
private cloud: ReturnType<typeof getCloudApiClient>,
|
||||
private cloud: CloudClient,
|
||||
private userToken: string,
|
||||
private modelKind: ModelKind
|
||||
) {}
|
||||
@ -12,7 +12,7 @@ export class ButlerAIClient implements AIClient {
|
||||
async evaluate(prompt: string) {
|
||||
const messages: PromptMessage[] = [{ role: MessageRole.User, content: prompt }];
|
||||
|
||||
const response = await this.cloud.ai.evaluatePrompt(this.userToken, {
|
||||
const response = await this.cloud.evaluateAIPrompt(this.userToken, {
|
||||
messages,
|
||||
max_tokens: 400,
|
||||
model_kind: this.modelKind
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { CloudClient } from './cloud';
|
||||
import { AnthropicAIClient } from '$lib/backend/aiClients/anthropic';
|
||||
import { ButlerAIClient } from '$lib/backend/aiClients/butler';
|
||||
import { OpenAIClient } from '$lib/backend/aiClients/openAI';
|
||||
@ -9,7 +10,6 @@ import {
|
||||
ModelKind,
|
||||
OpenAIModelName
|
||||
} from '$lib/backend/aiService';
|
||||
import { getCloudApiClient } from '$lib/backend/cloud';
|
||||
import * as toasts from '$lib/utils/toasts';
|
||||
import { expect, test, describe, vi } from 'vitest';
|
||||
import type { AIClient } from '$lib/backend/aiClient';
|
||||
@ -42,7 +42,7 @@ class DummyGitConfigService implements GitConfigService {
|
||||
}
|
||||
|
||||
const fetchMock = vi.fn();
|
||||
const cloud = getCloudApiClient({ fetch: fetchMock });
|
||||
const cloud = new CloudClient(fetchMock);
|
||||
|
||||
class DummyAIClient implements AIClient {
|
||||
constructor(private response = 'lorem ipsum') {}
|
||||
|
@ -5,7 +5,7 @@ import { splitMessage } from '$lib/utils/commitMessage';
|
||||
import * as toasts from '$lib/utils/toasts';
|
||||
import OpenAI from 'openai';
|
||||
import type { AIClient } from '$lib/backend/aiClient';
|
||||
import type { getCloudApiClient } from '$lib/backend/cloud';
|
||||
import type { CloudClient } from '$lib/backend/cloud';
|
||||
import type { GitConfigService } from '$lib/backend/gitConfigService';
|
||||
|
||||
const diffLengthLimit = 20000;
|
||||
@ -87,7 +87,7 @@ type SummarizeBranchOpts = {
|
||||
export class AIService {
|
||||
constructor(
|
||||
private gitConfig: GitConfigService,
|
||||
private cloud: ReturnType<typeof getCloudApiClient>
|
||||
private cloud: CloudClient
|
||||
) {}
|
||||
|
||||
async validateConfiguration(userToken?: string): Promise<boolean> {
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { isLoading, invoke } from './ipc';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { invoke } from './ipc';
|
||||
import type { PromptMessage } from '$lib/backend/aiClient';
|
||||
import type { ModelKind } from '$lib/backend/aiService';
|
||||
import { PUBLIC_API_BASE_URL, PUBLIC_CHAIN_API } from '$env/static/public';
|
||||
import { PUBLIC_API_BASE_URL } from '$env/static/public';
|
||||
|
||||
const apiUrl = new URL('/api/', new URL(PUBLIC_API_BASE_URL));
|
||||
|
||||
@ -10,12 +9,6 @@ function getUrl(path: string) {
|
||||
return new URL(path, apiUrl).toString();
|
||||
}
|
||||
|
||||
const chainApiUrl = new URL(PUBLIC_CHAIN_API);
|
||||
|
||||
function getChainUrl(path: string) {
|
||||
return new URL(path, chainApiUrl).toString();
|
||||
}
|
||||
|
||||
export type Feedback = {
|
||||
id: number;
|
||||
user_id: number;
|
||||
@ -67,40 +60,6 @@ async function parseResponseJSON(response: Response) {
|
||||
}
|
||||
}
|
||||
|
||||
type FetchMiddleware = (f: typeof fetch) => typeof fetch;
|
||||
|
||||
function fetchWith(fetch: typeof window.fetch, ...middlewares: FetchMiddleware[]) {
|
||||
return middlewares.reduce((f, middleware) => middleware(f), fetch);
|
||||
}
|
||||
|
||||
function withRequestId(fetch: any) {
|
||||
return async (url: RequestInfo | URL, options: any) => {
|
||||
const requestId = nanoid();
|
||||
if (!options) options = {};
|
||||
options.headers = {
|
||||
...options?.headers,
|
||||
'X-Request-Id': requestId
|
||||
};
|
||||
const result = fetch(url, options);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
function withLog(fetch: any) {
|
||||
return async (url: RequestInfo | URL, options: any) => {
|
||||
const item = { name: url.toString(), startedAt: new Date() };
|
||||
try {
|
||||
isLoading.push(item);
|
||||
const resp = await fetch(url, options);
|
||||
return resp;
|
||||
} catch (e: any) {
|
||||
console.error('fetch', e);
|
||||
throw e;
|
||||
} finally {
|
||||
isLoading.pop(item);
|
||||
}
|
||||
};
|
||||
}
|
||||
interface EvaluatePromptParams {
|
||||
messages: PromptMessage[];
|
||||
temperature?: number;
|
||||
@ -108,188 +67,175 @@ interface EvaluatePromptParams {
|
||||
model_kind?: ModelKind;
|
||||
}
|
||||
|
||||
export function getCloudApiClient(
|
||||
{ fetch: realFetch }: { fetch: typeof window.fetch } = {
|
||||
fetch: window.fetch
|
||||
export class CloudClient {
|
||||
fetch: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>) &
|
||||
((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>);
|
||||
|
||||
constructor(realFetch: typeof window.fetch = window.fetch) {
|
||||
this.fetch = realFetch;
|
||||
}
|
||||
) {
|
||||
const fetch = fetchWith(realFetch, withRequestId, withLog);
|
||||
return {
|
||||
login: {
|
||||
token: {
|
||||
create: (): Promise<LoginToken> =>
|
||||
fetch(getUrl('login/token.json'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({})
|
||||
})
|
||||
.then(parseResponseJSON)
|
||||
.then((token) => {
|
||||
const url = new URL(token.url);
|
||||
url.host = apiUrl.host;
|
||||
return {
|
||||
...token,
|
||||
url: url.toString()
|
||||
};
|
||||
})
|
||||
|
||||
async createLoginToken(): Promise<LoginToken> {
|
||||
const response = await this.fetch(getUrl('login/token.json'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
user: {
|
||||
get: (token: string): Promise<User> =>
|
||||
fetch(getUrl(`login/user/${token}.json`), {
|
||||
method: 'GET'
|
||||
}).then(parseResponseJSON)
|
||||
}
|
||||
},
|
||||
feedback: {
|
||||
create: async (
|
||||
token: string | undefined,
|
||||
params: {
|
||||
email?: string;
|
||||
message: string;
|
||||
context?: string;
|
||||
logs?: Blob | File;
|
||||
data?: Blob | File;
|
||||
repo?: Blob | File;
|
||||
}
|
||||
): Promise<Feedback> => {
|
||||
const formData = new FormData();
|
||||
formData.append('message', params.message);
|
||||
if (params.email) formData.append('email', params.email);
|
||||
if (params.context) formData.append('context', params.context);
|
||||
if (params.logs) formData.append('logs', params.logs);
|
||||
if (params.repo) formData.append('repo', params.repo);
|
||||
if (params.data) formData.append('data', params.data);
|
||||
const headers: HeadersInit = token ? { 'X-Auth-Token': token } : {};
|
||||
return fetch(getUrl(`feedback`), {
|
||||
method: 'PUT',
|
||||
headers,
|
||||
body: formData
|
||||
}).then(parseResponseJSON);
|
||||
}
|
||||
},
|
||||
user: {
|
||||
get: (token: string): Promise<User> =>
|
||||
fetch(getUrl(`user.json`), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
}).then(parseResponseJSON),
|
||||
update: async (token: string, params: { name?: string; picture?: File }) => {
|
||||
const formData = new FormData();
|
||||
if (params.name) {
|
||||
formData.append('name', params.name);
|
||||
}
|
||||
if (params.picture) {
|
||||
formData.append('avatar', params.picture);
|
||||
}
|
||||
return fetch(getUrl(`user.json`), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: formData
|
||||
}).then(parseResponseJSON);
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
evaluatePrompt: (token: string, params: EvaluatePromptParams): Promise<{ message: string }> =>
|
||||
fetch(getUrl('evaluate_prompt/predict.json'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
}).then(parseResponseJSON)
|
||||
},
|
||||
chat: {
|
||||
new: (token: string, repositoryId: string): Promise<{ id: string }> =>
|
||||
fetch(getChainUrl(`${repositoryId}/chat`), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
}).then(parseResponseJSON),
|
||||
history: (
|
||||
token: string,
|
||||
repositoryId: string,
|
||||
chatId: string
|
||||
): Promise<{ history: []; sequence: number }> =>
|
||||
fetch(getChainUrl(`${repositoryId}/chat/${chatId}`), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
}).then(parseResponseJSON),
|
||||
newMessage: (
|
||||
token: string,
|
||||
repositoryId: string,
|
||||
chatId: string,
|
||||
message: string
|
||||
): Promise<{ sequence: number }> =>
|
||||
fetch(getChainUrl(`${repositoryId}/chat/${chatId}`), {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: JSON.stringify({ text: message })
|
||||
}).then(parseResponseJSON)
|
||||
},
|
||||
projects: {
|
||||
create: (
|
||||
token: string,
|
||||
params: { name: string; description?: string; uid?: string }
|
||||
): Promise<Project> =>
|
||||
fetch(getUrl('projects.json'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
}).then(parseResponseJSON),
|
||||
update: (
|
||||
token: string,
|
||||
repositoryId: string,
|
||||
params: { name: string; description?: string }
|
||||
): Promise<Project> =>
|
||||
fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
}).then(parseResponseJSON),
|
||||
list: (token: string): Promise<Project[]> =>
|
||||
fetch(getUrl('projects.json'), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
}).then(parseResponseJSON),
|
||||
get: (token: string, repositoryId: string): Promise<Project> =>
|
||||
fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
}).then(parseResponseJSON),
|
||||
delete: (token: string, repositoryId: string): Promise<void> =>
|
||||
fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
}).then(parseResponseJSON)
|
||||
body: JSON.stringify({})
|
||||
});
|
||||
const token = await parseResponseJSON(response);
|
||||
const url = new URL(token.url);
|
||||
url.host = apiUrl.host;
|
||||
return {
|
||||
...token,
|
||||
url: url.toString()
|
||||
};
|
||||
}
|
||||
|
||||
async getLoginUser(token: string): Promise<User> {
|
||||
const response = await this.fetch(getUrl(`login/user/${token}.json`), {
|
||||
method: 'GET'
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async createFeedback(
|
||||
token: string | undefined,
|
||||
params: {
|
||||
email?: string;
|
||||
message: string;
|
||||
context?: string;
|
||||
logs?: Blob | File;
|
||||
data?: Blob | File;
|
||||
repo?: Blob | File;
|
||||
}
|
||||
};
|
||||
): Promise<Feedback> {
|
||||
const formData = new FormData();
|
||||
formData.append('message', params.message);
|
||||
if (params.email) formData.append('email', params.email);
|
||||
if (params.context) formData.append('context', params.context);
|
||||
if (params.logs) formData.append('logs', params.logs);
|
||||
if (params.repo) formData.append('repo', params.repo);
|
||||
if (params.data) formData.append('data', params.data);
|
||||
const headers: HeadersInit = token ? { 'X-Auth-Token': token } : {};
|
||||
const response = await this.fetch(getUrl(`feedback`), {
|
||||
method: 'PUT',
|
||||
headers,
|
||||
body: formData
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async getUser(token: string): Promise<User> {
|
||||
const response = await this.fetch(getUrl(`user.json`), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async updateUser(token: string, params: { name?: string; picture?: File }): Promise<any> {
|
||||
const formData = new FormData();
|
||||
if (params.name) {
|
||||
formData.append('name', params.name);
|
||||
}
|
||||
if (params.picture) {
|
||||
formData.append('avatar', params.picture);
|
||||
}
|
||||
const response = await this.fetch(getUrl(`user.json`), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async evaluateAIPrompt(
|
||||
token: string,
|
||||
params: EvaluatePromptParams
|
||||
): Promise<{ message: string }> {
|
||||
const response = await this.fetch(getUrl('evaluate_prompt/predict.json'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async createProject(
|
||||
token: string,
|
||||
params: {
|
||||
name: string;
|
||||
description?: string;
|
||||
uid?: string;
|
||||
}
|
||||
): Promise<Project> {
|
||||
const response = await this.fetch(getUrl('projects.json'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async updateProject(
|
||||
token: string,
|
||||
repositoryId: string,
|
||||
params: {
|
||||
name: string;
|
||||
description?: string;
|
||||
}
|
||||
): Promise<Project> {
|
||||
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': token
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async listProjects(token: string): Promise<Project[]> {
|
||||
const response = await this.fetch(getUrl('projects.json'), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async getProject(token: string, repositoryId: string): Promise<Project> {
|
||||
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
|
||||
async deleteProject(token: string, repositoryId: string): Promise<void> {
|
||||
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-Auth-Token': token
|
||||
}
|
||||
});
|
||||
return parseResponseJSON(response);
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncToCloud(projectId: string | undefined) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import SectionCard from './SectionCard.svelte';
|
||||
import WelcomeSigninAction from './WelcomeSigninAction.svelte';
|
||||
import { getCloudApiClient } from '$lib/backend/cloud';
|
||||
import { CloudClient } from '$lib/backend/cloud';
|
||||
import Link from '$lib/components/Link.svelte';
|
||||
import Spacer from '$lib/components/Spacer.svelte';
|
||||
import Toggle from '$lib/components/Toggle.svelte';
|
||||
@ -17,9 +17,9 @@
|
||||
export let project: Project;
|
||||
|
||||
const userService = getContextByClass(UserService);
|
||||
const cloud = getContextByClass(CloudClient);
|
||||
const user = userService.user;
|
||||
|
||||
const cloud = getCloudApiClient();
|
||||
const aiGenEnabled = projectAiGenEnabled(project.id);
|
||||
const aiGenAutoBranchNamingEnabled = projectAiGenAutoBranchNamingEnabled(project.id);
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
onMount(async () => {
|
||||
if (!project?.api) return;
|
||||
if (!$user) return;
|
||||
const cloudProject = await cloud.projects.get($user.access_token, project.api.repository_id);
|
||||
const cloudProject = await cloud.getProject($user.access_token, project.api.repository_id);
|
||||
if (cloudProject === project.api) return;
|
||||
dispatch('updated', { ...project, api: { ...cloudProject, sync: project.api.sync } });
|
||||
});
|
||||
@ -40,7 +40,7 @@
|
||||
try {
|
||||
const cloudProject =
|
||||
project.api ??
|
||||
(await cloud.projects.create($user.access_token, {
|
||||
(await cloud.createProject($user.access_token, {
|
||||
name: project.title,
|
||||
description: project.description,
|
||||
uid: project.id
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Button from './Button.svelte';
|
||||
import { getCloudApiClient, type LoginToken } from '$lib/backend/cloud';
|
||||
import { CloudClient, type LoginToken } from '$lib/backend/cloud';
|
||||
import { UserService } from '$lib/stores/user';
|
||||
import { getContextByClass } from '$lib/utils/context';
|
||||
import * as toasts from '$lib/utils/toasts';
|
||||
@ -8,7 +8,7 @@
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { derived, writable } from 'svelte/store';
|
||||
|
||||
const cloud = getCloudApiClient();
|
||||
const cloud = getContextByClass(CloudClient);
|
||||
const userService = getContextByClass(UserService);
|
||||
const user = userService.user;
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
let signUpOrLoginLoading = false;
|
||||
|
||||
async function pollForUser(token: string) {
|
||||
const apiUser = await cloud.login.user.get(token).catch(() => null);
|
||||
const apiUser = await cloud.getLoginUser(token).catch(() => null);
|
||||
if (apiUser) {
|
||||
userService.setUser(apiUser);
|
||||
return;
|
||||
@ -36,7 +36,7 @@
|
||||
async function onSignUpOrLoginClick() {
|
||||
signUpOrLoginLoading = true;
|
||||
try {
|
||||
token.set(await cloud.login.token.create());
|
||||
token.set(await cloud.createLoginToken());
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
toasts.error('Could not create login token');
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import TextArea from './TextArea.svelte';
|
||||
import { CloudClient } from '$lib/backend/cloud';
|
||||
import { invoke } from '$lib/backend/ipc';
|
||||
import * as zip from '$lib/backend/zip';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
@ -9,11 +10,9 @@
|
||||
import { getContextByClass } from '$lib/utils/context';
|
||||
import * as toasts from '$lib/utils/toasts';
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
import type { getCloudApiClient } from '$lib/backend/cloud';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
export let cloud: ReturnType<typeof getCloudApiClient>;
|
||||
|
||||
const cloud = getContextByClass(CloudClient);
|
||||
const userService = getContextByClass(UserService);
|
||||
const user = userService.user;
|
||||
|
||||
@ -76,7 +75,7 @@
|
||||
? zip.projectData({ projectId }).then((path) => readZipFile(path, 'project.zip'))
|
||||
: undefined
|
||||
]).then(async ([logs, data, repo]) =>
|
||||
cloud.feedback.create($user?.access_token, {
|
||||
cloud.createFeedback($user?.access_token, {
|
||||
email,
|
||||
message,
|
||||
context,
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { resetPostHog, setPostHogUser } from '$lib/analytics/posthog';
|
||||
import { resetSentry, setSentryUser } from '$lib/analytics/sentry';
|
||||
import { getCloudApiClient, type User } from '$lib/backend/cloud';
|
||||
import { invoke } from '$lib/backend/ipc';
|
||||
import { observableToStore } from '$lib/rxjs/store';
|
||||
import { sleep } from '$lib/utils/sleep';
|
||||
import { openExternalUrl } from '$lib/utils/url';
|
||||
import { BehaviorSubject, Observable, Subject, distinct, map, merge, shareReplay } from 'rxjs';
|
||||
import type { CloudClient, User } from '$lib/backend/cloud';
|
||||
import type { Readable } from 'svelte/motion';
|
||||
|
||||
export class UserService {
|
||||
private readonly cloud = getCloudApiClient();
|
||||
|
||||
readonly reset$ = new Subject<User | undefined>();
|
||||
readonly loading$ = new BehaviorSubject(false);
|
||||
|
||||
@ -35,7 +33,7 @@ export class UserService {
|
||||
user: Readable<User | undefined>;
|
||||
error: Readable<string | undefined>;
|
||||
|
||||
constructor() {
|
||||
constructor(private cloud: CloudClient) {
|
||||
[this.user, this.error] = observableToStore(this.user$);
|
||||
}
|
||||
|
||||
@ -60,7 +58,7 @@ export class UserService {
|
||||
this.logout();
|
||||
this.loading$.next(true);
|
||||
try {
|
||||
const token = await this.cloud.login.token.create();
|
||||
const token = await this.cloud.createLoginToken();
|
||||
openExternalUrl(token.url);
|
||||
|
||||
// Assumed min time for login flow
|
||||
@ -78,7 +76,7 @@ export class UserService {
|
||||
private async pollForUser(token: string): Promise<User | undefined> {
|
||||
let apiUser: User | null;
|
||||
for (let i = 0; i < 120; i++) {
|
||||
apiUser = await this.cloud.login.user.get(token).catch(() => null);
|
||||
apiUser = await this.cloud.getLoginUser(token).catch(() => null);
|
||||
if (apiUser) {
|
||||
this.setUser(apiUser);
|
||||
return apiUser;
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import { AIService } from '$lib/backend/aiService';
|
||||
import { AuthService } from '$lib/backend/auth';
|
||||
import { CloudClient } from '$lib/backend/cloud';
|
||||
import { GitConfigService } from '$lib/backend/gitConfigService';
|
||||
import { ProjectService } from '$lib/backend/projects';
|
||||
import { PromptService } from '$lib/backend/prompt';
|
||||
@ -24,7 +25,6 @@
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let data: LayoutData;
|
||||
$: ({ cloud } = data);
|
||||
|
||||
const userSettings = loadUserSettings();
|
||||
initTheme(userSettings);
|
||||
@ -38,6 +38,7 @@
|
||||
$: setContext(AIService, data.aiService);
|
||||
$: setContext(PromptService, data.promptService);
|
||||
$: setContext(AuthService, data.authService);
|
||||
$: setContext(CloudClient, data.cloud);
|
||||
|
||||
let shareIssueModal: ShareIssueModal;
|
||||
|
||||
@ -68,7 +69,7 @@
|
||||
<slot />
|
||||
</div>
|
||||
<Toaster />
|
||||
<ShareIssueModal bind:this={shareIssueModal} {cloud} />
|
||||
<ShareIssueModal bind:this={shareIssueModal} />
|
||||
<ToastController />
|
||||
<AppUpdater />
|
||||
<PromptModal />
|
||||
|
@ -2,7 +2,7 @@ import { initPostHog } from '$lib/analytics/posthog';
|
||||
import { initSentry } from '$lib/analytics/sentry';
|
||||
import { AIService } from '$lib/backend/aiService';
|
||||
import { AuthService } from '$lib/backend/auth';
|
||||
import { getCloudApiClient } from '$lib/backend/cloud';
|
||||
import { CloudClient } from '$lib/backend/cloud';
|
||||
import { GitConfigService } from '$lib/backend/gitConfigService';
|
||||
import { ProjectService } from '$lib/backend/projects';
|
||||
import { PromptService } from '$lib/backend/prompt';
|
||||
@ -39,11 +39,12 @@ export async function load({ fetch: realFetch }: { fetch: typeof fetch }) {
|
||||
// https://github.com/sveltejs/kit/issues/905
|
||||
const defaultPath = await (await import('@tauri-apps/api/path')).homeDir();
|
||||
|
||||
const cloud = new CloudClient(realFetch);
|
||||
const authService = new AuthService();
|
||||
const projectService = new ProjectService(defaultPath);
|
||||
const updaterService = new UpdaterService();
|
||||
const promptService = new PromptService();
|
||||
const userService = new UserService();
|
||||
const userService = new UserService(cloud);
|
||||
const user$ = userService.user$;
|
||||
|
||||
// We're declaring a remoteUrl$ observable here that is written to by `BaseBranchService`. This
|
||||
@ -55,8 +56,6 @@ export async function load({ fetch: realFetch }: { fetch: typeof fetch }) {
|
||||
const remoteUrl$ = new BehaviorSubject<string | undefined>(undefined);
|
||||
const githubService = new GitHubService(userService.accessToken$, remoteUrl$);
|
||||
|
||||
const cloud = getCloudApiClient({ fetch: realFetch });
|
||||
|
||||
const gitConfig = new GitConfigService();
|
||||
const aiService = new AIService(gitConfig, cloud);
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
||||
async function onDetailsUpdated(e: { detail: Project }) {
|
||||
const api =
|
||||
$user && e.detail.api
|
||||
? await cloud.projects.update($user?.access_token, e.detail.api.repository_id, {
|
||||
? await cloud.updateProject($user?.access_token, e.detail.api.repository_id, {
|
||||
name: e.detail.title,
|
||||
description: e.detail.description
|
||||
})
|
||||
|
@ -60,7 +60,7 @@
|
||||
|
||||
$: if ($user && !loaded) {
|
||||
loaded = true;
|
||||
cloud.user.get($user?.access_token).then((cloudUser) => {
|
||||
cloud.getUser($user?.access_token).then((cloudUser) => {
|
||||
cloudUser.github_access_token = $user?.github_access_token; // prevent overwriting with null
|
||||
userService.setUser(cloudUser);
|
||||
});
|
||||
@ -88,7 +88,7 @@
|
||||
const picture = formData.get('picture') as File | undefined;
|
||||
|
||||
try {
|
||||
const updatedUser = await cloud.user.update($user.access_token, {
|
||||
const updatedUser = await cloud.updateUser($user.access_token, {
|
||||
name: newName,
|
||||
picture: picture
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user