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

Add MoreMenu in the standard Viewer #3308

Merged
merged 5 commits into from
Jan 28, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -96,8 +96,8 @@
"cozy-scripts": "^8.4.0",
"cozy-sharing": "^21.2.0",
"cozy-stack-client": "^52.0.0",
"cozy-ui": "^116.0.0",
"cozy-viewer": "^15.0.0",
"cozy-ui": "^117.1.0",
"cozy-viewer": "^15.1.0",
"date-fns": "2.30.0",
"diacritics": "1.3.0",
"filesize": "10.1.6",
105 changes: 105 additions & 0 deletions src/hooks/useMoreMenuActions.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useCurrentFolderId } from 'hooks'
import { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'

import { useClient } from 'cozy-client'
import { useWebviewIntent } from 'cozy-intent'
import { useVaultClient } from 'cozy-keys-lib'
import {
useSharingContext,
useNativeFileSharing,
shareNative,
addToCozySharingLink,
syncToCozySharingLink,
useSharingInfos
} from 'cozy-sharing'
import {
makeActions,
print
} from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'

import { useModalContext } from 'lib/ModalContext'
import { share, download, trash, versions, hr } from 'modules/actions'
import { addToFavorites } from 'modules/actions/components/addToFavorites'
import { duplicateTo } from 'modules/actions/components/duplicateTo'
import { moveTo } from 'modules/actions/components/moveTo'
import { removeFromFavorites } from 'modules/actions/components/removeFromFavorites'

export const useMoreMenuActions = file => {
const [isPrintAvailable, setIsPrintAvailable] = useState(false)
const client = useClient()
const vaultClient = useVaultClient()
const webviewIntent = useWebviewIntent()
const { t, lang } = useI18n()
const navigate = useNavigate()
const { pushModal, popModal } = useModalContext()
const { allLoaded, hasWriteAccess, isOwner, byDocId } = useSharingContext()
const { showAlert } = useAlert()
const { isNativeFileSharingAvailable, shareFilesNative } =
useNativeFileSharing()
const currentFolderId = useCurrentFolderId()
const { isSharingShortcutCreated, addSharingLink, syncSharingLink } =
useSharingInfos()
const canWriteToCurrentFolder = hasWriteAccess(currentFolderId)
const isPDFDoc = file.mime === 'application/pdf'
const showPrintAction = isPDFDoc && isPrintAvailable
const isCozySharing = window.location.pathname === '/preview'

const actions = makeActions(
[
share,
shareNative,
isCozySharing && addToCozySharingLink,
isCozySharing && syncToCozySharingLink,
download,
showPrintAction && print,
hr,
moveTo,
duplicateTo,
addToFavorites,
removeFromFavorites,
hr,
versions,
hr,
trash
],
{
client,
t,
lang,
vaultClient,
pushModal,
popModal,
refresh: () => navigate('..'),
navigate,
hasWriteAccess: canWriteToCurrentFolder,
canMove: true,
isPublic: false,
allLoaded,
showAlert,
isOwner,
byDocId,
isNativeFileSharingAvailable,
shareFilesNative,
isSharingShortcutCreated,
openSharingLinkDisplayed: isCozySharing,
syncSharingLink,
addSharingLink
}
)

useEffect(() => {
const init = async () => {
const isAvailable =
(await webviewIntent?.call('isAvailable', 'print')) ?? true

setIsPrintAvailable(isAvailable)
}

init()
}, [webviewIntent])

return actions
}
12 changes: 9 additions & 3 deletions src/modules/actions/helpers.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,10 @@ import { joinPath } from 'lib/path'

export const navigateToModal = ({ navigate, pathname, files, path }) => {
const file = Array.isArray(files) ? files[0] : files
return navigate(joinPath(pathname, `file/${file.id}/${path}`))

return navigate(
pathname ? joinPath(pathname, `file/${file.id}/${path}`) : `v/${path}`
)
}

export const navigateToModalWithMultipleFile = ({
@@ -12,8 +15,11 @@ export const navigateToModalWithMultipleFile = ({
path,
search
}) => {
navigate(
{ pathname: joinPath(pathname, path), search: search ? `?${search}` : '' },
return navigate(
{
pathname: pathname ? joinPath(pathname, path) : `v/${path}`,
search: search ? `?${search}` : ''
},
{
state: { fileIds: files.map(file => file.id) }
}
44 changes: 40 additions & 4 deletions src/modules/navigation/AppRoute.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { Route, useParams, Navigate } from 'react-router-dom'
import { Route, useParams, Outlet, Navigate } from 'react-router-dom'

import { RealTimeQueries } from 'cozy-client'
import { AssistantDialog } from 'cozy-dataproxy-lib'
@@ -50,6 +50,13 @@ const FilesRedirect = () => {
return <Navigate to={`/folder/${folderId}`} replace={true} />
}

const OutletWrapper = ({ Component }) => (
<>
<Component />
<Outlet />
</>
)

const AppRoute = () => (
<SentryRoutes>
<Route path="external/:fileId" element={<ExternalRedirect />} />
@@ -65,7 +72,15 @@ const AppRoute = () => (
element={<Navigate to={ROOT_DIR_ID} replace={true} />}
/>
<Route path="folder/:folderId" element={<DriveFolderView />}>
<Route path="file/:fileId" element={<FilesViewerDrive />} />
<Route
path="file/:fileId"
element={<OutletWrapper Component={FilesViewerDrive} />}
>
<Route path="v/revision" element={<FileHistory />} />
<Route path="v/share" element={<ShareFileView />} />
<Route path="v/move" element={<MoveFilesView />} />
<Route path="v/duplicate" element={<FolderDuplicateView />} />
</Route>
<Route path="file/:fileId/revision" element={<FileHistory />} />
<Route path="file/:fileId/share" element={<ShareFileView />} />
<Route path="file/:fileId/qualify" element={<QualifyFileView />} />
@@ -104,7 +119,15 @@ const AppRoute = () => (
) : null}

<Route path="recent" element={<RecentView />}>
<Route path="file/:fileId" element={<FilesViewerRecent />} />
<Route
path="file/:fileId"
element={<OutletWrapper Component={FilesViewerRecent} />}
>
<Route path="v/revision" element={<FileHistory />} />
<Route path="v/share" element={<ShareFileView />} />
<Route path="v/move" element={<MoveFilesView />} />
<Route path="v/duplicate" element={<FolderDuplicateView />} />
</Route>
<Route path="file/:fileId/revision" element={<FileHistory />} />
<Route path="file/:fileId/share" element={<ShareFileView />} />
<Route path="file/:fileId/qualify" element={<QualifyFileView />} />
@@ -117,6 +140,7 @@ const AppRoute = () => (
path="trash"
element={<Navigate to={TRASH_DIR_ID} replace={true} />}
/>

<Route path="trash/:folderId" element={<TrashFolderView />}>
<Route path="file/:fileId" element={<FilesViewerTrash />} />
<Route path="file/:fileId/revision" element={<FileHistory />} />
@@ -127,7 +151,15 @@ const AppRoute = () => (
<Route path="sharings">
<Route index element={<SharingsView />} />
<Route element={<SharingsView />}>
<Route path="file/:fileId" element={<SharingsFilesViewer />} />
<Route
path="file/:fileId"
element={<OutletWrapper Component={SharingsFilesViewer} />}
>
<Route path="v/revision" element={<FileHistory />} />
<Route path="v/share" element={<ShareFileView />} />
<Route path="v/move" element={<MoveFilesView />} />
<Route path="v/duplicate" element={<FolderDuplicateView />} />
</Route>
{/* This route must be a child of SharingsView so the modal opens on top of the sharing view */}
<Route path="file/:fileId/revision" element={<FileHistory />} />
<Route path="file/:fileId/share" element={<ShareFileView />} />
@@ -145,16 +177,20 @@ const AppRoute = () => (
<Route path="move" element={<MoveFilesView />} />
</Route>
</Route>

<Route path="onlyoffice/:fileId" element={<OnlyOfficeView />}>
<Route path="paywall" element={<OnlyOfficePaywallView />} />
</Route>

<Route
path="onlyoffice/create/:folderId/:fileClass"
element={<OnlyOfficeCreateView />}
/>

<Route path="file/:fileId" element={<FileOpenerExternal />} />

<Route path="search" element={<SearchView />} />

<Route
path="assistant/:conversationId"
element={
12 changes: 5 additions & 7 deletions src/modules/viewer/FilesViewer.jsx
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ import {
} from 'lib/encryption'
import logger from 'lib/logger'
import Fallback from 'modules/viewer/Fallback'
import MoreMenu from 'modules/viewer/MoreMenu'
import {
isOfficeEnabled,
makeOnlyOfficeFileRoute
@@ -36,12 +37,11 @@ import {
* fetchMore() on the query
*/
const FilesViewer = ({ filesQuery, files, onClose, onChange }) => {
const { isDesktop } = useBreakpoints()
const fileId = useCurrentFileId()
const [currentFile, setCurrentFile] = useState(null)
const [currentDecryptedFileURL, setCurrentDecryptedFileURL] = useState(null)
const [fetchingMore, setFetchingMore] = useState(false)

const { isDesktop } = useBreakpoints()
const fileId = useCurrentFileId()
const client = useClient()
const { t } = useI18n()
const vaultClient = useVaultClient()
@@ -178,13 +178,11 @@ const FilesViewer = ({ filesQuery, files, onClose, onChange }) => {
isEnabled: isOfficeEnabled(isDesktop),
opener: file => navigate(makeOnlyOfficeFileRoute(file.id))
},
toolbarProps: {
showFilePath: true
}
toolbarProps: { showFilePath: true }
}}
>
<ToolbarButtons>
<SharingButton variant="iconButton" />
<MoreMenu />
</ToolbarButtons>
<FooterActionButtons>
<SharingButton />
48 changes: 48 additions & 0 deletions src/modules/viewer/MoreMenu.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import cx from 'classnames'
import React, { useState, useRef } from 'react'

import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
import Icon from 'cozy-ui/transpiled/react/Icon'
import IconButton from 'cozy-ui/transpiled/react/IconButton'
import DotsIcon from 'cozy-ui/transpiled/react/Icons/Dots'
import { useBreakpoints } from 'cozy-ui/transpiled/react/providers/Breakpoints'

import { useMoreMenuActions } from 'hooks/useMoreMenuActions'

const MoreMenu = ({ file }) => {
const [showMenu, setShowMenu] = useState(false)
const { isDesktop } = useBreakpoints()
const anchorRef = useRef()
const actions = useMoreMenuActions(file)

if (file.trashed) return null

return (
<>
<IconButton
ref={anchorRef}
variant="secondary"
className={cx({ 'u-white': isDesktop })}
onClick={() => setShowMenu(v => !v)}
>
<Icon icon={DotsIcon} />
</IconButton>
{showMenu && (
<ActionsMenu
open
ref={anchorRef}
docs={[file]}
actions={actions}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
autoClose
onClose={() => setShowMenu(false)}
/>
)}
</>
)
}

export default MoreMenu
20 changes: 10 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -6714,10 +6714,10 @@ cozy-tsconfig@1.2.0:
resolved "https://registry.yarnpkg.com/cozy-tsconfig/-/cozy-tsconfig-1.2.0.tgz#17e61f960f139fae4d26cbac2254b9ab632b269e"
integrity sha512-TRHnY9goF3FzVlUbP7BcHxuN2XAA4AmppT4fHHZmTKaSwYTByVR1Al+riFMDbce94kJZ1wzl9WNLWQuqzGZ6Cw==

cozy-ui@^116.0.0:
version "116.0.0"
resolved "https://registry.yarnpkg.com/cozy-ui/-/cozy-ui-116.0.0.tgz#a6a0dd235d8ba962145801d90a35c5e73d7b74f3"
integrity sha512-by/0V3Z488W1P5pMDXHk9mbPcOeKhHOVcMUd+/OteQvaHK/bTi/I2JJxoNmaYa1pBMChMlpjOiTvvnpEeEIYNQ==
cozy-ui@^117.1.0:
version "117.1.0"
resolved "https://registry.yarnpkg.com/cozy-ui/-/cozy-ui-117.1.0.tgz#9fd8941902164a22b6a3bf3837d5495f2ea538f5"
integrity sha512-BypU8IPv457Uvb+XcAPUqs3goiGslXIZ4GJZxNFlHbIPJHo2GcwPaT3xfBgWDy7BndlF8gJk9eLreEM+ZZDgFA==
dependencies:
"@babel/runtime" "^7.3.4"
"@date-io/date-fns" "1"
@@ -6746,10 +6746,10 @@ cozy-ui@^116.0.0:
react-swipeable-views "^0.13.3"
rooks "^5.11.2"

cozy-viewer@^15.0.0:
version "15.0.0"
resolved "https://registry.yarnpkg.com/cozy-viewer/-/cozy-viewer-15.0.0.tgz#e28b731015f845a38112ae3883e0d7cad05b4058"
integrity sha512-Sbj0ycKVU0N4uCmsP5b6hRS1PubPjn+qaoCiXXiHun44xNv07js05nyuqok8Eup5Q/EdJvE1XKAzEoEQZLtS1w==
cozy-viewer@^15.1.0:
version "15.1.0"
resolved "https://registry.yarnpkg.com/cozy-viewer/-/cozy-viewer-15.1.0.tgz#2b570ab6d23c063f40de6468d2e2a015198ee785"
integrity sha512-5sBXUXyLKgQnXM1JDe3wiUVFTHT8TQOQh3m4t7g0IDaYcbaYMmz/JvC79QAreYd7N+94K4MVqbt+j5JdxtSjWA==
dependencies:
classnames "^2.2.5"
hammerjs "^2.0.8"
@@ -13080,9 +13080,9 @@ msgpack5@^4.0.2:
readable-stream "^2.3.6"
safe-buffer "^5.1.2"

"mui-bottom-sheet@https://github.com/cozy/mui-bottom-sheet.git#v1.0.9":
"mui-bottom-sheet@git+https://github.com/cozy/mui-bottom-sheet.git#v1.0.9":
version "1.0.8"
resolved "https://github.com/cozy/mui-bottom-sheet.git#3dc4c2a245ab39079bc2f73546bccf80847be14c"
resolved "git+https://github.com/cozy/mui-bottom-sheet.git#3dc4c2a245ab39079bc2f73546bccf80847be14c"
dependencies:
"@juggle/resize-observer" "^3.1.3"
jest-environment-jsdom-sixteen "^1.0.3"
Loading