Clarify changed and deleted files in the file viewer (#2670)

* fix(app): distinguish changed and deleted files

Successful reads now reconcile transient missing events without letting stale
cached data mask a deletion. File notices expose only actions valid for the
current buffer state.

* fix(app): preserve file state ordering

* fix(app): allow retrying file checks

* fix(app): clear unchanged file conflicts

* fix(app): keep file conflict recovery consistent

Own disk observation and read ordering in one model so stale reads and failed subscriptions cannot produce contradictory actions.

* fix(app): allow retrying file preview errors

Preserve daemon read errors and expose retry for initial and non-editable file loads.
This commit is contained in:
Mohamed Boudra
2026-07-30 22:50:58 +02:00
committed by GitHub
parent 240d7f366d
commit b6f1274f47
19 changed files with 1282 additions and 194 deletions
+6 -22
View File
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { LoadingSpinner } from "@/components/ui/loading-spinner";
import { SegmentedControl } from "@/components/ui/segmented-control";
import type { Theme } from "@/styles/theme";
import { FileConflictAlert } from "./conflict-alert";
import { FileConflictAlert, type FileConflictAlertState } from "./conflict-alert";
import type { FileEditorStatus } from "./editor/model";
const ThemedSpinner = withUnistyles(LoadingSpinner);
@@ -18,9 +18,7 @@ export function FilePanelBar({
editorStatus,
cursor,
vimMode,
conflictUnavailable,
onOverwrite,
onReload,
conflict,
}: {
size: number;
lineCount?: number;
@@ -29,9 +27,7 @@ export function FilePanelBar({
editorStatus?: FileEditorStatus;
cursor?: { line: number; column: number };
vimMode?: string | null;
conflictUnavailable?: boolean;
onOverwrite?(): void;
onReload?(): void;
conflict?: FileConflictAlertState;
}) {
const { t } = useTranslation();
const markdownModes = [
@@ -84,9 +80,6 @@ export function FilePanelBar({
{editorStatus === "error" ? (
<Text style={styles.error}>{t("panels.file.editor.saveFailed")}</Text>
) : null}
{editorStatus === "conflict" ? (
<Text style={styles.error}>{t("panels.file.editor.changedOnDisk")}</Text>
) : null}
{vimMode ? (
<Text
style={styles.vim}
@@ -114,15 +107,7 @@ export function FilePanelBar({
/>
) : null}
</View>
{editorStatus === "conflict" && onOverwrite && onReload ? (
<View style={styles.notice}>
<FileConflictAlert
unavailable={conflictUnavailable ?? false}
onOverwrite={onOverwrite}
onReload={onReload}
/>
</View>
) : null}
{conflict ? <FileConflictAlert state={conflict} /> : null}
</View>
);
}
@@ -137,8 +122,6 @@ const styles = StyleSheet.create((theme) => ({
chrome: {
flexShrink: 0,
backgroundColor: theme.colors.surface1,
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
},
row: {
minHeight: 32,
@@ -146,6 +129,8 @@ const styles = StyleSheet.create((theme) => ({
alignItems: "center",
gap: theme.spacing[3],
paddingHorizontal: theme.spacing[3],
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
},
metadata: {
flex: 1,
@@ -174,5 +159,4 @@ const styles = StyleSheet.create((theme) => ({
fontFamily: theme.fontFamily.mono,
fontSize: theme.fontSize.xs,
},
notice: { paddingHorizontal: theme.spacing[3], paddingBottom: theme.spacing[3] },
}));