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: Show number of new sharing in the sidebar #2964

Merged
merged 4 commits into from
Aug 25, 2023
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
4 changes: 2 additions & 2 deletions src/drive/styles/filelist.styl
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ column-width-thumbnail-bigger = 7rem
.fil-content-header-action
display flex
justify-content flex-end
flex 1
width 4.8rem
flex-shrink 0
width 3.8rem
padding-right .8rem
margin-right 2rem

Expand Down
10 changes: 4 additions & 6 deletions src/drive/web/modules/filelist/cells/SharingShortcutBadge.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cx from 'classnames'
import { isSharingShortcutNew } from 'cozy-client/dist/models/file'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import { TableCell } from 'cozy-ui/transpiled/react/Table'
import Circle from 'cozy-ui/transpiled/react/Circle'

import styles from 'drive/styles/filelist.styl'

Expand All @@ -19,17 +20,14 @@ const SharingShortcutBadge = ({ file }) => {
)}
>
{isSharingShortcutNew(file) ? (
<div
className="u-w-1 u-h-1 u-bdrs-circle u-flex"
style={{ backgroundColor: 'var(--errorColor)' }}
>
<Circle size="xsmall" backgroundColor="var(--errorColor)">
<span
className="u-fw-bold u-fz-tiny u-white u-lh-tiny u-m-auto"
style={{ fontSize: '11px', lineHeight: '1rem' }}
aria-label={t('table.row_sharing_shortcut_aria_label')}
>
1
</span>
</div>
</Circle>
) : null}
</TableCell>
)
Expand Down
140 changes: 36 additions & 104 deletions src/drive/web/modules/navigation/Nav.jsx
Original file line number Diff line number Diff line change
@@ -1,115 +1,47 @@
/* global __TARGET__ */
import React, { useState } from 'react'
import cx from 'classnames'
import { useLocation } from 'react-router-dom'

import { useI18n } from 'cozy-ui/transpiled/react'
import UINav from 'cozy-ui/transpiled/react/Nav'

import UINav, {
NavItem,
NavIcon,
NavText,
NavLink as UINavLink
} from 'cozy-ui/transpiled/react/Nav'
import { NavItem } from 'drive/web/modules/navigation/NavItem'
import { SharingsNavItem } from 'drive/web/modules/navigation/SharingsNavItem'

/**
* Returns true if `to` and `pathname` match
* Supports `rx` for regex matches.
*/
const navLinkMatch = (rx, to, pathname) => {
return rx ? rx.test(pathname) : pathname.slice(1) === to
}

/**
* Like react-router NavLink but sets the lastClicked state (passed in props)
* to have a faster change of active (not waiting for the route to completely
* change).
*/
export const NavLink = ({
children,
to,
rx,
clickState: [lastClicked, setLastClicked]
}) => {
const location = useLocation()
const pathname = lastClicked ? lastClicked : location.pathname
const isActive = navLinkMatch(rx, to, pathname)
return (
<a
style={{ outline: 'none' }}
onClick={() => setLastClicked(to)}
href={`#${to}`}
className={cx(
UINavLink.className,
isActive ? UINavLink.activeClassName : null
)}
>
{children}
</a>
)
}

const NavItems = ({ items }) => {
const clickState = useState(null)
return (
<>
{items.map((item, i) =>
item ? (
<NavItem key={i} secondary={item.secondary}>
<NavLink to={item.to} rx={item.rx} clickState={clickState}>
{item.icon ? <NavIcon icon={item.icon} /> : null}
<NavText>{item.label}</NavText>
</NavLink>
</NavItem>
) : null
)}
</>
)
}
const folderRoute = /\/folder(\/.*)?/
const settingsRoute = /\/settings(\/.*)?/
const recentRoute = /\/recent(\/.*)?/
const sharingRoute = /\/sharings(\/.*)?/
const trashRoute = /\/trash(\/.*)?/
export const Nav = () => {
const { t } = useI18n()
const routes = [
{
to: '/folder',
icon: 'folder',
label: t('Nav.item_drive'),
rx: folderRoute
},
{
to: '/recent',
icon: 'clock',
label: t('Nav.item_recent'),
rx: recentRoute
},
{
to: '/sharings',
icon: 'share',
label: t('Nav.item_sharings'),
rx: sharingRoute
},
{
to: '/trash',
icon: 'trash',
label: t('Nav.item_trash'),
rx: trashRoute
}
]
if (__TARGET__ === 'mobile') {
routes.push({
to: '/settings',
icon: 'gear',
label: t('Nav.item_settings'),
rx: settingsRoute
})
}
const clickState = useState(null)

return (
<UINav>
<NavItems items={routes} />
<NavItem
to="/folder"
icon="folder"
label="drive"
rx={/\/folder(\/.*)?/}
clickState={clickState}
/>
<NavItem
to="/recent"
icon="clock"
label="recent"
rx={/\/recent(\/.*)?/}
clickState={clickState}
/>
<SharingsNavItem clickState={clickState} />
<NavItem
to="/trash"
icon="trash"
label="trash"
rx={/\/trash(\/.*)?/}
clickState={clickState}
/>
{__TARGET__ === 'mobile' ? (
<NavItem
to="/settings"
icon="gear"
label="settings"
rx={/\/settings(\/.*)?/}
clickState={clickState}
/>
) : null}
</UINav>
)
}
Expand Down
55 changes: 55 additions & 0 deletions src/drive/web/modules/navigation/NavContent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react'
import PropTypes from 'prop-types'

