Skip to content

Commit

Permalink
#276 Improve File Tree Tab
Browse files Browse the repository at this point in the history
- add a usable file list to filesReducer.ts state
  • Loading branch information
MaximilianZenz committed Feb 14, 2025
1 parent 956097a commit a3bf3b2
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import fileListStyles from './fileList.module.scss';
import { useSelector } from 'react-redux';
import { AppDispatch, RootState, store as globalStore, useAppDispatch } from '../../../../redux';
import { useEffect } from 'react';
import { FileListElementTypeType } from '../../../../types/data/fileListType.ts';
import { FileTreeElementTypeType } from '../../../../types/data/fileListType.ts';
import { filterFileTree, generateFileTree } from './fileListUtilities/fileTreeUtilities.tsx';
import FileListFolder from './fileListElements/fileListFolder.tsx';
import { DatabaseSettingsDataPluginType } from '../../../../types/settings/databaseSettingsType.ts';
import DataPluginStorage from '../../../../utils/dataPluginStorage.ts';
import { setFileList, setFilesDataPluginId } from '../../../../redux/reducer/data/filesReducer.ts';
import { DataPluginFile } from '../../../../plugins/interfaces/dataPluginInterfaces/dataPluginFiles.ts';

function FileList(props: { orientation?: string; search: string }) {
const dispatch: AppDispatch = useAppDispatch();
const currentDataPlugins = useSelector((state: RootState) => state.settings.database.dataPlugins);
const fileTrees = useSelector((state: RootState) => state.files.fileTrees);
const fileLists = useSelector((state: RootState) => state.files.fileLists);
const fileCounts = useSelector((state: RootState) => state.files.fileCounts);

Expand All @@ -29,15 +31,20 @@ function FileList(props: { orientation?: string; search: string }) {
dispatch(
setFileList({
dataPluginId: dP.id !== undefined ? dP.id : -1,
files: {
fileTree: {
name: '/',
type: FileListElementTypeType.Folder,
type: FileTreeElementTypeType.Folder,
children: generateFileTree(files),
checked: true,
foldedOut: true,
isRoot: true,
},
fileCount: files.length,
files: files.map((f: DataPluginFile) => {
return {
element: f,
checked: true,
};
}),
}),
),
)
Expand Down Expand Up @@ -68,6 +75,7 @@ function FileList(props: { orientation?: string; search: string }) {
}
});

