Skip to content

Adds Redux store#30

Closed
cguedes wants to merge 3 commits into
mainfrom
cguedes/redux-store
Closed

Adds Redux store#30
cguedes wants to merge 3 commits into
mainfrom
cguedes/redux-store

Conversation

@cguedes

@cguedes cguedes commented May 24, 2023

Copy link
Copy Markdown
Collaborator

This PR adds redux using redux toolkit to simplify the application data flow and closes issue #33

Slices

With this change, two slices were created:

  • selection - store for the selected text in the editor
  • openedFiles - store for the opened files, and the selected file.

UI Component Changes

With the addition of the redux store, the following components were changed/added:

  • TipTapEditor - to dispatch selection changes (debounced)
  • AIView - use the store instead of props
  • FoldersView - use the store instead of props
  • CenterPaneView - rewrite to display different types of center views depending on the selected file
  • OpenedFilesView - added to display and manage the selected files. It allow file selection and close

Current UI

image

@cguedes cguedes requested a review from sehyod May 24, 2023 12:32
sehyod
sehyod previously approved these changes May 24, 2023
Comment thread src/views/AIView.tsx Outdated
Comment thread src-tauri/src/main.rs Outdated
Comment thread src/views/CenterPaneView.tsx Outdated
@cguedes cguedes marked this pull request as ready for review May 24, 2023 15:59
@cguedes cguedes requested a review from sehyod May 24, 2023 15:59
sehyod
sehyod previously approved these changes May 24, 2023
Comment thread src/views/CenterPaneView.tsx
@hammer

hammer commented May 24, 2023

Copy link
Copy Markdown
Contributor

Nice work! In general I prefer to keep PRs restricted to a single coherent set of changes, so in the future please break out the startup scripts, TailwindCSS, and git hook changes separately if it's not too much trouble.

@cguedes

cguedes commented May 24, 2023

Copy link
Copy Markdown
Collaborator Author

Nice work! In general I prefer to keep PRs restricted to a single coherent set of changes, so in the future please break out the startup scripts, TailwindCSS, and git hook changes separately if it's not too much trouble.

You are right. I will do my best to scope these changes in separate PRs.

cguedes added 2 commits May 25, 2023 14:11
- Adds first slice for the selected text
- got rid of onSelectionChange

Still not convinced on where to store the slice files. Under `features` (like they have in the docs/examples) or `store` or `redux` (keeping all the redux store in a single place)
- Created OpenedFilesView (tab panes)
- Cleanup App, FoldersView and CenterPane
@cguedes

cguedes commented May 25, 2023

Copy link
Copy Markdown
Collaborator Author

@hammer @sergioramos I've cleaned up this PR to only include the code related to the redux-store (for the #33).

Created issues and PRs for all the other features implemented before here.

@cguedes cguedes requested a review from sehyod May 25, 2023 13:15
@danvk danvk self-requested a review May 25, 2023 13:58

@danvk danvk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It was interesting to learn about Redux Toolkit in the process of reviewing this PR! I left a lot of comments, but at a high level:

  • I do think it's a good idea to have some sort of state management tool for truly global application state.
  • That tool may or may not be RKT, as per our discussion of Recoil earlier today.
  • Even with a state management library, we should try to:
    1. Use built-in state management (useState) for state that's local to a component.
    2. Keep low-level components presentational (store agnostic) for better encapsulation / separation of concerns.
    3. Avoid multiple sources of truth (i.e. state in the global store and in local state).

import { EDITOR_EXTENSIONS, INITIAL_CONTENT } from './TipTapEditorConfigs';
import { EDITOR_EXTENSIONS } from './TipTapEditorConfigs';

