Skip to content

Commit

Permalink
Merge pull request #2548 from opral/non-reactive-errors
Browse files Browse the repository at this point in the history
Non reactive errors
  • Loading branch information
janfjohannes authored Apr 16, 2024
2 parents 791ebf1 + ebb3b76 commit a66f1f6
Show file tree
Hide file tree
Showing 9 changed files with 471 additions and 479 deletions.
250 changes: 143 additions & 107 deletions inlang/source-code/editor/src/pages/@host/@owner/@repository/State.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import {
openRepository,
createNodeishMemoryFs,
type Repository,
type LixError
} from "@lix-js/client"

import { publicEnv } from "@inlang/env-variables"
import {
LanguageTag,
Expand Down Expand Up @@ -66,6 +68,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 @@ -159,7 +171,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 @@ -254,7 +266,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 @@ -291,23 +303,18 @@ export function EditorStateProvider(props: { children: JSXElement }) {
// @ts-expect-error
window.repo = newRepo
}

if (newRepo.errors().length > 0) {
setLixErrors(newRepo.errors())
return
} else {
setLixErrors([])
}
setLixErrors([])

// @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 @@ -316,33 +323,86 @@ export function EditorStateProvider(props: { children: JSXElement }) {
}
)

repo()?.errors.subscribe((errors: any) => {
setLixErrors(errors)
})
async function pushChanges(args: {
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 @@ -385,13 +445,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 @@ -455,25 +515,62 @@ export function EditorStateProvider(props: { children: JSXElement }) {

const [githubRepositoryInformation, { refetch: refetchRepoInfo }] = createResource(
() => {
const loadedRepo = repo()
if (
localStorage?.user === undefined ||
routeParams().owner === undefined ||
routeParams().repository === undefined ||
repo() === undefined
loadedRepo === undefined
) {
return false
}
return {
repo: loadedRepo,
user: localStorage.user,
routeParams: routeParams(),
}
},
async () => {
const repoMeta = await repo()?.getMeta()
async ({ repo: loadedRepo }) => {
const repoMeta = await loadedRepo.getMeta()
if ("error" in repoMeta) {
setLixErrors([repoMeta.error, ...lixErrors()])
}
return repoMeta
}
)

const isForkSyncDisabled = () =>
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 @@ -496,7 +593,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 @@ -534,7 +631,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 @@ -560,6 +656,9 @@ export function EditorStateProvider(props: { children: JSXElement }) {
forkStatus,
mutateForkStatus,
refetchForkStatus,
pushChanges,
mergeUpstream,
createFork,
currentBranch,
branchNames,
githubRepositoryInformation,
Expand Down Expand Up @@ -622,66 +721,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" }
}
}
Loading

0 comments on commit a66f1f6

Please sign in to comment.