console.log(fileLists[filesDataPluginId]);
return (
<>
<div
Expand All @@ -79,8 +87,8 @@ function FileList(props: { orientation?: string; search: string }) {
}>
<div>{fileCounts[filesDataPluginId]} Files indexed</div>
<div>
{fileLists[filesDataPluginId] ? (
<FileListFolder folder={filterFileTree(fileLists[filesDataPluginId],props.search)} foldedOut={true}></FileListFolder>
{fileTrees[filesDataPluginId] ? (
<FileListFolder folder={filterFileTree(fileTrees[filesDataPluginId], props.search)} foldedOut={true}></FileListFolder>
) : (
<span className="loading loading-spinner loading-xs text-accent"></span>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import fileListElementsStyles from './fileListElements.module.scss';
import { FileListElementType } from '../../../../../types/data/fileListType.ts';
import { FileTreeElementType } from '../../../../../types/data/fileListType.ts';
import FileIcon from '../../../../../assets/file_gray.svg';
import { updateFileListElement } from '../../../../../redux/reducer/data/filesReducer.ts';
import { AppDispatch, useAppDispatch } from '../../../../../redux';
import { formatName } from '../fileListUtilities/fileTreeUtilities.tsx';

function FileListFile(props: { file: FileListElementType }) {
function FileListFile(props: { file: FileTreeElementType }) {
const dispatch: AppDispatch = useAppDispatch();
return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import fileListElementsStyles from './fileListElements.module.scss';
import { FileListElementType, FileListElementTypeType } from '../../../../../types/data/fileListType.ts';
import { FileTreeElementType, FileTreeElementTypeType } from '../../../../../types/data/fileListType.ts';
import FolderIcon from '../../../../../assets/folder_gray.svg';
import FolderOpenIcon from '../../../../../assets/folder_open_gray.svg';
import FileListFile from './fileListFile.tsx';
import { AppDispatch, useAppDispatch } from '../../../../../redux';
import { updateFileListElement } from '../../../../../redux/reducer/data/filesReducer.ts';
import { formatName } from '../fileListUtilities/fileTreeUtilities.tsx';

function FileListFolder(props: { folder: FileListElementType; foldedOut: boolean }) {
function FileListFolder(props: { folder: FileTreeElementType; foldedOut: boolean }) {
const dispatch: AppDispatch = useAppDispatch();

return (
Expand Down Expand Up @@ -43,9 +43,9 @@ function FileListFolder(props: { folder: FileListElementType; foldedOut: boolean
}
return 0;
})
.sort((e) => (e.type === FileListElementTypeType.Folder ? -1 : 1))
.sort((e) => (e.type === FileTreeElementTypeType.Folder ? -1 : 1))
.map((element, i) => {
if (element.type === FileListElementTypeType.Folder && element.children) {
if (element.type === FileTreeElementTypeType.Folder && element.children) {
return <FileListFolder key={`fileListElement${i}`} folder={element} foldedOut={false}></FileListFolder>;
} else {
return <FileListFile key={`fileListElement${i}`} file={element}></FileListFile>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { FileListElementType, FileListElementTypeType } from '../../../../../types/data/fileListType.ts';
import { FileTreeElementType, FileTreeElementTypeType } from '../../../../../types/data/fileListType.ts';
import { DataPluginFile } from '../../../../../plugins/interfaces/dataPluginInterfaces/dataPluginFiles.ts';
import fileListElementsStyles from '../fileListElements/fileListElements.module.scss';

export function generateFileTree(files: DataPluginFile[]): FileListElementType[] {
export function generateFileTree(files: DataPluginFile[]): FileTreeElementType[] {
return convertData(files).content;
}

Expand All @@ -18,15 +18,15 @@ function convertData(files: DataPluginFile[]) {
return convertedData;
}

function genPathObjectString(convertedData: FileListElementType[], pathParts: string[], file: DataPluginFile, id: number) {
function genPathObjectString(convertedData: FileTreeElementType[], pathParts: string[], file: DataPluginFile, id: number) {
const currElm = pathParts.shift();
id++;
if (currElm) {
if (pathParts.length === 0) {
convertedData.push({
name: currElm,
id: id,
type: FileListElementTypeType.File,
type: FileTreeElementTypeType.File,
checked: true,
element: file,
foldedOut: false,
Expand All @@ -38,7 +38,7 @@ function genPathObjectString(convertedData: FileListElementType[], pathParts: st
elem = {
name: currElm,
id: id,
type: FileListElementTypeType.Folder,
type: FileTreeElementTypeType.Folder,
children: [],
checked: true,
foldedOut: false,
Expand All @@ -58,21 +58,21 @@ function genPathObjectString(convertedData: FileListElementType[], pathParts: st
return id;
}

export function filterFileTree(fileTree: FileListElementType, search: string): FileListElementType {
export function filterFileTree(fileTree: FileTreeElementType, search: string): FileTreeElementType {
if (fileTree.children) {
return {
...fileTree,
searchTerm: search,
children: fileTree.children
?.map((child) => {
if (child.type === FileListElementTypeType.Folder) {
if (child.type === FileTreeElementTypeType.Folder) {
return filterFileTree(child, search);
} else {
return { ...child, searchTerm: search };
}
})
.filter((child) => {
if (child.type === FileListElementTypeType.Folder && child.children) {
if (child.type === FileTreeElementTypeType.Folder && child.children) {
return child.children.length > 0;
}
return child.element?.path.toLowerCase().includes(search.toLowerCase());
Expand Down
45 changes: 28 additions & 17 deletions binocular-frontend-new/src/redux/reducer/data/filesReducer.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import Config from '../../../config.ts';
import { FileListElementType } from '../../../types/data/fileListType.ts';
import { FileListElementType, FileTreeElementType } from '../../../types/data/fileListType.ts';

export interface FilesInitialState {
fileLists: { [id: number]: FileListElementType };
fileTrees: { [id: number]: FileTreeElementType };
fileLists: { [id: number]: FileListElementType[] };
fileCounts: { [id: number]: number };
dataPluginId: number | undefined;
}

const initialState: FilesInitialState = {
fileTrees: {},
fileLists: {},
fileCounts: {},
dataPluginId: undefined,
Expand All @@ -26,21 +28,27 @@ export const filesSlice = createSlice({
}
},
reducers: {
setFileList: (state, action: PayloadAction<{ dataPluginId: number; files: FileListElementType; fileCount: number }>) => {
setFileList: (state, action: PayloadAction<{ dataPluginId: number; fileTree: FileTreeElementType; files: FileListElementType[] }>) => {
const fileCount: number = state.fileCounts[action.payload.dataPluginId];
if (fileCount === undefined || fileCount !== action.payload.fileCount) {
if (fileCount === undefined || fileCount !== action.payload.files.length) {
state.fileTrees[action.payload.dataPluginId] = action.payload.fileTree;
state.fileCounts[action.payload.dataPluginId] = action.payload.files.length;
state.fileLists[action.payload.dataPluginId] = action.payload.files;
state.fileCounts[action.payload.dataPluginId] = action.payload.fileCount;
}
localStorage.setItem(`${filesSlice.name}StateV${Config.localStorageVersion}`, JSON.stringify(state));
},
setFilesDataPluginId: (state, action: PayloadAction<number>) => {
state.dataPluginId = action.payload;
localStorage.setItem(`${filesSlice.name}StateV${Config.localStorageVersion}`, JSON.stringify(state));
},
updateFileListElement: (state, action: PayloadAction<FileListElementType>) => {
state.fileLists[state.dataPluginId] = updateFileListElementRecursive(state.fileLists[state.dataPluginId], action.payload);

updateFileListElement: (state, action: PayloadAction<FileTreeElementType>) => {
const updatedPaths: string[] = updateFileTreeRecursive(state.fileTrees[state.dataPluginId], action.payload);
state.fileLists[state.dataPluginId] = state.fileLists[state.dataPluginId].map((f: FileListElementType) => {
if (updatedPaths.includes(f.element.path)) {
f.checked = action.payload.checked;
}
return f;
});
localStorage.setItem(`${filesSlice.name}StateV${Config.localStorageVersion}`, JSON.stringify(state));
},
},
Expand All @@ -49,24 +57,27 @@ export const filesSlice = createSlice({
export const { setFilesDataPluginId, setFileList, updateFileListElement } = filesSlice.actions;
export default filesSlice.reducer;

function updateFileListElementRecursive(
fileList: FileListElementType,
element: FileListElementType,
checked?: boolean,
): FileListElementType {
if (fileList.children) {
fileList.children = fileList.children.map((f: FileListElementType) => {
function updateFileTreeRecursive(fileTree: FileTreeElementType, element: FileTreeElementType, checked?: boolean): string[] {
const updatedPaths: string[] = [];
if (fileTree.children) {
fileTree.children = fileTree.children.map((f: FileTreeElementType) => {
let elementChecked = checked;
if (f.id === element.id) {
if (f.element?.path && !updatedPaths.includes(f.element.path)) {
updatedPaths.push(f.element.path);
}
elementChecked = element.checked;
f.foldedOut = element.foldedOut;
}
if (elementChecked !== undefined) {
if (f.element?.path && !updatedPaths.includes(f.element.path)) {
updatedPaths.push(f.element.path);
}
f.checked = elementChecked;
}
updateFileListElementRecursive(f, element, elementChecked);
updatedPaths.push(...updateFileTreeRecursive(f, element, elementChecked));
return f;
});
}
return fileList;
return updatedPaths;
}
10 changes: 7 additions & 3 deletions binocular-frontend-new/src/types/data/fileListType.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { DataPluginFile } from '../../plugins/interfaces/dataPluginInterfaces/dataPluginFiles.ts';

export interface FileListElementType {
element: DataPluginFile;
checked: boolean;
}
export interface FileTreeElementType {
name: string;
id?: number;
type: FileListElementTypeType;
type: FileTreeElementTypeType;
element?: DataPluginFile;
children?: FileListElementType[];
children?: FileTreeElementType[];
searchTerm?: string;
checked: boolean;
foldedOut: boolean;
isRoot: boolean;
}

export enum FileListElementTypeType {
export enum FileTreeElementTypeType {
Folder,
File,
}

0 comments on commit a3bf3b2

Please # to comment.