-
Notifications
You must be signed in to change notification settings - Fork 112
feat(core, plugin-history-sync): Initial stack setup process exposure #630
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
Merged
Merged
Changes from 11 commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
81845a1
add withResolvers utility
c8c1e4a
encapsulate transaction
de77b08
save
bab9e8a
Process
3bbe308
refactor
5fe5cc5
fix bug
fd79326
refactor
c51d673
Abst
449058d
proxyyyy
5bb1bb8
historysync
fb702d2
DONE?
ccc0abe
remove test data
8032a9f
remove unused utility
112c0ad
unused import
470221b
fix imports
3b4ca7f
fix
6e93e6c
small refactor
ce98101
install use hook polyfill
a261fa1
fix wrong refactoring
c4fbd54
gogo
936801b
run checkpoint on init
21c3bc2
go
f6b0252
fix imports
4f7bf48
fix test
4bbbfe1
fix names
eb987e6
fix typo
93be83f
opt
c7ea836
remove hook
9006a17
coordinate lifecycle
f59edee
rewrite changesets
969f9c4
fix sentence
83bd8e4
hide navigation process
94f74a4
fix
35a1708
export more
edcae24
fix
f569550
fix order
64720cb
fix
98c8e1f
hide navigation process
3cf81c5
useIsActivatedActivity
c7ee042
fix
6a24370
sort imports
orionmiz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import type { | ||
| DomainEvent, | ||
| PausedEvent, | ||
| PoppedEvent, | ||
| PushedEvent, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
extensions/plugin-history-sync/src/InitialSetupProcessStatusContext.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { createContext, useContext } from "react"; | ||
| import type { NavigationProcessStatus } from "./NavigationProcess/NavigationProcess"; | ||
|
|
||
| export const InitialSetupProcessStatusContext = | ||
| createContext<NavigationProcessStatus | null>(null); | ||
|
|
||
| export function useInitialSetupProcessStatus(): NavigationProcessStatus { | ||
| const status = useContext(InitialSetupProcessStatusContext); | ||
|
|
||
| if (!status) { | ||
| throw new Error("InitialSetupProcessStatusContext is not found"); | ||
| } | ||
|
|
||
| return status; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| export class Mutex { | ||
| private latestlyBookedSession: Promise<void> = Promise.resolve(); | ||
|
|
||
| acquire(): Promise<{ release: () => void }> { | ||
| return new Promise((resolveSessionHandle) => { | ||
| this.latestlyBookedSession = this.latestlyBookedSession.then( | ||
| () => | ||
| new Promise((resolveSession) => | ||
| resolveSessionHandle({ release: () => resolveSession() }), | ||
| ), | ||
| ); | ||
| }); | ||
| } | ||
|
|
||
| async runExclusively<T>(thunk: () => Promise<T>): Promise<Awaited<T>> { | ||
| const { release } = await this.acquire(); | ||
|
|
||
| try { | ||
| return await thunk(); | ||
| } finally { | ||
| release(); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import type { | ||
| DomainEvent, | ||
| PoppedEvent, | ||
| PushedEvent, | ||
| ReplacedEvent, | ||
| StepPoppedEvent, | ||
| StepPushedEvent, | ||
| StepReplacedEvent, | ||
| } from "@stackflow/core"; | ||
|
|
||
| export type NavigationEvent = | ||
| | PushedEvent | ||
| | PoppedEvent | ||
| | ReplacedEvent | ||
| | StepPushedEvent | ||
| | StepPoppedEvent | ||
| | StepReplacedEvent; | ||
|
|
||
| export function isNavigationEvent( | ||
| event: DomainEvent, | ||
| ): event is NavigationEvent { | ||
| return ( | ||
| event.name === "Pushed" || | ||
| event.name === "Popped" || | ||
| event.name === "Replaced" || | ||
| event.name === "StepPushed" || | ||
| event.name === "StepPopped" || | ||
| event.name === "StepReplaced" | ||
| ); | ||
| } |
69 changes: 69 additions & 0 deletions
69
extensions/plugin-history-sync/src/NavigationProcess/CompositNavigationProcess.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import type { NavigationEvent } from "NavigationEvent"; | ||
ENvironmentSet marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import type { PushedEvent, Stack, StepPushedEvent } from "@stackflow/core"; | ||
| import { | ||
| type NavigationProcess, | ||
| NavigationProcessStatus, | ||
| } from "./NavigationProcess"; | ||
|
|
||
| export class CompositNavigationProcess implements NavigationProcess { | ||
| private base: NavigationProcess; | ||
| private createDrived: (base: NavigationEvent[]) => NavigationProcess; | ||
| private derived: NavigationProcess | null; | ||
| private baseNavigationEvents: NavigationEvent[]; | ||
|
|
||
| constructor( | ||
| base: NavigationProcess, | ||
| createDrived: (base: NavigationEvent[]) => NavigationProcess, | ||
| ) { | ||
| this.base = base; | ||
| this.createDrived = createDrived; | ||
| this.derived = null; | ||
| this.baseNavigationEvents = []; | ||
| } | ||
|
|
||
| captureNavigationOpportunity( | ||
| stack: Stack | null, | ||
| navigationTime: number, | ||
| ): (PushedEvent | StepPushedEvent)[] { | ||
| if (this.derived) { | ||
| return this.derived.captureNavigationOpportunity(stack, navigationTime); | ||
| } | ||
|
|
||
| const events = this.base.captureNavigationOpportunity( | ||
| stack, | ||
| navigationTime, | ||
| ); | ||
|
|
||
| if ( | ||
| events.length === 0 && | ||
| this.base.getStatus() === NavigationProcessStatus.SUCCEEDED | ||
| ) { | ||
| this.derived = this.createDrived(this.baseNavigationEvents); | ||
|
|
||
| return this.derived.captureNavigationOpportunity(stack, navigationTime); | ||
| } | ||
|
|
||
| this.baseNavigationEvents.push(...events); | ||
|
|
||
| return events; | ||
| } | ||
ENvironmentSet marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| getStatus(): NavigationProcessStatus { | ||
| const baseStatus = this.base.getStatus(); | ||
|
|
||
| if (baseStatus === NavigationProcessStatus.SUCCEEDED) { | ||
| if (!this.derived) { | ||
| this.derived = this.createDrived(this.baseNavigationEvents); | ||
| } | ||
|
|
||
| const derivedStatus = this.derived.getStatus(); | ||
|
|
||
| if (derivedStatus === NavigationProcessStatus.IDLE) | ||
| return NavigationProcessStatus.PROGRESS; | ||
|
|
||
| return derivedStatus; | ||
| } | ||
|
|
||
| return baseStatus; | ||
| } | ||
| } | ||
27 changes: 27 additions & 0 deletions
27
extensions/plugin-history-sync/src/NavigationProcess/NavigationProcess.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import type { PushedEvent, Stack, StepPushedEvent } from "@stackflow/core"; | ||
|
|
||
| export interface NavigationProcess { | ||
| captureNavigationOpportunity( | ||
| stack: Stack | null, | ||
| navigationTime: number, | ||
| ): (PushedEvent | StepPushedEvent)[]; | ||
|
|
||
| getStatus(): NavigationProcessStatus; | ||
| } | ||
|
|
||
| export const NavigationProcessStatus = { | ||
| IDLE: "idle", | ||
| PROGRESS: "progress", | ||
| SUCCEEDED: "succeeded", | ||
| FAILED: "failed", | ||
| } as const; | ||
|
|
||
| export type NavigationProcessStatus = | ||
| (typeof NavigationProcessStatus)[keyof typeof NavigationProcessStatus]; | ||
|
|
||
| export function isTerminated(status: NavigationProcessStatus): boolean { | ||
| return ( | ||
| status === NavigationProcessStatus.SUCCEEDED || | ||
| status === NavigationProcessStatus.FAILED | ||
| ); | ||
| } |
94 changes: 94 additions & 0 deletions
94
extensions/plugin-history-sync/src/NavigationProcess/SerialNavigationProcess.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import { isNavigationEvent, type NavigationEvent } from "NavigationEvent"; | ||
ENvironmentSet marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import type { PushedEvent, Stack, StepPushedEvent } from "@stackflow/core"; | ||
| import { | ||
| isTerminated, | ||
| type NavigationProcess, | ||
| NavigationProcessStatus, | ||
| } from "./NavigationProcess"; | ||
|
|
||
| export class SerialNavigationProcess implements NavigationProcess { | ||
| private status: NavigationProcessStatus; | ||
| private pendingNavigations: (( | ||
| navigationTime: number, | ||
| ) => (PushedEvent | StepPushedEvent)[])[]; | ||
| private dispatchedEvents: (PushedEvent | StepPushedEvent)[]; | ||
| private baseNavigationEvents: NavigationEvent[]; | ||
|
|
||
| constructor( | ||
| navigations: (( | ||
| navigationTime: number, | ||
| ) => (PushedEvent | StepPushedEvent)[])[], | ||
| baseNavigationEvents: NavigationEvent[] = [], | ||
| ) { | ||
| this.status = | ||
| navigations.length > 0 | ||
| ? NavigationProcessStatus.IDLE | ||
| : NavigationProcessStatus.SUCCEEDED; | ||
| this.pendingNavigations = navigations.slice(); | ||
| this.dispatchedEvents = []; | ||
| this.baseNavigationEvents = baseNavigationEvents; | ||
| } | ||
|
|
||
| captureNavigationOpportunity( | ||
| stack: Stack | null, | ||
| navigationTime: number, | ||
| ): (PushedEvent | StepPushedEvent)[] { | ||
| if (isTerminated(this.status)) return []; | ||
| if (stack !== null && stack.globalTransitionState !== "idle") return []; | ||
|
|
||
| if (this.pendingNavigations.length === 0) { | ||
| this.status = NavigationProcessStatus.SUCCEEDED; | ||
|
|
||
| return []; | ||
| } | ||
|
|
||
| const navigationHistory = stack | ||
| ? stack.events.filter(isNavigationEvent) | ||
| : []; | ||
|
|
||
| if ( | ||
| !( | ||
| this.verifyAllDispatchedEventsAreInNavigationHistory( | ||
| navigationHistory, | ||
| ) && this.verifyNoUnknownNavigationEvents(navigationHistory) | ||
| ) | ||
| ) { | ||
| this.pendingNavigations = []; | ||
| this.status = NavigationProcessStatus.FAILED; | ||
|
|
||
| return []; | ||
| } | ||
ENvironmentSet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const nextNavigation = this.pendingNavigations.splice(0, 1); | ||
| const nextNavigationEvents = nextNavigation.flatMap((navigation) => | ||
| navigation(navigationTime), | ||
| ); | ||
|
|
||
| this.dispatchedEvents.push(...nextNavigationEvents); | ||
| this.status = NavigationProcessStatus.PROGRESS; | ||
|
|
||
| return nextNavigationEvents; | ||
| } | ||
|
|
||
| getStatus(): NavigationProcessStatus { | ||
| return this.status; | ||
| } | ||
|
|
||
| private verifyAllDispatchedEventsAreInNavigationHistory( | ||
| navigationHistory: NavigationEvent[], | ||
| ): boolean { | ||
| return this.dispatchedEvents.every((event) => | ||
| navigationHistory.some((e) => e.id === event.id), | ||
| ); | ||
| } | ||
|
|
||
| private verifyNoUnknownNavigationEvents( | ||
| navigationHistory: NavigationEvent[], | ||
| ): boolean { | ||
| return navigationHistory.every((event) => | ||
| [...this.baseNavigationEvents, ...this.dispatchedEvents].some( | ||
| (e) => e.id === event.id, | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.