Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non reactive errors #2548

Merged
merged 15 commits into from Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 14 additions & 8 deletions inlang/source-code/editor/src/interface/editor/EditorHeader.tsx
Expand Up @@ -54,14 +54,22 @@ function EditorHeader() {
(user()?.isLoggedIn && "mr-14")
}
>
<Button type="text" class="hidden sm:flex"
href={import.meta.env.PROD
? "https://inlang.com/c/lint-rules"
: "http://localhost:3000/c/lint-rules"}>
<Button
type="text"
class="hidden sm:flex"
href={
import.meta.env.PROD
? "https://inlang.com/c/lint-rules"
: "http://localhost:3000/c/lint-rules"
}
>
Find Lint Rules
</Button>
<sl-dropdown prop:placement="bottom-end" class="peer">
<button slot="trigger" class="flex pointer-events-auto justify-center items-center h-10 relative gap-2 rounded-md flex-grow-0 flex-shrink-0 text-sm font-medium text-left cursor-pointer transition-all duration-200 text-surface-700 hover:text-primary">
<button
slot="trigger"
class="flex pointer-events-auto justify-center items-center h-10 relative gap-2 rounded-md flex-grow-0 flex-shrink-0 text-sm font-medium text-left cursor-pointer transition-all duration-200 text-surface-700 hover:text-primary"
>
Need Help?
</button>
<sl-menu class="w-fit">
Expand All @@ -73,9 +81,7 @@ function EditorHeader() {
{link.name}
</a>
</sl-menu-item>
<Show
when={link.name === "About the ecosystem"}
>
<Show when={link.name === "About the ecosystem"}>
<div class="w-full border-b border-surface-200 my-1" />
</Show>
</>
Expand Down
6 changes: 2 additions & 4 deletions inlang/source-code/editor/src/interface/editor/Footer.tsx
Expand Up @@ -39,7 +39,7 @@ export const getFinkResourcesLinks = () => {
{
name: "Submit Feedback",
href: "https://github.com/orgs/opral/discussions/categories/-fink-general",
}
},
]
}

