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

feat: Merge back 1.74.2-beta.1 #3245

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
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
8 changes: 8 additions & 0 deletions src/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ declare module 'cozy-client/dist/models/file' {
) => boolean
}

declare module 'cozy-client/dist/models/note' {
export const fetchURL: (
client: import('cozy-client/types/CozyClient').CozyClient,
file: { id: string },
options: { pathname: string }
) => Promise<string>
}

declare module '*.svg' {
import { FC, SVGProps } from 'react'
const content: FC<SVGProps<SVGElement>>
Expand Down
6 changes: 6 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -848,5 +848,11 @@
},
"LastUpdate": {
"titleFormat": "MMMM DD, YYYY, HH:MM"
},
"PublicNoteRedirect": {
"error": {
"title": "Unable to access document",
"subtitle": "The share link appears to be missing or invalid. Please ask the document owner to check access"
}
}
}
6 changes: 6 additions & 0 deletions src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -849,5 +849,11 @@
},
"LastUpdate": {
"titleFormat": "DD MMMM YYYY, HH:MM"
},
"PublicNoteRedirect": {
"error": {
"title": "Impossible d'accéder au document",
"subtitle": "Le lien de partage semble manquant ou invalide. Merci de demander au propriétaire du document de vérifier les accès"
}
}
}
15 changes: 15 additions & 0 deletions src/modules/layout/DummyLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react'

import Sprite from 'cozy-ui/transpiled/react/Icon/Sprite'
import { Layout } from 'cozy-ui/transpiled/react/Layout'

const DummyLayout: React.FC = ({ children }) => {
return (
<Layout monoColumn={true}>
{children}
<Sprite />
</Layout>
)
}

