From 2236f3de37618ead59858abfe905300bafe73bb8 Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Sun, 6 Jul 2025 13:52:05 +0200 Subject: [PATCH] Checklists: Follow-up (#6044) --- src/assets/localization/fallback.strings | 1 - src/components/left/main/ChatBadge.scss | 2 +- .../middle/composer/ToDoListModal.tsx | 100 ++++++++++++------ .../middle/message/ContextMenuContainer.tsx | 2 +- src/components/middle/message/TodoList.scss | 6 ++ src/components/middle/message/TodoList.tsx | 6 +- src/global/actions/ui/messages.ts | 4 +- src/global/types/actions.ts | 2 +- src/global/types/tabState.ts | 2 +- src/types/language.d.ts | 1 - 10 files changed, 83 insertions(+), 43 deletions(-) diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index fc7c5304a..5e66c9666 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -2049,7 +2049,6 @@ "MessageActionTodoCompletionsAsNotDoneMultipleYou" = "You marked {tasks} as not done"; "MessageActionTodoTaskCount_one" = "{count} task"; "MessageActionTodoTaskCount_other" = "{count} tasks"; -"ToDoListNewTasks" = "New Tasks"; "MenuButtonAppendTodoList" = "Add a Task"; "MessageActionAppendTodo" = "{peer} added a new task \"{task}\" to {list}"; "MessageActionAppendTodoYou" = "You added a new task \"{task}\" to {list}"; diff --git a/src/components/left/main/ChatBadge.scss b/src/components/left/main/ChatBadge.scss index 61798b413..d8f9f9080 100644 --- a/src/components/left/main/ChatBadge.scss +++ b/src/components/left/main/ChatBadge.scss @@ -33,7 +33,7 @@ min-width: 1.5rem; height: 1.5rem; - padding: 0 0.4375rem !important; + padding: 0 0.4375rem; border-radius: 0.75rem; font-size: 0.875rem !important; diff --git a/src/components/middle/composer/ToDoListModal.tsx b/src/components/middle/composer/ToDoListModal.tsx index 17d221d97..5c6513271 100644 --- a/src/components/middle/composer/ToDoListModal.tsx +++ b/src/components/middle/composer/ToDoListModal.tsx @@ -1,7 +1,7 @@ import type { ChangeEvent } from 'react'; import type { ElementRef } from '../../../lib/teact/teact'; import { - memo, useEffect, useRef, useState, + memo, useEffect, useLayoutEffect, useMemo, useRef, useState, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; @@ -18,6 +18,7 @@ import { requestMeasure, requestNextMutation } from '../../../lib/fasterdom/fast import { selectChatMessage } from '../../../global/selectors'; import captureEscKeyListener from '../../../util/captureEscKeyListener'; import { generateUniqueNumberId } from '../../../util/generateUniqueId'; +import { MEMO_EMPTY_ARRAY } from '../../../util/memo'; import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev'; import useLang from '../../../hooks/useLang'; @@ -47,6 +48,7 @@ export type StateProps = { type Item = { id: number; text: string; + isDisabled?: boolean; }; const MAX_LIST_HEIGHT = 320; @@ -72,20 +74,34 @@ const ToDoListModal = ({ const [isOthersCanComplete, setIsOthersCanComplete] = useState(true); const [hasErrors, setHasErrors] = useState(false); + const lang = useLang(); + const isOpen = Boolean(modal); const renderingModal = useCurrentOrPrev(modal); - const isAddTaskMode = renderingModal?.isAddTaskMode; + // Treat "Add task" as edit mode for own checklists + const isAddTaskMode = renderingModal?.forNewTask && !editingMessage?.isOutgoing; - const lang = useLang(); const editingTodo = editingMessage?.content.todo?.todo; + const frozenTasks = useMemo(() => { + if (!isAddTaskMode || !editingTodo) { + return MEMO_EMPTY_ARRAY; + } + + return editingTodo.items.map((item) => ({ + id: item.id, + text: item.title.text, + isDisabled: true, + })); + }, [isAddTaskMode, editingTodo]); + const focusInput = useLastCallback((ref: ElementRef) => { if (isOpen && ref.current) { ref.current.focus(); } }); - useEffect(() => { + useLayoutEffect(() => { if (editingTodo) { setTitle(editingTodo.title.text); setIsOthersCanAppend(editingTodo.othersCanAppend ?? false); @@ -114,7 +130,20 @@ const ToDoListModal = ({ } }, [isOpen]); - useEffect(() => focusInput(titleInputRef), [focusInput, isOpen]); + useEffect(() => { + if (isOpen) { + // Wait for the DOM to be updated + requestMeasure(() => { + if (renderingModal?.forNewTask) { + const inputs = itemsListRef.current?.querySelectorAll('input'); + const lastInput = inputs?.[inputs.length - 1]; + lastInput?.focus(); + } else { + focusInput(titleInputRef); + } + }); + } + }, [focusInput, isOpen, renderingModal?.forNewTask]); const addNewItem = useLastCallback((newItems: Item[]) => { const id = generateUniqueNumberId(); @@ -283,34 +312,37 @@ const ToDoListModal = ({ } function renderItems() { - return items.map((item, index) => ( -
- updateItem(index, e.currentTarget.value)} - onKeyPress={handleKeyPress} - /> - {index !== items.length - 1 && ( - - )} -
- )); + const tasksToRender = [...frozenTasks, ...items]; + return tasksToRender.map((item, index) => { + const stateIndex = index - frozenTasks.length; + return ( +
+ updateItem(stateIndex, e.currentTarget.value)} + onKeyPress={handleKeyPress} + /> + {index !== tasksToRender.length - 1 && !item.isDisabled && ( + + )} +
+ ); + }); } return ( @@ -334,7 +366,7 @@ const ToDoListModal = ({

- {lang(isAddTaskMode ? 'ToDoListNewTasks' : 'TitleToDoList')} + {lang('TitleToDoList')}

{renderItems()} diff --git a/src/components/middle/message/ContextMenuContainer.tsx b/src/components/middle/message/ContextMenuContainer.tsx index 902916fbf..ff7b0f906 100644 --- a/src/components/middle/message/ContextMenuContainer.tsx +++ b/src/components/middle/message/ContextMenuContainer.tsx @@ -469,7 +469,7 @@ const ContextMenuContainer: FC = ({ openTodoListModal({ chatId: message.chatId, messageId: message.id, - isAddTaskMode: true, + forNewTask: true, }); } closeMenu(); diff --git a/src/components/middle/message/TodoList.scss b/src/components/middle/message/TodoList.scss index e27fdd276..8ecf2ee44 100644 --- a/src/components/middle/message/TodoList.scss +++ b/src/components/middle/message/TodoList.scss @@ -93,6 +93,12 @@ .label { line-height: 1.3125rem; } + + .subLabel { + margin-top: 0; + font-size: 0.75rem; + color: var(--secondary-color); + } } input:checked ~ .Checkbox-main { diff --git a/src/components/middle/message/TodoList.tsx b/src/components/middle/message/TodoList.tsx index 1cbb52b62..4be085bc7 100644 --- a/src/components/middle/message/TodoList.tsx +++ b/src/components/middle/message/TodoList.tsx @@ -41,7 +41,7 @@ const TodoList = ({ isCurrentUserPremium, isSynced, }: OwnProps & StateProps) => { - const { toggleTodoCompleted, showNotification } = getActions(); + const { toggleTodoCompleted, showNotification, requestConfetti } = getActions(); const { todo, completions } = todoList; const { title, items, othersCanComplete } = todo; const [completedTasks, setCompletedTasks] = useState([]); @@ -83,6 +83,10 @@ const TodoList = ({ completedIds: newCompletedId ? [Number(newCompletedId)] : [], incompletedIds: newIncompletedId ? [Number(newIncompletedId)] : [], }); + + if (newCompletedTasks.length === items.length) { + requestConfetti({}); + } }); const isReadOnly = Boolean(message.forwardInfo) || (!othersCanComplete && !message.isOutgoing); const isOutgoing = message.isOutgoing; diff --git a/src/global/actions/ui/messages.ts b/src/global/actions/ui/messages.ts index cff3290bd..aac841b45 100644 --- a/src/global/actions/ui/messages.ts +++ b/src/global/actions/ui/messages.ts @@ -765,14 +765,14 @@ addActionHandler('closePollModal', (global, actions, payload): ActionReturnType addActionHandler('openTodoListModal', (global, actions, payload): ActionReturnType => { const { - chatId, messageId, isAddTaskMode, tabId = getCurrentTabId(), + chatId, messageId, forNewTask, tabId = getCurrentTabId(), } = payload; return updateTabState(global, { todoListModal: { chatId, messageId, - isAddTaskMode, + forNewTask, }, }, tabId); }); diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index 077d4d2b1..f6b055ad5 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -2203,7 +2203,7 @@ export interface ActionPayloads { openTodoListModal: { chatId: string; messageId?: number; - isAddTaskMode?: boolean; + forNewTask?: boolean; } & WithTabId; closeTodoListModal: WithTabId | undefined; requestConfetti: (ConfettiParams & WithTabId) | WithTabId; diff --git a/src/global/types/tabState.ts b/src/global/types/tabState.ts index 94f7b92e9..1c34d70a4 100644 --- a/src/global/types/tabState.ts +++ b/src/global/types/tabState.ts @@ -535,7 +535,7 @@ export type TabState = { todoListModal?: { chatId: string; messageId?: number; - isAddTaskMode?: boolean; + forNewTask?: boolean; }; preparedMessageModal?: { diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 8beadf494..a6a369e08 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -1550,7 +1550,6 @@ export interface LangPair { 'AriaToDoCancel': undefined; 'TitleGroupToDoList': undefined; 'TitleYourToDoList': undefined; - 'ToDoListNewTasks': undefined; 'MenuButtonAppendTodoList': undefined; 'PremiumMore': undefined; 'SubscribeToTelegramPremiumForToggleTask': undefined;