Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Full docs live in [docs/](./docs). There's no docs site yet.

- [Install and first run](./docs/user/install.md)
- [Permission modes](./docs/user/permission-modes.md)
- [Thread labels](./docs/user/thread-labels.md)
- [Keyboard shortcuts](./docs/user/keybindings.md)
- [Remote access from a phone or another machine](./docs/user/remote-access.md)
- [Keeping app and server in sync](./docs/user/updating.md)
Expand Down
70 changes: 63 additions & 7 deletions apps/mobile/src/features/home/HomeHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EnvironmentId, SidebarThreadSortOrder } from "@t3tools/contracts";
import type { EnvironmentId, SidebarThreadSortOrder, ThreadLabel } from "@t3tools/contracts";
import type { MenuAction } from "@react-native-menu/menu";
import Constants from "expo-constants";
import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";
Expand All @@ -9,6 +9,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";

import { ControlPillMenu } from "../../components/ControlPill";
import { SymbolView } from "../../components/AppSymbol";
import { THREAD_LABEL_OPTIONS } from "@t3tools/shared/threadLabels";
import { T3Wordmark } from "../../components/T3Wordmark";
import { HOME_HORIZONTAL_INSET } from "../../lib/layoutMetrics";
import { resolveMobileStageLabel } from "../../lib/mobileBranding";
Expand Down Expand Up @@ -40,11 +41,13 @@ export function HomeHeader(props: {
readonly searchQuery: string;
readonly selectedEnvironmentId: EnvironmentId | null;
readonly selectedProjectKey: string | null;
readonly selectedThreadLabel: ThreadLabel | null;
readonly projectSortOrder: HomeProjectSortOrder;
readonly threadSortOrder: SidebarThreadSortOrder;
readonly onSearchQueryChange: (query: string) => void;
readonly onEnvironmentChange: (environmentId: EnvironmentId | null) => void;
readonly onProjectChange: (projectKey: string | null) => void;
readonly onThreadLabelChange: (label: ThreadLabel | null) => void;
readonly onProjectSortOrderChange: (sortOrder: HomeProjectSortOrder) => void;
readonly onThreadSortOrderChange: (sortOrder: SidebarThreadSortOrder) => void;
readonly onOpenSettings: () => void;
Expand All @@ -70,11 +73,13 @@ function AndroidHomeHeader(props: HomeHeaderProps) {
const stageLabel = resolveMobileStageLabel(Constants.expoConfig?.extra?.appVariant);
// Thread List v2 lays the list out in fixed creation order, so the
// sort/group filter controls would be silently ignored — hide them and
// key the "customized" icon state off the environment filter alone.
// key the "customized" icon state off the environment and label filters.
const threadListV2Enabled = useThreadListV2Enabled();
const hasCustomListOptions = threadListV2Enabled
? props.selectedEnvironmentId !== null || props.selectedProjectKey !== null
: hasCustomHomeListOptions(props);
? props.selectedEnvironmentId !== null ||
props.selectedProjectKey !== null ||
props.selectedThreadLabel !== null
: hasCustomHomeListOptions(props) || props.selectedThreadLabel !== null;
const menuActions = useMemo<MenuAction[]>(
() => [
{
Expand Down Expand Up @@ -113,6 +118,22 @@ function AndroidHomeHeader(props: HomeHeaderProps) {
],
},
] satisfies MenuAction[])),
{
id: "label",
title: "Label",
subactions: [
{
id: "label:all",
title: "All labels",
state: checkedMenuState(props.selectedThreadLabel === null),
},
...THREAD_LABEL_OPTIONS.map((option) => ({
id: `label:${option.value}`,
title: option.label,
state: checkedMenuState(props.selectedThreadLabel === option.value),
})),
],
},
...(threadListV2Enabled
? []
: ([
Expand Down Expand Up @@ -142,6 +163,7 @@ function AndroidHomeHeader(props: HomeHeaderProps) {
props.projects,
props.selectedEnvironmentId,
props.selectedProjectKey,
props.selectedThreadLabel,
props.threadSortOrder,
threadListV2Enabled,
],
Expand Down Expand Up @@ -178,6 +200,18 @@ function AndroidHomeHeader(props: HomeHeaderProps) {
return;
}

if (id === "label:all") {
props.onThreadLabelChange(null);
return;
}
if (id.startsWith("label:")) {
const label = THREAD_LABEL_OPTIONS.find(
(option) => option.value === id.slice("label:".length),
);
if (label) props.onThreadLabelChange(label.value);
return;
}

const projectSort = PROJECT_SORT_OPTIONS.find(
(option) => id === `project-sort:${option.value}`,
);
Expand Down Expand Up @@ -292,11 +326,13 @@ function IosHomeHeader(props: HomeHeaderProps) {
const iconColor = useThemeColor("--color-icon");
// Thread List v2 lays the list out in fixed creation order, so the
// sort/group filter controls would be silently ignored — hide them and
// key the "customized" icon state off the environment filter alone.
// key the "customized" icon state off the environment and label filters.
const threadListV2Enabled = useThreadListV2Enabled();
const hasCustomListOptions = threadListV2Enabled
? props.selectedEnvironmentId !== null || props.selectedProjectKey !== null
: hasCustomHomeListOptions(props);
? props.selectedEnvironmentId !== null ||
props.selectedProjectKey !== null ||
props.selectedThreadLabel !== null
: hasCustomHomeListOptions(props) || props.selectedThreadLabel !== null;
const focusSearch = useCallback(() => {
searchBarRef.current?.focus();
return searchBarRef.current !== null;
Expand Down Expand Up @@ -421,6 +457,26 @@ function IosHomeHeader(props: HomeHeaderProps) {
</NativeHeaderToolbar.Menu>
) : null}

<NativeHeaderToolbar.Menu title="Label">
<NativeHeaderToolbar.Label>Label</NativeHeaderToolbar.Label>
<NativeHeaderToolbar.MenuAction
isOn={props.selectedThreadLabel === null}
onPress={() => props.onThreadLabelChange(null)}
subtitle="Show threads with any label"
>
<NativeHeaderToolbar.Label>All labels</NativeHeaderToolbar.Label>
</NativeHeaderToolbar.MenuAction>
{THREAD_LABEL_OPTIONS.map((option) => (
<NativeHeaderToolbar.MenuAction
key={option.value}
isOn={props.selectedThreadLabel === option.value}
onPress={() => props.onThreadLabelChange(option.value)}
>
<NativeHeaderToolbar.Label>{option.label}</NativeHeaderToolbar.Label>
</NativeHeaderToolbar.MenuAction>
))}
</NativeHeaderToolbar.Menu>

{threadListV2Enabled ? null : (
<NativeHeaderToolbar.Menu title="Sort projects">
<NativeHeaderToolbar.Label>Sort projects</NativeHeaderToolbar.Label>
Expand Down
7 changes: 7 additions & 0 deletions apps/mobile/src/features/home/HomeRouteScreen.tsx
Comment thread
mtdewwolf marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as Arr from "effect/Array";
import * as Order from "effect/Order";
import { useNavigation } from "@react-navigation/native";
import type { ThreadLabel } from "@t3tools/contracts";
import { useEffect, useMemo, useState } from "react";

import { getCompactBrandHeaderOptions } from "../../components/CompactBrandTitle";
Expand Down Expand Up @@ -45,6 +46,7 @@ export function HomeRouteScreen() {
pinThread,
unpinThread,
unsettleThread,
setThreadLabel,
} = useThreadListActions();
const pendingTasks = usePendingNewTasks();
const { openPendingTask, confirmDeletePendingTask } = usePendingTaskListActions();
Expand Down Expand Up @@ -76,6 +78,7 @@ export function HomeRouteScreen() {
} = useHomeListOptions(availableEnvironmentIds);
const selectedEnvironmentId = listOptions.selectedEnvironmentId;
const [selectedProjectKey, setSelectedProjectKey] = useState<string | null>(null);
const [selectedThreadLabel, setSelectedThreadLabel] = useState<ThreadLabel | null>(null);
const projectFilterOptions = useMemo(
() =>
buildHomeProjectScopes({
Expand Down Expand Up @@ -134,10 +137,12 @@ export function HomeRouteScreen() {
searchQuery={searchQuery}
selectedEnvironmentId={selectedEnvironmentId}
selectedProjectKey={selectedProjectKey}
selectedThreadLabel={selectedThreadLabel}
projectSortOrder={listOptions.projectSortOrder}
threadSortOrder={listOptions.threadSortOrder}
onEnvironmentChange={setSelectedEnvironmentId}
onProjectChange={setSelectedProjectKey}
onThreadLabelChange={setSelectedThreadLabel}
onOpenSettings={() => navigation.navigate("SettingsSheet", { screen: "Settings" })}
onProjectSortOrderChange={setProjectSortOrder}
onSearchQueryChange={setSearchQuery}
Expand All @@ -159,6 +164,7 @@ export function HomeRouteScreen() {
onUnsettleThread={unsettleThread}
onPinThread={pinThread}
onUnpinThread={unpinThread}
onSetThreadLabel={setThreadLabel}
onEnvironmentChange={setSelectedEnvironmentId}
onProjectChange={setSelectedProjectKey}
onOpenEnvironments={() =>
Expand Down Expand Up @@ -197,6 +203,7 @@ export function HomeRouteScreen() {
searchQuery={searchQuery}
selectedEnvironmentId={selectedEnvironmentId}
selectedProjectKey={selectedProjectKey}
selectedThreadLabel={selectedThreadLabel}
threads={threads}
threadSortOrder={listOptions.threadSortOrder}
/>
Expand Down
Loading
Loading