Expand Down Expand Up @@ -99,9 +99,7 @@ const Footer = () => {
{link.name}
</a>
</sl-menu-item>
<Show
when={link.name === "About the ecosystem"}
>
<Show when={link.name === "About the ecosystem"}>
<div class="w-full border-b border-surface-200 my-1" />
</Show>
</>
Expand Down
239 changes: 133 additions & 106 deletions inlang/source-code/editor/src/pages/@host/@owner/@repository/State.tsx
Expand Up @@ -18,7 +18,7 @@ import type { LocalStorageSchema } from "#src/services/local-storage/index.js"
import { useLocalStorage } from "#src/services/local-storage/index.js"
import type { TourStepId } from "./components/Notification/TourHintWrapper.jsx"
import { setSearchParams } from "./helper/setSearchParams.js"
import { openRepository, createNodeishMemoryFs, type Repository } from "@lix-js/client"
import { openRepository, createNodeishMemoryFs, type Repository, LixError } from "@lix-js/client"
import { browserAuth } from "@lix-js/server"
import { publicEnv } from "@inlang/env-variables"
import {
Expand Down Expand Up @@ -56,6 +56,16 @@ type EditorStateSchema = {
*/
mutateForkStatus: (args: { ahead: number; behind: number; conflicts: boolean }) => void

pushChanges: (args: {
user: LocalStorageSchema["user"]
setFsChange: (date: Date) => void
setLastPullTime: (date: Date) => void
}) => Promise<Result<true, PushException>>

mergeUpstream: () => Promise<Awaited<ReturnType<Repository["mergeUpstream"]>>>

createFork: () => Promise<Awaited<ReturnType<Repository["createFork"]>>>

currentBranch: Resource<string | undefined>
/**
* The branch names of current repo.
Expand Down Expand Up @@ -149,7 +159,7 @@ type EditorStateSchema = {
/**
* Expose lix errors that happen wihle opening the repository
*/
lixErrors: () => ReturnType<Repository["errors"]>
lixErrors: () => LixError[]

/**
* Unpushed changes in the repository.
Expand Down Expand Up @@ -244,7 +254,7 @@ export function EditorStateProvider(props: { children: JSXElement }) {
const [localStorage] = useLocalStorage() ?? []

// get lix errors
const [lixErrors, setLixErrors] = createSignal<ReturnType<Repository["errors"]>>([])
const [lixErrors, setLixErrors] = createSignal<Error[]>([])

const [activeBranch, setActiveBranch] = createSignal<string | undefined>(
params.get("branch") || undefined
Expand Down Expand Up @@ -281,23 +291,19 @@ export function EditorStateProvider(props: { children: JSXElement }) {
// @ts-expect-error
window.repo = newRepo
}

if (newRepo.errors().length > 0) {
setLixErrors(newRepo.errors())
return
} else {
setLixErrors([])
}
// TODO replace this with `setLixErrors([])` as soon as repo throws
martin-lysk marked this conversation as resolved.
Show resolved Hide resolved
setLixErrors(newRepo.errors)

// @ts-ignore -- causes reactivity bugs because the sdk uses watch and triggers updates on changes caused by itself
newRepo.nodeishFs.watch = () => {}

setLastPullTime(new Date())

// Invalidate the project while we switch branches
setProject(undefined)
return newRepo
} catch (err) {
console.error(err)
} catch (e) {
setLixErrors([e as Error])
return
}
} else {
Expand All @@ -306,33 +312,86 @@ export function EditorStateProvider(props: { children: JSXElement }) {
}
)

repo()?.errors.subscribe((errors: any) => {
setLixErrors(errors)
})
async function pushChanges(args: {
janfjohannes marked this conversation as resolved.
Show resolved Hide resolved
user: LocalStorageSchema["user"]
setFsChange: (date: Date) => void
setLastPullTime: (date: Date) => void
}): Promise<Result<true, PushException>> {
const loadedRepo = repo()
if (!loadedRepo) {
return { error: new PushException("Repo not loaded") }
}

const isForkSyncDisabled = () =>
localStorage.disableForkSyncWarning?.some(
(repo) => repo.owner === routeParams().owner && repo.repository === routeParams().repository
)
if (typeof args.user === "undefined" || args.user?.isLoggedIn === false) {
return { error: new PushException("User not logged in") }
}

const [forkStatus, { refetch: refetchForkStatus, mutate: mutateForkStatus }] = createResource(
() => {
if (repo() && !isForkSyncDisabled()) {
return repo()
} else {
return false
}
},
async (args) => {
const value = await args.forkStatus()
if ("error" in value) {
return { ahead: 0, behind: 0, conflicts: false }
} else {
return value
const filesWithUncommittedChanges = await loadedRepo.statusList({
filter: (f: any) =>
f.endsWith("project_id") ||
f.endsWith(".json") ||
f.endsWith(".po") ||
f.endsWith(".yaml") ||
f.endsWith(".yml") ||
f.endsWith(".js") ||
f.endsWith(".ts"),
})

if (filesWithUncommittedChanges.length > 0) {
// commit changes
await loadedRepo.commit({
author: {
name: args.user.username,
email: args.user.email,
},
message: "Fink 🐦: update translations",
include: filesWithUncommittedChanges.map((f) => f[0]),
})
}

// triggering a side effect here to trigger a re-render
// of components that depends on fs
args.setFsChange(new Date())
// push changes
try {
const push = await loadedRepo.push()
if (push?.ok === false) {
return { error: new PushException("Failed to push", { cause: push.error }) }
}
},
{ initialValue: { ahead: 0, behind: 0, conflicts: false } }
)
await loadedRepo.pull({
author: {
name: args.user.username,
email: args.user.email,
},
fastForward: true,
singleBranch: true,
})
const time = new Date()
// triggering a rebuild of everything fs related
args.setFsChange(time)
args.setLastPullTime(time)
return { data: true }
} catch (error) {
return { error: (error as PushException) ?? "Unknown error" }
}
}

async function mergeUpstream(): Promise<Awaited<ReturnType<Repository["mergeUpstream"]>>> {
const loadedRepo = repo()
if (!loadedRepo) {
throw new Error("Repo not loaded")
}

return loadedRepo.mergeUpstream()
}

async function createFork() {
const loadedRepo = repo()
if (!loadedRepo) {
throw new Error("Repo not loaded yet")
}
return await loadedRepo.createFork()
}

const [projectList] = createResource(
() => {
Expand Down Expand Up @@ -375,13 +434,13 @@ export function EditorStateProvider(props: { children: JSXElement }) {
// open the inlang project and store it in a resource
const [project, { mutate: setProject }] = createResource(
() => {
if (repo() === undefined || lixErrors().length > 0 || activeProject() === undefined) {
if (repo() === undefined || activeProject() === undefined) {
return false
}
return { newRepo: repo(), lixErrors: lixErrors(), activeProject: activeProject() }
return { newRepo: repo(), activeProject: activeProject() }
},
async ({ newRepo, lixErrors, activeProject }) => {
if (lixErrors.length === 0 && newRepo) {
async ({ newRepo, activeProject }) => {
if (newRepo) {
const project = solidAdapter(
await loadProject({
repo: newRepo,
Expand Down Expand Up @@ -459,11 +518,40 @@ export function EditorStateProvider(props: { children: JSXElement }) {
}
},
async () => {
const repoMeta = await repo()?.getMeta()
const repoMeta = await repo()!.getMeta()
janfjohannes marked this conversation as resolved.
Show resolved Hide resolved
if ("error" in repoMeta) {
setLixErrors([repoMeta.error, ...lixErrors()])
}
return repoMeta
}
)

const isForkSyncDisabled = () =>
janfjohannes marked this conversation as resolved.
Show resolved Hide resolved
localStorage.disableForkSyncWarning?.some(
(repo) => repo.owner === routeParams().owner && repo.repository === routeParams().repository
)

const [forkStatus, { refetch: refetchForkStatus, mutate: mutateForkStatus }] = createResource(
() => {
const repoMeta = githubRepositoryInformation()
if (repo() && !isForkSyncDisabled() && repoMeta && !("error" in repoMeta) && repoMeta.isFork) {
return { repo: repo() }
} else {
return false
}
},
async (args) => {
const value = await args.repo!.forkStatus()
if ("error" in value) {
setLixErrors([new Error(value.error), ...lixErrors()])
return { ahead: 0, behind: 0, conflicts: false }
} else {
return value
}
},
{ initialValue: { ahead: 0, behind: 0, conflicts: false } }
)

const [previousLoginStatus, setPreviousLoginStatus] = createSignal(localStorage?.user?.isLoggedIn)
createEffect(
on(
Expand All @@ -486,7 +574,7 @@ export function EditorStateProvider(props: { children: JSXElement }) {

const [currentBranch] = createResource(
() => {
if (lixErrors().length > 0 || repo() === undefined) {
if (repo() === undefined) {
return {}
} else {
return { repo: repo() }
Expand Down Expand Up @@ -524,7 +612,6 @@ export function EditorStateProvider(props: { children: JSXElement }) {
return {
user: localStorage?.user?.isLoggedIn ?? "not logged in",
routeParams: currentPageContext.routeParams as EditorRouteParams,
currentRepo: repo(),
repoMeta: githubRepositoryInformation(),
}
},
Expand All @@ -550,6 +637,9 @@ export function EditorStateProvider(props: { children: JSXElement }) {
forkStatus,
mutateForkStatus,
refetchForkStatus,
pushChanges,
mergeUpstream,
createFork,
currentBranch,
branchNames,
githubRepositoryInformation,
Expand Down Expand Up @@ -612,66 +702,3 @@ export class UnknownException extends Error {
super(id)
}
}

/**
* Pushed changes and pulls right afterwards.
*/
export async function pushChanges(args: {
repo: Repository
user: LocalStorageSchema["user"]
setFsChange: (date: Date) => void
setLastPullTime: (date: Date) => void
}): Promise<Result<true, PushException>> {
if (typeof args.user === "undefined" || args.user?.isLoggedIn === false) {
return { error: new PushException("User not logged in") }
}

const filesWithUncommittedChanges = await args.repo.statusList({
filter: (f: any) =>
f.endsWith("project_id") ||
f.endsWith(".json") ||
f.endsWith(".po") ||
f.endsWith(".yaml") ||
f.endsWith(".yml") ||
f.endsWith(".js") ||
f.endsWith(".ts"),
})

if (filesWithUncommittedChanges.length > 0) {
// commit changes
await args.repo.commit({
author: {
name: args.user.username,
email: args.user.email,
},
message: "Fink 🐦: update translations",
include: filesWithUncommittedChanges.map((f) => f[0]),
})
}

// triggering a side effect here to trigger a re-render
// of components that depends on fs
args.setFsChange(new Date())
// push changes
try {
const push = await args.repo.push()
if (push?.ok === false) {
return { error: new PushException("Failed to push", { cause: push.error }) }
}
await args.repo.pull({
author: {
name: args.user.username,
email: args.user.email,
},
fastForward: true,
singleBranch: true,
})
const time = new Date()
// triggering a rebuild of everything fs related
args.setFsChange(time)
args.setLastPullTime(time)
return { data: true }
} catch (error) {
return { error: (error as PushException) ?? "Unknown error" }
}
}