-
Notifications
You must be signed in to change notification settings - Fork 323
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
Expand parent directories of opened projects #11947
base: develop
Are you sure you want to change the base?
Conversation
🧪 Storybook is successfully deployed!📊 Dashboard:
|
const { getText } = textProvider.useText() | ||
const driveStore = useDriveStore() | ||
const toggleDirectoryExpansion = useToggleDirectoryExpansion() | ||
const isExpanded = useStore(driveStore, (storeState) => | ||
storeState.expandedDirectoryIds.includes(item.id), | ||
const isExpanded = useStore( |
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 a good candidate for encapsulation behind a hook
setCategory(categoryId) | ||
setExpandedDirectoryIds(pathToDirectory.map(({ id }) => id).concat(targetDirectory)) | ||
setCategory(rootDirectoryInThePath.categoryId) | ||
const expandedDirectories = structuredClone(driveStore.getState().expandedDirectories) |
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 not good, this is a common thing to change and instead of encapsulating it behind a function/hook, we expose the internals outside.
Ideally, changing the expanded directories should not affect other components, because the logic should be encapsilated. Otherwise we violate SOLID principles
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.
should not affect other components
wdym?
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.
We're changing the internal structure of the data. If everything is encapsulated correctly, none of the consumers will be affected by this change. Internal change doesn't affect the API
@@ -219,6 +222,10 @@ export function useLocalCategoryList() { | |||
type: 'local', | |||
id: 'local', | |||
label: getText('localCategory'), | |||
/** The root path of this category. */ | |||
get rootPath() { |
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 get why it's a getter 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.
the root path of the local backend may change, and currently there's no way to reactively subscribe to it
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.
getter doesn't make it reactive either
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.
true, but we don't need it to be reactive afaik, we just need it to be correct at time of access, and this should (theoretically) do that
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.
Theoretically? Do we use the path somewhere, except
[category.rootPath, privateExpandedDirectoryIds, rootDirectoryId], |
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.
here is the main place where it is used:
for (const [rootPath, directoriesInCategory] of unsafeEntries(expandedDirectories)) { |
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 also don't understand why it's so complex. Why don't we store what we need in the localstorage? Why we have to do these... manipulations. We don't do anything like that when we expand folders.
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 both places you don't need the getter
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 it's so complex
because we need to traverse all ancestors here, not just the direct parent
afaict this is pretty difficult on the local backend because we don't have a real parentsPath
- and it'd be a bit difficult to do this without making changes because currently directory ids on the cloud backend include slashes which conflicts with the slash delimiter used in parentsPath
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.
Can we move this logic to LocalBackend
and RemoteBackend
? so we'll just call a method in related backend. Also I didn't see the unification with the Path column (AFAIR it does the same but for the cloud)
setExpandedDirectories(expandedDirectories) | ||
}) | ||
|
||
React.useEffect(() => { |
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 we use useEffect
here, instead of setting default values in the store?
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.
as mentioned on discord a couple days ago: we can do that but this code depends on a couple of other stores (see the start of the function)
i feel like putting this code in the store will make the store too tightly coupled to the other stores, but if you think that's fine then i'm happy to make the change
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.
We can pass the stores as props and keep them decoupled
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.
Your code couples stores but in unobvious way
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.
setting default values in the store
small problem: fetching parent directories for cloud projects is async
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.
small problem: fetching parent directories for cloud projects is async
We have suspense for that, haven't we?
const cloudProjects = launchedProjects.filter( | ||
(project) => project.type === backendModule.BackendType.remote, | ||
) | ||
const promises = cloudProjects.map((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.
I remember we came to conclusion to get the data from a project asset? and add a ts lambda for that.
Also, This should be a suspense query, otherwise the tree will expand unexpectedly.
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 add a ts lambda for that
sure, but adding a ts lambda is out of scope for this PR imo
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'm not satisfied with the implementation
🚫 Also, We need integration tests for this feature |
@@ -1,83 +0,0 @@ | |||
import { DirectoryId } from '#/services/Backend' |
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 did we remove the tests?
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.
behavior has been changed so these tests are no longer relevant
we can write a new test in the same style if that works?
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.
Yes for sure. Unrelevant tests can be removed, but we need to add new ones if we add a new
behavior. Coverage shouldn't decrease
@@ -41,8 +42,8 @@ interface DriveStore { | |||
readonly setCanDownload: (canDownload: boolean) => void | |||
readonly pasteData: PasteData<DrivePastePayload> | null | |||
readonly setPasteData: (pasteData: PasteData<DrivePastePayload> | null) => void | |||
readonly expandedDirectoryIds: readonly DirectoryId[] | |||
readonly setExpandedDirectoryIds: (selectedKeys: readonly DirectoryId[]) => void | |||
readonly expandedDirectories: Record<Path, readonly DirectoryId[]> |
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.
Btw, why is it Path
instead of CategoryId
?
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.
we want to expand parents of the current asset on all categories this asset appears in (so all categories whose path matches the path of the current asset)
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.
How many categories can we have with the same asset ?
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.
cloud: 2 (root + user or root + team)
local: unlimited (all parent directories of the asset, if multiple are favorited)
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.
AFAIR for local projects id ~~~ path,
Root
will be removed soon (no need to think about) as part of simplification
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.
*local categories? yeah, fair enough. so, what's the verdict? switch to CategoryId
now?
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.
Yeah, if possible it's better to use ids instead
const isExpanded = expandedDirectoryIds.includes(directoryId) | ||
return useEventCallback((id: DirectoryId, override?: boolean) => { | ||
const expandedDirectories = driveStore.getState().expandedDirectories | ||
const isExpanded = expandedDirectories[category.rootPath]?.includes(id) ?? false |
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.
good candidate for encapsulation
Pull Request Description
Important Notes
None
Testing Instructions
enso/
to local favorites and open project)Checklist
Please ensure that the following checklist has been satisfied before submitting the PR:
The documentation has been updated, if necessary.Screenshots/screencasts have been attached, if there are any visual changes. For interactive or animated visual changes, a screencast is preferred.Scala,
Java,
TypeScript,
and
Rust
style guides. In case you are using a language not listed above, follow the Rust style guide.
Unit tests have been written where possible.If meaningful changes were made to logic or tests affecting Enso Cloud integration in the libraries,or the Snowflake database integration, a run of the Extra Tests has been scheduled.
If applicable, it is suggested to paste a link to a successful run of the Extra Tests.