export function TipTapEditor({ editorRef, editorContent }: EditorProps) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I prefer to keep Props type declarations in the same place as their components. They're always tightly coupled so there's no point in separating them.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I agree. We did this to suppport multiple editor implementation(i.e. Lexical) and we wanted to keep a common interface, but we can drop that for now.

},
}));
}, [editorContent, onSelectionChange]);
setEditor(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is existing code, but will this create a new text editor any time we want to change the text in it? I imagine that would be expensive?

I would have expected this effect to (conceptually) look more like:

useEffect(() => {
  editor.setText(editorContent);
}, [editorContent]);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We need to refactor this code (interaction with TipTap), but unfortunately their API to set/reset content isn't straightforward. CC @sehyod

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes, unfortunately, using setText leads to unwanted behaviours, especially regarding the history, because there is no easy way to clear the history (cf ueberdosis/tiptap#491).
I think at some point we will have in-memory editors anyway, corresponding to open tabs, so the create operation would only be called once per file.

dispatch(setTextSelection(debouncedSelection));
}, [dispatch, debouncedSelection]);

useEffect(() => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Existing code, but suggest adding a one line comment explaining what this is doing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We moved that code already to the receiving party and it's now more simple to understand.
Also, this is only "sample interaction code" between the editor and the AI Panel.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there value in having nested directories under features? What would go in openedFiles other than openedFilesSlice.ts? I'd suggest flattening the structure until there's a reason to nest.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I wasn't sure we would have more files for each feature. But agree we should start with a simpler tree.


export interface FileEntryInfo {
entry: FileEntry;
type: FileEntryType;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This would typically be modeled as a tagged union since some combinations (type=PDF and isDirectory=true) don't make sense. This may be fine for now, but if we ever start adding properties that are only relevant for one type, this should be changed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Exactly. I wasn't sure if this was the correct approach.
I wanted to create a wrapper over the file entry with additional properties (ex: type) but wasn't sure how "complex" the TS should be.

Comment thread src/views/FoldersView.tsx
}

const FileTree = ({ files, root, ...props }: FileTreeBaseProps) => {
function FileTree({ files, root = false }: { files: FileEntry[]; root?: boolean }) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Keep FileTree and FileTreeNode as pure presentational components that don't know about the global store. FoldersView can tie these to the store. This will be helpful for maintaining some separation / abstraction.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sure, I think that we would build more code but in the end it pays off as we can test the UI component in isolation from the store, and we can also replace UI elements (ex: TreeNode) by libraries.

Keep low-level components presentational (store agnostic) for better encapsulation / separation of concerns.

Comment thread src/views/FoldersView.tsx

function handleOnClick(file: FileEntry): void {
dispatch(
openFile({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Now that you have a FileEntryInfo type, would it be simpler for FileTree to use that instead of muxing between FileEntry and FileEntryInfo?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I see, that's in line with the change of refactoring the component to be more isolated.

const dispatch = useDispatch();
const openedFiles = useAppSelector((state) => state.openedFiles);

if (openedFiles.files.length === 0) return null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a reason to return null in this case rather than an empty <div>?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

className={cx(
'flex flex-wrap items-center justify-between',
'cursor-pointer select-none whitespace-nowrap',
'px-2 py-1', //

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

what's going on with the // here? (also in a few other places)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is for legibility. That would make prettier wrap the array in multiple lines, even with fewer statements.
Also, the idea is to break (using cx) a long list of rules into simpler rules/lines. This also makes diff simpler.

dispatch(selectFile(fileInfo));
}}
className={cx(
'flex flex-wrap items-center justify-between',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

With the caveat that I haven't used TailwindCSS before so it's hard for me to judge whether this is normal… the logic of the component really feels like it's getting drowned out by all these class names. Would it make sense to factor out a meaningfully-named component that applies most of these styles? https://tailwindcss.com/docs/reusing-styles#extracting-components-and-partials

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sure. The focus of this PR was adding the state management (Redux) so I didn't added much ❤️ into that code. For the IDE issue I will fix this.

@sergioramos sergioramos marked this pull request as draft May 31, 2023 09:30
@cguedes

cguedes commented Jun 9, 2023

Copy link
Copy Markdown
Collaborator Author

Closing this as we decided not to use Redux for this project. See #33 for more details.

@cguedes cguedes closed this Jun 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants