Conversation
92ac06f to
47f23ac
Compare
There was a problem hiding this comment.
Great effort tackling the refactoring of this code! When working on it, I also felt the need for it, but didn't quite know where to start.
I am a bit worried, changing this code while we are not able to test it properly. Tests related to the peer selector are currently skipped and we also can't test that in the app directly.
I also found the following regression:
- when switching from one project to another the page of the second project does not load and errors with the following:
|
It seems like your changes also solved #970 |
|
@MeBrei fixed the regression! I'd say it's the perfect time to strongly type everything peer-related so it's easier to reason about when we have actual peers to test 😁 |
MeBrei
left a comment
There was a problem hiding this comment.
Looks good to me! Just added a comment for understanding the new way of using remote.
I think the tests are failing, because some contains have to be replaced with pick and using data-cy. The workaround for wait only works with get / pick. (One example is trying to do cy.contains(".i-am-well-hidden").click(); in the source browsing specs.
| bind:currentRevision={$currentRevision} | ||
| {revisions} /> | ||
| <Remote {store} context="project"> | ||
| {#if project} |
There was a problem hiding this comment.
Why do we need this check? I thought that the slot (without name) in Remote is only rendered on Success.
There was a problem hiding this comment.
With the old patten (let:data={project}), that was the case, but now project is supplied by the store itself within the <script> tags, and its type is Project | undefined.
Also, Success doesn't necessarily mean there is data - it could be possible in some cases for endpoints to return empty arrays successfully (e.g. the projects/ endpoint could return [] if the user has no projects).
| name: string; | ||
| peerId?: string; | ||
| // The id of the project this branch belongs to | ||
| projectId: string; |
There was a problem hiding this comment.
| projectId: string; | |
| projectUrn: string; |
There was a problem hiding this comment.
This is worthy of its own pr because Project has an id in proxy - relevant to #840
| const defaultRevision = | ||
| defaultRevisions.branches.find( | ||
| branch => branch.name === defaultRevisions.defaultBranch | ||
| ) || defaultRevisions.branches[0]; |
There was a problem hiding this comment.
And in the event that there are no branches?
There was a problem hiding this comment.
In this case, the revision selector won't show up but the project will still render. Before, the RevisionSelector would create a default revision that may or may not actually exist (https://github.com/radicle-dev/radicle-upstream/pull/995/files#diff-ac6e05ed01605cc8ea4ba88d402e6541L30).
Should we throw an error instead? I don't really like the idea of creating a revision in ui that we don't actually receive from proxy.
There was a problem hiding this comment.
Ya, I definitely agree that creating a made-up revision doesn't make sense. Could we have like an error window in the selector saying something like, "We couldn't find any revisions o.O"?
Again, some sort of error handling would be good to think about when we see partial functions like these :)
There was a problem hiding this comment.
Same question here about whether we'll address these here.
| }; | ||
|
|
||
| $: currentPeer = | ||
| availablePeers.find(peer => peer.id === currentPeerId) || availablePeers[0]; |
There was a problem hiding this comment.
Again, what happens if there are no availablePeers?
There was a problem hiding this comment.
The PeerSelector wouldn't render in that case - this would mean that proxy didn't return any Revisions, or that it returned Revisions without an Identity, which I don't think it's supposed to do 🤔 .
Alternatively we could throw an error, or display the disabled PeerSelector with some sort of 'No peers found" messaging.
There was a problem hiding this comment.
which I don't think it's supposed to do .
Don't assume external resources will always do the thing they're supposed to. That's what I'm trying to get at here: we should think about failure cases and show appropriate errors when possible. Indexing arrays is a code smell in my eyes.
There was a problem hiding this comment.
@xla do you plan to do anything about this in this PR, out of interest?
xla
left a comment
There was a problem hiding this comment.
Dope! Currently we can't just export let store: Readable<remote.Data<T>>; the situation?
ui/DesignSystem/Component/SourceBrowser/RevisionSelector.svelte
Outdated
Show resolved
Hide resolved
@xla we can't export generics from svelte components 😢 |
|
Took this on and got it up-to-speed. PTAL. |
|
Steps to repro:
Expected result: see the project source. |
| }; | ||
|
|
||
| $: currentPeer = | ||
| availablePeers.find(peer => peer.id === currentPeerId) || availablePeers[0]; |
There was a problem hiding this comment.
@xla do you plan to do anything about this in this PR, out of interest?
| } | ||
|
|
||
| // Currently supported revision types in UI: | ||
| export type SupportedRevision = Branch | Tag; |
There was a problem hiding this comment.
Is Sha not supported? 🤔 Or what's the issue here?
| const defaultRevision = | ||
| defaultRevisions.branches.find( | ||
| branch => branch.name === defaultRevisions.defaultBranch | ||
| ) || defaultRevisions.branches[0]; |
There was a problem hiding this comment.
Same question here about whether we'll address these here.
| export const currentPeerId = writable(null); | ||
| export const resetCurrentPeerId = (): void => currentPeerId.set(null); | ||
|
|
||
| export const currentRevision = writable<Branch | Tag | undefined>(undefined); |
There was a problem hiding this comment.
| export const currentRevision = writable<Branch | Tag | undefined>(undefined); | |
| export const currentRevision = writable<SupportedRevision | undefined>(undefined); |
We could re-use the above type right?
| kind: Kind.FetchCommits; | ||
| projectId: string; | ||
| revision: Branch; | ||
| revision: Branch | Tag; |
There was a problem hiding this comment.
| revision: Branch | Tag; | |
| revision: SupportedRevision; |
|
|
||
| interface UpdateCurrentRevision extends event.Event<Kind> { | ||
| kind: Kind.UpdateCurrentRevision; | ||
| revision: Branch | Tag; |
There was a problem hiding this comment.
| revision: Branch | Tag; | |
| revision: SupportedRevision; |
It's not used and generates svelte-check warnings.


Why?
When we created
Remote.svelte, we didn't have the option to type-check data in components. To enable its usage as we migrate from JS -> TS, I asserted ananytype here:...which essentially destroys any notion of type safety in the
datavalue (YOLO🤪). While this is convenient, it doesn't allow us to use the magic of the type system in the most critical places - i.e. when we're rendering data that we expect to be of a certain shape.The
Remote.sveltecomponent does make it very easy to loadslots based on the remote store status (Loading/Success/Error), which I think is ergonomic and useful and can perhaps be its only function until we have generics in svelte. With this in mind, I'd like to transition away from thelet:data={something}pattern and towards one where we know exactly what theTinData<T>is before we render components.To kick off this effort, I've typed the
Projectscreen and its associated components, created a single source of truth for the project -> peer -> revisions -> commits data flow, and added a lot of comments to clarify what exactly these types are supposed to be.Closes #970