feat: Added per-tag preference to use table layout and removed "Files Table View" from Labs
This commit is contained in:
@@ -12,7 +12,7 @@ import { NavigationController } from '@/Controllers/Navigation/NavigationControl
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { ContentType, FeatureIdentifier, SNTag } from '@standardnotes/snjs'
|
||||
import { ContentType, SNTag } from '@standardnotes/snjs'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -97,8 +97,6 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
|
||||
const hasNotes = items.some((item) => item.content_type === ContentType.Note)
|
||||
|
||||
const isFilesTableViewEnabled = application.features.isExperimentalFeatureEnabled(FeatureIdentifier.FilesTableView)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
@@ -112,13 +110,7 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
onKeyDown={onKeyDown}
|
||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||
>
|
||||
{items.map((item, index) => {
|
||||
const previousIndex = index - 1
|
||||
const previousItem = previousIndex >= 0 ? items[previousIndex] : undefined
|
||||
|
||||
const nextIndex = index + 1
|
||||
const nextItem = nextIndex < items.length ? items[nextIndex] : undefined
|
||||
|
||||
{items.map((item) => {
|
||||
return (
|
||||
<ContentListItem
|
||||
key={item.uuid}
|
||||
@@ -134,8 +126,6 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
onSelect={selectItem}
|
||||
tags={getTagsForItem(item)}
|
||||
notesController={notesController}
|
||||
isPreviousItemTiled={previousItem?.content_type === ContentType.File && !isFilesTableViewEnabled}
|
||||
isNextItemTiled={nextItem?.content_type === ContentType.File && !isFilesTableViewEnabled}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
import { ContentType, FeatureIdentifier, FileItem, SNNote } from '@standardnotes/snjs'
|
||||
import { ContentType, FileItem, SNNote } from '@standardnotes/snjs'
|
||||
import React, { FunctionComponent } from 'react'
|
||||
import FileListItem from './FileListItem'
|
||||
import FileListItemCard from './FileListItemCard'
|
||||
import NoteListItem from './NoteListItem'
|
||||
import { AbstractListItemProps, doListItemPropsMeritRerender } from './Types/AbstractListItemProps'
|
||||
import { ListableContentItem } from './Types/ListableContentItem'
|
||||
|
||||
const ContentListItem: FunctionComponent<AbstractListItemProps<ListableContentItem>> = (props) => {
|
||||
const isFilesTableViewEnabled = props.application.features.isExperimentalFeatureEnabled(
|
||||
FeatureIdentifier.FilesTableView,
|
||||
)
|
||||
|
||||
switch (props.item.content_type) {
|
||||
case ContentType.Note:
|
||||
return <NoteListItem {...props} item={props.item as SNNote} />
|
||||
case ContentType.File: {
|
||||
if (isFilesTableViewEnabled) {
|
||||
return <FileListItem {...props} item={props.item as FileItem} />
|
||||
}
|
||||
|
||||
return <FileListItemCard {...props} item={props.item as FileItem} />
|
||||
return <FileListItem {...props} item={props.item as FileItem} />
|
||||
}
|
||||
default:
|
||||
return null
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { PANEL_NAME_NOTES } from '@/Constants/Constants'
|
||||
import { FeatureIdentifier, FileItem, PrefKey, SystemViewId, WebAppEvent } from '@standardnotes/snjs'
|
||||
import { FileItem, PrefKey, WebAppEvent } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { forwardRef, useCallback, useEffect, useMemo } from 'react'
|
||||
import ContentList from '@/Components/ContentListView/ContentList'
|
||||
@@ -40,6 +40,7 @@ import ContentTableView from '../ContentTableView/ContentTableView'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
||||
import { PaneController } from '@/Controllers/PaneController/PaneController'
|
||||
|
||||
type Props = {
|
||||
accountMenuController: AccountMenuController
|
||||
@@ -54,6 +55,7 @@ type Props = {
|
||||
linkingController: LinkingController
|
||||
featuresController: FeaturesController
|
||||
historyModalController: HistoryModalController
|
||||
paneController: PaneController
|
||||
className?: string
|
||||
id: string
|
||||
children?: React.ReactNode
|
||||
@@ -75,6 +77,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
linkingController,
|
||||
featuresController,
|
||||
historyModalController,
|
||||
paneController,
|
||||
className,
|
||||
id,
|
||||
children,
|
||||
@@ -94,6 +97,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
renderedItems,
|
||||
items,
|
||||
isCurrentNoteTemplate,
|
||||
isTableViewEnabled,
|
||||
} = itemListController
|
||||
|
||||
const innerRef = useForwardedRef(ref)
|
||||
@@ -185,9 +189,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
}, [isFilesSmartView, filesController, createNewNote, toggleAppPane, application])
|
||||
|
||||
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||
const isFilesTableViewEnabled = application.features.isExperimentalFeatureEnabled(FeatureIdentifier.FilesTableView)
|
||||
const shouldShowFilesTableView =
|
||||
isFilesTableViewEnabled && !isMobileScreen && selectedTag?.uuid === SystemViewId.Files
|
||||
const shouldUseTableView = (isFilesSmartView || isTableViewEnabled) && !isMobileScreen
|
||||
|
||||
useEffect(() => {
|
||||
const searchBarElement = document.getElementById(ElementIds.SearchBar)
|
||||
@@ -211,7 +213,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
if (searchBarElement === document.activeElement) {
|
||||
searchBarElement?.blur()
|
||||
}
|
||||
if (shouldShowFilesTableView) {
|
||||
if (shouldUseTableView) {
|
||||
return
|
||||
}
|
||||
selectNextItem()
|
||||
@@ -221,7 +223,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
element: document.body,
|
||||
onKeyDown: () => {
|
||||
if (shouldShowFilesTableView) {
|
||||
if (shouldUseTableView) {
|
||||
return
|
||||
}
|
||||
selectPreviousItem()
|
||||
@@ -265,7 +267,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
selectNextItem,
|
||||
selectPreviousItem,
|
||||
selectionController,
|
||||
shouldShowFilesTableView,
|
||||
shouldUseTableView,
|
||||
])
|
||||
|
||||
const shortcutForCreate = useMemo(
|
||||
@@ -319,18 +321,19 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
addButtonLabel={addButtonLabel}
|
||||
addNewItem={addNewItem}
|
||||
isFilesSmartView={isFilesSmartView}
|
||||
isFilesTableViewEnabled={isFilesTableViewEnabled}
|
||||
isTableViewEnabled={isTableViewEnabled}
|
||||
optionsSubtitle={optionsSubtitle}
|
||||
selectedTag={selectedTag}
|
||||
filesController={filesController}
|
||||
itemListController={itemListController}
|
||||
paneController={paneController}
|
||||
/>
|
||||
)}
|
||||
{(!shouldShowFilesTableView || isMobileScreen) && (
|
||||
{(!shouldUseTableView || isMobileScreen) && (
|
||||
<SearchBar
|
||||
itemListController={itemListController}
|
||||
searchOptionsController={searchOptionsController}
|
||||
hideOptions={shouldShowFilesTableView}
|
||||
hideOptions={shouldUseTableView}
|
||||
/>
|
||||
)}
|
||||
<NoAccountWarning
|
||||
@@ -355,7 +358,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
<p className="empty-items-list opacity-50">Loading...</p>
|
||||
) : null}
|
||||
{!dailyMode && renderedItems.length ? (
|
||||
shouldShowFilesTableView ? (
|
||||
shouldUseTableView ? (
|
||||
<ContentTableView
|
||||
items={items}
|
||||
application={application}
|
||||
|
||||
@@ -13,6 +13,7 @@ import AddItemMenuButton from './AddItemMenuButton'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import SearchButton from './SearchButton'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
import { PaneController } from '@/Controllers/PaneController/PaneController'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -21,11 +22,12 @@ type Props = {
|
||||
addButtonLabel: string
|
||||
addNewItem: () => void
|
||||
isFilesSmartView: boolean
|
||||
isFilesTableViewEnabled: boolean
|
||||
isTableViewEnabled: boolean
|
||||
optionsSubtitle?: string
|
||||
selectedTag: AnyTag
|
||||
filesController: FilesController
|
||||
itemListController: ItemListController
|
||||
paneController: PaneController
|
||||
}
|
||||
|
||||
const ContentListHeader = ({
|
||||
@@ -35,11 +37,12 @@ const ContentListHeader = ({
|
||||
addButtonLabel,
|
||||
addNewItem,
|
||||
isFilesSmartView,
|
||||
isFilesTableViewEnabled,
|
||||
isTableViewEnabled,
|
||||
optionsSubtitle,
|
||||
selectedTag,
|
||||
filesController,
|
||||
itemListController,
|
||||
paneController,
|
||||
}: Props) => {
|
||||
const displayOptionsContainerRef = useRef<HTMLDivElement>(null)
|
||||
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
|
||||
@@ -79,19 +82,13 @@ const ContentListHeader = ({
|
||||
isFilesSmartView={isFilesSmartView}
|
||||
isOpen={showDisplayOptionsMenu}
|
||||
selectedTag={selectedTag}
|
||||
paneController={paneController}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [
|
||||
showDisplayOptionsMenu,
|
||||
toggleDisplayOptionsMenu,
|
||||
displayOptionsButtonRef,
|
||||
application,
|
||||
isFilesSmartView,
|
||||
selectedTag,
|
||||
])
|
||||
}, [showDisplayOptionsMenu, toggleDisplayOptionsMenu, application, isFilesSmartView, selectedTag, paneController])
|
||||
|
||||
const AddButton = useMemo(() => {
|
||||
return (
|
||||
@@ -106,12 +103,12 @@ const ContentListHeader = ({
|
||||
}, [addButtonLabel, addNewItem, filesController, isDailyEntry, isFilesSmartView])
|
||||
|
||||
const SearchBarButton = useMemo(() => {
|
||||
if (!isFilesSmartView || !isFilesTableViewEnabled || isMobileScreen) {
|
||||
if (!isTableViewEnabled || isMobileScreen) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <SearchButton itemListController={itemListController} />
|
||||
}, [isFilesSmartView, isFilesTableViewEnabled, isMobileScreen, itemListController])
|
||||
}, [isTableViewEnabled, isMobileScreen, itemListController])
|
||||
|
||||
const FolderName = useMemo(() => {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
CollectionSort,
|
||||
CollectionSortProperty,
|
||||
FeatureIdentifier,
|
||||
IconType,
|
||||
isSmartView,
|
||||
isSystemView,
|
||||
@@ -25,6 +24,8 @@ import NoSubscriptionBanner from '@/Components/NoSubscriptionBanner/NoSubscripti
|
||||
import MenuRadioButtonItem from '@/Components/Menu/MenuRadioButtonItem'
|
||||
import MenuSwitchButtonItem from '@/Components/Menu/MenuSwitchButtonItem'
|
||||
import { Pill } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
|
||||
|
||||
const DailyEntryModeEnabled = true
|
||||
|
||||
@@ -33,6 +34,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
isOpen,
|
||||
isFilesSmartView,
|
||||
selectedTag,
|
||||
paneController,
|
||||
}) => {
|
||||
const isSystemTag = isSmartView(selectedTag) && isSystemView(selectedTag)
|
||||
const selectedTagPreferences = isSystemTag
|
||||
@@ -217,6 +219,14 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
void changePreferences({ entryMode: isDailyEntry ? 'normal' : 'daily' })
|
||||
}, [isDailyEntry, changePreferences])
|
||||
|
||||
const toggleTableView = useCallback(() => {
|
||||
const useTableView = !preferences.useTableView
|
||||
void changePreferences({ useTableView })
|
||||
if (useTableView) {
|
||||
paneController.setPaneLayout(PaneLayout.TableView)
|
||||
}
|
||||
}, [preferences.useTableView, changePreferences, paneController])
|
||||
|
||||
const TabButton: FunctionComponent<{
|
||||
label: string
|
||||
mode: PreferenceMode
|
||||
@@ -250,8 +260,9 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
const isFilesTableViewEnabled = application.features.isExperimentalFeatureEnabled(FeatureIdentifier.FilesTableView)
|
||||
const shouldHideNonApplicableOptions = isFilesTableViewEnabled && selectedTag?.uuid === SystemViewId.Files
|
||||
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||
const isTableViewEnabled = Boolean(isFilesSmartView || preferences.useTableView)
|
||||
const shouldHideNonApplicableOptions = isTableViewEnabled && !isMobileScreen
|
||||
|
||||
return (
|
||||
<Menu className="text-sm" a11yLabel="Notes list options menu" isOpen={isOpen}>
|
||||
@@ -410,7 +421,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
{currentMode === 'tag' && !isSystemTag && DailyEntryModeEnabled && (
|
||||
{currentMode === 'tag' && !isSystemTag && DailyEntryModeEnabled && !isTableViewEnabled && (
|
||||
<>
|
||||
<MenuItemSeparator />
|
||||
<MenuSwitchButtonItem
|
||||
@@ -432,6 +443,28 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
{currentMode === 'tag' && !isSystemTag && !isDailyEntry && (
|
||||
<>
|
||||
<MenuItemSeparator />
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={isTableViewEnabled}
|
||||
onChange={toggleTableView}
|
||||
>
|
||||
<div className="flex flex-col pr-5">
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="text-base font-semibold uppercase text-text lg:text-xs">Table view</div>
|
||||
<Pill className="py-0 px-1.5" style="success">
|
||||
Labs
|
||||
</Pill>
|
||||
</div>
|
||||
<div className="mt-1">Display the notes & files in the current tag in a table layout</div>
|
||||
</div>
|
||||
</MenuSwitchButtonItem>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!shouldHideNonApplicableOptions && (!isSystemTag || currentMode === 'global') && (
|
||||
<>
|
||||
<MenuItemSeparator />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { AnyTag } from '@/Controllers/Navigation/AnyTagType'
|
||||
import { PaneController } from '@/Controllers/PaneController/PaneController'
|
||||
|
||||
export type DisplayOptionsMenuPositionProps = {
|
||||
top: number
|
||||
@@ -11,4 +12,5 @@ export type DisplayOptionsMenuProps = {
|
||||
selectedTag: AnyTag
|
||||
isOpen: boolean
|
||||
isFilesSmartView: boolean
|
||||
paneController: PaneController
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user