import { NavIcon, NavText } from 'cozy-ui/transpiled/react/Nav'
import Badge from 'cozy-ui/transpiled/react/Badge'
import useBreakpoints from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
import Circle from 'cozy-ui/transpiled/react/Circle'

const NavContent = ({ icon, badgeContent, label }) => {
const { isDesktop } = useBreakpoints()

if (badgeContent) {
if (isDesktop) {
return (
<>
<NavIcon icon={icon} />
<NavText>{label}</NavText>
<Circle
size="xsmall"
className="u-ml-auto u-mr-1"
backgroundColor="var(--errorColor)"
>
<span style={{ fontSize: '11px', lineHeight: '1rem' }}>
{badgeContent > 99 ? '99+' : badgeContent}
</span>
</Circle>
</>
)
} else {
return (
<>
<Badge badgeContent={badgeContent} color="error" withBorder={false}>
<NavIcon icon={icon} />
</Badge>
<NavText>{label}</NavText>
</>
)
}
}

return (
<>
<NavIcon icon={icon} />
<NavText>{label}</NavText>
</>
)
}

NavContent.propTypes = {
icon: PropTypes.string,
badgeContent: PropTypes.number,
label: PropTypes.string
}

export { NavContent }
34 changes: 34 additions & 0 deletions src/drive/web/modules/navigation/NavItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'
import PropTypes from 'prop-types'

import { NavItem as UINavItem } from 'cozy-ui/transpiled/react/Nav'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'

import { NavContent } from 'drive/web/modules/navigation/NavContent'
import { NavLink } from 'drive/web/modules/navigation/NavLink'

const NavItem = ({ to, icon, label, rx, clickState, badgeContent }) => {
const { t } = useI18n()

return (
<UINavItem>
<NavLink to={to} rx={rx} clickState={clickState}>
<NavContent
icon={icon}
label={t(`Nav.item_${label}`)}
badgeContent={badgeContent}
/>
</NavLink>
</UINavItem>
)
}

NavItem.propTypes = {
to: PropTypes.string.isRequired,
icon: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
rx: PropTypes.shape(RegExp),
badgeContent: PropTypes.number
}

export { NavItem }
45 changes: 45 additions & 0 deletions src/drive/web/modules/navigation/NavLink.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react'
import cx from 'classnames'
import { useLocation } from 'react-router-dom'
import PropTypes from 'prop-types'

import { NavLink as UINavLink } from 'cozy-ui/transpiled/react/Nav'

import { navLinkMatch } from 'drive/web/modules/navigation/helpers'

/**
* Like react-router NavLink but sets the lastClicked state (passed in props)
* to have a faster change of active (not waiting for the route to completely
* change).
*/
const NavLink = ({
children,
to,
rx,
clickState: [lastClicked, setLastClicked]
}) => {
const location = useLocation()
const pathname = lastClicked ? lastClicked : location.pathname
const isActive = navLinkMatch(rx, to, pathname)
return (
<a
style={{ outline: 'none' }}
onClick={() => setLastClicked(to)}
href={`#${to}`}
className={cx(
UINavLink.className,
isActive ? UINavLink.activeClassName : null
)}
>
{children}
</a>
)
}

NavLink.propTypes = {
children: PropTypes.node.isRequired,
to: PropTypes.string.isRequired,
rx: PropTypes.shape(RegExp)
}

export { NavLink }
27 changes: 27 additions & 0 deletions src/drive/web/modules/navigation/SharingsNavItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'

import { useQuery } from 'cozy-client'

import { buildNewSharingShortcutQuery } from 'drive/web/modules/queries'
import { NavItem } from 'drive/web/modules/navigation/NavItem'

const SharingsNavItem = ({ clickState }) => {
const newSharingShortcutQuery = buildNewSharingShortcutQuery()
const newSharingShortcutResult = useQuery(
newSharingShortcutQuery.definition,
newSharingShortcutQuery.options
)

return (
<NavItem
to="/sharings"
icon="share"
label="sharings"
rx={/\/sharings(\/.*)?/}
clickState={clickState}
badgeContent={newSharingShortcutResult.data?.length}
/>
)
}

export { SharingsNavItem }
7 changes: 7 additions & 0 deletions src/drive/web/modules/navigation/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Returns true if `to` and `pathname` match
* Supports `rx` for regex matches.
*/
export const navLinkMatch = (rx, to, pathname) => {
return rx ? rx.test(pathname) : pathname.slice(1) === to
}
14 changes: 14 additions & 0 deletions src/drive/web/modules/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,17 @@ export const buildFolderByPathQuery = path => ({
fetchPolicy: defaultFetchPolicy
}
})

export const buildNewSharingShortcutQuery = () => ({
definition: () =>
Q('io.cozy.files')
.where({
'metadata.sharing.status': 'new',
class: 'shortcut'
})
.indexFields(['metadata.sharing.status', 'class']),
options: {
as: 'io.cozy.files/metadata.sharing.status/new/class/shortcut',
fetchPolicy: defaultFetchPolicy
}
})
Loading