Skip to content
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

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from

Conversation

somebody1234
Copy link
Contributor

Pull Request Description

Important Notes

None

Testing Instructions

  • Open a project on each backend (cloud and local) and test that it works for both
  • Open a project and make sure it's expanded on a different category (team category and root, user category and root, add subdirectory of 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.
  • All code follows the
    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.

@somebody1234 somebody1234 added CI: No changelog needed Do not require a changelog entry for this PR. x-new-feature Type: new feature request g-dashboard labels Dec 30, 2024
Copy link

github-actions bot commented Dec 30, 2024

🧪 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(
Copy link
Contributor

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)
Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Contributor

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() {
Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Contributor

@MrFlashAccount MrFlashAccount Jan 6, 2025

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],
?

Copy link
Contributor Author

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)) {

Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Contributor

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(() => {
Copy link
Contributor

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?

Copy link
Contributor Author

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

Copy link
Contributor

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

Copy link
Contributor

@MrFlashAccount MrFlashAccount Jan 6, 2025

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

Copy link
Contributor Author

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

Copy link
Contributor

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) =>
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

@MrFlashAccount MrFlashAccount left a 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

@MrFlashAccount
Copy link
Contributor

MrFlashAccount commented Jan 6, 2025

🚫 Also, We need integration tests for this feature

@@ -1,83 +0,0 @@
import { DirectoryId } from '#/services/Backend'
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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[]>
Copy link
Contributor

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?

Copy link
Contributor Author

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)

Copy link
Contributor

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 ?

Copy link
Contributor Author

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)

Copy link
Contributor

@MrFlashAccount MrFlashAccount Jan 6, 2025

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

Copy link
Contributor Author

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?

Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good candidate for encapsulation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: No changelog needed Do not require a changelog entry for this PR. g-dashboard x-new-feature Type: new feature request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants