feat: Added per-tag preference to use table layout and removed "Files Table View" from Labs

This commit is contained in:
Aman Harwara
2023-01-06 17:00:19 +05:30
parent eea97362f8
commit dd8ccdeadc
16 changed files with 98 additions and 76 deletions

View File

@@ -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}
/>
)
})}

View File

@@ -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

View File

@@ -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}

View File

@@ -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 (

View File

@@ -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 />

View File

@@ -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
}

View File

@@ -305,6 +305,7 @@ const PanesSystemComponent = () => {
linkingController={viewControllerManager.linkingController}
featuresController={viewControllerManager.featuresController}
historyModalController={viewControllerManager.historyModalController}
paneController={viewControllerManager.paneController}
>
{showPanelResizers && listRef && (
<PanelResizer

View File

@@ -186,7 +186,7 @@ function Table<Data>({ table }: { table: Table<Data> }) {
break
case KeyboardKey.Down:
event.preventDefault()
if (focusedRowIndex.current < rowCount) {
if (focusedRowIndex.current <= rowCount) {
const nextRow = focusedRowIndex.current + 1
focusCell(nextRow, focusedCellIndex.current)
}

View File

@@ -1,5 +1,5 @@
import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem'
import { destroyAllObjectProperties } from '@/Utils'
import { destroyAllObjectProperties, isMobileScreen } from '@/Utils'
import {
ApplicationEvent,
CollectionSort,
@@ -71,6 +71,7 @@ export class ItemListController extends AbstractViewController implements Intern
hideNotePreview: false,
hideEditorIcon: false,
}
isTableViewEnabled = false
private reloadItemsPromise?: Promise<unknown>
override deinit() {
@@ -451,6 +452,10 @@ export class ItemListController extends AbstractViewController implements Intern
this.selectionController.deselectItem(activeItem)
if (this.shouldSelectFirstItem(itemsReloadSource)) {
if (this.isTableViewEnabled && !isMobileScreen()) {
return
}
log(LoggingDomain.Selection, 'Selecting next item after closing active one')
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]
: selectedTag?.preferences
this.isTableViewEnabled = Boolean(selectedTagPreferences?.useTableView)
const currentSortBy = this.displayOptions.sortBy
let sortBy =
selectedTagPreferences?.sortBy ||
@@ -729,6 +736,10 @@ export class ItemListController extends AbstractViewController implements Intern
selectFirstItem = async () => {
const item = this.getFirstNonProtectedItem()
if (this.isTableViewEnabled && !isMobileScreen()) {
return
}
if (item) {
log(LoggingDomain.Selection, 'Selecting first item', item.uuid)

View File

@@ -14,6 +14,7 @@ import {
InternalEventPublishStrategy,
VectorIconNameOrEmoji,
isTag,
PrefKey,
} from '@standardnotes/snjs'
import { action, computed, makeAutoObservable, makeObservable, observable, reaction, runInAction } from 'mobx'
import { WebApplication } from '../../Application/Application'
@@ -268,6 +269,14 @@ export class NavigationController
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 {
return (
this.selected instanceof SmartView && Object.values(SystemViewId).includes(this.selected.uuid as SystemViewId)
@@ -466,8 +475,8 @@ export class NavigationController
.catch(console.error)
}
if (tag && this.isTagFilesView(tag)) {
this.application.paneController.setPaneLayout(PaneLayout.FilesView)
if (tag && (this.isTagFilesView(tag) || this.tagUsesTableView(tag))) {
this.application.paneController.setPaneLayout(PaneLayout.TableView)
} else if (userTriggered) {
this.application.paneController.setPaneLayout(PaneLayout.ItemSelection)
}

View File

@@ -1,6 +1,6 @@
export enum PaneLayout {
TagSelection = 'tag-selection',
ItemSelection = 'item-selection',
FilesView = 'files-view',
TableView = 'files-view',
Editing = 'editing',
}

View File

@@ -11,20 +11,20 @@ export function panesForLayout(layout: PaneLayout, application: WebApplication):
} else if (
layout === PaneLayout.ItemSelection ||
layout === PaneLayout.Editing ||
layout === PaneLayout.FilesView
layout === PaneLayout.TableView
) {
return [AppPaneId.Items, AppPaneId.Editor]
}
} else if (screen.isMobile) {
if (layout === PaneLayout.TagSelection) {
return [AppPaneId.Navigation]
} else if (layout === PaneLayout.ItemSelection || layout === PaneLayout.FilesView) {
} else if (layout === PaneLayout.ItemSelection || layout === PaneLayout.TableView) {
return [AppPaneId.Navigation, AppPaneId.Items]
} else if (layout === PaneLayout.Editing) {
return [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]
}
} else {
if (layout === PaneLayout.FilesView) {
if (layout === PaneLayout.TableView) {
return [AppPaneId.Navigation, AppPaneId.Items]
} else {
return [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]