Path: blob/master/src/packages/frontend/editors/task-editor/update-visible.ts
1691 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Update which tasks and hashtags are visible, and their order.7*/89import { List, Map, Set, fromJS } from "immutable";1011import {12cmp,13parse_hashtags,14search_match,15search_split,16} from "@cocalc/util/misc";17import { get_search } from "./search";18import { SORT_INFO, HEADINGS, HEADINGS_DIR } from "./headings-info";19import { Counts, LocalTaskStateMap, LocalViewStateMap, TaskMap } from "./types";2021// Show tasks for a few seconds, even after marked done:22export const DONE_CUTOFF_MS = 3 * 1000;2324export function update_visible(25tasks: Map<string, TaskMap>,26local_task_state: LocalTaskStateMap,27local_view_state: LocalViewStateMap,28counts: Counts,29current_task_id?: string,30) {31const show_deleted = !!local_view_state.get("show_deleted");32const show_done = !!local_view_state.get("show_done");3334const now = Date.now();35const _is_visible: { [id: string]: boolean } = {}; // cache36let redoSoonMs = 0;37function is_visible(task: TaskMap, id: string): boolean {38const c = _is_visible[id];39if (c != null) {40return c;41}4243if (!show_deleted && task.get("deleted")) {44_is_visible[id] = false;45} else if (!show_done && task.get("done")) {46if (now - (task.get("last_edited") ?? 0) > DONE_CUTOFF_MS) {47_is_visible[id] = false;48} else {49_is_visible[id] = true;50const redo = DONE_CUTOFF_MS - (now - (task.get("last_edited") ?? 0));51if (redo > 0) {52redoSoonMs = Math.max(redo, redoSoonMs) + 1000;53}54}55} else {56_is_visible[id] = true;57}58return _is_visible[id];59}6061const relevant_tags: { [tag: string]: true } = {};62tasks.forEach((task: TaskMap, id: string) => {63if (!is_visible(task, id)) {64return;65}66const desc = task.get("desc") ?? "";67for (const x of parse_hashtags(desc)) {68const tag = desc.slice(x[0] + 1, x[1]).toLowerCase();69relevant_tags[tag] = true;70}71});7273const search0 = get_search(local_view_state, relevant_tags);74const search: (string | RegExp)[] = search_split(search0.toLowerCase());7576const new_counts = {77done: 0,78deleted: 0,79};80let current_is_visible = false;8182let sort_column = local_view_state.getIn(["sort", "column"]) ?? HEADINGS[0];83if (!HEADINGS.includes(sort_column)) {84sort_column = HEADINGS[0];85}86if (SORT_INFO[sort_column] == null) {87SORT_INFO[sort_column] = SORT_INFO[HEADINGS[0]];88}89const sort_info = SORT_INFO[sort_column];90const sort_key = sort_info.key;91let sort_dir = local_view_state.getIn(["sort", "dir"]) ?? HEADINGS_DIR[0];92if (sort_info.reverse) {93// reverse sort order -- done for due date94if (sort_dir === "asc") {95sort_dir = "desc";96} else {97sort_dir = "asc";98}99}100// undefined always gets pushed to the bottom (only applies to due date in practice)101const sort_default = sort_dir === "desc" ? -1e15 : 1e15;102let hashtags = Set<string>(); // contains all the hashtags of visible tasks103const v: [string | number | undefined, string][] = [];104tasks.forEach((task: TaskMap, id: string) => {105if (task.get("done")) {106new_counts.done += 1;107}108if (task.get("deleted")) {109new_counts.deleted += 1;110}111112const editing_desc = local_task_state?.getIn([id, "editing_desc"]);113if (!editing_desc && !is_visible(task, id)) {114return;115}116117const desc = task.get("desc") ?? "";118let visible: boolean;119if (search_match(desc, search) || editing_desc) {120visible = true; // tag of a currently visible task121if (id === current_task_id) {122current_is_visible = true;123}124v.push([task.get(sort_key) ?? sort_default, id]);125} else {126visible = false; // not a tag of any currently visible task127}128129for (const x of parse_hashtags(desc)) {130const tag = desc.slice(x[0] + 1, x[1]).toLowerCase();131if (visible) {132hashtags = hashtags.add(tag);133}134}135});136137if (sort_dir === "desc") {138v.sort((a, b) => -cmp(a[0], b[0]));139} else {140v.sort((a, b) => cmp(a[0], b[0]));141}142143const w = v.map((x) => x[1]);144const visible = fromJS(w);145if ((current_task_id == null || !current_is_visible) && visible.size > 0) {146current_task_id = visible.get(0);147} else if (!current_is_visible && visible.size === 0) {148current_task_id = undefined;149}150151if (counts.get("done") !== new_counts.done) {152counts = counts.set("done", new_counts.done);153}154if (counts.get("deleted") !== new_counts.deleted) {155counts = counts.set("deleted", new_counts.deleted);156}157const t: string[] = [];158for (const x of search) {159if (x[0] !== "#" && x[0] !== "-") {160t.push(`${x}`);161}162}163const search_terms = Set(t);164const t2: string[] = [];165for (const x of search) {166if (x[0] !== "#") {167t2.push(`${x}`);168}169}170const nonhash_search = List(t2);171172return {173visible,174current_task_id,175counts,176hashtags: fromJS(hashtags),177search_desc: search.join(" "),178search_terms,179nonhash_search,180redoSoonMs,181};182}183184185