Skip to content

Commit 546d20a

Browse files
george-hub331lidelSgtPooki
authored
feat: Add thumbnails support for files in grid view (#2337)
* feat: Add grid view mode with thumbnails for files page Introduces a new grid view mode for the files page, allowing users to switch between list and grid views. Includes: - New FilesGrid component - View mode toggle buttons - Shared props between list and grid views - Updated rendering logic to support both view modes * feat: Enhance files page with multi-select and keyboard navigation Adds multi-select functionality and improved keyboard navigation to the files page grid view: - Implement file selection with checkboxes - Add keyboard navigation between files - Create a new SelectedActions component for bulk operations - Update styling to support selection and focus states * fix: Improve FilesGrid and GridFile styling and linting - Adjust GridFile thumbnail opacity for better visual appearance * feat: Improve file selection and view mode in Files Page - Add select all checkbox for grid view - Move selected state management to parent component - Refactor file selection logic to support bulk and individual selection - Minor UI layout adjustments * feat: Add text preview for files in grid view - Implement text preview for text-based files in Grid File - Add support for reading file contents with doRead action - Enhance FileThumbnail to display text previews - Add localized "Select all entries" text across language files, this was done using google translate * chore: remove yarn.lock file * feat: use larger icon size when preview is not available for grids * feat: Improve view mode UI and file hash display - Move view mode buttons to Header component - Truncate file hash in grid view for better readability - Adjust Header layout and styling * feat: Add view mode translations and adjust UI styling - Add translation for view mode - Apply consistent height to view mode buttons in FilesPage - Adjust Header component margins and layout - Add responsive margin for breadcrumbs header * feat: Improve FilesGrid keyboard navigation and responsiveness * style: Reduce border width from 2px to 1px in default state * fix: resolve issue with storybook test * feat: Add keyboard shortcuts and drag-and-drop support for gridfiles - Implement keyboard shortcut modal with comprehensive keyboard navigation - Add drop zone and drag-and-drop functionality for files in grid view - Enhance list file navigation and selection with improved keyboard controls - Add visual indicators for drag and drop interactions in grid view * fix: avoid jiggling ui moving 2px and changing border weight does not render well and makes ui jiggle. switching to static sizing + reusing dotted border convention from the old Files screen * fix: avoid changing unrelated translations we dont want to cause unnecessary work for people at https://app.transifex.com/ipfs/ipfs-webui/dashboard/ * fix: remove shadow on hover we do not use shadows in UI anywhere else, let's keep grid view UI simple and follow similar visual indicators as list view * chore: remove translation edits * chore: rename shortcut model filename * chore: fix mem leak and side effects * chore: rename new files to kebab case * fix: grid keyboard nav * fix: migrate to typescript * chore: fix lint failures * chore: fix runtime error * fix: simplify keyboard shortcut condition * fix: resolve issue with enter shortcut on grid * chore: remove default props on FilesList * feat: add grid view step to files tour * fix: improve keyboard navigation and refactor grid file component * feat: add e2e tests for grid view functionality * chore: remove e2e unnecessary setup script * fix: resolve error with grid e2e tests --------- Co-authored-by: Marcin Rataj <[email protected]> Co-authored-by: Russell Dempsey <[email protected]>
1 parent 8939bea commit 546d20a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1788
-129
lines changed

@types/ipfs/index.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ declare module 'ipfs' {
1616
}
1717

1818
declare export interface CoreService {
19+
// TODO: cat returns AsyncIterable<Uint8Array>. see https://github.com/ipfs/js-kubo-rpc-client/blob/1ab7941819dd1a48df653ee159e6983608e72132/src/index.ts#L353C50-L353C75
1920
cat(pathOrCID: string | CID, options?: CatOptions): AsyncIterable<Buffer>;
2021
ls(pathOrCID: string | CID, options?: ListOptions): AsyncIterable<ListEntry>;
2122
add(file: FileContent | FileObject, options?: AddOptions): Promise<UnixFSEntry>;
@@ -94,9 +95,9 @@ declare module 'ipfs' {
9495
}
9596

9697
declare export type PinType =
97-
| "recursive"
98-
| "direct"
99-
| "indirect"
98+
| 'recursive'
99+
| 'direct'
100+
| 'indirect'
100101

101102
declare export type PinEntry = {
102103
cid: CID,

config-overrides.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,16 @@ function webpackOverride (config) {
121121
fullySpecified: false
122122
}
123123
})
124-
config.resolve.extensions = ['.js', '.jsx', '.tsx', '.ts', '...']
124+
125+
// Make sure .tsx and .ts extensions are properly prioritized
126+
// This ordering allows imports like './file.js' to resolve to './file.ts' or './file.tsx'
127+
config.resolve.extensions = ['.js', '.jsx', '.ts', '.tsx', '...']
128+
129+
// Enable resolving .js imports to .ts/.tsx files without specific aliases
130+
config.resolve.extensionAlias = {
131+
'.js': ['.js', '.ts', '.tsx'],
132+
'.jsx': ['.jsx', '.tsx']
133+
}
125134

126135
// Instrument for code coverage in development mode
127136
const REACT_APP_ENV = process.env.REACT_APP_ENV ?? process.env.NODE_ENV ?? 'development'

public/locales/en/files.json

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
"addByCar": "From CAR",
1818
"bulkImport": "Bulk import",
1919
"newFolder": "New folder",
20+
"viewList": "Show items in list",
21+
"viewGrid": "Show items in grid",
2022
"generating": "Generating…",
23+
"dropHere": "Drop here to move",
2124
"actions": {
2225
"copyHash": "Copy CID",
2326
"share": "Share link",
@@ -46,6 +49,28 @@
4649
"checkboxRemoveLocalPin": "Also remove local pin (recommended)",
4750
"checkboxUnpinFromServices": "Unpin from all pinning services"
4851
},
52+
"shortcutModal": {
53+
"title": "Keyboard Shortcuts",
54+
"description": "The following keyboard shortcuts are available in the Files section:",
55+
"navigation": "Navigation",
56+
"selection": "Selection",
57+
"actions": "Actions",
58+
"other": "Other",
59+
"moveDown": "Move down",
60+
"moveUp": "Move up",
61+
"moveLeft": "Move left",
62+
"moveRight": "Move right",
63+
"navigate": "Navigate to selected item",
64+
"rename": "Rename selected item",
65+
"delete": "Delete selected item(s)",
66+
"toggleSelection": "Toggle selection",
67+
"selectAll": "Select all items",
68+
"deselectAll": "Deselect all items",
69+
"copy": "Copy selected item(s)",
70+
"paste": "Paste item(s)",
71+
"cut": "Cut selected item(s)",
72+
"showShortcuts": "Show keyboard shortcuts"
73+
},
4974
"pinningModal": {
5075
"title": "Select where you would like to pin these items.",
5176
"complianceLabel": "🔍 Check pinning services' compliance",
@@ -81,6 +106,7 @@
81106
"description": "Insert the name of the folder you want to create."
82107
},
83108
"filesListLabel": "Files list",
109+
"filesGridLabel": "Files grid",
84110
"filesList": {
85111
"noFiles": "<0>No files in this directory. Click the “Import” button to add some.</0>"
86112
},
@@ -117,6 +143,11 @@
117143
"paragraph1": "Finally, the listing where you can find your files and folders. Drag and drop files or folders to add them.",
118144
"paragraph2": "You can share or download files, inspect them in the IPLD Explorer, rename or remove them!",
119145
"paragraph3": "Pro tip: drag and drop a file from any other page of the Web UI to add them to the root of your MFS."
146+
},
147+
"step6": {
148+
"title": "Files grid",
149+
"paragraph1": "The grid view displays your files and folders as thumbnails, making it easy to visually browse your content.",
150+
"paragraph2": "You can share or download files, inspect them in the IPLD Explorer, rename or remove them!"
120151
}
121152
},
122153
"previewLimitReached": "This preview is limited to 10 KiB. Click the download button to access the full file.",
@@ -153,5 +184,6 @@
153184
"pleaseWait": "Please wait while the initial 20 copies of the updated IPNS record are stored with the help of DHT peers…"
154185
},
155186
"noPinsInProgress": "All done, no remote pins in progress.",
156-
"remotePinningInProgress": "Remote pinning in progress:"
187+
"remotePinningInProgress": "Remote pinning in progress:",
188+
"selectAllEntries": "Select all entries"
157189
}

