Skip to content

Conversation

@cimigree
Copy link
Contributor

@cimigree cimigree commented Oct 9, 2025

Closes digidem/comapeo-mobile#1389
Adds utility to sort presets.
Adds test file for that.
Uses utility in a new hook to return the presets sorted by default order in project settings.
I originally did this in comapeo-mobile so I hope it translates ok! https://github.com/digidem/comapeo-mobile/pull/1420/files

…n a new hook to return the presets sorted by default order in project settings.
@cimigree cimigree requested a review from achou11 October 9, 2025 22:12
…ion to support sorting presets. Updates docs, test, and exports.
@cimigree cimigree requested a review from achou11 October 13, 2025 17:08
Comment on lines 8 to 14
function makePreset(
docId: string,
name: string,
geometry: Array<'point' | 'line'>,
): PresetDoc {
return { docId, name, geometry } as unknown as PresetDoc
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think it could be preferable to use @mapeo/mock-data for this e.g.

function makePreset(overrides: Partial<Preset>) {
	const preset = generate('preset', { count: 1 })[0]!
	return {
		...preset,
		...overrides,
	}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Done. af6e133

Comment on lines 56 to 59
const presetMap = new Map(presets.map((p) => [p.docId, p]))
const orderedPresets = orderedPresetIds
.map((id) => presetMap.get(id))
.filter((p): p is PresetDoc => Boolean(p))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think this can be reduced to something like this:

Suggested change
const presetMap = new Map(presets.map((p) => [p.docId, p]))
const orderedPresets = orderedPresetIds
.map((id) => presetMap.get(id))
.filter((p): p is PresetDoc => Boolean(p))
const orderedPresets: Array<Preset> = []
for (const presetId of orderedPresetIds) {
const matches = presets.filter((p) => p.docId === presetId)
orderedPresets.push(...matches)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took Gregor's function (pretty much as is...) af6e133

@gmaclennan
Copy link
Member

gmaclennan commented Oct 13, 2025

Hi folks, sorry I had not been keeping up with this work being done, in parallel to the improvements on categories.

I think the new naming for "defaultPresets" will help with understanding and implementation: Category Selection. This defines what categories (presets) should be presented to the user when they are selecting a category for a given datatype, including the order that they should be displayed in.

The category selection could be a subset of all the categories that are defined, because a project maintainer might want to keep a category defined for matching existing data, but not allow users to select it for new data.

Until we change the language everywhere, we have to work with the "defaultPresets" language for now.

I think adding this as a hook could help with naming and understanding:

function Example() {
  const presets = usePresetsSelection({ projectId, dataType: 'observation' })
  return (<CategoryScreen>
    {presets.map(preset => <CategoryItem title={preset.name} icon={preset.icon} />}
  </CategoryScreen>)
}
const dataTypeToGeom = {
  observation: 'point',
  track: 'line'
} as const

/**
 * Returns an ordered list of presets to be shown to the user when 
 * selecting the category for the given data type (observation or track)
 * If the project does not have a presets selection defined, then all presets
 * are returned, sorted by name.
 */
function usePresetsSelection(dataType: 'observation' | 'track') {
  const { data: projectSettings } = useProjectSettings({ projectId })
  const { data: presets } = useManyDocs({ projectId, docType: 'preset', lang })

  const defaults = projectSettings.defaultPresets[dataTypeToGeom[dataType]]
  if (!defaults.length) {
    return sortByName(presets)
  }
  const presetsSelection: Preset[] = []
  for (const presetDocId of defaults) {
    const preset = presets.find(preset = preset.docId = presetDocId)
    if (preset) {
      presetsSelection.push(preset)
    } else {
      // TODO: log error to Sentry
    }
  }
  if (!presetsSelection.length) {
    return sortByName(presets)
  }
  return presetsSelection
}

This would might benefit from being memoized in the future, which is why it's good to have it as a hook so that optimization can be an implementation detail of core-react.

We could start a gradual rollout of the new language here with a hook name like useCategorySelection() but that could create confusion if we don't rename everywhere at once?

@cimigree
Copy link
Contributor Author

@achou11 and/ or @gmaclennan
Thanks for all the feedback! I want to make sure I understand what everyone wants/ what is best moving forward before making more changes. @achou11 - Your reviews pushed toward simplifying to just a helper function that sorts. I made those changes. @gmaclennan - You're suggesting we do need a hook after all (usePresetsSelection), which handles the complete "category selection" logic (filtering by geometry, ordering by defaults, fallback to alphabetical). A few questions to make sure I understand:

  • Should I go back to a hook-based approach like I had originally? And follow Gregor's pattern with usePresetsSelection({ projectId, dataType: 'observations' | 'tracks' })
  • Should the helper function still be exported for advanced use cases, or should everything go through the hook?
  • Naming: Should we use usePresetsSelection (current schema naming) or useCategorySelection (future naming)? I'm inclined toward usePresetsSelection to match the existing projectSettings.defaultPresets property.

For Andrew's other comments (using @mapeo/mock-data, default config test, type improvements) - should I wait until we settle on the architecture first?

Happy to go either direction, just want to make sure we're all aligned on expectations.

@achou11
Copy link
Member

achou11 commented Oct 13, 2025

I think the new naming for "defaultPresets" will help with understanding and implementation: Category Selection. This defines what categories (presets) should be presented to the user when they are selecting a category for a given datatype, including the order that they should be displayed in.
...
The category selection could be a subset of all the categories that are defined, because a project maintainer might want to keep a category defined for matching existing data, but not allow users to select it for new data.

@gmaclennan thanks for elaborating. These I haven't fully processed everything but these 2 points are definitely clarifying things a bit for me.

We could start a gradual rollout of the new language here with a hook name like useCategorySelection() but that could create confusion if we don't rename everywhere at once?

Yeah i think for now let's keep the same language and then make a single push for updating the language as a separate piece of work.


@cimigree apologies for the confusion, but given the reasons outlined by Gregor, let's opt for exporting a hook implementation.

@achou11
Copy link
Member

achou11 commented Oct 13, 2025

@cimigree

Should I go back to a hook-based approach like I had originally? And follow Gregor's pattern with usePresetsSelection({ projectId, dataType: 'observations' | 'tracks' })

Yes to both 👍

Should the helper function still be exported for advanced use cases, or should everything go through the hook?

No, no need to export that function as it becomes an internal implementation detail.

Naming: Should we use usePresetsSelection (current schema naming) or useCategorySelection (future naming)? I'm inclined toward usePresetsSelection to match the existing projectSettings.defaultPresets property.

Yeah let's stick with the existing naming conventions for now. It'll be easier to a do a bigger renaming of things as a singular piece of work later.

@achou11
Copy link
Member

achou11 commented Oct 13, 2025

For Andrew's other comments (using @mapeo/mock-data, default config test, type improvements) - should I wait until we settle on the architecture first?

Think it'd still be helpful to have these in place if they're still relevant after working on the implementation. I'd imagine that they will be but can revisit when you're ready to re-review the implementation

@gmaclennan
Copy link
Member

I'm in agreement with what Andrew says here. Sorry about any confusion, I know that categories and related things like "default presets" are poorly defined and I'm working towards clarifying that, but it's currently in flux as I put together documentation.

@awana-lockfile-bot
Copy link

package-lock.json changes

Click to toggle table visibility
Name Status Previous Current
@faker-js/faker ADDED - 9.9.0
@jsep-plugin/assignment ADDED - 1.3.0
@jsep-plugin/regex ADDED - 1.0.4
@mapeo/default-config ADDED - 5.0.0
@mapeo/mock-data ADDED - 5.0.0
call-me-maybe ADDED - 1.0.2
dereference-json-schema ADDED - 0.2.1
esprima ADDED - 4.0.1
format-util ADDED - 1.0.5
jsep ADDED - 1.4.0
json-schema-faker ADDED - 0.5.9
json-schema-ref-parser ADDED - 6.1.0
jsonpath-plus ADDED - 10.3.0
ono ADDED - 4.0.11
sprintf-js ADDED - 1.0.3

@socket-security
Copy link

socket-security bot commented Oct 13, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​mapeo/​default-config@​5.0.048100378890
Added@​mapeo/​mock-data@​5.0.0751009792100

View full report

@cimigree
Copy link
Contributor Author

Ok, @achou11 I had to add some packages for the testing. Hope that is ok. I think I understood what was wanted and hope we are getting close to the desired implementation.

@cimigree cimigree requested a review from achou11 October 13, 2025 21:05
Copy link
Member

@achou11 achou11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Some minor non-blocking comments but otherwise feel free to merge when ready

EDIT: Might be worth updating the PR title to mention that a new hook has been added.

@cimigree cimigree changed the title feat: sorts presets by default order feat: adds hook to sort presets by default order Oct 14, 2025
@cimigree cimigree merged commit 9f4e5f0 into main Oct 14, 2025
9 checks passed
@cimigree cimigree deleted the feat/sort-presets-default branch October 14, 2025 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use project-settings defaultPresets to render category chooser

3 participants