Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
146a4ee
refactor: Moved all logics that're related to shift selection out of …
lethemanh Oct 15, 2025
a346dcd
refactor: Implemented new hook and helper functions for shift selecti…
lethemanh Oct 15, 2025
46c91d6
refactor: Implemented unit test for shift selection :recycle:
lethemanh Oct 15, 2025
5bc25de
refactor: Modified folder view component to trigger shift selection :…
lethemanh Oct 15, 2025
98d5e16
refactor: Modified useLongPress to be available with shift selection …
lethemanh Oct 15, 2025
ff1f734
fix: Resolve problem when deselect last item by using shift arrow :bug:
lethemanh Oct 15, 2025
32f4f16
refactor: Rename props that're used to interact with file :recycle:
lethemanh Oct 15, 2025
ed2fa87
refactor: Change logic to update selected items by using reduce :recy…
lethemanh Oct 16, 2025
c7086c6
fix: Resolve sharing link issue :bug:
lethemanh Oct 20, 2025
27d317a
chore: bump version 🔖
rezk2ll Oct 20, 2025
066b323
feat(shell): Add useShell hook to get shell context
ecnivtwelve Oct 24, 2025
126c534
fix(shell): useShell when no provider
ecnivtwelve Oct 24, 2025
10506ac
feat(shell): Show file selection in shell
ecnivtwelve Oct 24, 2025
9e32377
feat(shell): Use shell to open Docs files in Docs
ecnivtwelve Oct 24, 2025
fa26c74
Merge branch 'master' into feat/shell-release
ecnivtwelve Dec 1, 2025
6013d0c
chore: Fix lint
ecnivtwelve Dec 1, 2025
6fbbf5f
fix: Error on sharings view
ecnivtwelve Dec 1, 2025
f8cf12a
chore: Fix lint issues and refactor code
ecnivtwelve Dec 1, 2025
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
116 changes: 116 additions & 0 deletions src/hooks/useShell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Imports
import React, { createContext, useContext, useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'

import { BarLeft } from 'cozy-bar'

Check warning on line 5 in src/hooks/useShell.tsx

View workflow job for this annotation

GitHub Actions / Build and publish

There should be at least one empty line between import groups
import type { File } from '@/components/FolderPicker/types'

// Types
interface ShellContextType {
runsInShell: boolean
setRunsInShell: React.Dispatch<React.SetStateAction<boolean>>
selectedFile: string | null
setSelectedFile: React.Dispatch<React.SetStateAction<string | null>>
openFileInParent: (file: File) => void
}

// Context
const ShellContext = createContext<ShellContextType | undefined>(undefined)

export const ShellProvider = ({
children
}: {
children: React.ReactNode
}): JSX.Element => {
const navigate = useNavigate()

const [runsInShell, setRunsInShell] = useState(false)
const [selectedFile, setSelectedFile] = useState<string | null>(null)

useEffect(() => {
if (window.top) {
window.top.postMessage('loaded', '*')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

who is the window.top here?

}

window.onmessage = function (e: MessageEvent): void {
if (e.data == undefined || e.data == null || typeof e.data !== 'string')
return
if (e.data === 'inShell:true') {
setRunsInShell(true)
}
if (e.data.startsWith('selectedFile:')) {
const fileId = e.data.split('selectedFile:')[1].trim()
setSelectedFile(fileId)
}
if (e.data.startsWith('openFolder:')) {
const folderId = e.data.split('openFolder:')[1].trim()
navigate(`/folder/${folderId}`)
}
}
}, [navigate])

if (runsInShell) {
const CSS = `
.coz-bar-container nav, .coz-bar-container a {
display: none !important;
}

.coz-bar-container button[aria-label="Rechercher"] {
margin-right: -12px;
}
`

const style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(CSS))
document.head.appendChild(style)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be done by a new entry / target.


const openFileInParent = (file: File): void => {
if ('metadata' in file && window.top) {
const id = file.metadata.externalId || ''

Check warning on line 71 in src/hooks/useShell.tsx

View workflow job for this annotation

GitHub Actions / Build and publish

Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator
window.top.postMessage('openFile:' + id, '*')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who is listening / definning this openFile ?

}
}

const contextValue: ShellContextType = {
runsInShell,
setRunsInShell,
selectedFile,
setSelectedFile,
openFileInParent
}

return (
<ShellContext.Provider value={contextValue}>
{runsInShell && (
<BarLeft>
<div style={{ width: 12 }}></div>
</BarLeft>
)}

{children}
</ShellContext.Provider>
)
}

// Hook
export const useShell = (): ShellContextType => {
const context = useContext(ShellContext)
if (!context) {
return {
runsInShell: false,
setRunsInShell: (): void => {
return
},
selectedFile: null,
setSelectedFile: (): void => {
return
},
openFileInParent: (): void => {
return
}
}
}
return context
}
6 changes: 5 additions & 1 deletion src/modules/filelist/File.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import styles from '@/styles/filelist.styl'

import { useClipboardContext } from '@/contexts/ClipboardProvider'
import { useShell } from '@/hooks/useShell'
import { useViewSwitcherContext } from '@/lib/ViewSwitcherContext'
import { ActionMenuWithHeader } from '@/modules/actionmenu/ActionMenuWithHeader'
import { getContextMenuActions } from '@/modules/actions/helpers'
Expand Down Expand Up @@ -74,6 +75,7 @@ const File = ({
onToggleSelect
}) => {
const { viewType } = useViewSwitcherContext()
const { runsInShell, selectedFile } = useShell()

const [actionMenuVisible, setActionMenuVisible] = useState(false)
const filerowMenuToggleRef = useRef()
Expand Down Expand Up @@ -102,9 +104,11 @@ const File = ({
const isCut = isItemCut(attributes._id)

const selected = isItemSelected(attributes._id)
const selectedInShell =
runsInShell && selectedFile && selectedFile === attributes._id

const filContentRowSelected = cx(styles['fil-content-row'], {
[styles['fil-content-row-selected']]: selected,
[styles['fil-content-row-selected']]: selected || selectedInShell,
[styles['fil-content-row-actioned']]: actionMenuVisible,
[styles['fil-content-row-disabled']]: styleDisabled || isCut
})
Expand Down
19 changes: 18 additions & 1 deletion src/modules/navigation/hooks/useFileLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { usePublicContext } from '@/modules/public/PublicProvider'
import { getFolderPath } from '@/modules/routeUtils'
import { isOfficeEnabled as computeOfficeEnabled } from '@/modules/views/OnlyOffice/helpers'
import { useShell } from '@/hooks/useShell'

export interface LinkResult {
app: string
Expand Down Expand Up @@ -55,6 +56,7 @@ const useFileLink = (
const { isDesktop } = useBreakpoints()
const isOfficeEnabled = computeOfficeEnabled(isDesktop)
const { isPublic } = usePublicContext()
const { runsInShell, openFileInParent } = useShell()

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
const cozyUrl = client?.getStackClient().uri as string
Expand Down Expand Up @@ -128,13 +130,28 @@ const useFileLink = (
shouldBeOpenedInNewTab
) {
window.open(href, '_blank')
} else if (runsInShell && file.type && file.type === 'file') {
if (file.name && file.name.endsWith('.docs-note')) {
openFileInParent(file)
} else {
window.open(href, '_blank')
}
} else if (app === 'drive') {
navigate(to)
} else {
window.location.href = href
}
},
[app, href, navigate, to, shouldBeOpenedInNewTab]
[
app,
href,
navigate,
to,
shouldBeOpenedInNewTab,
runsInShell,
file,
openFileInParent
]
)

return {
Expand Down
5 changes: 4 additions & 1 deletion src/targets/browser/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ import AppRoute from '@/modules/navigation/AppRoute'

// ambient styles
import styles from '@/styles/main.styl' // eslint-disable-line no-unused-vars
import { ShellProvider } from '@/hooks/useShell'

const AppComponent = props => (
<App {...props}>
<HashRouter>
<AppRoute />
<ShellProvider>
<AppRoute />
</ShellProvider>
</HashRouter>
</App>
)
Expand Down
Loading