src/bundles/files/actions.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,19 @@ const actions = () => ({
185185
}
186186
},
187187

188+
/**
189+
* Reads data from a CID with optional offset and length.
190+
* @param {import('multiformats/cid').CID} cid - The CID to read from
191+
* @param {number} [offset] - The starting point to read from
192+
* @param {number} [length] - The number of bytes to read
193+
*/
194+
doRead: (cid, offset = 0, length) => perform(ACTIONS.READ_FILE, async (ipfs) => {
195+
if (!ipfs) {
196+
throw new Error('IPFS is not available')
197+
}
198+
return ipfs.cat(cid, { offset, length })
199+
}),
200+
188201
/**
189202
* Fetches conten for the currently selected path. And updates
190203
* `state.pageContent` on succesful completion.

src/bundles/files/consts.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ export const ACTIONS = {
4242
/** @type {'FILES_WRITE_UPDATED'} */
4343
WRITE_UPDATED: ('FILES_WRITE_UPDATED'),
4444
/** @type {'FILES_UPDATE_SORT'} */
45-
UPDATE_SORT: ('FILES_UPDATE_SORT')
45+
UPDATE_SORT: ('FILES_UPDATE_SORT'),
46+
/** @type {'FILES_READ'} */
47+
READ_FILE: ('FILES_READ')
4648
}
4749

