feat: Added per-tag preference to use table layout and removed "Files Table View" from Labs
This commit is contained in:
@@ -38,8 +38,6 @@ export enum FeatureIdentifier {
|
|||||||
TaskEditor = 'org.standardnotes.simple-task-editor',
|
TaskEditor = 'org.standardnotes.simple-task-editor',
|
||||||
TokenVaultEditor = 'org.standardnotes.token-vault',
|
TokenVaultEditor = 'org.standardnotes.token-vault',
|
||||||
|
|
||||||
FilesTableView = 'org.standardnotes.files-table-view',
|
|
||||||
|
|
||||||
DeprecatedBoldEditor = 'org.standardnotes.bold-editor',
|
DeprecatedBoldEditor = 'org.standardnotes.bold-editor',
|
||||||
DeprecatedMarkdownBasicEditor = 'org.standardnotes.simple-markdown-editor',
|
DeprecatedMarkdownBasicEditor = 'org.standardnotes.simple-markdown-editor',
|
||||||
DeprecatedMarkdownMathEditor = 'org.standardnotes.fancy-markdown-editor',
|
DeprecatedMarkdownMathEditor = 'org.standardnotes.fancy-markdown-editor',
|
||||||
@@ -53,4 +51,4 @@ export enum FeatureIdentifier {
|
|||||||
*/
|
*/
|
||||||
export const LegacyFileSafeIdentifier = 'org.standardnotes.legacy.file-safe'
|
export const LegacyFileSafeIdentifier = 'org.standardnotes.legacy.file-safe'
|
||||||
|
|
||||||
export const ExperimentalFeatures = [FeatureIdentifier.FilesTableView]
|
export const ExperimentalFeatures = []
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
import { RoleName, SubscriptionName } from '@standardnotes/common'
|
|
||||||
import { FeatureDescription } from '../Feature/FeatureDescription'
|
import { FeatureDescription } from '../Feature/FeatureDescription'
|
||||||
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
|
|
||||||
import { PermissionName } from '../Permission/PermissionName'
|
|
||||||
|
|
||||||
export function experimentalFeatures(): FeatureDescription[] {
|
export function experimentalFeatures(): FeatureDescription[] {
|
||||||
const filesTableView: FeatureDescription = {
|
return []
|
||||||
identifier: FeatureIdentifier.FilesTableView,
|
|
||||||
name: 'Files Table View',
|
|
||||||
description:
|
|
||||||
'Replaces the current Files view with a table view, with name, size, and date sort options. Requires reload to take effect.',
|
|
||||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
|
||||||
availableInRoles: [RoleName.PlusUser, RoleName.ProUser],
|
|
||||||
permission_name: PermissionName.FilesTableView,
|
|
||||||
}
|
|
||||||
|
|
||||||
return [filesTableView]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,4 @@ export enum PermissionName {
|
|||||||
TwoFactorAuth = 'server:two-factor-auth',
|
TwoFactorAuth = 'server:two-factor-auth',
|
||||||
SubscriptionSharing = 'server:subscription-sharing',
|
SubscriptionSharing = 'server:subscription-sharing',
|
||||||
SuperEditor = 'editor:super-editor',
|
SuperEditor = 'editor:super-editor',
|
||||||
FilesTableView = 'app:files-table-view',
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,4 +18,5 @@ export interface TagPreferences {
|
|||||||
editorIdentifier?: EditorIdentifier
|
editorIdentifier?: EditorIdentifier
|
||||||
entryMode?: 'normal' | 'daily'
|
entryMode?: 'normal' | 'daily'
|
||||||
panelWidth?: number
|
panelWidth?: number
|
||||||
|
useTableView?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { NavigationController } from '@/Controllers/Navigation/NavigationControl
|
|||||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||||
import { ElementIds } from '@/Constants/ElementIDs'
|
import { ElementIds } from '@/Constants/ElementIDs'
|
||||||
import { classNames } from '@standardnotes/utils'
|
import { classNames } from '@standardnotes/utils'
|
||||||
import { ContentType, FeatureIdentifier, SNTag } from '@standardnotes/snjs'
|
import { ContentType, SNTag } from '@standardnotes/snjs'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -97,8 +97,6 @@ const ContentList: FunctionComponent<Props> = ({
|
|||||||
|
|
||||||
const hasNotes = items.some((item) => item.content_type === ContentType.Note)
|
const hasNotes = items.some((item) => item.content_type === ContentType.Note)
|
||||||
|
|
||||||
const isFilesTableViewEnabled = application.features.isExperimentalFeatureEnabled(FeatureIdentifier.FilesTableView)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@@ -112,13 +110,7 @@ const ContentList: FunctionComponent<Props> = ({
|
|||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||||
>
|
>
|
||||||
{items.map((item, index) => {
|
{items.map((item) => {
|
||||||
const previousIndex = index - 1
|
|
||||||
const previousItem = previousIndex >= 0 ? items[previousIndex] : undefined
|
|
||||||
|
|
||||||
const nextIndex = index + 1
|
|
||||||
const nextItem = nextIndex < items.length ? items[nextIndex] : undefined
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentListItem
|
<ContentListItem
|
||||||
key={item.uuid}
|
key={item.uuid}
|
||||||
@@ -134,8 +126,6 @@ const ContentList: FunctionComponent<Props> = ({
|
|||||||
onSelect={selectItem}
|
onSelect={selectItem}
|
||||||
tags={getTagsForItem(item)}
|
tags={getTagsForItem(item)}
|
||||||
notesController={notesController}
|
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 React, { FunctionComponent } from 'react'
|
||||||
import FileListItem from './FileListItem'
|
import FileListItem from './FileListItem'
|
||||||
import FileListItemCard from './FileListItemCard'
|
|
||||||
import NoteListItem from './NoteListItem'
|
import NoteListItem from './NoteListItem'
|
||||||
import { AbstractListItemProps, doListItemPropsMeritRerender } from './Types/AbstractListItemProps'
|
import { AbstractListItemProps, doListItemPropsMeritRerender } from './Types/AbstractListItemProps'
|
||||||
import { ListableContentItem } from './Types/ListableContentItem'
|
import { ListableContentItem } from './Types/ListableContentItem'
|
||||||
|
|
||||||
const ContentListItem: FunctionComponent<AbstractListItemProps<ListableContentItem>> = (props) => {
|
const ContentListItem: FunctionComponent<AbstractListItemProps<ListableContentItem>> = (props) => {
|
||||||
const isFilesTableViewEnabled = props.application.features.isExperimentalFeatureEnabled(
|
|
||||||
FeatureIdentifier.FilesTableView,
|
|
||||||
)
|
|
||||||
|
|
||||||
switch (props.item.content_type) {
|
switch (props.item.content_type) {
|
||||||
case ContentType.Note:
|
case ContentType.Note:
|
||||||
return <NoteListItem {...props} item={props.item as SNNote} />
|
return <NoteListItem {...props} item={props.item as SNNote} />
|
||||||
case ContentType.File: {
|
case ContentType.File: {
|
||||||
if (isFilesTableViewEnabled) {
|
return <FileListItem {...props} item={props.item as FileItem} />
|
||||||
return <FileListItem {...props} item={props.item as FileItem} />
|
|
||||||
}
|
|
||||||
|
|
||||||
return <FileListItemCard {...props} item={props.item as FileItem} />
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
} from '@standardnotes/ui-services'
|
} from '@standardnotes/ui-services'
|
||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
import { PANEL_NAME_NOTES } from '@/Constants/Constants'
|
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 { observer } from 'mobx-react-lite'
|
||||||
import { forwardRef, useCallback, useEffect, useMemo } from 'react'
|
import { forwardRef, useCallback, useEffect, useMemo } from 'react'
|
||||||
import ContentList from '@/Components/ContentListView/ContentList'
|
import ContentList from '@/Components/ContentListView/ContentList'
|
||||||
@@ -40,6 +40,7 @@ import ContentTableView from '../ContentTableView/ContentTableView'
|
|||||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||||
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
||||||
|
import { PaneController } from '@/Controllers/PaneController/PaneController'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
accountMenuController: AccountMenuController
|
accountMenuController: AccountMenuController
|
||||||
@@ -54,6 +55,7 @@ type Props = {
|
|||||||
linkingController: LinkingController
|
linkingController: LinkingController
|
||||||
featuresController: FeaturesController
|
featuresController: FeaturesController
|
||||||
historyModalController: HistoryModalController
|
historyModalController: HistoryModalController
|
||||||
|
paneController: PaneController
|
||||||
className?: string
|
className?: string
|
||||||
id: string
|
id: string
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
@@ -75,6 +77,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
linkingController,
|
linkingController,
|
||||||
featuresController,
|
featuresController,
|
||||||
historyModalController,
|
historyModalController,
|
||||||
|
paneController,
|
||||||
className,
|
className,
|
||||||
id,
|
id,
|
||||||
children,
|
children,
|
||||||
@@ -94,6 +97,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
renderedItems,
|
renderedItems,
|
||||||
items,
|
items,
|
||||||
isCurrentNoteTemplate,
|
isCurrentNoteTemplate,
|
||||||
|
isTableViewEnabled,
|
||||||
} = itemListController
|
} = itemListController
|
||||||
|
|
||||||
const innerRef = useForwardedRef(ref)
|
const innerRef = useForwardedRef(ref)
|
||||||
@@ -185,9 +189,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
}, [isFilesSmartView, filesController, createNewNote, toggleAppPane, application])
|
}, [isFilesSmartView, filesController, createNewNote, toggleAppPane, application])
|
||||||
|
|
||||||
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||||
const isFilesTableViewEnabled = application.features.isExperimentalFeatureEnabled(FeatureIdentifier.FilesTableView)
|
const shouldUseTableView = (isFilesSmartView || isTableViewEnabled) && !isMobileScreen
|
||||||
const shouldShowFilesTableView =
|
|
||||||
isFilesTableViewEnabled && !isMobileScreen && selectedTag?.uuid === SystemViewId.Files
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const searchBarElement = document.getElementById(ElementIds.SearchBar)
|
const searchBarElement = document.getElementById(ElementIds.SearchBar)
|
||||||
@@ -211,7 +213,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
if (searchBarElement === document.activeElement) {
|
if (searchBarElement === document.activeElement) {
|
||||||
searchBarElement?.blur()
|
searchBarElement?.blur()
|
||||||
}
|
}
|
||||||
if (shouldShowFilesTableView) {
|
if (shouldUseTableView) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectNextItem()
|
selectNextItem()
|
||||||
@@ -221,7 +223,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
||||||
element: document.body,
|
element: document.body,
|
||||||
onKeyDown: () => {
|
onKeyDown: () => {
|
||||||
if (shouldShowFilesTableView) {
|
if (shouldUseTableView) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectPreviousItem()
|
selectPreviousItem()
|
||||||
@@ -265,7 +267,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
selectNextItem,
|
selectNextItem,
|
||||||
selectPreviousItem,
|
selectPreviousItem,
|
||||||
selectionController,
|
selectionController,
|
||||||
shouldShowFilesTableView,
|
shouldUseTableView,
|
||||||
])
|
])
|
||||||
|
|
||||||
const shortcutForCreate = useMemo(
|
const shortcutForCreate = useMemo(
|
||||||
@@ -319,18 +321,19 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
addButtonLabel={addButtonLabel}
|
addButtonLabel={addButtonLabel}
|
||||||
addNewItem={addNewItem}
|
addNewItem={addNewItem}
|
||||||
isFilesSmartView={isFilesSmartView}
|
isFilesSmartView={isFilesSmartView}
|
||||||
isFilesTableViewEnabled={isFilesTableViewEnabled}
|
isTableViewEnabled={isTableViewEnabled}
|
||||||
optionsSubtitle={optionsSubtitle}
|
optionsSubtitle={optionsSubtitle}
|
||||||
selectedTag={selectedTag}
|
selectedTag={selectedTag}
|
||||||
filesController={filesController}
|
filesController={filesController}
|
||||||
itemListController={itemListController}
|
itemListController={itemListController}
|
||||||
|
paneController={paneController}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{(!shouldShowFilesTableView || isMobileScreen) && (
|
{(!shouldUseTableView || isMobileScreen) && (
|
||||||
<SearchBar
|
<SearchBar
|
||||||
itemListController={itemListController}
|
itemListController={itemListController}
|
||||||
searchOptionsController={searchOptionsController}
|
searchOptionsController={searchOptionsController}
|
||||||
hideOptions={shouldShowFilesTableView}
|
hideOptions={shouldUseTableView}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<NoAccountWarning
|
<NoAccountWarning
|
||||||
@@ -355,7 +358,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
<p className="empty-items-list opacity-50">Loading...</p>
|
<p className="empty-items-list opacity-50">Loading...</p>
|
||||||
) : null}
|
) : null}
|
||||||
{!dailyMode && renderedItems.length ? (
|
{!dailyMode && renderedItems.length ? (
|
||||||
shouldShowFilesTableView ? (
|
shouldUseTableView ? (
|
||||||
<ContentTableView
|
<ContentTableView
|
||||||
items={items}
|
items={items}
|
||||||
application={application}
|
application={application}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import AddItemMenuButton from './AddItemMenuButton'
|
|||||||
import { FilesController } from '@/Controllers/FilesController'
|
import { FilesController } from '@/Controllers/FilesController'
|
||||||
import SearchButton from './SearchButton'
|
import SearchButton from './SearchButton'
|
||||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||||
|
import { PaneController } from '@/Controllers/PaneController/PaneController'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -21,11 +22,12 @@ type Props = {
|
|||||||
addButtonLabel: string
|
addButtonLabel: string
|
||||||
addNewItem: () => void
|
addNewItem: () => void
|
||||||
isFilesSmartView: boolean
|
isFilesSmartView: boolean
|
||||||
isFilesTableViewEnabled: boolean
|
isTableViewEnabled: boolean
|
||||||
optionsSubtitle?: string
|
optionsSubtitle?: string
|
||||||
selectedTag: AnyTag
|
selectedTag: AnyTag
|
||||||
filesController: FilesController
|
filesController: FilesController
|
||||||
itemListController: ItemListController
|
itemListController: ItemListController
|
||||||
|
paneController: PaneController
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContentListHeader = ({
|
const ContentListHeader = ({
|
||||||
@@ -35,11 +37,12 @@ const ContentListHeader = ({
|
|||||||
addButtonLabel,
|
addButtonLabel,
|
||||||
addNewItem,
|
addNewItem,
|
||||||
isFilesSmartView,
|
isFilesSmartView,
|
||||||
isFilesTableViewEnabled,
|
isTableViewEnabled,
|
||||||
optionsSubtitle,
|
optionsSubtitle,
|
||||||
selectedTag,
|
selectedTag,
|
||||||
filesController,
|
filesController,
|
||||||
itemListController,
|
itemListController,
|
||||||
|
paneController,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const displayOptionsContainerRef = useRef<HTMLDivElement>(null)
|
const displayOptionsContainerRef = useRef<HTMLDivElement>(null)
|
||||||
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
|
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
|
||||||
@@ -79,19 +82,13 @@ const ContentListHeader = ({
|
|||||||
isFilesSmartView={isFilesSmartView}
|
isFilesSmartView={isFilesSmartView}
|
||||||
isOpen={showDisplayOptionsMenu}
|
isOpen={showDisplayOptionsMenu}
|
||||||
selectedTag={selectedTag}
|
selectedTag={selectedTag}
|
||||||
|
paneController={paneController}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}, [
|
}, [showDisplayOptionsMenu, toggleDisplayOptionsMenu, application, isFilesSmartView, selectedTag, paneController])
|
||||||
showDisplayOptionsMenu,
|
|
||||||
toggleDisplayOptionsMenu,
|
|
||||||
displayOptionsButtonRef,
|
|
||||||
application,
|
|
||||||
isFilesSmartView,
|
|
||||||
selectedTag,
|
|
||||||
])
|
|
||||||
|
|
||||||
const AddButton = useMemo(() => {
|
const AddButton = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
@@ -106,12 +103,12 @@ const ContentListHeader = ({
|
|||||||
}, [addButtonLabel, addNewItem, filesController, isDailyEntry, isFilesSmartView])
|
}, [addButtonLabel, addNewItem, filesController, isDailyEntry, isFilesSmartView])
|
||||||
|
|
||||||
const SearchBarButton = useMemo(() => {
|
const SearchBarButton = useMemo(() => {
|
||||||
if (!isFilesSmartView || !isFilesTableViewEnabled || isMobileScreen) {
|
if (!isTableViewEnabled || isMobileScreen) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <SearchButton itemListController={itemListController} />
|
return <SearchButton itemListController={itemListController} />
|
||||||
}, [isFilesSmartView, isFilesTableViewEnabled, isMobileScreen, itemListController])
|
}, [isTableViewEnabled, isMobileScreen, itemListController])
|
||||||
|
|
||||||
const FolderName = useMemo(() => {
|
const FolderName = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
CollectionSort,
|
CollectionSort,
|
||||||
CollectionSortProperty,
|
CollectionSortProperty,
|
||||||
FeatureIdentifier,
|
|
||||||
IconType,
|
IconType,
|
||||||
isSmartView,
|
isSmartView,
|
||||||
isSystemView,
|
isSystemView,
|
||||||
@@ -25,6 +24,8 @@ import NoSubscriptionBanner from '@/Components/NoSubscriptionBanner/NoSubscripti
|
|||||||
import MenuRadioButtonItem from '@/Components/Menu/MenuRadioButtonItem'
|
import MenuRadioButtonItem from '@/Components/Menu/MenuRadioButtonItem'
|
||||||
import MenuSwitchButtonItem from '@/Components/Menu/MenuSwitchButtonItem'
|
import MenuSwitchButtonItem from '@/Components/Menu/MenuSwitchButtonItem'
|
||||||
import { Pill } from '@/Components/Preferences/PreferencesComponents/Content'
|
import { Pill } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||||
|
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||||
|
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
|
||||||
|
|
||||||
const DailyEntryModeEnabled = true
|
const DailyEntryModeEnabled = true
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
isOpen,
|
isOpen,
|
||||||
isFilesSmartView,
|
isFilesSmartView,
|
||||||
selectedTag,
|
selectedTag,
|
||||||
|
paneController,
|
||||||
}) => {
|
}) => {
|
||||||
const isSystemTag = isSmartView(selectedTag) && isSystemView(selectedTag)
|
const isSystemTag = isSmartView(selectedTag) && isSystemView(selectedTag)
|
||||||
const selectedTagPreferences = isSystemTag
|
const selectedTagPreferences = isSystemTag
|
||||||
@@ -217,6 +219,14 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
void changePreferences({ entryMode: isDailyEntry ? 'normal' : 'daily' })
|
void changePreferences({ entryMode: isDailyEntry ? 'normal' : 'daily' })
|
||||||
}, [isDailyEntry, changePreferences])
|
}, [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<{
|
const TabButton: FunctionComponent<{
|
||||||
label: string
|
label: string
|
||||||
mode: PreferenceMode
|
mode: PreferenceMode
|
||||||
@@ -250,8 +260,9 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFilesTableViewEnabled = application.features.isExperimentalFeatureEnabled(FeatureIdentifier.FilesTableView)
|
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||||
const shouldHideNonApplicableOptions = isFilesTableViewEnabled && selectedTag?.uuid === SystemViewId.Files
|
const isTableViewEnabled = Boolean(isFilesSmartView || preferences.useTableView)
|
||||||
|
const shouldHideNonApplicableOptions = isTableViewEnabled && !isMobileScreen
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu className="text-sm" a11yLabel="Notes list options menu" isOpen={isOpen}>
|
<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 />
|
<MenuItemSeparator />
|
||||||
<MenuSwitchButtonItem
|
<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') && (
|
{!shouldHideNonApplicableOptions && (!isSystemTag || currentMode === 'global') && (
|
||||||
<>
|
<>
|
||||||
<MenuItemSeparator />
|
<MenuItemSeparator />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
import { AnyTag } from '@/Controllers/Navigation/AnyTagType'
|
import { AnyTag } from '@/Controllers/Navigation/AnyTagType'
|
||||||
|
import { PaneController } from '@/Controllers/PaneController/PaneController'
|
||||||
|
|
||||||
export type DisplayOptionsMenuPositionProps = {
|
export type DisplayOptionsMenuPositionProps = {
|
||||||
top: number
|
top: number
|
||||||
@@ -11,4 +12,5 @@ export type DisplayOptionsMenuProps = {
|
|||||||
selectedTag: AnyTag
|
selectedTag: AnyTag
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
isFilesSmartView: boolean
|
isFilesSmartView: boolean
|
||||||
|
paneController: PaneController
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,6 +305,7 @@ const PanesSystemComponent = () => {
|
|||||||
linkingController={viewControllerManager.linkingController}
|
linkingController={viewControllerManager.linkingController}
|
||||||
featuresController={viewControllerManager.featuresController}
|
featuresController={viewControllerManager.featuresController}
|
||||||
historyModalController={viewControllerManager.historyModalController}
|
historyModalController={viewControllerManager.historyModalController}
|
||||||
|
paneController={viewControllerManager.paneController}
|
||||||
>
|
>
|
||||||
{showPanelResizers && listRef && (
|
{showPanelResizers && listRef && (
|
||||||
<PanelResizer
|
<PanelResizer
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ function Table<Data>({ table }: { table: Table<Data> }) {
|
|||||||
break
|
break
|
||||||
case KeyboardKey.Down:
|
case KeyboardKey.Down:
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (focusedRowIndex.current < rowCount) {
|
if (focusedRowIndex.current <= rowCount) {
|
||||||
const nextRow = focusedRowIndex.current + 1
|
const nextRow = focusedRowIndex.current + 1
|
||||||
focusCell(nextRow, focusedCellIndex.current)
|
focusCell(nextRow, focusedCellIndex.current)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem'
|
import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem'
|
||||||
import { destroyAllObjectProperties } from '@/Utils'
|
import { destroyAllObjectProperties, isMobileScreen } from '@/Utils'
|
||||||
import {
|
import {
|
||||||
ApplicationEvent,
|
ApplicationEvent,
|
||||||
CollectionSort,
|
CollectionSort,
|
||||||
@@ -71,6 +71,7 @@ export class ItemListController extends AbstractViewController implements Intern
|
|||||||
hideNotePreview: false,
|
hideNotePreview: false,
|
||||||
hideEditorIcon: false,
|
hideEditorIcon: false,
|
||||||
}
|
}
|
||||||
|
isTableViewEnabled = false
|
||||||
private reloadItemsPromise?: Promise<unknown>
|
private reloadItemsPromise?: Promise<unknown>
|
||||||
|
|
||||||
override deinit() {
|
override deinit() {
|
||||||
@@ -451,6 +452,10 @@ export class ItemListController extends AbstractViewController implements Intern
|
|||||||
this.selectionController.deselectItem(activeItem)
|
this.selectionController.deselectItem(activeItem)
|
||||||
|
|
||||||
if (this.shouldSelectFirstItem(itemsReloadSource)) {
|
if (this.shouldSelectFirstItem(itemsReloadSource)) {
|
||||||
|
if (this.isTableViewEnabled && !isMobileScreen()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
log(LoggingDomain.Selection, 'Selecting next item after closing active one')
|
log(LoggingDomain.Selection, 'Selecting next item after closing active one')
|
||||||
this.selectionController.selectNextItem({ userTriggered: false })
|
this.selectionController.selectNextItem({ userTriggered: false })
|
||||||
}
|
}
|
||||||
@@ -513,6 +518,8 @@ export class ItemListController extends AbstractViewController implements Intern
|
|||||||
? this.application.getPreference(PrefKey.SystemViewPreferences)?.[selectedTag.uuid as SystemViewId]
|
? this.application.getPreference(PrefKey.SystemViewPreferences)?.[selectedTag.uuid as SystemViewId]
|
||||||
: selectedTag?.preferences
|
: selectedTag?.preferences
|
||||||
|
|
||||||
|
this.isTableViewEnabled = Boolean(selectedTagPreferences?.useTableView)
|
||||||
|
|
||||||
const currentSortBy = this.displayOptions.sortBy
|
const currentSortBy = this.displayOptions.sortBy
|
||||||
let sortBy =
|
let sortBy =
|
||||||
selectedTagPreferences?.sortBy ||
|
selectedTagPreferences?.sortBy ||
|
||||||
@@ -729,6 +736,10 @@ export class ItemListController extends AbstractViewController implements Intern
|
|||||||
selectFirstItem = async () => {
|
selectFirstItem = async () => {
|
||||||
const item = this.getFirstNonProtectedItem()
|
const item = this.getFirstNonProtectedItem()
|
||||||
|
|
||||||
|
if (this.isTableViewEnabled && !isMobileScreen()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
log(LoggingDomain.Selection, 'Selecting first item', item.uuid)
|
log(LoggingDomain.Selection, 'Selecting first item', item.uuid)
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
InternalEventPublishStrategy,
|
InternalEventPublishStrategy,
|
||||||
VectorIconNameOrEmoji,
|
VectorIconNameOrEmoji,
|
||||||
isTag,
|
isTag,
|
||||||
|
PrefKey,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { action, computed, makeAutoObservable, makeObservable, observable, reaction, runInAction } from 'mobx'
|
import { action, computed, makeAutoObservable, makeObservable, observable, reaction, runInAction } from 'mobx'
|
||||||
import { WebApplication } from '../../Application/Application'
|
import { WebApplication } from '../../Application/Application'
|
||||||
@@ -268,6 +269,14 @@ export class NavigationController
|
|||||||
return tag.uuid === SystemViewId.Files
|
return tag.uuid === SystemViewId.Files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagUsesTableView(tag: AnyTag): boolean {
|
||||||
|
const isSystemView = tag instanceof SmartView && Object.values(SystemViewId).includes(tag.uuid as SystemViewId)
|
||||||
|
const useTableView = isSystemView
|
||||||
|
? this.application.getPreference(PrefKey.SystemViewPreferences)?.[tag.uuid as SystemViewId]
|
||||||
|
: tag?.preferences
|
||||||
|
return Boolean(useTableView)
|
||||||
|
}
|
||||||
|
|
||||||
public isInAnySystemView(): boolean {
|
public isInAnySystemView(): boolean {
|
||||||
return (
|
return (
|
||||||
this.selected instanceof SmartView && Object.values(SystemViewId).includes(this.selected.uuid as SystemViewId)
|
this.selected instanceof SmartView && Object.values(SystemViewId).includes(this.selected.uuid as SystemViewId)
|
||||||
@@ -466,8 +475,8 @@ export class NavigationController
|
|||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag && this.isTagFilesView(tag)) {
|
if (tag && (this.isTagFilesView(tag) || this.tagUsesTableView(tag))) {
|
||||||
this.application.paneController.setPaneLayout(PaneLayout.FilesView)
|
this.application.paneController.setPaneLayout(PaneLayout.TableView)
|
||||||
} else if (userTriggered) {
|
} else if (userTriggered) {
|
||||||
this.application.paneController.setPaneLayout(PaneLayout.ItemSelection)
|
this.application.paneController.setPaneLayout(PaneLayout.ItemSelection)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export enum PaneLayout {
|
export enum PaneLayout {
|
||||||
TagSelection = 'tag-selection',
|
TagSelection = 'tag-selection',
|
||||||
ItemSelection = 'item-selection',
|
ItemSelection = 'item-selection',
|
||||||
FilesView = 'files-view',
|
TableView = 'files-view',
|
||||||
Editing = 'editing',
|
Editing = 'editing',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ export function panesForLayout(layout: PaneLayout, application: WebApplication):
|
|||||||
} else if (
|
} else if (
|
||||||
layout === PaneLayout.ItemSelection ||
|
layout === PaneLayout.ItemSelection ||
|
||||||
layout === PaneLayout.Editing ||
|
layout === PaneLayout.Editing ||
|
||||||
layout === PaneLayout.FilesView
|
layout === PaneLayout.TableView
|
||||||
) {
|
) {
|
||||||
return [AppPaneId.Items, AppPaneId.Editor]
|
return [AppPaneId.Items, AppPaneId.Editor]
|
||||||
}
|
}
|
||||||
} else if (screen.isMobile) {
|
} else if (screen.isMobile) {
|
||||||
if (layout === PaneLayout.TagSelection) {
|
if (layout === PaneLayout.TagSelection) {
|
||||||
return [AppPaneId.Navigation]
|
return [AppPaneId.Navigation]
|
||||||
} else if (layout === PaneLayout.ItemSelection || layout === PaneLayout.FilesView) {
|
} else if (layout === PaneLayout.ItemSelection || layout === PaneLayout.TableView) {
|
||||||
return [AppPaneId.Navigation, AppPaneId.Items]
|
return [AppPaneId.Navigation, AppPaneId.Items]
|
||||||
} else if (layout === PaneLayout.Editing) {
|
} else if (layout === PaneLayout.Editing) {
|
||||||
return [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]
|
return [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (layout === PaneLayout.FilesView) {
|
if (layout === PaneLayout.TableView) {
|
||||||
return [AppPaneId.Navigation, AppPaneId.Items]
|
return [AppPaneId.Navigation, AppPaneId.Items]
|
||||||
} else {
|
} else {
|
||||||
return [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]
|
return [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]
|
||||||
|
|||||||
Reference in New Issue
Block a user