-
Notifications
You must be signed in to change notification settings - Fork 491
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
Refactor major components to streamline logic #1589
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not familiar with redux enough to provide meaningful feedback there, but switching from post-success and post-failure to a simple try/catch makes it easier to reason about and maintain 👍
Small ask: as noted during our chat today, we need to understand if/how this change impacts the way WebUI's opt-in Countly metrics are gathered (not a blocker, but we should know the impact before merging this).
@rafaelramalho19 mind blocking some time this week to review this?
(cc @olizilla / @hacdias in case they are around and have thoughts)
This was a good call @lidel, indeed analytics seems to use mix of regexps and strings to hook into these actions, which is also a reason I have missed those: https://github.com/ipfs-shipyard/ipfs-webui/blob/master/src/bundles/analytics.js#L6-L20 I will look into updating that module to take into account the changes made here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Gozala I am mildly in favor, but afraid I am not aware of all ramifications of this refactor.
Mind scheduling a call with @olizilla this or next week to go over changes and unblock this?
(Ideally, me and @rafaelramalho19 would join it too)
src/bundles/files/index.js
Outdated
} | ||
} | ||
case 'Active': { | ||
const { pending, rest } = pullPendig(state.pending, job) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have a typo in the function name 😄 it should be pullPending
@@ -150,14 +158,20 @@ const readSetting = (id) => { | |||
} | |||
|
|||
try { | |||
return JSON.parse(setting) | |||
return JSON.parse(setting || '') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you fallbacking to ''
? If you do JSON.parse('')
it actually gives you the "Unexpected end of JSON input" error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because setting could be a null
(e.g. if try above failed), while JSON.parse
has type of (input:string) -> JSON
. Passing string|null
would be rejected by a type checker. setting || ''
on the other hand has string
type.
I would say that error in this scenario is an expected behavior.
@@ -150,14 +158,20 @@ const readSetting = (id) => { | |||
} | |||
|
|||
try { | |||
return JSON.parse(setting) | |||
return JSON.parse(setting || '') | |||
} catch (_) { | |||
// res was probably a string, so pass it on. | |||
return setting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the ''
fallback should be here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well return type of this function is string | null
so null
is fine. It maybe reasonable to change type to string
and check for ''
, but it would be bit trickier to catch all the places where ''
case isn't considered because type checker won't really help, unlike spotting cases where string | null
is treated as string.
src/bundles/ipfs-provider.js
Outdated
/** | ||
* @returns {function(Context):Promise<void>} | ||
*/ | ||
doInitIpfs: () => async (context) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to refactor from store
to context
? From the redux bundler documentation, they always call this variable store and not context.
Doing store.dispatch
seems more intuitive than context.dispatch
🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out what redux-bundle passes into doX
style things isn't a store but rather it's own context thing that contains store
(see https://reduxbundler.com/api-reference/bundle#bundle-doxA) I think I originally was calling it a store as well, but then it got confusing because in some cases it is store and in others it isn't.
} | ||
case 'Exit': { | ||
const { result } = task | ||
if (result.ok) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would invert this into early exit
if (!result.ok) {
return { ...state, ready: false, failed: true }
}
// ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why ?
Co-authored-by: Rafael Ramalho <[email protected]>
Co-authored-by: Rafael Ramalho <[email protected]>
fcd81d3
to
d9343c7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, no regressions as far I can tell, and should make progress reporting easier to reason about.
@Gozala what would be the next step for having visual feedback during file import? Any PRs/issues in other repos that I could help landing/unblocking?
@rafaelramalho19 please block some time this week to do another pass at this 🙏
declare module "redux-bundler" { | ||
|
||
interface CreateSelector { | ||
<State, I, O>(n1: string, f: (inn: I) => O): (state: State) => O, | ||
<State, I1, I2, O>(n1: string, n2: string, f: (i1: I1, i2: I2) => O): (state: State) => O | ||
<State, I1, I2, I3, O>(n1: string, n2: string, n3: string, f: (i1: I1, i2: I2, i3: I3) => O): (state: State) => O | ||
<State, I1, I2, I3, I4, O>(n1: string, n2: string, n3: string, n4: string, f: (i1: I1, i2: I2, i3: I3, i4: I4) => O): (state: State) => O | ||
<State, I1, I2, I3, I4, I5, O>(n1: string, n2: string, n3: string, n4: string, n5: string, f: (i1: I1, i2: I2, i3: I3, i4: I4, i5: I5) => O): (state: State) => O | ||
|
||
<State, I1, O>(s1: (state: State) => I1, f: (inn: I1) => O): (state: State) => O, | ||
<State, I1, I2, O>(s1: (state: State) => I1, s2: (state: State) => I2, f: (i1: I1, i2: I2) => O): (state: State) => O | ||
<State, I1, I2, I3, O>(s1: (state: State) => I1, s2: (state: State) => I2, s3: (state: State) => I3, f: (i1: I1, i2: I2, i3: I3) => O): (state: State) => O | ||
<State, I1, I2, I3, I4, O>(s1: (state: State) => I1, s2: (state: State) => I2, s3: (state: State) => I3, s4: (state: State) => I4, f: (i1: I1, i2: I2, i3: I3, i4: I4) => O): (state: State) => O | ||
<State, I1, I2, I3, I4, I5, O>(s1: (state: State) => I1, s2: (state: State) => I2, s3: (state: State) => I3, s4: (state: State) => I4, s5: (state: State) => I5, f: (i1: I1, i2: I2, i3: I3, i4: I4, i5: I5) => O): (state: State) => O | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💭 OOF, I have bad feelings about maintaining this long term.
@Gozala thoughts on how feasible it is to pick HenrikJoreteg/redux-bundler#69 up and add those types to the upstream package (with GitHub action that tests them there)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe it is worth it honestly. Because it is not really possible to add proper typings to the redux-bundler. What I do here is just provide some subset which also requires very particular use of redux-bundler (splitting actions and selectors, which seems to go against it's design goals), and even then have to manually annotate. Given these limitations, I doubt it would be accepted, I also don't think the effort to do that is going to pay off.
If we were to invest more time into this, I would rather consider using redux-bundler alternatives that would be more type friendly / ready than redux-bundler is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the 2 small nits it's good to merge.
Good job 👍
Problem Statement
As I worked on #1568 I had hard time figuring out what was happening where, in fact I left a TODO there because I was unable to figure out what
make
function in thebundles/files/utils
was trying to do:https://github.com/ipfs-shipyard/ipfs-webui/blob/16e15a25ab999d4912ea05ebb81e9bad67457770/src/bundles/files/utils.js#L12-L78
Then as I was trying to find what listenes to
FILES_WRITE_UPDATED
actions, I was able to find nothing. Later I discovered thatmake
function does some trickery where it dispatchesFILES_X_STARTED
,FILES_X_FINISHED
actions and also does some extra stuff on finish. This all makes really difficult to know what happens where.Solution
This pull request is attempt to streamline everything, so that looking at the specific
doX
function tells you all you need to know without having to look around other places to get a sense what happens after finish etc... In order to accomplish that it switches fromFILES_X_STARTED
/FILES_X_FINISHED
style action dispatch to makingFILES_X
actions dispatch events withjob
field that that updates it's state (as in state machine) fromIdle
->Active
-> (Done
|Failed
).This also gets rid of all the post success followups:
https://github.com/ipfs-shipyard/ipfs-webui/blob/16e15a25ab999d4912ea05ebb81e9bad67457770/src/bundles/files/utils.js#L42-L60
And post failure followups:
https://github.com/ipfs-shipyard/ipfs-webui/blob/16e15a25ab999d4912ea05ebb81e9bad67457770/src/bundles/files/utils.js#L61-L76
By using
try { ... } finally { ... }
in in thedoX
functions, so it's plain and clear:https://github.com/ipfs-shipyard/ipfs-webui/blob/f235c331c80068a023359efd50cbef48a6008d9b/src/bundles/files/actions.js#L324-L338
Pull request also introduces idea of non atomic tasks that (start, make progress, finish or fail) so that
doX
tasks that report progress don't need to do custom workarounds:https://github.com/ipfs-shipyard/ipfs-webui/blob/16e15a25ab999d4912ea05ebb81e9bad67457770/src/bundles/files/actions.js#L298-L310
Instead spawned tasks (that follow above described state machine) can yield, to produce update states:
https://github.com/ipfs-shipyard/ipfs-webui/blob/f235c331c80068a023359efd50cbef48a6008d9b/src/bundles/files/actions.js#L243-L251
And it adds typing for all of this so any inconsistencies could caught early on. In the process I also fixed bunch that existed.