diff --git a/jgclark.Dashboard/CHANGELOG.md b/jgclark.Dashboard/CHANGELOG.md
index ddb725dca..ca717e5ed 100644
--- a/jgclark.Dashboard/CHANGELOG.md
+++ b/jgclark.Dashboard/CHANGELOG.md
@@ -10,18 +10,24 @@ For more details see the [plugin's documentation](https://github.com/NotePlan/pl
- TODO: enlarged default window size on iPads
-->
-## [2.3.0.b10] 2025-09-03???
-- Fix: the done count not including items from project notes
+## [2.3.0.b10] 2025-09-???
+### Changed
+- When the "filter out lower-priority items" is on, this now calculated across all sections, not just each one independently. The label that says there are hidden items now includes "(click to show all)" text to make it clearer how to turn off the filter for that section.
+### Fixed
+- the done count not including items from project notes
## [2.3.0.b9] 2025-09-01
-- Added tagCache age to section info (if 'Comparison' FFlag is turned on)
+### Changes
- Add another check to regenerate or update tagMentionCache after 24 and 1 hour respectively
- Make 'Show folder name in note link?' setting indented under 'Show note link for tasks?'
- I have stopped some of the unnecessary generation of Project List. Let me know how it goes, @dwertheimer. (Will require rebuilding jgclark.Reviews plugin as well (to v1.2.4).)
-- Also added some logging to track down when Project list generation is happening when it shouldn't be. If 'Show Section Timings' Flag is on, then it will also write a log to note "@Meta/Project Generation Log".
+- Tweak some TaskDialog button labels
+### Fixed
- Fix color of blockID marker in light mode themes
- Further fix to display of calendar events in tasks
-- Tweak some TaskDialog button labels
+### Dev
+- Also added some logging to track down when Project list generation is happening when it shouldn't be. If 'Show Section Timings' Flag is on, then it will also write a log to note "@Meta/Project Generation Log".
+- Added tagCache age to section info (if 'Comparison' FFlag is turned on)
## [2.3.0.b8] 2025-08-27
- Add temporary workaround for error in DataStore.listOverdueParagraphs() for regular Teamspace notes
diff --git a/jgclark.Dashboard/src/react/components/Dashboard.jsx b/jgclark.Dashboard/src/react/components/Dashboard.jsx
index 908bb50f1..045a09b61 100644
--- a/jgclark.Dashboard/src/react/components/Dashboard.jsx
+++ b/jgclark.Dashboard/src/react/components/Dashboard.jsx
@@ -11,17 +11,14 @@
import React, { useEffect, useRef, useMemo } from 'react'
import useRefreshTimer from '../customHooks/useRefreshTimer.jsx'
import useWatchForResizes from '../customHooks/useWatchForResizes.jsx'
-import {
- dontDedupeSectionCodes,
- sectionDisplayOrder,
- sectionPriority,
-} from '../../constants.js'
+import { dontDedupeSectionCodes, sectionDisplayOrder, sectionPriority } from '../../constants.js'
import { copyUpdatedSectionItemData } from '../../dataGeneration.js'
import { findSectionItems } from '../../dashboardHelpers.js'
import { dashboardSettingDefs, dashboardFilterDefs } from '../../dashboardSettings.js'
import { useAppContext } from './AppContext.jsx'
import Dialog from './Dialog.jsx'
import { getSectionsWithoutDuplicateLines, countTotalSectionItems, countTotalVisibleSectionItems, sortSections, showSectionSettingItems } from './Section/sectionHelpers.js'
+import { calculateMaxPriorityAcrossAllSections } from './Section/useSectionSortAndFilter.jsx'
import Header from './Header'
import IdleTimer from './IdleTimer.jsx'
import Section from './Section/Section.jsx'
@@ -109,7 +106,7 @@ const Dashboard = ({ pluginData }: Props): React$Node => {
const deduplicatedSections = useMemo(() => {
if (sections.length >= 1 && dashboardSettings.hideDuplicates) {
// FIXME: this seems to be called for every section, even on refresh when only 1 section is requested
- // But TB and PROJ sections need to be ignored here, as they have different item types
+ // TB and PROJ sections need to be ignored here, as they have different item types
const dedupedSections = getSectionsWithoutDuplicateLines(origSections.slice(), ['filename', 'content'], sectionPriority, dontDedupeSectionCodes, dashboardSettings)
totalSectionItems = countTotalVisibleSectionItems(dedupedSections, dashboardSettings)
@@ -258,6 +255,15 @@ const Dashboard = ({ pluginData }: Props): React$Node => {
}
}, [pluginData.startDelayedRefreshTimer])
+ // Recalculate maximum priority when sections change (e.g., when items are removed)
+ useEffect(() => {
+ const newMaxPriority = calculateMaxPriorityAcrossAllSections(sections)
+ if (newMaxPriority !== pluginData.currentMaxPriorityFromAllVisibleSections) {
+ logInfo('Dashboard', `Recalculating max priority: ${pluginData.currentMaxPriorityFromAllVisibleSections} -> ${newMaxPriority}`)
+ updatePluginData({ ...pluginData, currentMaxPriorityFromAllVisibleSections: newMaxPriority }, `Recalculated max priority after sections changed: ${newMaxPriority}`)
+ }
+ }, [sections, pluginData.currentMaxPriorityFromAllVisibleSections])
+
//----------------------------------------------------------------------
// Handlers
//----------------------------------------------------------------------
@@ -292,25 +298,17 @@ const Dashboard = ({ pluginData }: Props): React$Node => {
return (
{autoUpdateEnabled && (
-
+
)}
{/* Note: this is where I might want to put further periodic data generation functions: completed task counter etc. */}
{reactSettings?.perspectivesTableVisible && (
-
+
)}
{sections.map((section, index) => (
-
{pluginData.perspectiveChanging && (
-
+
)}
{pluginData?.logSettings?._logLevel === 'DEV' && (
-
+
)}
diff --git a/jgclark.Dashboard/src/react/components/ItemGrid.jsx b/jgclark.Dashboard/src/react/components/ItemGrid.jsx
index 8ae7a73e7..a43fac54a 100644
--- a/jgclark.Dashboard/src/react/components/ItemGrid.jsx
+++ b/jgclark.Dashboard/src/react/components/ItemGrid.jsx
@@ -16,24 +16,25 @@ import { logDebug, logInfo } from '@helpers/react/reactDev.js'
type Props = {
items: Array,
thisSection: TSection,
+ onToggleShowAll?: () => void,
}
-function ItemGrid({ items, thisSection }: Props): React$Node {
+function ItemGrid({ items, thisSection, onToggleShowAll }: Props): React$Node {
const visibleItems = items.length
? items.map((item) => (
- // Using a complex key to ensure React updates components when item content changes (not just when ID changes)
-
+ // Using a complex key to ensure React updates components when item content changes (not just when ID changes)
+
))
: []
- // Calculate a subtle green background colour for the section if there are no items,
+ // Calculate a subtle green background colour for the section if there are no items,
// or if the first item is a congrats message,
// or if the section has asked for a coloured background.
const sectionBackgroundColor =
items.length === 0 || items[0].itemType === 'itemCongrats'
? `color-mix(in srgb, var(--bg-main-color), green 4%)`
- : (thisSection.showColoredBackground && thisSection.sectionTitleColorPart)
- ? `color-mix(in srgb, var(--bg-main-color), var(--fg-${thisSection.sectionTitleColorPart}) 4%)`
+ : thisSection.showColoredBackground && thisSection.sectionTitleColorPart
+ ? `color-mix(in srgb, var(--bg-main-color), var(--fg-${thisSection.sectionTitleColorPart}) 4%)`
: 'var(--bg-main-color)'
// if (sectionBackgroundColor !== 'var(--bg-main-color)') logDebug('ItemGrid', `sectionBackgroundColor: ${sectionBackgroundColor} from ${String(items.length)} items`)
diff --git a/jgclark.Dashboard/src/react/components/ItemRow.jsx b/jgclark.Dashboard/src/react/components/ItemRow.jsx
index e58d04811..53a841669 100644
--- a/jgclark.Dashboard/src/react/components/ItemRow.jsx
+++ b/jgclark.Dashboard/src/react/components/ItemRow.jsx
@@ -17,6 +17,7 @@ import { logDebug, logInfo } from '@helpers/react/reactDev'
type Props = {
item: TSectionItem,
thisSection: TSection,
+ onToggleShowAll?: () => void,
}
/**
@@ -24,7 +25,7 @@ type Props = {
* Loads the proper Component depending on itemType
* Note: the contentClassName are CSS classes that are used to style the item row, and are defined in Section.css
*/
-function ItemRow({ item, thisSection }: Props): Node {
+function ItemRow({ item, thisSection, onToggleShowAll }: Props): Node {
const { itemType } = item
let congratsMessage = 'Nothing on this list'
@@ -37,38 +38,26 @@ function ItemRow({ item, thisSection }: Props): Node {
<>
{itemType === 'project' ? (
- )
- : itemType === 'projectCongrats' ? (
-
- )
- : itemType === 'noSearchResults' ? (
-
- )
- : itemType === 'preLimitOverdues' ? (
-
- )
- : itemType === 'filterIndicator' ? (
-
- )
- : itemType === 'itemCongrats' ? (
-
- )
- : itemType === 'info' ? (
-
- )
- : (
-
- )}
+ ) : itemType === 'projectCongrats' ? (
+
+ ) : itemType === 'noSearchResults' ? (
+
+ ) : itemType === 'preLimitOverdues' ? (
+
+ ) : itemType === 'filterIndicator' ? (
+
+ ) : itemType === 'itemCongrats' ? (
+
+ ) : itemType === 'info' ? (
+
+ ) : (
+
+ )}
>
)
}
diff --git a/jgclark.Dashboard/src/react/components/Section/Section.jsx b/jgclark.Dashboard/src/react/components/Section/Section.jsx
index bf7fa4503..5902981ed 100644
--- a/jgclark.Dashboard/src/react/components/Section/Section.jsx
+++ b/jgclark.Dashboard/src/react/components/Section/Section.jsx
@@ -2,7 +2,7 @@
//--------------------------------------------------------------------------
// Dashboard React component to show a whole Dashboard Section
// Called by Dashboard component.
-// Last updated 2025-05-23 for v2.3.0.b3
+// Last updated 2025-09-05 for v2.3.0.b10
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
@@ -36,13 +36,13 @@ const Section = ({ section, onButtonClick }: SectionProps): React$Node => {
//----------------------------------------------------------------------
// Context
//----------------------------------------------------------------------
- const { dashboardSettings, reactSettings, setReactSettings, pluginData, sendActionToPlugin } = useAppContext()
+ const { dashboardSettings, reactSettings, setReactSettings, pluginData, sendActionToPlugin, updatePluginData } = useAppContext()
// logDebug('Section', `🔸 Section: ${section.sectionCode} (${String(section.sectionItems?.length ?? 0)} items in '${section.name}')`)
//----------------------------------------------------------------------
// State
//----------------------------------------------------------------------
- const [items, setItems] = useState < Array < TSectionItem >> ([])
+ const [items, setItems] = useState>([])
//----------------------------------------------------------------------
// Constants
@@ -50,9 +50,25 @@ const Section = ({ section, onButtonClick }: SectionProps): React$Node => {
const { sectionFilename, totalCount } = section
const isReferencedSection = section.isReferenced ?? false
+ // Get the current max priority from all visible sections (updated dynamically as sections process)
+ const currentMaxPriorityFromAllVisibleSections = pluginData.currentMaxPriorityFromAllVisibleSections
+
+ // Debug: log the values we're getting
+ logInfo('Section', `Section ${section.sectionCode} render: currentMaxPriorityFromAllVisibleSections=${currentMaxPriorityFromAllVisibleSections}`)
+
//----------------------------------------------------------------------
// Effects
//----------------------------------------------------------------------
+
+ // Watch for changes to currentMaxPriorityFromAllVisibleSections and force re-render
+ // This ensures that when one section updates the global max priority, all other sections
+ // will re-render and re-filter their items based on the new priority threshold
+ useEffect(() => {
+ // This effect will run whenever currentMaxPriorityFromAllVisibleSections changes
+ // The dependency on pluginData will trigger a re-render when updatePluginData is called
+ // of this component, which will cause useSectionSortAndFilter to recalculate
+ logInfo('Section', `Section ${section.sectionCode} detected pluginData change, currentMaxPriorityFromAllVisibleSections=${currentMaxPriorityFromAllVisibleSections}`)
+ }, [pluginData, section.sectionCode])
useEffect(() => {
if (!section) {
logError('Section', `No Section passed in.`)
@@ -153,11 +169,39 @@ const Section = ({ section, onButtonClick }: SectionProps): React$Node => {
//----------------------------------------------------------------------
// Note: this is where the display filtering/sorting/limiting happens.
- const { itemsToShow, numFilteredOut, limitApplied } = useSectionSortAndFilter(section, items, dashboardSettings)
-
+ const {
+ filteredItems: _filteredItems,
+ itemsToShow,
+ numFilteredOut: _numFilteredOut,
+ limitApplied,
+ maxPrioritySeenInThisSection,
+ toggleShowAllTasks,
+ } = useSectionSortAndFilter(section, items, dashboardSettings, currentMaxPriorityFromAllVisibleSections)
+
+ // Debug: log the values from useSectionSortAndFilter
+ logInfo('Section', `Section ${section.sectionCode} after useSectionSortAndFilter: maxPrioritySeenInThisSection=${maxPrioritySeenInThisSection}`)
+
+ // Update global max priority when this section finds a higher priority
+ useEffect(() => {
+ logDebug(
+ 'Section',
+ `Section ${section.sectionCode}${
+ section.sectionCode === 'TAG' ? ` (${section.name})` : ''
+ } useEffect running: maxPrioritySeenInThisSection=${maxPrioritySeenInThisSection}, currentMaxPriorityFromAllVisibleSections=${currentMaxPriorityFromAllVisibleSections}`,
+ )
+ if (maxPrioritySeenInThisSection > currentMaxPriorityFromAllVisibleSections) {
+ logInfo('Section', `Section ${section.sectionCode} found higher priority: ${maxPrioritySeenInThisSection} > ${currentMaxPriorityFromAllVisibleSections}, updating pluginData`)
+ updatePluginData(
+ { ...pluginData, currentMaxPriorityFromAllVisibleSections: maxPrioritySeenInThisSection },
+ `Section ${section.sectionCode} found higher priority: ${maxPrioritySeenInThisSection}`,
+ )
+ logInfo('Section', `Section ${section.sectionCode} set currentMaxPriorityFromAllVisibleSections to ${maxPrioritySeenInThisSection}`)
+ }
+ }, [maxPrioritySeenInThisSection, currentMaxPriorityFromAllVisibleSections, section.sectionCode])
//----------------------------------------------------------------------
// Handlers
//----------------------------------------------------------------------
+
// handle a click to start interactive processing
const handleInteractiveProcessingClick = useCallback(
(e: MouseEvent): void => {
@@ -181,6 +225,7 @@ const Section = ({ section, onButtonClick }: SectionProps): React$Node => {
onButtonClick(button)
}
+ // handle a clicking on the section title -> open the note in Editor if it has an associated filename
const handleSectionClick = (e: MouseEvent): void => {
if (!sectionFilename) return
const { modifierName } = extractModifierKeys(e) // Indicates whether a modifier key was pressed
@@ -189,7 +234,7 @@ const Section = ({ section, onButtonClick }: SectionProps): React$Node => {
}
//----------------------------------------------------------------------
- // Render
+ // Calculate values to use for rendering
//----------------------------------------------------------------------
// $FlowIgnore[invalid-computed-prop]
@@ -236,22 +281,29 @@ const Section = ({ section, onButtonClick }: SectionProps): React$Node => {
* limited: {L} of {T} projects ready to review
*/
// Replace {countWithLimit} with the number of items, and pluralise it if neccesary
- descriptionToUse = descriptionToUse.replace('{countWithLimit}', limitApplied
- ? `first ${items.length} of ${totalCount ?? '?'}`
- : `${totalCount ?? '?'}`)
+ descriptionToUse = descriptionToUse.replace('{countWithLimit}', limitApplied ? `first ${items.length} of ${totalCount ?? '?'}` : `${totalCount ?? '?'}`)
// Replace {count} with the number of items, and pluralise it if neccesary
- descriptionToUse = descriptionToUse.replace('{count}', `${totalCount ?? '?'} ${getTaskOrItemDisplayString(totalCount ?? 0, dashboardSettings.ignoreChecklistItems ? 'task' : 'item')}`)
+ descriptionToUse = descriptionToUse.replace(
+ '{count}',
+ `${totalCount ?? '?'} ${getTaskOrItemDisplayString(totalCount ?? 0, dashboardSettings.ignoreChecklistItems ? 'task' : 'item')}`,
+ )
// Replace {closedOrOpenTaskCount} with the number of completed or open tasks, depending on the 'showProgressInSections' setting
if (descriptionToUse.includes('{closedOrOpenTaskCount}')) {
let closedOrOpenTaskCountString = ''
switch (dashboardSettings.showProgressInSections) {
case 'number closed':
- closedOrOpenTaskCountString = `closed ${section.doneCounts?.completedTasks ?? '0'} ${getTaskOrItemDisplayString(section.doneCounts?.completedTasks ?? 0, dashboardSettings.ignoreChecklistItems ? 'task' : 'item')}`
+ closedOrOpenTaskCountString = `closed ${section.doneCounts?.completedTasks ?? '0'} ${getTaskOrItemDisplayString(
+ section.doneCounts?.completedTasks ?? 0,
+ dashboardSettings.ignoreChecklistItems ? 'task' : 'item',
+ )}`
break
case 'number open':
- closedOrOpenTaskCountString = `${totalCount ? String(totalCount) : '?'} open ${getTaskOrItemDisplayString(totalCount ?? 0, dashboardSettings.ignoreChecklistItems ? 'task' : 'item')}`
+ closedOrOpenTaskCountString = `${totalCount ? String(totalCount) : '?'} open ${getTaskOrItemDisplayString(
+ totalCount ?? 0,
+ dashboardSettings.ignoreChecklistItems ? 'task' : 'item',
+ )}`
break
default:
closedOrOpenTaskCountString = String(totalCount ?? 0)
@@ -268,9 +320,13 @@ const Section = ({ section, onButtonClick }: SectionProps): React$Node => {
// Prep a task-completion circle to the description for calendar non-referenced sections (where showProgressInSections !== 'none')
let completionCircle = null
if (numItemsToShow > 0 && ['DT', 'DY', 'W', 'LW', 'M', 'Q', 'Y'].includes(section.sectionCode) && section.doneCounts && dashboardSettings.showProgressInSections !== 'none') {
- const percentComplete = section.doneCounts.completedTasks / (section.doneCounts.completedTasks + items.length) * 100.0
- completionCircle =
-
+ const percentComplete = (section.doneCounts.completedTasks / (section.doneCounts.completedTasks + items.length)) * 100.0
+ completionCircle = (
+
{
spinnerMode={false}
/>{' '}
+ )
}
// If we have no data items to show (other than a congrats message), don't show description
// const descriptionDiv = numItemsToShow > 0 ? : null
- const descriptionDiv = numItemsToShow > 0 ?
- (
- {completionCircle}
- {/* {descriptionToUse} */}
- {descriptionToUse}
- {/* {totalCountString} */}
-
) : null
+ const descriptionDiv =
+ numItemsToShow > 0 ? (
+
+ {completionCircle}
+ {/* {descriptionToUse} */}
+ {descriptionToUse}
+ {/* {totalCountString} */}
+
+ ) : null
// Decide whether to show interactiveProcessing button
+ // Note: don't show IP button if there are no items to show, or if the first item is a single item type that we don't want to count (e.g. 'Nothing left on this list')
// TODO(later): enable again for PROJ
const showIPButton =
dashboardSettings.enableInteractiveProcessing &&
+ interactiveProcessingPossibleSectionTypes.includes(section.sectionCode) &&
numItemsToShow > 1 &&
- !treatSingleItemTypesAsZeroItems.includes(itemsToShow[0].itemType) &&
- interactiveProcessingPossibleSectionTypes.includes(section.sectionCode)
+ // TODO: use this next line instead if we want to pass all items to interactive processing, not just the [possibly filtered] numItemsToShow
+ // (numItemsToShow > 1 || (numItemsToShow === 1 && numFilteredOut > 0)) &&
+ !treatSingleItemTypesAsZeroItems.includes(itemsToShow[0].itemType)
// TB section can show up blank, without this extra check
if (itemsToShow.length === 0) {
@@ -365,13 +427,15 @@ const Section = ({ section, onButtonClick }: SectionProps): React$Node => {
{numItemsToShow}
+ {/* Note: use this instead of above if we want to pass all items to interactive processing, not just the [possibly filtered] numItemsToShow */}
+ {/* {numItemsToShow + numFilteredOut} */}
// >
)}
-
+
)
}
diff --git a/jgclark.Dashboard/src/react/components/Section/useSectionSortAndFilter.jsx b/jgclark.Dashboard/src/react/components/Section/useSectionSortAndFilter.jsx
index aca0f4e01..fcabc430d 100644
--- a/jgclark.Dashboard/src/react/components/Section/useSectionSortAndFilter.jsx
+++ b/jgclark.Dashboard/src/react/components/Section/useSectionSortAndFilter.jsx
@@ -6,7 +6,7 @@
// - Sort = sort items by priority, startTime, endTime (using itemSort() below)
// - Limit = only show the first N of M items
//
-// Last updated 2025-07-22 for v2.3.0.b
+// Last updated 2025-09-05 for v2.3.0.b10
//-----------------------------------------------------------------------------
import { useState, useEffect, useMemo } from 'react'
@@ -20,24 +20,62 @@ type UseSectionSortAndFilter = {
itemsToShow: Array,
numFilteredOut: number,
limitApplied: boolean,
+ maxPrioritySeenInThisSection: number,
+ toggleShowAllTasks: () => void,
}
-const useSectionSortAndFilter = (section: TSection, items: Array, dashboardSettings: any): UseSectionSortAndFilter => {
+const useSectionSortAndFilter = (
+ section: TSection,
+ items: Array,
+ dashboardSettings: any,
+ currentMaxPriorityFromAllVisibleSections: number,
+): UseSectionSortAndFilter => {
+ //----------------------------------------------------------------------
+ // Context
+ //----------------------------------------------------------------------
+
// Memoize the items array to prevent unnecessary re-renders
const memoizedItems = useMemo(() => items, [items])
const memoizedDashboardSettings = useMemo(() => dashboardSettings, [dashboardSettings])
+ // const memoizedCurrentMaxPriorityFromAllVisibleSections = useMemo(() => currentMaxPriorityFromAllVisibleSections, [currentMaxPriorityFromAllVisibleSections])
+
+ //----------------------------------------------------------------------
+ // State
+ //----------------------------------------------------------------------
const [filteredItems, setFilteredItems] = useState>([])
const [itemsToShow, setItemsToShow] = useState>([])
const [numFilteredOut, setFilteredOut] = useState(0)
const [limitApplied, setLimitApplied] = useState(false)
+ // Store the calculated max priority to return immediately
+ const [calculatedMaxPriority, setCalculatedMaxPriority] = useState(-1)
+
+ // Local state to track whether to show all tasks (ignore priority filtering)
+ const [showAllTasks, setShowAllTasks] = useState(false)
+
+ //----------------------------------------------------------------------
+ // Constants
+ // ---------------------------------------------------------------------
+
+ const limitToApply = memoizedDashboardSettings.maxItemsToShowInSection ?? 20
+ const filterByPriority = memoizedDashboardSettings.filterPriorityItems ?? false
+
+ //----------------------------------------------------------------------
+ // Effects
+ //----------------------------------------------------------------------
+
useEffect(() => {
+ logInfo(
+ 'useSectionSortAndFilter',
+ `Section ${section.sectionCode}${section.sectionCode === 'TAG' ? ` (${section.name})` : ''} useEffect running with ${memoizedItems.length} items`,
+ )
if (memoizedItems.length === 0) {
setFilteredItems([])
setItemsToShow([])
setFilteredOut(0)
setLimitApplied(false)
+ setCalculatedMaxPriority(-1)
return
}
@@ -74,20 +112,33 @@ const useSectionSortAndFilter = (section: TSection, items: Array,
totalCountToUse = totalCountToUse - (memoizedItems.length - typeWantedItems.length)
}
- // Find highest priority seen
- let maxPrioritySeen = -1
- for (const i of typeWantedItems) {
- if (i.para?.priority && i.para.priority > maxPrioritySeen) {
- maxPrioritySeen = i.para.priority
+ // Find highest priority seen (globally), and then filter out lower-priority items (if wanted)
+ const newCalculatedMaxPriority = getMaxPriorityInItems(typeWantedItems)
+ logInfo('useSectionSortAndFilter', `Section ${section.sectionCode} calculated max priority: ${newCalculatedMaxPriority}`)
+ setCalculatedMaxPriority(newCalculatedMaxPriority)
+
+ // TODO: but how do we downgrade this after it has been raised?
+ // Hopefully by re-setting at start of refresh calls
+ const filteredItems = (() => {
+ if (!filterByPriority || showAllTasks) {
+ return typeWantedItems.slice()
}
- }
- // and then filter out lower-priority items (if wanted)
- const filterByPriority = memoizedDashboardSettings.filterPriorityItems ?? false
- const filteredItems = filterByPriority
- ? typeWantedItems.filter((f) => (f.para?.priority ?? 0) >= maxPrioritySeen)
- : typeWantedItems.slice()
+
+ // If priority filtering is enabled but there are no priority items, show nothing
+ if (currentMaxPriorityFromAllVisibleSections === -1) {
+ return []
+ }
+
+ // Filter items that have priority >= currentMaxPriorityFromAllVisibleSections
+ return typeWantedItems.filter((f) => (f.para?.priority ?? 0) >= currentMaxPriorityFromAllVisibleSections)
+ })()
const priorityFilteringHappening = memoizedItems.length > filteredItems.length
- // logDebug('useSectionSortAndFilter', `${section.sectionCode}: ${memoizedItems.length} items; maxPri = ${String(maxPrioritySeen)}; leaves ${String(filteredItems.length)} filteredItems`)
+ logInfo(
+ 'useSectionSortAndFilter',
+ `${section.sectionCode} ${section.name}: ${memoizedItems.length} items; currentMaxPriorityFromAllVisibleSections = ${String(
+ currentMaxPriorityFromAllVisibleSections,
+ )}; maxPrioritySeenInThisSection = ${String(newCalculatedMaxPriority)}; leaves ${String(filteredItems.length)} filteredItems`,
+ )
// clo(filteredItems, 'useSectionSortAndFilter filteredItems:')
filteredItems.sort(itemSort)
@@ -97,7 +148,6 @@ const useSectionSortAndFilter = (section: TSection, items: Array,
// logDebug('useSectionSortAndFilter', `after reordering children: ${String(orderedFilteredItems.map(fi => fi.ID).join(','))}`)
// If more than limitToApply items, then just keep the first 'maxItemsToShowInSection' items, otherwise keep all
- const limitToApply = memoizedDashboardSettings.maxItemsToShowInSection ?? 20
const itemsToShow = limitToApply > 0 ? orderedFilteredItems.slice(0, limitToApply) : orderedFilteredItems.slice()
// TEST: not picking up for PRIORITY
// Requirement thinking, with example numbers:
@@ -114,9 +164,11 @@ const useSectionSortAndFilter = (section: TSection, items: Array,
ID: `${section.ID}-Filter`,
itemType: 'filterIndicator',
para: {
- content: `There ${numFilteredOut >= 2 ? 'are' : 'is'} also ${String(numFilteredOut)} ${priorityFilteringHappening ? 'lower-priority' : ''} ${
- numFilteredOut >= 2 ? 'items' : 'item'
- } currently hidden`,
+ content: showAllTasks
+ ? `Showing all ${typeWantedItems.length} items (click to filter by priority)`
+ : `There ${numFilteredOut >= 2 ? 'are' : 'is'} also ${String(numFilteredOut)} ${priorityFilteringHappening ? 'lower-priority' : ''} ${
+ numFilteredOut >= 2 ? 'items' : 'item'
+ } currently hidden (click to show all)`,
filename: '',
type: 'text',
noteType: 'Notes',
@@ -133,9 +185,50 @@ const useSectionSortAndFilter = (section: TSection, items: Array,
setFilteredOut(numFilteredOut)
setLimitApplied(limitApplied)
}
- }, [section, memoizedItems, memoizedDashboardSettings])
+ }, [section, memoizedItems, memoizedDashboardSettings, currentMaxPriorityFromAllVisibleSections, showAllTasks])
+
+ // Function to toggle showing all tasks
+ const toggleShowAllTasks = () => {
+ setShowAllTasks(!showAllTasks)
+ }
+
+ logInfo('useSectionSortAndFilter', `Section ${section.sectionCode} returning maxPrioritySeenInThisSection: ${calculatedMaxPriority}`)
+ return { filteredItems, itemsToShow, numFilteredOut, limitApplied, maxPrioritySeenInThisSection: calculatedMaxPriority, toggleShowAllTasks }
+}
+
+//----------------------------------------------------------------------
+// Supporting Functions
+//----------------------------------------------------------------------
+
+function getMaxPriorityInItems(items: Array): number {
+ let maxPrioritySeenInThisSection = -1
+ for (const i of items) {
+ if (i.para?.priority && i.para.priority > maxPrioritySeenInThisSection) {
+ maxPrioritySeenInThisSection = i.para.priority
+ logInfo('useSectionSortAndFilter', `- raised max priority to ${String(maxPrioritySeenInThisSection)}`)
+ }
+ }
+ return maxPrioritySeenInThisSection
+}
+
+/**
+ * Calculate the maximum priority across all visible sections
+ * @param {Array} sections - All sections to check
+ * @returns {number} The maximum priority found across all sections, or -1 if no items have priority
+ */
+export function calculateMaxPriorityAcrossAllSections(sections: Array): number {
+ let globalMaxPriority = -1
+
+ sections.forEach((section) => {
+ if (section.sectionItems && section.sectionItems.length > 0) {
+ const sectionMaxPriority = getMaxPriorityInItems(section.sectionItems)
+ if (sectionMaxPriority > globalMaxPriority) {
+ globalMaxPriority = sectionMaxPriority
+ }
+ }
+ })
- return { filteredItems, itemsToShow, numFilteredOut, limitApplied }
+ return globalMaxPriority
}
// sort items by itemType, priority, startTime, endTime
diff --git a/jgclark.Dashboard/src/react/components/TasksFiltered.jsx b/jgclark.Dashboard/src/react/components/TasksFiltered.jsx
index 414a2a6c6..553ba46ce 100644
--- a/jgclark.Dashboard/src/react/components/TasksFiltered.jsx
+++ b/jgclark.Dashboard/src/react/components/TasksFiltered.jsx
@@ -13,24 +13,29 @@ import { clo, logDebug, logWarn } from '@helpers/react/reactDev.js'
type Props = {
item: TSectionItem,
+ onToggleShowAll?: () => void,
}
/**
* Component for displaying a filter indicator.
*/
-const TasksFiltered = ({ item }: Props): Node => {
+const TasksFiltered = ({ item, onToggleShowAll }: Props): Node => {
const { dashboardSettings, dispatchDashboardSettings } = useAppContext()
function handleLineClick(_e: MouseEvent) {
- // logDebug('TasksFiltered/handleLineClick', `Trying to update filterPriorityItems setting`)
- // setDashboardSettings((prevSettings) => ({ ...prevSettings, ['filterPriorityItems']: false }))
- const newPayload = {
- ...dashboardSettings,
- ['filterPriorityItems']: false,
+ if (onToggleShowAll) {
+ // Use local toggle function if provided
+ logDebug('TasksFiltered', `handleLineClick calling local onToggleShowAll`)
+ onToggleShowAll()
+ } else {
+ // Fall back to global setting update
+ logDebug('TasksFiltered', `handleLineClick Calling UPDATE_DASHBOARD_SETTINGS`)
+ const newPayload = {
+ ...dashboardSettings,
+ ['filterPriorityItems']: false,
+ }
+ dispatchDashboardSettings({ type: DASHBOARD_ACTIONS.UPDATE_DASHBOARD_SETTINGS, payload: newPayload, reason: `Turning off filterPriorityItems` })
}
- logDebug('TasksFiltered', `handleLineClick Calling UPDATE_DASHBOARD_SETTINGS`)
- dispatchDashboardSettings({ type: DASHBOARD_ACTIONS.UPDATE_DASHBOARD_SETTINGS, payload: newPayload, reason: `Turning off filterPriorityItems` })
- //TODO: REFACTOR:Maybe update isModified & sendActionToPlugin to save the settings and remove from the useDashboardSettings hook
}
return (
diff --git a/jgclark.Dashboard/src/reactMain.js b/jgclark.Dashboard/src/reactMain.js
index 34354944c..b2d6876ff 100644
--- a/jgclark.Dashboard/src/reactMain.js
+++ b/jgclark.Dashboard/src/reactMain.js
@@ -532,6 +532,7 @@ export async function getPluginData(dashboardSettings: TDashboardSettings, persp
},
totalDoneCount: 0,
firstRun: true,
+ currentMaxPriorityFromAllVisibleSections: 0,
}
logDebug('getPluginData', `After forming initial pluginData, firstRun = false`)
diff --git a/jgclark.Dashboard/src/refreshClickHandlers.js b/jgclark.Dashboard/src/refreshClickHandlers.js
index 85371e1a2..4ec8c3410 100644
--- a/jgclark.Dashboard/src/refreshClickHandlers.js
+++ b/jgclark.Dashboard/src/refreshClickHandlers.js
@@ -34,7 +34,7 @@ export async function refreshAllSections(): Promise {
const startTime = new Date()
const reactWindowData = await getGlobalSharedData(WEBVIEW_WINDOW_ID)
// show refreshing message until done
- await setPluginData({ refreshing: true }, 'Starting Refreshing all sections')
+ await setPluginData({ refreshing: true, currentMaxPriorityFromAllVisibleSections: 0 }, 'Starting Refreshing all sections')
// refresh all sections' data
const newSections = await getAllSectionsData(reactWindowData.demoMode, false, false)
@@ -132,10 +132,9 @@ export async function incrementallyRefreshSomeSections(
return handlerResult(true)
}
catch (error) {
- await setPluginData({ refreshing: false }, `Error in incrementallyRefreshSomeSections; closing modal spinner`)
+ await setPluginData({ refreshing: false }, `Error in incrementallyRefreshSomeSections; will try to close modal spinner`)
logError('incrementallyRefreshSomeSections', error)
await sendBannerMessage(WEBVIEW_WINDOW_ID, `Error in incrementallyRefreshSomeSections: ${error.message}`)
- logDebug('incrementallyRefreshSomeSections', `Will also hide modal spinner`)
return handlerResult(false)
}
}
@@ -158,31 +157,34 @@ export async function refreshSomeSections(data: MessageDataObject, calledByTrigg
const reactWindowData = await getGlobalSharedData(WEBVIEW_WINDOW_ID)
const pluginData: TPluginData = reactWindowData.pluginData
// show refreshing message until done
- if (!pluginData.refreshing === true) await setPluginData({ refreshing: sectionCodes }, `Starting refresh for sections ${sectionCodes.toString()}`)
+ if (!pluginData.refreshing === true) await setPluginData({ refreshing: sectionCodes, currentMaxPriorityFromAllVisibleSections: 0 }, `Starting refresh for sections ${sectionCodes.toString()}`)
let existingSections = pluginData.sections
- // Now remove some sections that are no longer wanted:
- // - referenced sections if separateSectionForReferencedNotes is off
- // - TODO: sections that no longer match the sectionCodes. (Though this is clearly done somewhere else that works, so leaving alone for now)
+ // Now remove any referenced sections if separateSectionForReferencedNotes is now off
if (!pluginData.dashboardSettings.separateSectionForReferencedNotes) {
logDebug('refreshSomeSections', `Removing any referenced sections from inherited set of sections. Started with ${existingSections.length} sections [${getDisplayListOfSectionCodes(existingSections)}]`)
existingSections = existingSections.filter((section) => !section.isReferenced)
logDebug('refreshSomeSections', `removal -> ${existingSections.length} sections [${getDisplayListOfSectionCodes(existingSections)}]`)
}
- // force the section refresh for the wanted sections
+ // Now remove any sections that no longer match the sectionCodes to display.
+ // Note: this is clearly done somewhere else! (so won't do here as well)
+
+ // Force the wanted sections to refresh
const newSections = await getSomeSectionsData(sectionCodes, pluginData.demoMode, calledByTrigger)
// logTimer('refreshSomeSections', startTime, `- after getSomeSectionsData(): [${getDisplayListOfSectionCodes(newSections)}]`)
const mergedSections = mergeSections(existingSections, newSections)
// logTimer('refreshSomeSections', startTime, `- after mergeSections(): [${getDisplayListOfSectionCodes(mergedSections)}]`)
const updates: TAnyObject = { sections: mergedSections }
- // and update the total done counts
- // TODO: turning off for now, as was being called too often? Need to figure this out.
+
+ // Update the total done counts.
+ // Note: this is being done somewhere else, so turning off here
// updates.totalDoneCounts = getTotalDoneCountsFromSections(mergedSections)
if (!pluginData.refreshing === true) updates.refreshing = false
await setPluginData(updates, `Finished refreshSomeSections for [${String(sectionCodes)}] (${timer(startTime)})`)
+
// count sectionItems in all sections
const totalSectionItems = mergedSections.reduce((acc, section) => acc + section.sectionItems.length, 0)
// logDebug('refreshSomeSections', `Total section items: ${totalSectionItems} from [${sectionCodes.toString()}]`)
diff --git a/jgclark.Dashboard/src/types.js b/jgclark.Dashboard/src/types.js
index fafd76d4d..32e6efe56 100644
--- a/jgclark.Dashboard/src/types.js
+++ b/jgclark.Dashboard/src/types.js
@@ -1,7 +1,7 @@
// @flow
//-----------------------------------------------------------------------------
// Types for Dashboard code
-// Last updated 2025-08-26 for v2.3.0.b8, @jgclark
+// Last updated 2025-09-04 for v2.3.0.b10, @jgclark
//-----------------------------------------------------------------------------
// Types for Settings
@@ -187,7 +187,7 @@ export type TParagraphForDashboard = {
rawContent: string,
indents: number, // indent level (i.e. children will be 1+)
lineIndex: number, // needed for child ordering processing
- priority: number,
+ priority: number, // -1, 1 to 4
blockId?: string,
startTime?: string, // this is still definitely used to style time blocks
endTime?: string,
@@ -392,7 +392,8 @@ export type TPluginData = {
},
demoMode: boolean /* use fake content for demo/test purposes */,
totalDoneCount?: number,
- startDelayedRefreshTimer?: boolean /* start the delayed refresh timer hack set in post processing commands*/,
+ startDelayedRefreshTimer?: boolean /* start the delayed refresh timer hack set in post processing commands */,
+ currentMaxPriorityFromAllVisibleSections: number, /* the highest priority seen in the current section (to help display filtering) */
}
/**