mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
post merge
This commit is contained in:
commit
44f572414d
16 changed files with 415 additions and 125 deletions
|
|
@ -141,12 +141,14 @@ There is another older – the app looks and feels much better now ;) – [artic
|
|||
|
||||
### Short-Syntax
|
||||
|
||||
Can be used when adding a task.
|
||||
Can be used when adding a task. <strong>(Each of these can be disabled in settings->short syntax)</strong>
|
||||
|
||||
- `# <tag-name>`: add a tag to the task
|
||||
(`"task-description #tag1"`)
|
||||
- `<number>m` or `<number>h`: set time-estimate for the task
|
||||
(`"task-description 10m"` or `"task-description 5h"`)
|
||||
- `@<time>`: add due time to the task
|
||||
(`"task-description @fri 10pm"`)
|
||||
- `+ <project-name>`: add the task to an existing project
|
||||
(`"task-description +Important Project"`)
|
||||
- `Ctr + 2`: toggle between moving the new task to the bottom and top of the list
|
||||
|
|
@ -315,13 +317,12 @@ Packaging the app is done via [electron-builder](https://github.com/electron-use
|
|||
|
||||
### Building for Android
|
||||
|
||||
*This feature was added on October 7, 2024. See [Pull Request #57](https://github.com/johannesjo/super-productivity-android/pull/57).*
|
||||
_This feature was added on October 7, 2024. See [Pull Request #57](https://github.com/johannesjo/super-productivity-android/pull/57)._
|
||||
|
||||
To build the Android version of Super Productivity, please refer to the [Android Build Documentation](./android/offline.md), which includes instructions on configuring **Connectivity-Free Mode** and **Online-Only Mode (Compatibility Mode)**.
|
||||
|
||||
Ensure you follow the setup steps properly to configure the environment for building the app.
|
||||
|
||||
|
||||
## Run as Docker Container
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export enum MODEL_VERSION {
|
|||
// needs to be always the same as TASK !!!
|
||||
TASK_ARCHIVE = 3.5,
|
||||
PROJECT = 6.14,
|
||||
GLOBAL_CONFIG = 3.4,
|
||||
GLOBAL_CONFIG = 3.5,
|
||||
METRIC = 1.0,
|
||||
SIMPLE_COUNTER = 2.0,
|
||||
NOTE = 1.0,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ export const DEFAULT_GLOBAL_CONFIG: GlobalConfigState = {
|
|||
**Why do I want it?**
|
||||
`,
|
||||
},
|
||||
shortSyntax: {
|
||||
isEnableProject: true,
|
||||
isEnableDue: true,
|
||||
isEnableTag: true,
|
||||
},
|
||||
evaluation: {
|
||||
isHideEvaluationSheet: false,
|
||||
},
|
||||
|
|
|
|||
31
src/app/features/config/form-cfgs/short-syntax-form.const.ts
Normal file
31
src/app/features/config/form-cfgs/short-syntax-form.const.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { T } from '../../../t.const';
|
||||
import { ConfigFormSection, ShortSyntaxConfig } from '../global-config.model';
|
||||
|
||||
export const SHORT_SYNTAX_FORM_CFG: ConfigFormSection<ShortSyntaxConfig> = {
|
||||
title: T.GCF.SHORT_SYNTAX.TITLE,
|
||||
key: 'shortSyntax',
|
||||
help: T.GCF.SHORT_SYNTAX.HELP,
|
||||
items: [
|
||||
{
|
||||
key: 'isEnableProject',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: T.GCF.SHORT_SYNTAX.IS_ENABLE_PROJECT,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'isEnableTag',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: T.GCF.SHORT_SYNTAX.IS_ENABLE_TAG,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'isEnableDue',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: T.GCF.SHORT_SYNTAX.IS_ENABLE_DUE,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -18,6 +18,7 @@ import { FOCUS_MODE_FORM_CFG } from './form-cfgs/focus-mode-form.const';
|
|||
import { IS_FIREFOX } from '../../util/is-firefox';
|
||||
import { CALENDAR_FORM_CFG } from './form-cfgs/calendar-form.const';
|
||||
import { REMINDER_FORM_CFG } from './form-cfgs/reminder-form.const';
|
||||
import { SHORT_SYNTAX_FORM_CFG } from './form-cfgs/short-syntax-form.const';
|
||||
|
||||
const filterGlobalConfigForm = (cfg: ConfigFormSection<any>): boolean => {
|
||||
return (
|
||||
|
|
@ -29,6 +30,7 @@ const filterGlobalConfigForm = (cfg: ConfigFormSection<any>): boolean => {
|
|||
export const GLOBAL_CONFIG_FORM_CONFIG: ConfigFormConfig = [
|
||||
LANGUAGE_SELECTION_FORM_FORM,
|
||||
MISC_SETTINGS_FORM_CFG,
|
||||
SHORT_SYNTAX_FORM_CFG,
|
||||
IDLE_FORM_CFG,
|
||||
KEYBOARD_SETTINGS_FORM_CFG,
|
||||
TIME_TRACKING_FORM_CFG,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ export type MiscConfig = Readonly<{
|
|||
isDisableAnimations: boolean;
|
||||
}>;
|
||||
|
||||
export type ShortSyntaxConfig = Readonly<{
|
||||
isEnableProject: boolean;
|
||||
isEnableDue: boolean;
|
||||
isEnableTag: boolean;
|
||||
}>;
|
||||
|
||||
export type TimeTrackingConfig = Readonly<{
|
||||
trackingInterval: number;
|
||||
defaultEstimate: number;
|
||||
|
|
@ -170,6 +176,7 @@ export type FocusModeConfig = Readonly<{
|
|||
export type GlobalConfigState = Readonly<{
|
||||
lang: LanguageConfig;
|
||||
misc: MiscConfig;
|
||||
shortSyntax: ShortSyntaxConfig;
|
||||
evaluation: EvaluationConfig;
|
||||
idle: IdleConfig;
|
||||
takeABreak: TakeABreakConfig;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
IdleConfig,
|
||||
MiscConfig,
|
||||
ScheduleConfig,
|
||||
ShortSyntaxConfig,
|
||||
SoundConfig,
|
||||
SyncConfig,
|
||||
TakeABreakConfig,
|
||||
|
|
@ -19,6 +20,7 @@ import {
|
|||
selectEvaluationConfig,
|
||||
selectIdleConfig,
|
||||
selectMiscConfig,
|
||||
selectShortSyntaxConfig,
|
||||
selectSoundConfig,
|
||||
selectSyncConfig,
|
||||
selectTakeABreakConfig,
|
||||
|
|
@ -38,6 +40,10 @@ export class GlobalConfigService {
|
|||
shareReplay(1),
|
||||
);
|
||||
|
||||
shortSyntax$: Observable<ShortSyntaxConfig> = this._store.pipe(
|
||||
select(selectShortSyntaxConfig),
|
||||
);
|
||||
|
||||
sound$: Observable<SoundConfig> = this._store.pipe(
|
||||
select(selectSoundConfig),
|
||||
shareReplay(1),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
PomodoroConfig,
|
||||
ReminderConfig,
|
||||
ScheduleConfig,
|
||||
ShortSyntaxConfig,
|
||||
SoundConfig,
|
||||
SyncConfig,
|
||||
TakeABreakConfig,
|
||||
|
|
@ -29,6 +30,10 @@ export const selectMiscConfig = createSelector(
|
|||
selectConfigFeatureState,
|
||||
(cfg): MiscConfig => cfg.misc,
|
||||
);
|
||||
export const selectShortSyntaxConfig = createSelector(
|
||||
selectConfigFeatureState,
|
||||
(cfg): ShortSyntaxConfig => cfg.shortSyntax,
|
||||
);
|
||||
export const selectSoundConfig = createSelector(
|
||||
selectConfigFeatureState,
|
||||
(cfg): SoundConfig => cfg.sound,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import { OpenProjectCfg } from '../../issue/providers/open-project/open-project.
|
|||
import { GiteaCfg } from '../../issue/providers/gitea/gitea.model';
|
||||
import { RedmineCfg } from '../../issue/providers/redmine/redmine.model';
|
||||
|
||||
// TODO rename to selectProjectFeatureState
|
||||
export const selectProjectFeatureState =
|
||||
createFeatureSelector<ProjectState>(PROJECT_FEATURE_NAME);
|
||||
const { selectAll } = projectAdapter.getSelectors();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ import { IS_ANDROID_WEB_VIEW } from '../../../util/is-android-web-view';
|
|||
import { Store } from '@ngrx/store';
|
||||
import { PlannerActions } from '../../planner/store/planner.actions';
|
||||
import { getWorklogStr } from '../../../util/get-work-log-str';
|
||||
import { GlobalConfigService } from '../../config/global-config.service';
|
||||
import { ShortSyntaxConfig } from '../../config/global-config.model';
|
||||
import { DEFAULT_GLOBAL_CONFIG } from '../../config/default-global-config.const';
|
||||
|
||||
@Component({
|
||||
selector: 'add-task-bar',
|
||||
|
|
@ -126,6 +129,7 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
|
|||
tags,
|
||||
projects,
|
||||
defaultColor: activeWorkContext.theme.primary,
|
||||
shortSyntaxConfig: this._shortSyntaxConfig,
|
||||
}),
|
||||
),
|
||||
startWith([]),
|
||||
|
|
@ -165,6 +169,7 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
|
|||
private _saveTmpTodoTimeout?: number;
|
||||
private _lastAddedTaskId?: string;
|
||||
private _subs: Subscription = new Subscription();
|
||||
private _shortSyntaxConfig: ShortSyntaxConfig = DEFAULT_GLOBAL_CONFIG.shortSyntax;
|
||||
|
||||
constructor(
|
||||
private _taskService: TaskService,
|
||||
|
|
@ -175,6 +180,7 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
|
|||
private _tagService: TagService,
|
||||
private _cd: ChangeDetectorRef,
|
||||
private _store: Store,
|
||||
private _globalConfigService: GlobalConfigService,
|
||||
) {
|
||||
this._subs.add(
|
||||
this.activatedIssueTask$.subscribe((v) => (this.activatedIssueTask = v)),
|
||||
|
|
@ -182,6 +188,11 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
|
|||
this._subs.add(this.shortSyntaxTags$.subscribe((v) => (this.shortSyntaxTags = v)));
|
||||
this._subs.add(this.inputVal$.subscribe((v) => (this.inputVal = v)));
|
||||
this._subs.add(this.tagSuggestions$.subscribe((v) => (this.tagSuggestions = v)));
|
||||
this._subs.add(
|
||||
this._globalConfigService.shortSyntax$.subscribe(
|
||||
(shortSyntaxConfig) => (this._shortSyntaxConfig = shortSyntaxConfig),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { DEFAULT_TODAY_TAG_COLOR } from '../../work-context/work-context.const';
|
|||
import { Tag } from '../../tag/tag.model';
|
||||
import { Project } from '../../project/project.model';
|
||||
import { getWorklogStr } from '../../../util/get-work-log-str';
|
||||
import { ShortSyntaxConfig } from '../../config/global-config.model';
|
||||
|
||||
export interface ShortSyntaxTag {
|
||||
title: string;
|
||||
|
|
@ -18,11 +19,13 @@ export const shortSyntaxToTags = ({
|
|||
tags,
|
||||
projects,
|
||||
defaultColor,
|
||||
shortSyntaxConfig,
|
||||
}: {
|
||||
val: string;
|
||||
tags: Tag[];
|
||||
projects: Project[];
|
||||
defaultColor: string;
|
||||
shortSyntaxConfig: ShortSyntaxConfig;
|
||||
}): ShortSyntaxTag[] => {
|
||||
const r = shortSyntax(
|
||||
{
|
||||
|
|
@ -30,6 +33,7 @@ export const shortSyntaxToTags = ({
|
|||
tagIds: [],
|
||||
projectId: undefined,
|
||||
},
|
||||
shortSyntaxConfig,
|
||||
tags,
|
||||
projects,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { getWorklogStr } from '../../util/get-work-log-str';
|
|||
import { Tag } from '../tag/tag.model';
|
||||
import { DEFAULT_TAG } from '../tag/tag.const';
|
||||
import { Project } from '../project/project.model';
|
||||
import { DEFAULT_GLOBAL_CONFIG } from '../config/default-global-config.const';
|
||||
|
||||
const TASK: TaskCopy = {
|
||||
id: 'id',
|
||||
|
|
@ -44,12 +45,13 @@ const ALL_TAGS: Tag[] = [
|
|||
{ ...DEFAULT_TAG, id: 'A_id', title: 'A' },
|
||||
{ ...DEFAULT_TAG, id: 'multi_word_id', title: 'Multi Word Tag' },
|
||||
];
|
||||
const CONFIG = DEFAULT_GLOBAL_CONFIG.shortSyntax;
|
||||
|
||||
const getPlannedDateTimestampFromShortSyntaxReturnValue = (
|
||||
taskInput: TaskCopy,
|
||||
now: Date = new Date(),
|
||||
): number => {
|
||||
const r = shortSyntax(taskInput, undefined, undefined, now);
|
||||
const r = shortSyntax(taskInput, CONFIG, undefined, undefined, now);
|
||||
const parsedDateInMilliseconds = r?.taskChanges?.plannedAt as number;
|
||||
return parsedDateInMilliseconds;
|
||||
};
|
||||
|
|
@ -111,15 +113,18 @@ const checkIfCorrectDateAndTime = (
|
|||
|
||||
describe('shortSyntax', () => {
|
||||
it('should ignore for no short syntax', () => {
|
||||
const r = shortSyntax(TASK);
|
||||
const r = shortSyntax(TASK, CONFIG);
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should ignore if the changes cause no further changes', () => {
|
||||
const r = shortSyntax({
|
||||
...TASK,
|
||||
title: 'So what shall I do',
|
||||
});
|
||||
const r = shortSyntax(
|
||||
{
|
||||
...TASK,
|
||||
title: 'So what shall I do',
|
||||
},
|
||||
CONFIG,
|
||||
);
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
|
|
@ -129,7 +134,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Fun title 10m/1h',
|
||||
};
|
||||
const r = shortSyntax(t);
|
||||
const r = shortSyntax(t, CONFIG);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -150,7 +155,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Fun title whatever 1h/120m',
|
||||
};
|
||||
const r = shortSyntax(t);
|
||||
const r = shortSyntax(t, CONFIG);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -165,6 +170,15 @@ describe('shortSyntax', () => {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore time short syntax when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title whatever 1h/120m',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableDue: false });
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should recognize short syntax for date', () => {
|
||||
|
|
@ -188,29 +202,22 @@ describe('shortSyntax', () => {
|
|||
expect(isTimeSetCorrectly).toBeTrue();
|
||||
});
|
||||
|
||||
it('should correctly parse day of the week', () => {
|
||||
it('should ignore schedule syntax with time only when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Test @4pm',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableDue: false });
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should ignore day of the week when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Test @Friday',
|
||||
};
|
||||
const now = new Date('Fri Feb 09 2024 13:31:29 ');
|
||||
const parsedDateInMilliseconds = getPlannedDateTimestampFromShortSyntaxReturnValue(
|
||||
t,
|
||||
now,
|
||||
);
|
||||
const parsedDate = new Date(parsedDateInMilliseconds);
|
||||
// 5 represents Friday
|
||||
expect(parsedDate.getDay()).toEqual(5);
|
||||
const dayIncrement = 7;
|
||||
// If today happens to be Friday, the parsed date will be the next Friday,
|
||||
// 7 days from today
|
||||
const nextFriday = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate() + dayIncrement,
|
||||
);
|
||||
const isDateSetCorrectly = checkSameDay(parsedDate, nextFriday);
|
||||
expect(isDateSetCorrectly).toBeTrue();
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableDue: false });
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should correctly parse day of the week', () => {
|
||||
|
|
@ -244,7 +251,17 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: '#134 Fun title',
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should not trigger for tasks with starting # (e.g. github issues) when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: '#134 Fun title',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableTag: false }, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
|
@ -254,7 +271,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: '#134 Fun title #blu',
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
|
|
@ -267,12 +284,22 @@ describe('shortSyntax', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not trigger for multiple tasks when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: '#134 Fun title #blu',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableTag: false }, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should work with tags', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title #blu #A',
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
|
|
@ -285,12 +312,22 @@ describe('shortSyntax', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("shouldn't work with tags when disabled", () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title #blu #A',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableTag: false }, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should not trigger for # without space before', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title#blu',
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
|
@ -300,7 +337,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Fun title#blu #bla',
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
|
|
@ -319,7 +356,7 @@ describe('shortSyntax', () => {
|
|||
title: 'Fun title #blu #hihi',
|
||||
tagIds: ['blu_id', 'A', 'multi_word_id'],
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
|
|
@ -332,13 +369,24 @@ describe('shortSyntax', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not overwrite existing tags when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title #blu #hihi',
|
||||
tagIds: ['blu_id', 'A', 'multi_word_id'],
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableTag: false }, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should add new tag names', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title #blu #idontexist',
|
||||
tagIds: [],
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual({
|
||||
newTagTitles: ['idontexist'],
|
||||
|
|
@ -351,13 +399,24 @@ describe('shortSyntax', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not add new tag names when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title #blu #idontexist',
|
||||
tagIds: [],
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableTag: false }, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should add new "asd #asd" tag', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'asd #asd',
|
||||
tagIds: [],
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual({
|
||||
newTagTitles: ['asd'],
|
||||
|
|
@ -369,6 +428,17 @@ describe('shortSyntax', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not add new "asd #asd" tag when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'asd #asd',
|
||||
tagIds: [],
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableTag: false }, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should add tags for sub tasks', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
|
|
@ -376,7 +446,7 @@ describe('shortSyntax', () => {
|
|||
title: 'Fun title #blu #idontexist',
|
||||
tagIds: [],
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual({
|
||||
newTagTitles: ['idontexist'],
|
||||
|
|
@ -385,14 +455,26 @@ describe('shortSyntax', () => {
|
|||
taskChanges: { tagIds: ['blu_id'], title: 'Fun title' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should not add tags for sub tasks when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
parentId: 'SOMEPARENT',
|
||||
title: 'Fun title #blu #idontexist',
|
||||
tagIds: [],
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableTag: false }, ALL_TAGS);
|
||||
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
describe('should work with all combined', () => {
|
||||
it('', () => {
|
||||
describe('should work with tags and time estimates combined', () => {
|
||||
it('tag before time estimate', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title #blu 10m/1h',
|
||||
};
|
||||
const r = shortSyntax(t, ALL_TAGS);
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -409,23 +491,63 @@ describe('shortSyntax', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// TODO make this work maybe
|
||||
// it('', () => {
|
||||
// const t = {
|
||||
// ...TASK,
|
||||
// title: 'Fun title 10m/1h #blu'
|
||||
// };
|
||||
// const r = shortSyntax(t, ALL_TAGS);
|
||||
// expect(r).toEqual({
|
||||
// title: 'Fun title',
|
||||
// // timeSpent: 7200000,
|
||||
// timeSpentOnDay: {
|
||||
// [getWorklogStr()]: 600000
|
||||
// },
|
||||
// timeEstimate: 3600000,
|
||||
// tagIds: ['blu_id']
|
||||
// });
|
||||
// });
|
||||
it('time estimate before tag', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title 10m/1h #blu',
|
||||
};
|
||||
const r = shortSyntax(t, CONFIG, ALL_TAGS);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
projectId: undefined,
|
||||
taskChanges: {
|
||||
title: 'Fun title',
|
||||
timeSpentOnDay: {
|
||||
[getWorklogStr()]: 600000,
|
||||
},
|
||||
timeEstimate: 3600000,
|
||||
tagIds: ['blu_id'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('time estimate disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title 10m/1h #blu',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableDue: false }, ALL_TAGS);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
projectId: undefined,
|
||||
taskChanges: {
|
||||
title: 'Fun title 10m/1h',
|
||||
tagIds: ['blu_id'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('tags disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title 10m/1h #blu',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableTag: false }, ALL_TAGS);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
projectId: undefined,
|
||||
taskChanges: {
|
||||
title: 'Fun title #blu',
|
||||
timeSpentOnDay: {
|
||||
[getWorklogStr()]: 600000,
|
||||
},
|
||||
timeEstimate: 3600000,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('projects', () => {
|
||||
|
|
@ -448,7 +570,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Fun title +ProjectEasyShort',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -459,12 +581,21 @@ describe('shortSyntax', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("shouldn't work when disabled", () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title +ProjectEasyShort',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableProject: false }, [], projects);
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should not parse without missing whitespace before', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title+ProjectEasyShort',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
|
||||
|
|
@ -473,7 +604,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Fun title +ProjectEasyShort 10m/1h',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -489,12 +620,49 @@ describe('shortSyntax', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should work together with time estimates when disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title +ProjectEasyShort 10m/1h',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableProject: false }, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
projectId: undefined,
|
||||
taskChanges: {
|
||||
title: 'Fun title +ProjectEasyShort',
|
||||
// timeSpent: 7200000,
|
||||
timeSpentOnDay: {
|
||||
[getWorklogStr()]: 600000,
|
||||
},
|
||||
timeEstimate: 3600000,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should work together with disabled time estimates', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title +ProjectEasyShort 10m/1h',
|
||||
};
|
||||
const r = shortSyntax(t, { ...CONFIG, isEnableDue: false }, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
projectId: 'ProjectEasyShortID',
|
||||
taskChanges: {
|
||||
title: 'Fun title 10m/1h',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with only the beginning of a project title if it is at least 3 chars long', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Fun title +Project',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -510,7 +678,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Fun title +Some Project Title',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -526,7 +694,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Fun title +Some Pro',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -542,7 +710,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Other fun title +SomePro',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -558,14 +726,11 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Other fun title +Non existing project',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO
|
||||
// describe('due:', () => {});
|
||||
|
||||
describe('combined', () => {
|
||||
it('should work when time comes first', () => {
|
||||
const projects = [
|
||||
|
|
@ -578,7 +743,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Fun title 10m/1h +ProjectEasyShort',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: [],
|
||||
remindAt: null,
|
||||
|
|
@ -605,7 +770,7 @@ describe('shortSyntax', () => {
|
|||
...TASK,
|
||||
title: 'Some task +ProjectEasyShort 30m #tag',
|
||||
};
|
||||
const r = shortSyntax(t, [], projects);
|
||||
const r = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(r).toEqual({
|
||||
newTagTitles: ['tag'],
|
||||
remindAt: null,
|
||||
|
|
@ -640,7 +805,7 @@ describe('shortSyntax', () => {
|
|||
expect(parsedDate.getDay()).toEqual(5);
|
||||
const isTimeSetCorrectly = checkIfDateHasCorrectTime(parsedDate, 16, 0);
|
||||
expect(isTimeSetCorrectly).toBeTrue();
|
||||
const parsedTaskInfo = shortSyntax(t, [], projects);
|
||||
const parsedTaskInfo = shortSyntax(t, CONFIG, [], projects);
|
||||
expect(parsedTaskInfo?.projectId).toEqual(projects[0].id);
|
||||
// The time spent value is stored to the property equal to today
|
||||
// in format YYYY-MM-DD of the object `timeSpentOnDay`
|
||||
|
|
@ -665,10 +830,11 @@ describe('shortSyntax', () => {
|
|||
0,
|
||||
);
|
||||
expect(isPlannedDateAndTimeCorrect).toBeTrue();
|
||||
const parsedTaskInfo = shortSyntax(t, []);
|
||||
const parsedTaskInfo = shortSyntax(t, CONFIG, []);
|
||||
expect(parsedTaskInfo?.newTagTitles.includes('html')).toBeTrue();
|
||||
expect(parsedTaskInfo?.newTagTitles.includes('css')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should parse scheduled date using local time zone when unspecified', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
|
|
@ -677,5 +843,18 @@ describe('shortSyntax', () => {
|
|||
const plannedTimestamp = getPlannedDateTimestampFromShortSyntaxReturnValue(t);
|
||||
expect(checkIfCorrectDateAndTime(plannedTimestamp, 'saturday', 13, 37)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should work when all are disabled', () => {
|
||||
const t = {
|
||||
...TASK,
|
||||
title: 'Test @fri 4pm #html #css +ProjectEasyShort',
|
||||
};
|
||||
const r = shortSyntax(t, {
|
||||
isEnableDue: false,
|
||||
isEnableProject: false,
|
||||
isEnableTag: false,
|
||||
});
|
||||
expect(r).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ import { getWorklogStr } from '../../util/get-work-log-str';
|
|||
import { stringToMs } from '../../ui/duration/string-to-ms.pipe';
|
||||
import { Tag } from '../tag/tag.model';
|
||||
import { Project } from '../project/project.model';
|
||||
import { ShortSyntaxConfig } from '../config/global-config.model';
|
||||
|
||||
const SHORT_SYNTAX_TIME_REG_EX = / t?(([0-9]+(m|h|d)+)? *\/ *)?([0-9]+(m|h|d)+) *$/;
|
||||
type ProjectChanges = {
|
||||
title?: string;
|
||||
projectId?: string;
|
||||
};
|
||||
type TagChanges = {
|
||||
taskChanges?: Partial<TaskCopy>;
|
||||
newTagTitlesToCreate?: string[];
|
||||
};
|
||||
type DueChanges = {
|
||||
title?: string;
|
||||
plannedAt?: number;
|
||||
};
|
||||
|
||||
const SHORT_SYNTAX_TIME_REG_EX = / t?(([0-9]+(m|h|d)+)? *\/ *)?([0-9]+(m|h|d)+)/;
|
||||
// NOTE: should come after the time reg ex is executed so we don't have to deal with those strings too
|
||||
|
||||
const CH_PRO = '+';
|
||||
|
|
@ -25,6 +39,7 @@ const SHORT_SYNTAX_DUE_REG_EX = new RegExp(`\\${CH_DUE}[^${ALL_SPECIAL}]+`, 'gi'
|
|||
|
||||
export const shortSyntax = (
|
||||
task: Task | Partial<Task>,
|
||||
config: ShortSyntaxConfig,
|
||||
allTags?: Tag[],
|
||||
allProjects?: Project[],
|
||||
now = new Date(),
|
||||
|
|
@ -44,40 +59,51 @@ export const shortSyntax = (
|
|||
}
|
||||
|
||||
// TODO clean up this mess
|
||||
let taskChanges: Partial<TaskCopy>;
|
||||
let taskChanges: Partial<TaskCopy> = {};
|
||||
let changesForProject: ProjectChanges = {};
|
||||
let changesForTag: TagChanges = {};
|
||||
|
||||
// NOTE: we do this twice... :-O ...it's weird, but required to make whitespaces work as separator and not as one
|
||||
taskChanges = parseTimeSpentChanges(task);
|
||||
const changesForScheduledDate = parseScheduledDate(task, now);
|
||||
taskChanges = {
|
||||
...taskChanges,
|
||||
...changesForScheduledDate,
|
||||
};
|
||||
const changesForProject = parseProjectChanges(
|
||||
{ ...task, title: taskChanges.title || task.title },
|
||||
allProjects?.filter((p) => !p.isArchived && !p.isHiddenFromMenu),
|
||||
);
|
||||
if (changesForProject.projectId) {
|
||||
if (config.isEnableDue) {
|
||||
// NOTE: we do this twice... :-O ...it's weird, but required to make whitespaces work as separator and not as one
|
||||
taskChanges = parseTimeSpentChanges(task);
|
||||
taskChanges = {
|
||||
...taskChanges,
|
||||
title: changesForProject.title,
|
||||
...parseScheduledDate(task, now),
|
||||
};
|
||||
}
|
||||
|
||||
const changesForTag = parseTagChanges(
|
||||
{ ...task, title: taskChanges.title || task.title },
|
||||
allTags,
|
||||
);
|
||||
taskChanges = {
|
||||
...taskChanges,
|
||||
...changesForTag.taskChanges,
|
||||
};
|
||||
taskChanges = {
|
||||
...taskChanges,
|
||||
// NOTE: because we pass the new taskChanges here we need to assignments...
|
||||
...parseTimeSpentChanges(taskChanges),
|
||||
// title: taskChanges.title?.trim(),
|
||||
};
|
||||
if (config.isEnableProject) {
|
||||
changesForProject = parseProjectChanges(
|
||||
{ ...task, title: taskChanges.title || task.title },
|
||||
allProjects?.filter((p) => !p.isArchived && !p.isHiddenFromMenu),
|
||||
);
|
||||
if (changesForProject.projectId) {
|
||||
taskChanges = {
|
||||
...taskChanges,
|
||||
title: changesForProject.title,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (config.isEnableTag) {
|
||||
changesForTag = parseTagChanges(
|
||||
{ ...task, title: taskChanges.title || task.title },
|
||||
allTags,
|
||||
);
|
||||
taskChanges = {
|
||||
...taskChanges,
|
||||
...(changesForTag.taskChanges || {}),
|
||||
};
|
||||
}
|
||||
|
||||
if (config.isEnableDue) {
|
||||
taskChanges = {
|
||||
...taskChanges,
|
||||
// NOTE: because we pass the new taskChanges here we need to assignments...
|
||||
...parseTimeSpentChanges(taskChanges),
|
||||
// title: taskChanges.title?.trim(),
|
||||
};
|
||||
}
|
||||
|
||||
// const changesForDue = parseDueChanges({...task, title: taskChanges.title || task.title});
|
||||
// if (changesForDue.remindAt) {
|
||||
|
|
@ -93,7 +119,7 @@ export const shortSyntax = (
|
|||
|
||||
return {
|
||||
taskChanges,
|
||||
newTagTitles: changesForTag.newTagTitlesToCreate,
|
||||
newTagTitles: changesForTag.newTagTitlesToCreate || [],
|
||||
remindAt: null,
|
||||
projectId: changesForProject.projectId,
|
||||
// remindAt: changesForDue.remindAt
|
||||
|
|
@ -103,18 +129,14 @@ export const shortSyntax = (
|
|||
const parseProjectChanges = (
|
||||
task: Partial<TaskCopy>,
|
||||
allProjects?: Project[],
|
||||
): {
|
||||
title?: string;
|
||||
projectId?: string;
|
||||
} => {
|
||||
// don't allow for issue tasks
|
||||
if (task.issueId) {
|
||||
return {};
|
||||
}
|
||||
if (!Array.isArray(allProjects) || !allProjects || allProjects.length === 0) {
|
||||
return {};
|
||||
}
|
||||
if (!task.title) {
|
||||
): ProjectChanges => {
|
||||
if (
|
||||
task.issueId || // don't allow for issue tasks
|
||||
!task.title ||
|
||||
!Array.isArray(allProjects) ||
|
||||
!allProjects ||
|
||||
allProjects.length === 0
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -167,10 +189,7 @@ const parseProjectChanges = (
|
|||
return {};
|
||||
};
|
||||
|
||||
const parseTagChanges = (
|
||||
task: Partial<TaskCopy>,
|
||||
allTags?: Tag[],
|
||||
): { taskChanges: Partial<TaskCopy>; newTagTitlesToCreate: string[] } => {
|
||||
const parseTagChanges = (task: Partial<TaskCopy>, allTags?: Tag[]): TagChanges => {
|
||||
const taskChanges: Partial<TaskCopy> = {};
|
||||
|
||||
const newTagTitlesToCreate: string[] = [];
|
||||
|
|
@ -240,7 +259,7 @@ const parseTagChanges = (
|
|||
};
|
||||
};
|
||||
|
||||
const parseScheduledDate = (task: Partial<TaskCopy>, now: Date): Partial<Task> => {
|
||||
const parseScheduledDate = (task: Partial<TaskCopy>, now: Date): DueChanges => {
|
||||
if (!task.title) {
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { T } from '../../../t.const';
|
|||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { DialogConfirmComponent } from '../../../ui/dialog-confirm/dialog-confirm.component';
|
||||
import { LayoutService } from '../../../core-ui/layout/layout.service';
|
||||
import { DEFAULT_GLOBAL_CONFIG } from '../../config/default-global-config.const';
|
||||
|
||||
@Injectable()
|
||||
export class ShortSyntaxEffects {
|
||||
|
|
@ -79,7 +80,13 @@ export class ShortSyntaxEffects {
|
|||
),
|
||||
),
|
||||
mergeMap(([{ task, originalAction }, tags, projects, defaultProjectId]) => {
|
||||
const r = shortSyntax(task, tags, projects);
|
||||
const r = shortSyntax(
|
||||
task,
|
||||
this._globalConfigService?.cfg?.shortSyntax ||
|
||||
DEFAULT_GLOBAL_CONFIG.shortSyntax,
|
||||
tags,
|
||||
projects,
|
||||
);
|
||||
if (environment.production) {
|
||||
console.log('shortSyntax', r);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -677,7 +677,7 @@ const T = {
|
|||
TASK_PLANNED_FOR: 'F.PLANNER.S.TASK_PLANNED_FOR',
|
||||
},
|
||||
TASK_DRAWER: 'F.PLANNER.TASK_DRAWER',
|
||||
EDIT_REPEATED_TASK: 'F.PLANNER.EDIT_REPEATED_TASK'
|
||||
EDIT_REPEATED_TASK: 'F.PLANNER.EDIT_REPEATED_TASK',
|
||||
},
|
||||
POMODORO: {
|
||||
BACK_TO_WORK: 'F.POMODORO.BACK_TO_WORK',
|
||||
|
|
@ -1482,6 +1482,13 @@ const T = {
|
|||
TASK_NOTES_TPL: 'GCF.MISC.TASK_NOTES_TPL',
|
||||
TITLE: 'GCF.MISC.TITLE',
|
||||
},
|
||||
SHORT_SYNTAX: {
|
||||
TITLE: 'GCF.SHORT_SYNTAX.TITLE',
|
||||
HELP: 'GCF.SHORT_SYNTAX.HELP',
|
||||
IS_ENABLE_PROJECT: 'GCF.SHORT_SYNTAX.IS_ENABLE_PROJECT',
|
||||
IS_ENABLE_TAG: 'GCF.SHORT_SYNTAX.IS_ENABLE_TAG',
|
||||
IS_ENABLE_DUE: 'GCF.SHORT_SYNTAX.IS_ENABLE_DUE',
|
||||
},
|
||||
POMODORO: {
|
||||
BREAK_DURATION: 'GCF.POMODORO.BREAK_DURATION',
|
||||
CYCLES_BEFORE_LONGER_BREAK: 'GCF.POMODORO.CYCLES_BEFORE_LONGER_BREAK',
|
||||
|
|
|
|||
|
|
@ -1458,6 +1458,13 @@
|
|||
"TASK_NOTES_TPL": "Task description template",
|
||||
"TITLE": "Misc Settings"
|
||||
},
|
||||
"SHORT_SYNTAX": {
|
||||
"TITLE": "Short Syntax",
|
||||
"HELP": "<p>Here you can control short syntax options when creating a task</p>",
|
||||
"IS_ENABLE_PROJECT": "Enable project short syntax (+<Project name>)",
|
||||
"IS_ENABLE_TAG": "Enable tag short syntax (#<Tag>)",
|
||||
"IS_ENABLE_DUE": "Enable due short syntax (@<Due time>)"
|
||||
},
|
||||
"POMODORO": {
|
||||
"BREAK_DURATION": "Duration of short breaks",
|
||||
"CYCLES_BEFORE_LONGER_BREAK": "Start longer break after X work sessions",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue