-
Notifications
You must be signed in to change notification settings - Fork 51
Conversation
92ac06f
to
47f23ac
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.
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 😁 |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
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.
Mostly observing 👀
export interface Branch { | ||
type: RevisionType.Branch; | ||
name: string; | ||
peerId?: string; | ||
// The id of the project this branch belongs to | ||
projectId: string; |
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.
projectId: string; | |
projectUrn: string; |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And in the event that there are no branches?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question here about whether we'll address these here.
dispatch("select", { peerId }); | ||
}; | ||
|
||
$: currentPeer = | ||
availablePeers.find(peer => peer.id === currentPeerId) || availablePeers[0]; |
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.
Again, what happens if there are no availablePeers
?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xla do you plan to do anything about this in this PR, out of interest?
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.
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. |
dispatch("select", { peerId }); | ||
}; | ||
|
||
$: currentPeer = | ||
availablePeers.find(peer => peer.id === currentPeerId) || availablePeers[0]; |
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.
@xla do you plan to do anything about this in this PR, out of interest?
} | ||
|
||
export interface Sha { | ||
type: RevisionType.Sha; | ||
sha: string; | ||
} | ||
|
||
// Currently supported revision types in UI: | ||
export type SupportedRevision = Branch | Tag; |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const currentRevision = writable<Branch | Tag | undefined>(undefined); | |
export const currentRevision = writable<SupportedRevision | undefined>(undefined); |
We could re-use the above type
right?
@@ -170,12 +228,13 @@ interface FetchCommit extends event.Event<Kind> { | |||
interface FetchCommits extends event.Event<Kind> { | |||
kind: Kind.FetchCommits; | |||
projectId: string; | |||
revision: Branch; | |||
revision: Branch | Tag; |
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.
revision: Branch | Tag; | |
revision: SupportedRevision; |
|
||
interface UpdateCurrentRevision extends event.Event<Kind> { | ||
kind: Kind.UpdateCurrentRevision; | ||
revision: Branch | Tag; |
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.
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 anany
type here:...which essentially destroys any notion of type safety in the
data
value (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.svelte
component does make it very easy to loadslot
s 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 theT
inData<T>
is before we render components.To kick off this effort, I've typed the
Project
screen 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