4850
export const SORTING = {

src/components/checkbox/Checkbox.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,24 @@ import PropTypes from 'prop-types'
33
import Tick from '../../icons/GlyphSmallTick.js'
44
import './Checkbox.css'
55

6+
/**
7+
* @param {Object} props
8+
* @param {string} [props.className]
9+
* @param {React.ReactNode} [props.label]
10+
* @param {boolean} [props.disabled]
11+
* @param {boolean} [props.checked]
12+
* @param {function(boolean): void} props.onChange
13+
* @returns {React.ReactElement}
14+
*/
615
const Checkbox = ({ className, label, disabled, checked, onChange, ...props }) => {
716
className = `Checkbox dib sans-serif ${className}`
817
if (!disabled) {
918
className += ' pointer'
1019
}
1120

21+
/**
22+
* @param {React.ChangeEvent<HTMLInputElement>} event
23+
*/
1224
const change = (event) => {
1325
onChange(event.target.checked)
1426
}
@@ -19,9 +31,9 @@ const Checkbox = ({ className, label, disabled, checked, onChange, ...props }) =
1931
<span className='dib v-mid br1 w1 h1 pointer'>
2032
<Tick className='w1 h1 o-0 fill-aqua' viewBox='25 25 50 50' />
2133
</span>
22-
<span className='v-mid pl2'>
34+
{Boolean(label) && <span className='v-mid pl2'>
2335
{label}
24-
</span>
36+
</span>}
2537
</label>
2638
)
2739
}

src/components/text-input-modal/TextInputModal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class TextInputModal extends React.Component {
123123
onKeyPress={this.onKeyPress}
124124
value={this.state.value}
125125
required
126-
className={`input-reset charcoal ba b--black-20 br1 pa2 mb2 db w-90 center focus-outline ${this.inputClass}`}
126+
className={`input-reset charcoal ba b--black-20 br1 pa2 mb2 db w-90 center focus-outline modal-input ${this.inputClass}`}
127127
type='text' />
128128
</ModalBody>
129129

0 commit comments

Comments
 (0)