Checklists: Follow-up (#6044)
This commit is contained in:
parent
310b5490a5
commit
2236f3de37
@ -2049,7 +2049,6 @@
|
|||||||
"MessageActionTodoCompletionsAsNotDoneMultipleYou" = "You marked {tasks} as not done";
|
"MessageActionTodoCompletionsAsNotDoneMultipleYou" = "You marked {tasks} as not done";
|
||||||
"MessageActionTodoTaskCount_one" = "{count} task";
|
"MessageActionTodoTaskCount_one" = "{count} task";
|
||||||
"MessageActionTodoTaskCount_other" = "{count} tasks";
|
"MessageActionTodoTaskCount_other" = "{count} tasks";
|
||||||
"ToDoListNewTasks" = "New Tasks";
|
|
||||||
"MenuButtonAppendTodoList" = "Add a Task";
|
"MenuButtonAppendTodoList" = "Add a Task";
|
||||||
"MessageActionAppendTodo" = "{peer} added a new task \"{task}\" to {list}";
|
"MessageActionAppendTodo" = "{peer} added a new task \"{task}\" to {list}";
|
||||||
"MessageActionAppendTodoYou" = "You added a new task \"{task}\" to {list}";
|
"MessageActionAppendTodoYou" = "You added a new task \"{task}\" to {list}";
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
min-width: 1.5rem;
|
min-width: 1.5rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
padding: 0 0.4375rem !important;
|
padding: 0 0.4375rem;
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
|
|
||||||
font-size: 0.875rem !important;
|
font-size: 0.875rem !important;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import type { ElementRef } from '../../../lib/teact/teact';
|
import type { ElementRef } from '../../../lib/teact/teact';
|
||||||
import {
|
import {
|
||||||
memo, useEffect, useRef, useState,
|
memo, useEffect, useLayoutEffect, useMemo, useRef, useState,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ import { requestMeasure, requestNextMutation } from '../../../lib/fasterdom/fast
|
|||||||
import { selectChatMessage } from '../../../global/selectors';
|
import { selectChatMessage } from '../../../global/selectors';
|
||||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||||
import { generateUniqueNumberId } from '../../../util/generateUniqueId';
|
import { generateUniqueNumberId } from '../../../util/generateUniqueId';
|
||||||
|
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||||
|
|
||||||
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
@ -47,6 +48,7 @@ export type StateProps = {
|
|||||||
type Item = {
|
type Item = {
|
||||||
id: number;
|
id: number;
|
||||||
text: string;
|
text: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_LIST_HEIGHT = 320;
|
const MAX_LIST_HEIGHT = 320;
|
||||||
@ -72,20 +74,34 @@ const ToDoListModal = ({
|
|||||||
const [isOthersCanComplete, setIsOthersCanComplete] = useState(true);
|
const [isOthersCanComplete, setIsOthersCanComplete] = useState(true);
|
||||||
const [hasErrors, setHasErrors] = useState<boolean>(false);
|
const [hasErrors, setHasErrors] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const isOpen = Boolean(modal);
|
const isOpen = Boolean(modal);
|
||||||
const renderingModal = useCurrentOrPrev(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 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<HTMLInputElement>) => {
|
const focusInput = useLastCallback((ref: ElementRef<HTMLInputElement>) => {
|
||||||
if (isOpen && ref.current) {
|
if (isOpen && ref.current) {
|
||||||
ref.current.focus();
|
ref.current.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (editingTodo) {
|
if (editingTodo) {
|
||||||
setTitle(editingTodo.title.text);
|
setTitle(editingTodo.title.text);
|
||||||
setIsOthersCanAppend(editingTodo.othersCanAppend ?? false);
|
setIsOthersCanAppend(editingTodo.othersCanAppend ?? false);
|
||||||
@ -114,7 +130,20 @@ const ToDoListModal = ({
|
|||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [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 addNewItem = useLastCallback((newItems: Item[]) => {
|
||||||
const id = generateUniqueNumberId();
|
const id = generateUniqueNumberId();
|
||||||
@ -283,34 +312,37 @@ const ToDoListModal = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderItems() {
|
function renderItems() {
|
||||||
return items.map((item, index) => (
|
const tasksToRender = [...frozenTasks, ...items];
|
||||||
<div className="item-wrapper">
|
return tasksToRender.map((item, index) => {
|
||||||
<InputText
|
const stateIndex = index - frozenTasks.length;
|
||||||
maxLength={MAX_OPTION_LENGTH}
|
return (
|
||||||
label={index !== items.length - 1 || items.length === maxItemsCount
|
<div className="item-wrapper">
|
||||||
? lang('TitleTask')
|
<InputText
|
||||||
: lang('TitleAddTask')}
|
maxLength={MAX_OPTION_LENGTH}
|
||||||
error={getItemsError(index)}
|
label={index !== tasksToRender.length - 1 || tasksToRender.length === maxItemsCount
|
||||||
value={item.text}
|
? lang('TitleTask')
|
||||||
|
: lang('TitleAddTask')}
|
||||||
onChange={(e) => updateItem(index, e.currentTarget.value)}
|
error={getItemsError(stateIndex)}
|
||||||
onKeyPress={handleKeyPress}
|
value={item.text}
|
||||||
/>
|
disabled={item.isDisabled}
|
||||||
{index !== items.length - 1 && (
|
onChange={(e) => updateItem(stateIndex, e.currentTarget.value)}
|
||||||
<Button
|
onKeyPress={handleKeyPress}
|
||||||
className="item-remove-button"
|
/>
|
||||||
round
|
{index !== tasksToRender.length - 1 && !item.isDisabled && (
|
||||||
color="translucent"
|
<Button
|
||||||
size="smaller"
|
className="item-remove-button"
|
||||||
ariaLabel={lang('Delete')}
|
round
|
||||||
|
color="translucent"
|
||||||
onClick={() => removeItem(index)}
|
size="smaller"
|
||||||
>
|
ariaLabel={lang('Delete')}
|
||||||
<Icon name="close" />
|
onClick={() => removeItem(stateIndex)}
|
||||||
</Button>
|
>
|
||||||
)}
|
<Icon name="close" />
|
||||||
</div>
|
</Button>
|
||||||
));
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -334,7 +366,7 @@ const ToDoListModal = ({
|
|||||||
|
|
||||||
<div className="options-list custom-scroll" ref={itemsListRef}>
|
<div className="options-list custom-scroll" ref={itemsListRef}>
|
||||||
<h3 className="items-header">
|
<h3 className="items-header">
|
||||||
{lang(isAddTaskMode ? 'ToDoListNewTasks' : 'TitleToDoList')}
|
{lang('TitleToDoList')}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{renderItems()}
|
{renderItems()}
|
||||||
|
|||||||
@ -469,7 +469,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
openTodoListModal({
|
openTodoListModal({
|
||||||
chatId: message.chatId,
|
chatId: message.chatId,
|
||||||
messageId: message.id,
|
messageId: message.id,
|
||||||
isAddTaskMode: true,
|
forNewTask: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
closeMenu();
|
closeMenu();
|
||||||
|
|||||||
@ -93,6 +93,12 @@
|
|||||||
.label {
|
.label {
|
||||||
line-height: 1.3125rem;
|
line-height: 1.3125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subLabel {
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked ~ .Checkbox-main {
|
input:checked ~ .Checkbox-main {
|
||||||
|
|||||||
@ -41,7 +41,7 @@ const TodoList = ({
|
|||||||
isCurrentUserPremium,
|
isCurrentUserPremium,
|
||||||
isSynced,
|
isSynced,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { toggleTodoCompleted, showNotification } = getActions();
|
const { toggleTodoCompleted, showNotification, requestConfetti } = getActions();
|
||||||
const { todo, completions } = todoList;
|
const { todo, completions } = todoList;
|
||||||
const { title, items, othersCanComplete } = todo;
|
const { title, items, othersCanComplete } = todo;
|
||||||
const [completedTasks, setCompletedTasks] = useState<string[]>([]);
|
const [completedTasks, setCompletedTasks] = useState<string[]>([]);
|
||||||
@ -83,6 +83,10 @@ const TodoList = ({
|
|||||||
completedIds: newCompletedId ? [Number(newCompletedId)] : [],
|
completedIds: newCompletedId ? [Number(newCompletedId)] : [],
|
||||||
incompletedIds: newIncompletedId ? [Number(newIncompletedId)] : [],
|
incompletedIds: newIncompletedId ? [Number(newIncompletedId)] : [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (newCompletedTasks.length === items.length) {
|
||||||
|
requestConfetti({});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const isReadOnly = Boolean(message.forwardInfo) || (!othersCanComplete && !message.isOutgoing);
|
const isReadOnly = Boolean(message.forwardInfo) || (!othersCanComplete && !message.isOutgoing);
|
||||||
const isOutgoing = message.isOutgoing;
|
const isOutgoing = message.isOutgoing;
|
||||||
|
|||||||
@ -765,14 +765,14 @@ addActionHandler('closePollModal', (global, actions, payload): ActionReturnType
|
|||||||
|
|
||||||
addActionHandler('openTodoListModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openTodoListModal', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId, messageId, isAddTaskMode, tabId = getCurrentTabId(),
|
chatId, messageId, forNewTask, tabId = getCurrentTabId(),
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
return updateTabState(global, {
|
return updateTabState(global, {
|
||||||
todoListModal: {
|
todoListModal: {
|
||||||
chatId,
|
chatId,
|
||||||
messageId,
|
messageId,
|
||||||
isAddTaskMode,
|
forNewTask,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2203,7 +2203,7 @@ export interface ActionPayloads {
|
|||||||
openTodoListModal: {
|
openTodoListModal: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messageId?: number;
|
messageId?: number;
|
||||||
isAddTaskMode?: boolean;
|
forNewTask?: boolean;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeTodoListModal: WithTabId | undefined;
|
closeTodoListModal: WithTabId | undefined;
|
||||||
requestConfetti: (ConfettiParams & WithTabId) | WithTabId;
|
requestConfetti: (ConfettiParams & WithTabId) | WithTabId;
|
||||||
|
|||||||
@ -535,7 +535,7 @@ export type TabState = {
|
|||||||
todoListModal?: {
|
todoListModal?: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messageId?: number;
|
messageId?: number;
|
||||||
isAddTaskMode?: boolean;
|
forNewTask?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
preparedMessageModal?: {
|
preparedMessageModal?: {
|
||||||
|
|||||||
1
src/types/language.d.ts
vendored
1
src/types/language.d.ts
vendored
@ -1550,7 +1550,6 @@ export interface LangPair {
|
|||||||
'AriaToDoCancel': undefined;
|
'AriaToDoCancel': undefined;
|
||||||
'TitleGroupToDoList': undefined;
|
'TitleGroupToDoList': undefined;
|
||||||
'TitleYourToDoList': undefined;
|
'TitleYourToDoList': undefined;
|
||||||
'ToDoListNewTasks': undefined;
|
|
||||||
'MenuButtonAppendTodoList': undefined;
|
'MenuButtonAppendTodoList': undefined;
|
||||||
'PremiumMore': undefined;
|
'PremiumMore': undefined;
|
||||||
'SubscribeToTelegramPremiumForToggleTask': undefined;
|
'SubscribeToTelegramPremiumForToggleTask': undefined;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user