Skip to content

Commit c14b38a

Browse files
SuchitraSwainlidel
andauthored
fix: flash when flipping to/from previewing item (#2437)
* [2406] bug: flash when flipping to/from previewing item * fix: hide addFilesInfo while files are loading prevents flash when navigating to directory or closing file preview by checking filesIsFetching state before rendering addFilesInfo component * fix: prevent flash when navigating in files view - add path guard to prevent rendering stale directory content during navigation - only re-sort content when new data arrives, not during fetch initialization - merge duplicate useEffect in grid-file to eliminate race condition - remove obsolete key from commonProps - hide empty state message at root to avoid duplicate with addFilesInfo - change empty state color to charcoal-muted for consistency - make empty state messages non-selectable with noselect - add isRoot to filesPathInfo TypeScript interface * refactor: return null instead of empty div replace empty <div/> returns with null for better React practices - no unnecessary DOM nodes, better performance, clearer intent --------- Co-authored-by: Suchitra Swain <suchitraswain.2012gmail.com> Co-authored-by: Marcin Rataj <lidel@lidel.org>
1 parent f239113 commit c14b38a

File tree

9 files changed

+54
-25
lines changed

9 files changed

+54
-25
lines changed

src/bundles/files/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ const createFilesBundle = () => {
9595
: null
9696
let pageContent = result || state.pageContent
9797
// Apply current sorting to the fetched content
98-
if (pageContent && pageContent.type === 'directory' && pageContent.content) {
98+
if (result && pageContent && pageContent.type === 'directory' && pageContent.content) {
9999
const originalContent = pageContent.originalContent || pageContent.content // Preserve original
100100
const sortedContent = getSortedContent({ ...pageContent, originalContent }, state.sorting, state.pins)
101101
pageContent = {

src/files/FilesPage.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const FilesPage = ({
3232
doFetchPinningServices, doFilesFetch, doPinsFetch, doFilesSizeGet, doFilesDownloadLink, doFilesDownloadCarLink, doFilesWrite, doAddCarFile, doFilesBulkCidImport, doFilesAddPath, doUpdateHash,
3333
doFilesUpdateSorting, doFilesNavigateTo, doFilesMove, doSetCliOptions, doFetchRemotePins, remotePins, pendingPins, failedPins,
3434
ipfsProvider, ipfsConnected, doFilesMakeDir, doFilesShareLink, doFilesCidProvide, doFilesDelete, doSetPinning, onRemotePinClick, doPublishIpnsKey,
35-
files, filesPathInfo, pinningServices, toursEnabled, handleJoyrideCallback, isCliTutorModeEnabled, cliOptions, filesSorting, t
35+
files, filesPathInfo, filesIsFetching, pinningServices, toursEnabled, handleJoyrideCallback, isCliTutorModeEnabled, cliOptions, filesSorting, t
3636
}) => {
3737
const { doExploreUserProvidedPath } = useExplore()
3838
const contextMenuRef = useRef()
@@ -196,7 +196,12 @@ const FilesPage = ({
196196
/* eslint-disable-next-line react-hooks/exhaustive-deps */
197197
, [files?.content, files?.pins, selected])
198198

199-
if (!files || files.type === 'file') return (<div/>)
199+
if (!files || files.type === 'file') return null
200+
201+
// Don't render stale content during navigation
202+
if (files.path && filesPathInfo.path && files.path !== filesPathInfo.path) {
203+
return null
204+
}
200205

201206
if (files.type === 'unknown') {
202207
const path = files.path
@@ -214,7 +219,6 @@ const FilesPage = ({
214219
}
215220

216221
const commonProps = {
217-
key: window.encodeURIComponent(files.path),
218222
updateSorting: doFilesUpdateSorting,
219223
files: files.content || [],
220224
pins: files.pins || [],
@@ -388,6 +392,7 @@ const FilesPage = ({
388392

389393
<InfoBoxes isRoot={filesPathInfo.isMfs && filesPathInfo.isRoot}
390394
isCompanion={false}
395+
isFetching={filesIsFetching}
391396
filesExist={!!(files && files.content && files.content.length)} />
392397

393398
<Modals
@@ -424,13 +429,14 @@ const Preview = ({ files, onDownload, onClose }) => {
424429
if (files && files.type === 'file') {
425430
return (<FilePreview {...files} onDownload={onDownload} onClose={onClose} />)
426431
}
427-
return (<div/>)
432+
return null
428433
}
429434

430435
export default connect(
431436
'selectIpfsProvider',
432437
'selectIpfsConnected',
433438
'selectFiles',
439+
'selectFilesIsFetching',
434440
'selectRemotePins',
435441
'selectPendingPins',
436442
'selectFailedPins',

src/files/file-preview/file-thumbnail.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,21 @@ interface FileThumbnailPropsConnected extends FileThumbnailProps {
1818
const FileThumbnail: FC<FileThumbnailPropsConnected> = ({ name, cid, availableGatewayUrl, textPreview, onLoad }) => {
1919
const [error, setError] = useState(false)
2020
const [imageLoaded, setImageLoaded] = useState(false)
21-
const [isLoading, setIsLoading] = useState(false)
2221
const type = typeFromExt(name)
2322

2423
const handleImageError = useCallback(() => {
2524
setError(true)
2625
setImageLoaded(false)
27-
setIsLoading(false)
2826
}, [])
2927

3028
const handleImageLoad = useCallback(() => {
3129
setImageLoaded(true)
32-
setIsLoading(false)
3330
onLoad?.()
3431
}, [onLoad])
3532

3633
useEffect(() => {
3734
setImageLoaded(false)
3835
setError(false)
39-
setIsLoading(false)
4036
}, [cid, name])
4137

4238
if (error || (!textPreview && !type.startsWith('image'))) {
@@ -46,7 +42,7 @@ const FileThumbnail: FC<FileThumbnailPropsConnected> = ({ name, cid, availableGa
4642
if (type === 'image') {
4743
const src = `${availableGatewayUrl}/ipfs/${cid}?filename=${encodeURIComponent(name)}`
4844
return (
49-
<div className={`file-thumbnail ${!imageLoaded || isLoading ? 'is-loading' : ''}`}>
45+
<div className={`file-thumbnail ${!imageLoaded ? 'is-loading' : ''}`}>
5046
<div className="file-thumbnail-loading" />
5147
<img
5248
className="w-100 h-100 object-contain br2"
@@ -63,7 +59,7 @@ const FileThumbnail: FC<FileThumbnailPropsConnected> = ({ name, cid, availableGa
6359

6460
if (textPreview) {
6561
return (
66-
<div className={`file-thumbnail file-thumbnail-text ${(isLoading || textPreview.length === 0) ? 'is-loading' : ''}`}>
62+
<div className={`file-thumbnail file-thumbnail-text ${textPreview.length === 0 ? 'is-loading' : ''}`}>
6763
<div className="file-thumbnail-loading" />
6864
<pre className="file-thumbnail-content">
6965
{textPreview}

src/files/files-grid/files-grid.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export interface FilesGridProps {
2121
type SetPinningProps = { cid: CID, pinned: boolean }
2222

2323
interface FilesGridPropsConnected extends FilesGridProps {
24-
filesPathInfo: { isMfs: boolean }
24+
filesPathInfo: { isMfs: boolean, isRoot: boolean }
2525
t: TFunction
2626
onRemove: (files: ContextMenuFile[]) => void
2727
onRename: (files: ContextMenuFile[]) => void
@@ -192,9 +192,9 @@ const FilesGrid = ({
192192
onSelect={handleSelect}
193193
/>
194194
))}
195-
{files.length === 0 && (
195+
{files.length === 0 && !filesPathInfo?.isRoot && (
196196
<Trans i18nKey='filesList.noFiles' t={t}>
197-
<div className='pv3 b--light-gray files-grid-empty bt tc gray f6'>
197+
<div className='pv3 b--light-gray files-grid-empty bt tc charcoal-muted f6 noselect'>
198198
There are no available files. Add some!
199199
</div>
200200
</Trans>

src/files/files-grid/grid-file.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,19 @@
158158
z-index: 0;
159159
}
160160

161+
.grid-file-icon-fallback {
162+
position: absolute;
163+
top: 0;
164+
left: 0;
165+
width: 100%;
166+
height: 100%;
167+
display: flex;
168+
align-items: center;
169+
justify-content: center;
170+
background-color: #f5f5f5;
171+
z-index: 0;
172+
}
173+
161174
.grid-file-dots {
162175
position: absolute;
163176
top: 8px;

src/files/files-grid/grid-file.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ const GridFile: FC<GridFilePropsConnected> = ({
105105
const [textPreview, setTextPreview] = useState<string | null>(null)
106106

107107
useEffect(() => {
108+
setHasPreview(false)
109+
setTextPreview(null)
110+
108111
const fetchTextPreview = async () => {
109112
const isTextFile = type.startsWith('text/') ||
110113
type === 'txt' ||
@@ -218,7 +221,11 @@ const GridFile: FC<GridFilePropsConnected> = ({
218221
textPreview={textPreview}
219222
onLoad={() => setHasPreview(true)}
220223
/>
221-
{!hasPreview && <FileIcon style={{ width: 80 }} name={name} type={type} />}
224+
{!hasPreview && (
225+
<div className="grid-file-icon-fallback">
226+
<FileIcon style={{ width: 80 }} name={name} type={type} />
227+
</div>
228+
)}
222229
<button
223230
ref={dotsWrapper}
224231
className="grid-file-dots"

src/files/files-list/FilesList.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,19 @@ export const FilesList = ({
235235
listRef.current?.forceUpdateGrid?.()
236236
}
237237

238-
const emptyRowsRenderer = () => (
239-
<Trans i18nKey='filesList.noFiles' t={t}>
240-
<div className='pv3 b--light-gray bt tc gray f6'>
241-
There are no available files. Add some!
242-
</div>
243-
</Trans>
244-
)
238+
const emptyRowsRenderer = () => {
239+
if (filesPathInfo.isRoot) {
240+
// Root has special more prominent message (AddFilesInfo)
241+
return null
242+
}
243+
return (
244+
<Trans i18nKey='filesList.noFiles' t={t}>
245+
<div className='pv3 b--light-gray bt tc charcoal-muted f6 noselect'>
246+
There are no available files. Add some!
247+
</div>
248+
</Trans>
249+
)
250+
}
245251

246252
const rowRenderer = ({ index, key, style }) => {
247253
const pinsString = pins.map(p => p.toString())

src/files/info-boxes/InfoBoxes.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ import PropTypes from 'prop-types'
33
import CompanionInfo from './companion-info/CompanionInfo.js'
44
import AddFilesInfo from './add-files-info/AddFilesInfo.js'
55

6-
const InfoBoxes = ({ isRoot, isCompanion, filesExist }) => (
6+
const InfoBoxes = ({ isRoot, isCompanion, isFetching, filesExist }) => (
77
<div>
88
{ isRoot && isCompanion && <CompanionInfo /> }
9-
{ isRoot && !filesExist && !isCompanion && <AddFilesInfo /> }
9+
{ isRoot && !filesExist && !isCompanion && !isFetching && <AddFilesInfo /> }
1010
</div>
1111
)
1212

1313
InfoBoxes.propTypes = {
1414
isRoot: PropTypes.bool.isRequired,
1515
isCompanion: PropTypes.bool.isRequired,
16+
isFetching: PropTypes.bool.isRequired,
1617
filesExist: PropTypes.bool.isRequired
1718
}
1819

src/files/info-boxes/add-files-info/AddFilesInfo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const AddFilesInfo = ({ t }) => (
66
<div className='mv4 tc navy f5' >
77
<Box style={{ background: 'rgba(105, 196, 205, 0.1)' }}>
88
<Trans i18nKey='addFilesInfo' t={t}>
9-
<p className='ma0'>No files here yet! Add files to your local IPFS node by clicking the <strong>Import</strong> button above.</p>
9+
<p className='ma0 noselect'>No files here yet! Add files to your local IPFS node by clicking the <strong>Import</strong> button above.</p>
1010
</Trans>
1111
</Box>
1212
</div>

0 commit comments

Comments
 (0)