export { DummyLayout }
3 changes: 3 additions & 0 deletions src/modules/navigation/AppRoute.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ROOT_DIR_ID, TRASH_DIR_ID } from 'constants/config'
import { SentryRoutes } from 'lib/sentry'
import { UploaderComponent } from 'modules//views/Upload/UploaderComponent'
import Layout from 'modules/layout/Layout'
import { PublicNoteRedirect } from 'modules/navigation/PublicNoteRedirect'
import FileOpenerExternal from 'modules/viewer/FileOpenerExternal'
import HarvestRoutes from 'modules/views/Drive/HarvestRoutes'
import { SharedDrivesFolderView } from 'modules/views/Drive/SharedDrivesFolderView'
Expand Down Expand Up @@ -50,6 +51,8 @@ const FilesRedirect = () => {
const AppRoute = () => (
<SentryRoutes>
<Route path="external/:fileId" element={<ExternalRedirect />} />
<Route path="note/:fileId" element={<PublicNoteRedirect />} />

<Route element={<Layout />}>
<Route path="upload" element={<UploaderComponent />} />
<Route path="/files/:folderId" element={<FilesRedirect />} />
Expand Down
7 changes: 3 additions & 4 deletions src/modules/navigation/ExternalRedirect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { useParams } from 'react-router-dom'

import { useClient, useFetchShortcut } from 'cozy-client'
import Empty from 'cozy-ui/transpiled/react/Empty'
import Sprite from 'cozy-ui/transpiled/react/Icon/Sprite'
import { translate } from 'cozy-ui/transpiled/react/providers/I18n'

import EmptyIcon from 'assets/icons/icon-folder-broken.svg'
import { DummyLayout } from 'modules/layout/DummyLayout'

const ExternalRedirect = ({ t }) => {
const { fileId } = useParams()
Expand All @@ -17,8 +17,7 @@ const ExternalRedirect = ({ t }) => {
}

return (
<>
<Sprite />
<DummyLayout>
{fetchStatus === 'failed' && (
<Empty
data-testid="empty-share"
Expand All @@ -35,7 +34,7 @@ const ExternalRedirect = ({ t }) => {
text={t('External.redirection.text')}
/>
)}
</>
</DummyLayout>
)
}

Expand Down
68 changes: 68 additions & 0 deletions src/modules/navigation/PublicNoteRedirect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { FC, useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'

import { useClient } from 'cozy-client'
import { fetchURL } from 'cozy-client/dist/models/note'
import Empty from 'cozy-ui/transpiled/react/Empty'
import Icon from 'cozy-ui/transpiled/react/Icon'
import SadCozyIcon from 'cozy-ui/transpiled/react/Icons/SadCozy'
import Spinner from 'cozy-ui/transpiled/react/Spinner'
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'

import { joinPath } from 'lib/path'
import { DummyLayout } from 'modules/layout/DummyLayout'

const PublicNoteRedirect: FC = () => {
const { t } = useI18n()
const { fileId } = useParams()
const client = useClient()

const [noteUrl, setNoteUrl] = useState<string | null>(null)
const [fetchStatus, setFetchStatus] = useState<
'failed' | 'loading' | 'pending' | 'loaded'
>('pending')

useEffect(() => {
const fetchNoteUrl = async (fileId: string): Promise<void> => {
setFetchStatus('loading')
try {
const url = await fetchURL(
client,
{
id: fileId
},
{
pathname: joinPath(location.pathname, '')
}
)
setNoteUrl(url)
setFetchStatus('loaded')
} catch (error) {
setFetchStatus('failed')
}
}

if (fileId) {
void fetchNoteUrl(fileId)
}
}, [fileId, client])

if (noteUrl) {
window.location.href = noteUrl
}

return (
<DummyLayout>
{fetchStatus === 'failed' && (
<Empty
icon={<Icon icon={SadCozyIcon} color="var(--primaryColor)" />}
title={t('PublicNoteRedirect.error.title')}
text={t('PublicNoteRedirect.error.subtitle')}
/>
)}
{fetchStatus !== 'failed' && <Spinner size="xxlarge" middle noMargin />}
</DummyLayout>
)
}

export { PublicNoteRedirect }
80 changes: 78 additions & 2 deletions src/modules/navigation/hooks/helpers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,76 @@ describe('computeFileType', () => {
expect(computeFileType(file)).toBe('nextcloud-file')
})

it('should return "note" for notes', () => {
it('should return "public-note-same-instance" for public notes on the same instance', () => {
const file = {
_type: 'io.cozy.files',
name: 'My journal.cozy-note',
type: 'file',
metadata: {
title: '',
version: '0'
},
cozyMetadata: {
createdOn: 'https://example.com/'
}
}
expect(
computeFileType(file, { isPublic: true, cozyUrl: 'https://example.com' })
).toBe('public-note-same-instance')
})

it('should return "note" for notes on the same instance', () => {
const file = {
_type: 'io.cozy.files',
name: 'My journal.cozy-note',
type: 'file',
metadata: {
title: '',
version: '0'
},
cozyMetadata: {
createdOn: 'https://example.com/'
}
}
expect(computeFileType(file)).toBe('note')
expect(computeFileType(file, { cozyUrl: 'https://example.com/' })).toBe(
'note'
)
})

it('should return "public-note" for notes on an another instance', () => {
const file = {
_type: 'io.cozy.files',
name: 'My journal.cozy-note',
type: 'file',
metadata: {
title: '',
version: '0'
},
cozyMetadata: {
createdOn: 'https://example.com/'
}
}
expect(computeFileType(file, { cozyUrl: 'https://another.com/' })).toBe(
'public-note'
)
})

it('should return "public-note" for public notes', () => {
const file = {
_type: 'io.cozy.files',
name: 'My journal.cozy-note',
type: 'file',
metadata: {
title: '',
version: '0'
},
cozyMetadata: {
createdOn: 'https://example.com/'
}
}
expect(
computeFileType(file, { isPublic: true, cozyUrl: 'https://another.com' })
).toBe('public-note')
})

it('should return "onlyoffice" for files opened by OnlyOffice when Office is enabled', () => {
Expand Down Expand Up @@ -158,6 +217,23 @@ describe('computePath', () => {
)
})

it('should return correct path for public-note', () => {
const file = { _id: 'note123' }
expect(
computePath(file, { type: 'public-note', pathname: '/public' })
).toBe('/note/note123')
})

it('should return correct path for public-note-same-instance', () => {
const file = { _id: 'note123' }
expect(
computePath(file, {
type: 'public-note-same-instance',
pathname: '/public'
})
).toBe('/?id=note123')
})

it('should return correct path for shortcut', () => {
const file = { _id: 'shortcut123' }
expect(computePath(file, { type: 'shortcut', pathname: '/any' })).toBe(
Expand Down
38 changes: 30 additions & 8 deletions src/modules/navigation/hooks/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import {

import type { File } from 'components/FolderPicker/types'
import { TRASH_DIR_ID } from 'constants/config'
import { joinPath } from 'lib/path'
import { isNextcloudShortcut } from 'modules/nextcloud/helpers'
import { makeOnlyOfficeFileRoute } from 'modules/views/OnlyOffice/helpers'

interface ComputeFileTypeOptions {
isOfficeEnabled?: boolean
isPublic?: boolean
cozyUrl?: string
}

interface ComputePathOptions {
Expand All @@ -22,7 +25,11 @@ interface ComputePathOptions {

export const computeFileType = (
file: File,
{ isOfficeEnabled = false }: ComputeFileTypeOptions = {}
{
isOfficeEnabled = false,
isPublic = false,
cozyUrl = ''
}: ComputeFileTypeOptions = {}
): string => {
if (file._id === TRASH_DIR_ID) {
return 'trash'
Expand All @@ -31,7 +38,16 @@ export const computeFileType = (
} else if (file._type === 'io.cozy.remote.nextcloud.files') {
return isDirectory(file) ? 'nextcloud-directory' : 'nextcloud-file'
} else if (isNote(file)) {
return 'note'
// createdOn url ends with a trailing slash whereas cozyUrl does not joinPath fixes this
const isSameInstance =
joinPath(cozyUrl, '') === file.cozyMetadata?.createdOn
if (isPublic && isSameInstance) {
return 'public-note-same-instance'
} else if (isSameInstance) {
return 'note'
} else {
return 'public-note'
}
} else if (shouldBeOpenedByOnlyOffice(file) && isOfficeEnabled) {
return 'onlyoffice'
} else if (isNextcloudShortcut(file)) {
Expand All @@ -46,13 +62,15 @@ export const computeFileType = (
}

export const computeApp = (type: string): string => {
if (type === 'nextcloud-file') {
return 'nextcloud'
}
if (type === 'note') {
return 'notes'
switch (type) {
case 'nextcloud-file':
return 'nextcloud'
case 'note':
case 'public-note-same-instance':
return 'notes'
default:
return 'drive'
}
return 'drive'
}

export const computePath = (
Expand All @@ -75,6 +93,10 @@ export const computePath = (
return file.links?.self ?? ''
case 'note':
return `/n/${file._id}`
case 'public-note-same-instance':
return `/?id=${file._id}`
case 'public-note':
return `/note/${file._id}`
case 'shortcut':
return `/external/${file._id}`
case 'directory':
Expand Down
17 changes: 13 additions & 4 deletions src/modules/navigation/hooks/useFileLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useClient, generateWebLink } from 'cozy-client'
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'

import type { File } from 'components/FolderPicker/types'
import { joinPath } from 'lib/path'
import {
computeFileType,
computeApp,
Expand Down Expand Up @@ -48,8 +49,13 @@ const useFileLink = (file: File): UseFileLinkResult => {
const isOfficeEnabled = computeOfficeEnabled(isDesktop)
const { isPublic } = usePublicContext()

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
const cozyUrl = client?.getStackClient().uri as string

const type = computeFileType(file, {
isOfficeEnabled
isOfficeEnabled,
isPublic,
cozyUrl
})
const app = computeApp(type)
const path = computePath(file, {
Expand Down Expand Up @@ -81,11 +87,14 @@ const useFileLink = (file: File): UseFileLinkResult => {
? path
: generateWebLink({
slug: app,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
cozyUrl: client?.getStackClient().uri,
cozyUrl,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
subDomainType: client?.getInstanceOptions().subdomain,
pathname: currentURL.pathname,
// Inside notes, we need to add / at the end of /public/ or /preview/ to avoid 409 error
pathname:
type === 'public-note-same-instance'
? joinPath(currentURL.pathname, '')
: currentURL.pathname,
searchParams: searchParams as unknown as unknown[],
hash: to.pathname
})
Expand Down
Loading
Loading