fix(app): session review re-rendering too aggressively
This commit is contained in:
@@ -189,8 +189,10 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
const [opened, setOpened] = createSignal<SessionReviewFocus | null>(null)
|
const [opened, setOpened] = createSignal<SessionReviewFocus | null>(null)
|
||||||
|
|
||||||
const open = () => props.open ?? store.open
|
const open = () => props.open ?? store.open
|
||||||
|
const files = createMemo(() => props.diffs.map((d) => d.file))
|
||||||
|
const diffs = createMemo(() => new Map(props.diffs.map((d) => [d.file, d] as const)))
|
||||||
const diffStyle = () => props.diffStyle ?? (props.split ? "split" : "unified")
|
const diffStyle = () => props.diffStyle ?? (props.split ? "split" : "unified")
|
||||||
const hasDiffs = () => props.diffs.length > 0
|
const hasDiffs = () => files().length > 0
|
||||||
|
|
||||||
const handleChange = (open: string[]) => {
|
const handleChange = (open: string[]) => {
|
||||||
props.onOpenChange?.(open)
|
props.onOpenChange?.(open)
|
||||||
@@ -199,7 +201,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleExpandOrCollapseAll = () => {
|
const handleExpandOrCollapseAll = () => {
|
||||||
const next = open().length > 0 ? [] : props.diffs.map((d) => d.file)
|
const next = open().length > 0 ? [] : files()
|
||||||
handleChange(next)
|
handleChange(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,51 +324,54 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
<div data-slot="session-review-container" class={props.classes?.container}>
|
<div data-slot="session-review-container" class={props.classes?.container}>
|
||||||
<Show when={hasDiffs()} fallback={props.empty}>
|
<Show when={hasDiffs()} fallback={props.empty}>
|
||||||
<Accordion multiple value={open()} onChange={handleChange}>
|
<Accordion multiple value={open()} onChange={handleChange}>
|
||||||
<For each={props.diffs}>
|
<For each={files()}>
|
||||||
{(diff) => {
|
{(file) => {
|
||||||
let wrapper: HTMLDivElement | undefined
|
let wrapper: HTMLDivElement | undefined
|
||||||
|
|
||||||
const expanded = createMemo(() => open().includes(diff.file))
|
const diff = createMemo(() => diffs().get(file))
|
||||||
|
const item = () => diff()!
|
||||||
|
|
||||||
|
const expanded = createMemo(() => open().includes(file))
|
||||||
const [force, setForce] = createSignal(false)
|
const [force, setForce] = createSignal(false)
|
||||||
|
|
||||||
const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === diff.file))
|
const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === file))
|
||||||
const commentedLines = createMemo(() => comments().map((c) => c.selection))
|
const commentedLines = createMemo(() => comments().map((c) => c.selection))
|
||||||
|
|
||||||
const beforeText = () => (typeof diff.before === "string" ? diff.before : "")
|
const beforeText = () => (typeof item().before === "string" ? item().before : "")
|
||||||
const afterText = () => (typeof diff.after === "string" ? diff.after : "")
|
const afterText = () => (typeof item().after === "string" ? item().after : "")
|
||||||
const changedLines = () => diff.additions + diff.deletions
|
const changedLines = () => item().additions + item().deletions
|
||||||
|
|
||||||
const tooLarge = createMemo(() => {
|
const tooLarge = createMemo(() => {
|
||||||
if (!expanded()) return false
|
if (!expanded()) return false
|
||||||
if (force()) return false
|
if (force()) return false
|
||||||
if (isImageFile(diff.file)) return false
|
if (isImageFile(file)) return false
|
||||||
return changedLines() > MAX_DIFF_CHANGED_LINES
|
return changedLines() > MAX_DIFF_CHANGED_LINES
|
||||||
})
|
})
|
||||||
|
|
||||||
const isAdded = () => diff.status === "added" || (beforeText().length === 0 && afterText().length > 0)
|
const isAdded = () => item().status === "added" || (beforeText().length === 0 && afterText().length > 0)
|
||||||
const isDeleted = () =>
|
const isDeleted = () =>
|
||||||
diff.status === "deleted" || (afterText().length === 0 && beforeText().length > 0)
|
item().status === "deleted" || (afterText().length === 0 && beforeText().length > 0)
|
||||||
const isImage = () => isImageFile(diff.file)
|
const isImage = () => isImageFile(file)
|
||||||
const isAudio = () => isAudioFile(diff.file)
|
const isAudio = () => isAudioFile(file)
|
||||||
|
|
||||||
const diffImageSrc = dataUrlFromValue(diff.after) ?? dataUrlFromValue(diff.before)
|
const diffImageSrc = createMemo(() => dataUrlFromValue(item().after) ?? dataUrlFromValue(item().before))
|
||||||
const [imageSrc, setImageSrc] = createSignal<string | undefined>(diffImageSrc)
|
const [imageSrc, setImageSrc] = createSignal<string | undefined>(diffImageSrc())
|
||||||
const [imageStatus, setImageStatus] = createSignal<"idle" | "loading" | "error">("idle")
|
const [imageStatus, setImageStatus] = createSignal<"idle" | "loading" | "error">("idle")
|
||||||
|
|
||||||
const diffAudioSrc = dataUrlFromValue(diff.after) ?? dataUrlFromValue(diff.before)
|
const diffAudioSrc = createMemo(() => dataUrlFromValue(item().after) ?? dataUrlFromValue(item().before))
|
||||||
const [audioSrc, setAudioSrc] = createSignal<string | undefined>(diffAudioSrc)
|
const [audioSrc, setAudioSrc] = createSignal<string | undefined>(diffAudioSrc())
|
||||||
const [audioStatus, setAudioStatus] = createSignal<"idle" | "loading" | "error">("idle")
|
const [audioStatus, setAudioStatus] = createSignal<"idle" | "loading" | "error">("idle")
|
||||||
const [audioMime, setAudioMime] = createSignal<string | undefined>(undefined)
|
const [audioMime, setAudioMime] = createSignal<string | undefined>(undefined)
|
||||||
|
|
||||||
const selectedLines = createMemo(() => {
|
const selectedLines = createMemo(() => {
|
||||||
const current = selection()
|
const current = selection()
|
||||||
if (!current || current.file !== diff.file) return null
|
if (!current || current.file !== file) return null
|
||||||
return current.range
|
return current.range
|
||||||
})
|
})
|
||||||
|
|
||||||
const draftRange = createMemo(() => {
|
const draftRange = createMemo(() => {
|
||||||
const current = commenting()
|
const current = commenting()
|
||||||
if (!current || current.file !== diff.file) return null
|
if (!current || current.file !== file) return null
|
||||||
return current.range
|
return current.range
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -417,6 +422,21 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
requestAnimationFrame(updateAnchors)
|
requestAnimationFrame(updateAnchors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!isImage()) return
|
||||||
|
const src = diffImageSrc()
|
||||||
|
setImageSrc(src)
|
||||||
|
setImageStatus("idle")
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!isAudio()) return
|
||||||
|
const src = diffAudioSrc()
|
||||||
|
setAudioSrc(src)
|
||||||
|
setAudioStatus("idle")
|
||||||
|
setAudioMime(undefined)
|
||||||
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
comments()
|
comments()
|
||||||
scheduleAnchors()
|
scheduleAnchors()
|
||||||
@@ -430,7 +450,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!open().includes(diff.file)) return
|
if (!open().includes(file)) return
|
||||||
if (!isImage()) return
|
if (!isImage()) return
|
||||||
if (imageSrc()) return
|
if (imageSrc()) return
|
||||||
if (imageStatus() !== "idle") return
|
if (imageStatus() !== "idle") return
|
||||||
@@ -440,7 +460,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
if (!reader) return
|
if (!reader) return
|
||||||
|
|
||||||
setImageStatus("loading")
|
setImageStatus("loading")
|
||||||
reader(diff.file)
|
reader(file)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
const src = dataUrl(result)
|
const src = dataUrl(result)
|
||||||
if (!src) {
|
if (!src) {
|
||||||
@@ -456,7 +476,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!open().includes(diff.file)) return
|
if (!open().includes(file)) return
|
||||||
if (!isAudio()) return
|
if (!isAudio()) return
|
||||||
if (audioSrc()) return
|
if (audioSrc()) return
|
||||||
if (audioStatus() !== "idle") return
|
if (audioStatus() !== "idle") return
|
||||||
@@ -465,7 +485,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
if (!reader) return
|
if (!reader) return
|
||||||
|
|
||||||
setAudioStatus("loading")
|
setAudioStatus("loading")
|
||||||
reader(diff.file)
|
reader(file)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
const src = dataUrl(result)
|
const src = dataUrl(result)
|
||||||
if (!src) {
|
if (!src) {
|
||||||
@@ -489,7 +509,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelection({ file: diff.file, range })
|
setSelection({ file, range })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLineSelectionEnd = (range: SelectedLineRange | null) => {
|
const handleLineSelectionEnd = (range: SelectedLineRange | null) => {
|
||||||
@@ -500,8 +520,8 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelection({ file: diff.file, range })
|
setSelection({ file, range })
|
||||||
setCommenting({ file: diff.file, range })
|
setCommenting({ file, range })
|
||||||
}
|
}
|
||||||
|
|
||||||
const openComment = (comment: SessionReviewComment) => {
|
const openComment = (comment: SessionReviewComment) => {
|
||||||
@@ -517,22 +537,22 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion.Item
|
<Accordion.Item
|
||||||
value={diff.file}
|
value={file}
|
||||||
id={diffId(diff.file)}
|
id={diffId(file)}
|
||||||
data-file={diff.file}
|
data-file={file}
|
||||||
data-slot="session-review-accordion-item"
|
data-slot="session-review-accordion-item"
|
||||||
data-selected={props.focusedFile === diff.file ? "" : undefined}
|
data-selected={props.focusedFile === file ? "" : undefined}
|
||||||
>
|
>
|
||||||
<StickyAccordionHeader>
|
<StickyAccordionHeader>
|
||||||
<Accordion.Trigger>
|
<Accordion.Trigger>
|
||||||
<div data-slot="session-review-trigger-content">
|
<div data-slot="session-review-trigger-content">
|
||||||
<div data-slot="session-review-file-info">
|
<div data-slot="session-review-file-info">
|
||||||
<FileIcon node={{ path: diff.file, type: "file" }} />
|
<FileIcon node={{ path: file, type: "file" }} />
|
||||||
<div data-slot="session-review-file-name-container">
|
<div data-slot="session-review-file-name-container">
|
||||||
<Show when={diff.file.includes("/")}>
|
<Show when={file.includes("/")}>
|
||||||
<span data-slot="session-review-directory">{`\u202A${getDirectory(diff.file)}\u202C`}</span>
|
<span data-slot="session-review-directory">{`\u202A${getDirectory(file)}\u202C`}</span>
|
||||||
</Show>
|
</Show>
|
||||||
<span data-slot="session-review-filename">{getFilename(diff.file)}</span>
|
<span data-slot="session-review-filename">{getFilename(file)}</span>
|
||||||
<Show when={props.onViewFile}>
|
<Show when={props.onViewFile}>
|
||||||
<Tooltip value="Open file" placement="top" gutter={4}>
|
<Tooltip value="Open file" placement="top" gutter={4}>
|
||||||
<button
|
<button
|
||||||
@@ -541,7 +561,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
aria-label="Open file"
|
aria-label="Open file"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
props.onViewFile?.(diff.file)
|
props.onViewFile?.(file)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon name="open-file" size="small" />
|
<Icon name="open-file" size="small" />
|
||||||
@@ -557,7 +577,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
<span data-slot="session-review-change" data-type="added">
|
<span data-slot="session-review-change" data-type="added">
|
||||||
{i18n.t("ui.sessionReview.change.added")}
|
{i18n.t("ui.sessionReview.change.added")}
|
||||||
</span>
|
</span>
|
||||||
<DiffChanges changes={diff} />
|
<DiffChanges changes={item()} />
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={isDeleted()}>
|
<Match when={isDeleted()}>
|
||||||
@@ -571,7 +591,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
</span>
|
</span>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<DiffChanges changes={diff} />
|
<DiffChanges changes={item()} />
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
<span data-slot="session-review-diff-chevron">
|
<span data-slot="session-review-diff-chevron">
|
||||||
@@ -586,7 +606,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
data-slot="session-review-diff-wrapper"
|
data-slot="session-review-diff-wrapper"
|
||||||
ref={(el) => {
|
ref={(el) => {
|
||||||
wrapper = el
|
wrapper = el
|
||||||
anchors.set(diff.file, el)
|
anchors.set(file, el)
|
||||||
scheduleAnchors()
|
scheduleAnchors()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -594,7 +614,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Match when={isImage() && imageSrc()}>
|
<Match when={isImage() && imageSrc()}>
|
||||||
<div data-slot="session-review-image-container">
|
<div data-slot="session-review-image-container">
|
||||||
<img data-slot="session-review-image" src={imageSrc()} alt={diff.file} />
|
<img data-slot="session-review-image" src={imageSrc()} alt={file} />
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={isImage() && isDeleted()}>
|
<Match when={isImage() && isDeleted()}>
|
||||||
@@ -634,7 +654,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
<Match when={!isImage()}>
|
<Match when={!isImage()}>
|
||||||
<Dynamic
|
<Dynamic
|
||||||
component={diffComponent}
|
component={diffComponent}
|
||||||
preloadedDiff={diff.preloaded}
|
preloadedDiff={item().preloaded}
|
||||||
diffStyle={diffStyle()}
|
diffStyle={diffStyle()}
|
||||||
onRendered={() => {
|
onRendered={() => {
|
||||||
props.onDiffRendered?.()
|
props.onDiffRendered?.()
|
||||||
@@ -646,12 +666,12 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
selectedLines={selectedLines()}
|
selectedLines={selectedLines()}
|
||||||
commentedLines={commentedLines()}
|
commentedLines={commentedLines()}
|
||||||
before={{
|
before={{
|
||||||
name: diff.file!,
|
name: file,
|
||||||
contents: typeof diff.before === "string" ? diff.before : "",
|
contents: typeof item().before === "string" ? item().before : "",
|
||||||
}}
|
}}
|
||||||
after={{
|
after={{
|
||||||
name: diff.file!,
|
name: file,
|
||||||
contents: typeof diff.after === "string" ? diff.after : "",
|
contents: typeof item().after === "string" ? item().after : "",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
@@ -689,10 +709,10 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
onCancel={() => setCommenting(null)}
|
onCancel={() => setCommenting(null)}
|
||||||
onSubmit={(comment) => {
|
onSubmit={(comment) => {
|
||||||
props.onLineComment?.({
|
props.onLineComment?.({
|
||||||
file: diff.file,
|
file,
|
||||||
selection: range(),
|
selection: range(),
|
||||||
comment,
|
comment,
|
||||||
preview: selectionPreview(diff, range()),
|
preview: selectionPreview(item(), range()),
|
||||||
})
|
})
|
||||||
setCommenting(null)
|
setCommenting(null)
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user