|
32 | 32 | import { HOOKS_SERVICE } from "$lib/hooks/hooksService"; |
33 | 33 | import { IRC_API_SERVICE } from "$lib/irc/ircApiService"; |
34 | 34 | import { createCommitSelection } from "$lib/selection/key"; |
| 35 | + import { getStackContext } from "$lib/stack/stackController.svelte"; |
35 | 36 | import { STACK_SERVICE } from "$lib/stacks/stackService.svelte"; |
| 37 | +
|
36 | 38 | import { combineResults } from "$lib/state/helpers"; |
37 | | - import { UI_STATE } from "$lib/state/uiState.svelte"; |
| 39 | + import { ensureValue } from "$lib/utils/validation"; |
38 | 40 | import { inject } from "@gitbutler/core/context"; |
39 | 41 | import { persisted } from "@gitbutler/shared/persisted"; |
40 | 42 | import { Button, Modal, RadioButton, TestId } from "@gitbutler/ui"; |
|
45 | 47 | import type { BranchDetails } from "$lib/stacks/stack"; |
46 | 48 |
|
47 | 49 | interface Props { |
48 | | - projectId: string; |
49 | | - stackId?: string; |
50 | | - laneId: string; |
51 | 50 | branchName: string; |
52 | 51 | lastBranch: boolean; |
53 | 52 | branchDetails: BranchDetails; |
54 | 53 | stackingReorderDropzoneManager: ReorderCommitDzFactory; |
55 | 54 | roundedTop?: boolean; |
56 | | - active?: boolean; |
57 | | - visibleRange?: { start: number; end: number }; |
58 | | -
|
59 | | - handleUncommit: (commitId: string, branchName: string) => Promise<void>; |
60 | | - startEditingCommitMessage: (branchName: string, commitId: string) => void; |
61 | | - onclick?: () => void; |
62 | | - onFileClick?: (index: number) => void; |
63 | 55 | } |
64 | 56 |
|
65 | | - let { |
66 | | - projectId, |
67 | | - stackId, |
68 | | - laneId, |
69 | | - branchName, |
70 | | - branchDetails, |
71 | | - lastBranch, |
72 | | - stackingReorderDropzoneManager, |
73 | | - roundedTop, |
74 | | - active, |
75 | | - visibleRange, |
76 | | - handleUncommit, |
77 | | - startEditingCommitMessage, |
78 | | - onclick, |
79 | | - onFileClick, |
80 | | - }: Props = $props(); |
| 57 | + let { branchName, branchDetails, lastBranch, stackingReorderDropzoneManager, roundedTop }: Props = |
| 58 | + $props(); |
81 | 59 |
|
| 60 | + const controller = getStackContext(); |
82 | 61 | const stackService = inject(STACK_SERVICE); |
83 | | - const uiState = inject(UI_STATE); |
84 | 62 | const forge = inject(DEFAULT_FORGE_FACTORY); |
85 | 63 | const hooksService = inject(HOOKS_SERVICE); |
86 | 64 | const ircApiService = inject(IRC_API_SERVICE); |
87 | 65 | const dropzoneRegistry = inject(DROPZONE_REGISTRY); |
88 | 66 | const dragStateService = inject(DRAG_STATE_SERVICE); |
89 | 67 |
|
| 68 | + const projectId = $derived(controller.projectId); |
| 69 | + const stackId = $derived(controller.stackId); |
| 70 | +
|
90 | 71 | const commitReactionsQuery = $derived(ircApiService.commitReactions()); |
91 | 72 | const commitReactions = $derived(commitReactionsQuery?.response ?? {}); |
92 | 73 |
|
93 | 74 | const [integrateUpstreamCommits, integrating] = stackService.integrateUpstreamCommits; |
94 | 75 |
|
95 | | - const projectState = $derived(uiState.project(projectId)); |
96 | | - const exclusiveAction = $derived(projectState.exclusiveAction.current); |
| 76 | + const exclusiveAction = $derived(controller.exclusiveAction); |
97 | 77 | const commitAction = $derived(exclusiveAction?.type === "commit" ? exclusiveAction : undefined); |
98 | | - const isCommitting = $derived( |
99 | | - exclusiveAction?.type === "commit" && exclusiveAction.stackId === stackId, |
100 | | - ); |
101 | | - const laneState = $derived(uiState.lane(laneId)); |
102 | | - const selection = $derived(laneState.selection); |
| 78 | + const selection = $derived(controller.selection); |
103 | 79 | const runHooks = $derived(projectRunCommitHooks(projectId)); |
104 | 80 |
|
105 | 81 | const selectedBranchName = $derived(selection.current?.branchName); |
|
113 | 89 | let integrationModal = $state<Modal>(); |
114 | 90 |
|
115 | 91 | async function handleCommitClick(commitId: string, upstream: boolean) { |
116 | | - const currentSelection = laneState.selection.current; |
| 92 | + const currentSelection = controller.selection.current; |
117 | 93 | // Toggle: if this exact commit is already selected, clear the selection |
118 | 94 | if (currentSelection?.commitId === commitId && currentSelection?.branchName === branchName) { |
119 | | - laneState.selection.set(undefined); |
| 95 | + controller.selection.set(undefined); |
120 | 96 | } else { |
121 | | - laneState.selection.set({ branchName, commitId, upstream, previewOpen: true }); |
| 97 | + controller.selection.set({ branchName, commitId, upstream, previewOpen: true }); |
122 | 98 | } |
123 | | - onclick?.(); |
| 99 | + controller.clearWorktreeSelection(); |
| 100 | + } |
| 101 | +
|
| 102 | + async function handleUncommit(commitId: string) { |
| 103 | + await stackService.uncommit({ |
| 104 | + projectId, |
| 105 | + stackId: ensureValue(stackId), |
| 106 | + branchName, |
| 107 | + commitId, |
| 108 | + }); |
| 109 | + } |
| 110 | +
|
| 111 | + function startEditingCommitMessage(commitId: string) { |
| 112 | + controller.selection.set({ branchName, commitId, previewOpen: true }); |
| 113 | + controller.projectState.exclusiveAction.set({ |
| 114 | + type: "edit-commit-message", |
| 115 | + stackId, |
| 116 | + branchName, |
| 117 | + commitId, |
| 118 | + }); |
124 | 119 | } |
125 | 120 |
|
126 | 121 | function kickOffIntegration() { |
|
215 | 210 | {/snippet} |
216 | 211 |
|
217 | 212 | {#snippet commitReorderDz(dropzone: ReorderCommitDzHandler)} |
218 | | - {#if !isCommitting} |
| 213 | + {#if !controller.isCommitting} |
219 | 214 | <Dropzone handlers={[dropzone]}> |
220 | 215 | {#snippet overlay({ hovered, activated })} |
221 | 216 | <CommitLineOverlay {hovered} {activated} /> |
|
247 | 242 | {@const lastCommit = i === upstreamOnlyCommits.length - 1} |
248 | 243 | {@const selected = commit.id === selectedCommitId && branchName === selectedBranchName} |
249 | 244 | {@const commitId = commit.id} |
250 | | - {#if !isCommitting} |
| 245 | + {#if !controller.isCommitting} |
251 | 246 | <CommitRow |
252 | 247 | type="Remote" |
253 | 248 | {stackId} |
|
259 | 254 | {first} |
260 | 255 | {lastCommit} |
261 | 256 | {selected} |
262 | | - {active} |
| 257 | + active={controller.active} |
263 | 258 | reactions={commitReactions[commit.id]} |
264 | 259 | onclick={() => handleCommitClick(commit.id, true)} |
265 | 260 | disableCommitActions={false} |
|
283 | 278 | {#snippet template(commit, { first, last })} |
284 | 279 | {@const commitId = commit.id} |
285 | 280 | {@const selected = commit.id === selectedCommitId && branchName === selectedBranchName} |
286 | | - {#if isCommitting} |
| 281 | + {#if controller.isCommitting} |
287 | 282 | <!-- Only commits to the base can be `last`, see next `CommitGoesHere`. --> |
288 | 283 | <CommitGoesHere |
289 | 284 | {commitId} |
|
293 | 288 | {first} |
294 | 289 | last={false} |
295 | 290 | onclick={() => { |
296 | | - projectState.exclusiveAction.set({ |
| 291 | + controller.projectState.exclusiveAction.set({ |
297 | 292 | type: "commit", |
298 | 293 | stackId, |
299 | 294 | branchName, |
|
311 | 306 | {@const { amendHandler, squashHandler, hunkHandler } = createCommitDropHandlers({ |
312 | 307 | projectId, |
313 | 308 | stackId, |
314 | | - stackService, |
315 | | - hooksService, |
316 | | - uiState, |
317 | 309 | commit: dzCommit, |
318 | 310 | runHooks: $runHooks, |
319 | 311 | okWithForce: true, |
320 | 312 | onCommitIdChange: (newId) => { |
321 | | - const wasSelected = laneState.selection.current?.commitId === commitId; |
| 313 | + const wasSelected = controller.selection.current?.commitId === commitId; |
322 | 314 | if (stackId && wasSelected) { |
323 | 315 | const previewOpen = selection.current?.previewOpen ?? false; |
324 | | - uiState.lane(stackId).selection.set({ branchName, commitId: newId, previewOpen }); |
| 316 | + controller.laneState.selection.set({ branchName, commitId: newId, previewOpen }); |
325 | 317 | } |
326 | 318 | }, |
327 | 319 | })} |
|
378 | 370 | {lastBranch} |
379 | 371 | {selected} |
380 | 372 | {tooltip} |
381 | | - {active} |
| 373 | + active={controller.active} |
382 | 374 | reactions={commitReactions[commit.id]} |
383 | 375 | onclick={() => handleCommitClick(commit.id, false)} |
384 | 376 | disableCommitActions={false} |
|
391 | 383 | commitMessage: commit.message, |
392 | 384 | commitStatus: commit.state.type, |
393 | 385 | commitUrl: forge.current.commitUrl(commitId), |
394 | | - onUncommitClick: () => handleUncommit(commit.id, branchName), |
395 | | - onEditMessageClick: () => startEditingCommitMessage(branchName, commit.id), |
| 386 | + onUncommitClick: () => handleUncommit(commit.id), |
| 387 | + onEditMessageClick: () => startEditingCommitMessage(commit.id), |
396 | 388 | }} |
397 | 389 | <CommitContextMenu |
398 | 390 | showOnHover |
|
417 | 409 | title="Changed files" |
418 | 410 | {projectId} |
419 | 411 | {stackId} |
420 | | - {visibleRange} |
| 412 | + visibleRange={controller.visibleRange} |
421 | 413 | draggableFiles |
422 | 414 | selectionId={createCommitSelection({ commitId: commitId, stackId })} |
423 | 415 | persistId={`commit-${commitId}`} |
|
432 | 424 | allowUnselect={false} |
433 | 425 | onFileClick={(index) => { |
434 | 426 | // Ensure the commit is selected so the preview shows it |
435 | | - const currentSelection = laneState.selection.current; |
| 427 | + const currentSelection = controller.selection.current; |
436 | 428 | if ( |
437 | 429 | currentSelection?.commitId !== commitId || |
438 | 430 | currentSelection?.branchName !== branchName |
439 | 431 | ) { |
440 | | - laneState.selection.set({ |
| 432 | + controller.selection.set({ |
441 | 433 | branchName, |
442 | 434 | commitId, |
443 | 435 | upstream: false, |
444 | 436 | previewOpen: true, |
445 | 437 | }); |
446 | 438 | } |
447 | | - onFileClick?.(index); |
| 439 | + controller.jumpToIndex(index); |
448 | 440 | }} |
449 | 441 | /> |
450 | 442 | {/snippet} |
|
456 | 448 | {@render commitReorderDz( |
457 | 449 | stackingReorderDropzoneManager.belowCommit(branchName, commit.id), |
458 | 450 | )} |
459 | | - {#if isCommitting && last} |
| 451 | + {#if controller.isCommitting && last} |
460 | 452 | <CommitGoesHere |
461 | 453 | commitId={branchDetails.baseCommit} |
462 | 454 | {first} |
|
465 | 457 | exclusiveAction.parentCommitId === branchDetails.baseCommit && |
466 | 458 | commitAction?.branchName === branchName} |
467 | 459 | onclick={() => { |
468 | | - projectState.exclusiveAction.set({ |
| 460 | + controller.projectState.exclusiveAction.set({ |
469 | 461 | type: "commit", |
470 | 462 | stackId, |
471 | 463 | branchName, |
|
0 commit comments