@@ -2,11 +2,12 @@ import { toCalendarDateTime, toZoned } from "@internationalized/date";
22import { Notice , type TFile } from "obsidian" ;
33import type React from "react" ;
44import { useEffect , useState } from "react" ;
5- import { Button } from "react-aria-components" ;
5+ import { Button , Label , Menu , MenuItem , MenuTrigger } from "react-aria-components" ;
66
77import { t } from "@/i18n" ;
88import { timezone , today } from "@/infra/time" ;
99import {
10+ type AddTaskAction ,
1011 type DueDateDefaultSetting ,
1112 type LabelsDefaultSetting ,
1213 type ProjectDefaultSetting ,
@@ -15,11 +16,13 @@ import {
1516import { ModalContext , PluginContext } from "@/ui/context" ;
1617
1718import type TodoistPlugin from "../.." ;
18- import type { Label } from "../../api/domain/label" ;
19+ import type { Label as TodoistLabel } from "../../api/domain/label" ;
1920import type { CreateTaskParams , Priority } from "../../api/domain/task" ;
21+ import { ObsidianIcon } from "../components/obsidian-icon" ;
2022import { type Deadline , DeadlineSelector } from "./DeadlineSelector" ;
2123import { type DueDate , DueDateSelector } from "./DueDateSelector" ;
2224import { LabelSelector } from "./LabelSelector" ;
25+ import { Popover } from "./Popover" ;
2326import { PrioritySelector } from "./PrioritySelector" ;
2427import { type ProjectIdentifier , ProjectSelector } from "./ProjectSelector" ;
2528import { TaskContentInput } from "./TaskContentInput" ;
@@ -94,13 +97,13 @@ const calculateDefaultProject = (
9497const calculateDefaultLabels = (
9598 plugin : TodoistPlugin ,
9699 labelsSetting : LabelsDefaultSetting ,
97- ) : Label [ ] => {
100+ ) : TodoistLabel [ ] => {
98101 if ( labelsSetting . length === 0 ) {
99102 return [ ] ;
100103 }
101104
102105 const allLabels = Array . from ( plugin . services . todoist . data ( ) . labels . iter ( ) ) ;
103- const validLabels : Label [ ] = [ ] ;
106+ const validLabels : TodoistLabel [ ] = [ ] ;
104107 const deletedLabelNames : string [ ] = [ ] ;
105108
106109 for ( const defaultLabel of labelsSetting ) {
@@ -163,7 +166,7 @@ const CreateTaskModalContent: React.FC<CreateTaskProps> = ({
163166 calculateDefaultDueDate ( settings . taskCreationDefaultDueDate ) ,
164167 ) ;
165168 const [ priority , setPriority ] = useState < Priority > ( 1 ) ;
166- const [ labels , setLabels ] = useState < Label [ ] > ( ( ) =>
169+ const [ labels , setLabels ] = useState < TodoistLabel [ ] > ( ( ) =>
167170 calculateDefaultLabels ( plugin , settings . taskCreationDefaultLabels ) ,
168171 ) ;
169172 const [ deadline , setDeadline ] = useState < Deadline | undefined > ( ) ;
@@ -172,6 +175,7 @@ const CreateTaskModalContent: React.FC<CreateTaskProps> = ({
172175 ) ;
173176
174177 const [ options , setOptions ] = useState < TaskCreationOptions > ( initialOptions ) ;
178+ const [ currentAction , setCurrentAction ] = useState < AddTaskAction > ( settings . defaultAddTaskAction ) ;
175179
176180 const isPremium = plugin . services . todoist . isPremium ( ) ;
177181 const isSubmitButtonDisabled = content === "" && options . appendLinkTo !== "content" ;
@@ -194,7 +198,7 @@ const CreateTaskModalContent: React.FC<CreateTaskProps> = ({
194198 return builder . join ( "" ) ;
195199 } ;
196200
197- const createTask = async ( ) => {
201+ const createTask = async ( action : AddTaskAction ) => {
198202 if ( isSubmitButtonDisabled ) {
199203 return ;
200204 }
@@ -225,17 +229,50 @@ const CreateTaskModalContent: React.FC<CreateTaskProps> = ({
225229 }
226230
227231 try {
228- await plugin . services . todoist . actions . createTask (
232+ const task = await plugin . services . todoist . actions . createTask (
229233 buildWithLink ( content , options . appendLinkTo === "content" ) ,
230234 params ,
231235 ) ;
232- new Notice ( i18n . successNotice ) ;
236+
237+ let url : string | undefined ;
238+ switch ( action ) {
239+ case "add-copy-app" :
240+ url = `todoist://task?id=${ task . id } ` ;
241+ break ;
242+ case "add-copy-web" :
243+ url = `https://todoist.com/app/project/${ task . projectId } /task/${ task . id } ` ;
244+ break ;
245+ }
246+
247+ if ( url !== undefined ) {
248+ const markdownLink = `[${ task . content } ](${ url } )` ;
249+ try {
250+ await navigator . clipboard . writeText ( markdownLink ) ;
251+ new Notice ( i18n . linkCopiedNotice ) ;
252+ } catch ( clipboardErr ) {
253+ new Notice ( i18n . linkCopyFailedNotice ) ;
254+ console . error ( "Failed to copy to clipboard" , clipboardErr ) ;
255+ }
256+ } else {
257+ new Notice ( i18n . successNotice ) ;
258+ }
233259 } catch ( err ) {
234260 new Notice ( i18n . errorNotice ) ;
235261 console . error ( "Failed to create task" , err ) ;
236262 }
237263 } ;
238264
265+ const getActionLabel = ( action : AddTaskAction ) : string => {
266+ switch ( action ) {
267+ case "add" :
268+ return i18n . addTaskButtonLabel ;
269+ case "add-copy-app" :
270+ return i18n . addTaskAndCopyAppLabel ;
271+ case "add-copy-web" :
272+ return i18n . addTaskAndCopyWebLabel ;
273+ }
274+ } ;
275+
239276 const linkDestinationMessage = getLinkDestinationMessage ( options . appendLinkTo , i18n ) ;
240277
241278 return (
@@ -246,7 +283,7 @@ const CreateTaskModalContent: React.FC<CreateTaskProps> = ({
246283 content = { content }
247284 onChange = { setContent }
248285 autofocus = { true }
249- onEnterKey = { createTask }
286+ onEnterKey = { ( ) => createTask ( currentAction ) }
250287 />
251288 < TaskContentInput
252289 className = "task-description"
@@ -277,14 +314,42 @@ const CreateTaskModalContent: React.FC<CreateTaskProps> = ({
277314 < Button onPress = { ( ) => modal . close ( ) } aria-label = { i18n . cancelButtonLabel } >
278315 { i18n . cancelButtonLabel }
279316 </ Button >
280- < Button
281- className = "mod-cta"
282- isDisabled = { isSubmitButtonDisabled }
283- onPress = { createTask }
284- aria-label = { i18n . addTaskButtonLabel }
285- >
286- { i18n . addTaskButtonLabel }
287- </ Button >
317+ < div className = "add-task-button-group" >
318+ < Button
319+ className = "mod-cta add-task-primary"
320+ isDisabled = { isSubmitButtonDisabled }
321+ onPress = { ( ) => createTask ( currentAction ) }
322+ aria-label = { getActionLabel ( currentAction ) }
323+ >
324+ { getActionLabel ( currentAction ) }
325+ </ Button >
326+ < MenuTrigger >
327+ < Button
328+ className = "mod-cta add-task-dropdown"
329+ isDisabled = { isSubmitButtonDisabled }
330+ aria-label = { i18n . actionMenuLabel }
331+ >
332+ < ObsidianIcon id = "chevron-down" size = "s" />
333+ </ Button >
334+ < Popover >
335+ < Menu
336+ className = "add-task-action-menu task-option-dialog"
337+ aria-label = { i18n . actionMenuLabel }
338+ onAction = { ( key ) => setCurrentAction ( key as AddTaskAction ) }
339+ >
340+ < MenuItem key = "add" id = "add" >
341+ < Label > { i18n . addTaskButtonLabel } </ Label >
342+ </ MenuItem >
343+ < MenuItem key = "add-copy-app" id = "add-copy-app" >
344+ < Label > { i18n . addTaskAndCopyAppLabel } </ Label >
345+ </ MenuItem >
346+ < MenuItem key = "add-copy-web" id = "add-copy-web" >
347+ < Label > { i18n . addTaskAndCopyWebLabel } </ Label >
348+ </ MenuItem >
349+ </ Menu >
350+ </ Popover >
351+ </ MenuTrigger >
352+ </ div >
288353 </ div >
289354 </ div >
290355 </ div >
0 commit comments