Skip to content

Commit 114b979

Browse files
committed
tasks: implement full fragment support...
- worth spending on hour on...
1 parent ea040b3 commit 114b979

File tree

5 files changed

+130
-59
lines changed

5 files changed

+130
-59
lines changed

src/packages/frontend/editors/task-editor/actions.ts

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
Task Actions
88
*/
99

10-
const LAST_EDITED_THRESH_S = 30;
11-
const TASKS_HELP_URL = "https://doc.cocalc.com/tasks.html";
12-
1310
import { fromJS, Map } from "immutable";
1411
import { throttle } from "lodash";
1512
import { delay } from "awaiting";
@@ -26,6 +23,7 @@ import { create_key_handler } from "./keyboard";
2623
import { toggle_checkbox } from "./desc-rendering";
2724
import { Actions } from "../../app-framework";
2825
import {
26+
Align,
2927
HashtagState,
3028
Headings,
3129
HeadingsDir,
@@ -40,6 +38,10 @@ import { TaskStore } from "./store";
4038
import { SyncDB } from "@cocalc/sync/editor/db";
4139
import { webapp_client } from "../../webapp-client";
4240
import type { Actions as TaskFrameActions } from "@cocalc/frontend/frame-editors/task-editor/actions";
41+
import Fragment from "@cocalc/frontend/misc/fragment-id";
42+
43+
const LAST_EDITED_THRESH_S = 30;
44+
const TASKS_HELP_URL = "https://doc.cocalc.com/tasks.html";
4345

4446
export class TaskActions extends Actions<TaskState> {
4547
public syncdb: SyncDB;
@@ -53,13 +55,14 @@ export class TaskActions extends Actions<TaskState> {
5355
private set_save_status?: () => void;
5456
private frameId: string;
5557
private frameActions: TaskFrameActions;
58+
private virtuosoRef?;
5659

5760
public _init(
5861
project_id: string,
5962
path: string,
6063
syncdb: SyncDB,
6164
store: TaskStore,
62-
truePath: string // because above path is auxpath for each frame.
65+
truePath: string, // because above path is auxpath for each frame.
6366
): void {
6467
this._update_visible = throttle(this.__update_visible, 500);
6568
this.project_id = project_id;
@@ -136,7 +139,7 @@ export class TaskActions extends Actions<TaskState> {
136139
local_task_state,
137140
view,
138141
counts,
139-
current_task_id
142+
current_task_id,
140143
);
141144

142145
if (obj.visible.size == 0 && view.get("search")?.trim().length == 0) {
@@ -148,7 +151,7 @@ export class TaskActions extends Actions<TaskState> {
148151
local_task_state,
149152
view,
150153
counts,
151-
current_task_id
154+
current_task_id,
152155
);
153156
}
154157

@@ -229,6 +232,21 @@ export class TaskActions extends Actions<TaskState> {
229232
}
230233
}
231234

235+
clearAllFilters = (obj?) => {
236+
this.set_local_view_state(
237+
{
238+
show_deleted: false,
239+
show_done: false,
240+
show_max: false,
241+
selected_hashtags: {},
242+
search: "",
243+
...obj,
244+
},
245+
false,
246+
);
247+
this.__update_visible();
248+
};
249+
232250
public async save(): Promise<void> {
233251
if (this.is_closed) {
234252
return;
@@ -297,7 +315,7 @@ export class TaskActions extends Actions<TaskState> {
297315
task_id?: string,
298316
obj?: object,
299317
setState: boolean = false,
300-
save: boolean = true // make new commit to syncdb state
318+
save: boolean = true, // make new commit to syncdb state
301319
): void {
302320
if (obj == null || this.is_closed) {
303321
return;
@@ -334,7 +352,7 @@ export class TaskActions extends Actions<TaskState> {
334352
// **immediately**; this would happen
335353
// eventually as a result of the syncdb set above.
336354
let tasks = this.store.get("tasks") ?? fromJS({});
337-
task = tasks.get(task_id) ?? fromJS({ task_id }) as any;
355+
task = tasks.get(task_id) ?? (fromJS({ task_id }) as any);
338356
if (task == null) throw Error("bug");
339357
for (let k in obj) {
340358
const v = obj[k];
@@ -405,7 +423,7 @@ export class TaskActions extends Actions<TaskState> {
405423
const pos_j = tasks.getIn([visible.get(j), "position"]);
406424
this.set_task(task_id, { position: pos_j }, true);
407425
this.set_task(visible.get(j), { position: pos_i }, true);
408-
this.scroll_into_view();
426+
this.scrollIntoView();
409427
}
410428

411429
public time_travel(): void {
@@ -419,11 +437,14 @@ export class TaskActions extends Actions<TaskState> {
419437
window.open(TASKS_HELP_URL, "_blank")?.focus();
420438
}
421439

422-
public set_current_task(task_id: string): void {
423-
if (this.getFrameData("current_task_id") == task_id) return;
440+
set_current_task = (task_id: string): void => {
441+
if (this.getFrameData("current_task_id") == task_id) {
442+
return;
443+
}
424444
this.setFrameData({ current_task_id: task_id });
425-
this.scroll_into_view();
426-
}
445+
this.scrollIntoView();
446+
this.setFragment(task_id);
447+
};
427448

428449
public set_current_task_delta(delta: number): void {
429450
const task_id = this.getFrameData("current_task_id");
@@ -492,7 +513,7 @@ export class TaskActions extends Actions<TaskState> {
492513
this.set_task(
493514
task_id,
494515
{ done: !this.store.getIn(["tasks", task_id, "done"]) },
495-
true
516+
true,
496517
);
497518
}
498519
}
@@ -523,15 +544,15 @@ export class TaskActions extends Actions<TaskState> {
523544

524545
public set_due_date(
525546
task_id: string | undefined,
526-
date: number | undefined
547+
date: number | undefined,
527548
): void {
528549
this.set_task(task_id, { due_date: date });
529550
}
530551

531552
public set_desc(
532553
task_id: string | undefined,
533554
desc: string,
534-
save: boolean = true
555+
save: boolean = true,
535556
): void {
536557
this.set_task(task_id, { desc }, false, save);
537558
}
@@ -646,14 +667,57 @@ export class TaskActions extends Actions<TaskState> {
646667
this.setFrameData({ focus_find_box: false });
647668
}
648669

649-
async scroll_into_view(): Promise<void> {
650-
await delay(50);
651-
this.setFrameData({ scroll_into_view: true });
652-
}
670+
setVirtuosoRef = (virtuosoRef) => {
671+
this.virtuosoRef = virtuosoRef;
672+
};
653673

654-
public scroll_into_view_done(): void {
655-
this.setFrameData({ scroll_into_view: false });
656-
}
674+
// scroll the current_task_id into view, possibly changing filters
675+
// in order to make it visibile, if necessary.
676+
scrollIntoView = async (align: Align = "view") => {
677+
if (this.virtuosoRef?.current == null) {
678+
return;
679+
}
680+
const current_task_id = this.getFrameData("current_task_id");
681+
if (current_task_id == null) {
682+
return;
683+
}
684+
let visible = this.getFrameData("visible");
685+
if (visible == null) {
686+
return;
687+
}
688+
// Figure out the index of current_task_id.
689+
let index = visible.indexOf(current_task_id);
690+
if (index === -1) {
691+
const task = this.store.getIn(["tasks", current_task_id]);
692+
if (task == null) {
693+
// no such task anywhere, not even in trash, etc
694+
return;
695+
}
696+
if (
697+
this.getFrameData("search_desc")?.trim() ||
698+
task.get("deleted") ||
699+
task.get("done")
700+
) {
701+
// active search -- try clearing it.
702+
this.clearAllFilters({
703+
show_deleted: !!task.get("deleted"),
704+
show_done: !!task.get("done"),
705+
});
706+
visible = this.getFrameData("visible");
707+
index = visible.indexOf(current_task_id);
708+
if (index == -1) {
709+
return;
710+
}
711+
} else {
712+
return;
713+
}
714+
}
715+
if (align == "start" || align == "center" || align == "end") {
716+
this.virtuosoRef.current.scrollToIndex({ index, align });
717+
} else {
718+
this.virtuosoRef.current.scrollIntoView({ index });
719+
}
720+
};
657721

658722
public set_show_max(show_max: number): void {
659723
this.set_local_view_state({ show_max }, false);
@@ -669,7 +733,7 @@ export class TaskActions extends Actions<TaskState> {
669733
public toggle_desc_checkbox(
670734
task_id: string,
671735
index: number,
672-
checked: boolean
736+
checked: boolean,
673737
): void {
674738
let desc = this.store.getIn(["tasks", task_id, "desc"]);
675739
if (desc == null) {
@@ -738,6 +802,14 @@ export class TaskActions extends Actions<TaskState> {
738802
.getProjectActions(this.project_id)
739803
.open_file({ path, foreground: true });
740804
}
805+
806+
setFragment = (id?) => {
807+
if (!id) {
808+
Fragment.clear();
809+
} else {
810+
Fragment.set({ id });
811+
}
812+
};
741813
}
742814

743815
function getPositions(tasks): number[] {

src/packages/frontend/editors/task-editor/editor.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ interface Props {
3434
export const TaskEditor: React.FC<Props> = React.memo(
3535
({ actions, path, project_id, desc, read_only }) => {
3636
const useEditor = useEditorRedux<TaskState>({ project_id, path });
37-
3837
const tasks = useEditor("tasks");
39-
4038
const visible = desc.get("data-visible");
4139
const local_task_state = desc.get("data-local_task_state") ?? fromJS({});
4240
const local_view_state = desc.get("data-local_view_state") ?? fromJS({});
@@ -46,7 +44,6 @@ export const TaskEditor: React.FC<Props> = React.memo(
4644
const search_terms = desc.get("data-search_terms");
4745
const search_desc = desc.get("data-search_desc");
4846
const focus_find_box = desc.get("data-focus_find_box");
49-
const scroll_into_view = desc.get("data-scroll_into_view");
5047

5148
if (tasks == null || visible == null) {
5249
return (
@@ -126,7 +123,6 @@ export const TaskEditor: React.FC<Props> = React.memo(
126123
current_task_id={current_task_id}
127124
local_task_state={local_task_state}
128125
scrollState={(local_view_state as any).get("scrollState")?.toJS?.()}
129-
scroll_into_view={scroll_into_view}
130126
font_size={desc.get("font_size")}
131127
sortable={
132128
!read_only &&

src/packages/frontend/editors/task-editor/list.tsx

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ interface Props {
3131
current_task_id?: string;
3232
local_task_state?: LocalTaskStateMap;
3333
scrollState?: any;
34-
scroll_into_view?: boolean;
3534
font_size: number;
3635
sortable?: boolean;
3736
read_only?: boolean;
@@ -48,7 +47,6 @@ export default function TaskList({
4847
current_task_id,
4948
local_task_state,
5049
scrollState,
51-
scroll_into_view,
5250
font_size,
5351
sortable,
5452
read_only,
@@ -90,23 +88,8 @@ export default function TaskList({
9088
}, [search_terms]);
9189

9290
useEffect(() => {
93-
if (actions && scroll_into_view) {
94-
_scroll_into_view();
95-
actions.scroll_into_view_done();
96-
}
97-
}, [scroll_into_view]);
98-
99-
function _scroll_into_view() {
100-
if (current_task_id == null) {
101-
return;
102-
}
103-
// Figure out the index of current_task_id.
104-
const index = visible.indexOf(current_task_id);
105-
if (index === -1) {
106-
return;
107-
}
108-
virtuosoRef.current?.scrollIntoView({ index });
109-
}
91+
actions?.setVirtuosoRef(virtuosoRef);
92+
}, [actions, virtuosoRef]);
11093

11194
function render_task(task_id, index?) {
11295
if (index === visible.size) {

src/packages/frontend/editors/task-editor/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ export interface Task {
1111
deleted?: boolean;
1212
position?: number;
1313
desc?: string;
14-
due_date?: number
14+
due_date?: number;
1515
done?: boolean;
1616
last_edited?: number;
1717
}
1818

19+
export type Align = "start" | "center" | "end" | "view" | false;
20+
1921
export type Headings = "Custom" | "Due" | "Changed";
2022
export type HeadingsDir = "asc" | "desc";
2123

0 commit comments

Comments
 (0)