From 81d4e3216ec925ca2135728538cc406d9030bba3 Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Tue, 24 Sep 2019 15:40:30 +0500 Subject: [PATCH 01/10] Add deadline / reminder props to typings and (de)serialization code --- src/helpers/tasks.ts | 4 ++++ src/typings/tasks.ts | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/helpers/tasks.ts b/src/helpers/tasks.ts index bc05be3..d06f364 100644 --- a/src/helpers/tasks.ts +++ b/src/helpers/tasks.ts @@ -11,6 +11,8 @@ export const taskFromRecord = (task: TaskRecord): Task => { newTask.Text = "text" in task ? (task.text as string) : ""; newTask.Completed = task.status !== 0 ? true : false; newTask.LastMod = task.last_mod as number; + newTask.Deadline = "deadline" in task ? (task.deadline as number) : 0; + newTask.Reminder = "reminder" in task ? (task.reminder as number) : 0; return newTask; }; @@ -24,6 +26,8 @@ export const taskToRecord = (task: Task): TaskRecord => { parent_id: task.PID, text: task.Text, status: task.Completed ? 1 : 0, + deadline: task.Deadline, + reminder: task.Reminder, } as TaskRecord; return record; }; diff --git a/src/typings/tasks.ts b/src/typings/tasks.ts index b809125..3e85034 100644 --- a/src/typings/tasks.ts +++ b/src/typings/tasks.ts @@ -4,9 +4,7 @@ export interface TaskRecord { text?: string; status: number; last_mod?: number; - /** TODO: Not implemented yet. */ deadline?: number; - /** TODO: Not implemented yet. */ reminder?: number; } @@ -16,6 +14,8 @@ export class Task { Text: string; Completed: boolean; LastMod: number; + Deadline: number; + Reminder: number; /** Informational field for tasks that have been created offline, those are * to be pushed to the server at the next sync. */ ToSync: boolean; @@ -31,5 +31,7 @@ export class Task { this.LastMod = Date.now(); this.ToSync = false; this.ToRemove = false; + this.Deadline = 0; + this.Reminder = 0; } } From fba8a3a4aadccd34565393991c922160f65074a5 Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Sun, 5 Jan 2020 18:28:27 +0500 Subject: [PATCH 02/10] Add a dependency on date-fns --- package.json | 1 + yarn.lock | 141 +++++---------------------------------------------- 2 files changed, 14 insertions(+), 128 deletions(-) diff --git a/package.json b/package.json index df98d54..0145efa 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@types/react-router-dom": "^5.1.3", "axios": "^0.19.0", "bulma": "^0.8.0", + "date-fns": "^2.8.1", "node-sass": "^4.13.0", "react": "^16.12.0", "react-dom": "^16.12.0", diff --git a/yarn.lock b/yarn.lock index 1791e8a..a9dabae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3378,6 +3378,11 @@ data-urls@^1.0.0, data-urls@^1.1.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" +date-fns@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.8.1.tgz#2109362ccb6c87c3ca011e9e31f702bc09e4123b" + integrity sha512-EL/C8IHvYRwAHYgFRse4MGAPSqlJVlOrhVYZ75iQBKrnv+ZedmYsgwH3t+BCDuZDXpoo07+q9j4qgSSOa7irJg== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3392,7 +3397,7 @@ debug@=3.1.0: dependencies: ms "2.0.0" -debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3428,11 +3433,6 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -3516,11 +3516,6 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" @@ -4551,13 +4546,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs-minipass@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.0.0.tgz#a6415edab02fae4b9e9230bc87ee2e4472003cd1" @@ -5091,7 +5079,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5122,13 +5110,6 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -ignore-walk@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== - dependencies: - minimatch "^3.0.4" - ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" @@ -5237,7 +5218,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.5, ini@~1.3.0: +ini@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -6805,14 +6786,6 @@ minipass-pipeline@^1.2.2: dependencies: minipass "^3.0.0" -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - minipass@^3.0.0, minipass@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" @@ -6820,13 +6793,6 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -6943,15 +6909,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -needle@^2.2.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -7052,22 +7009,6 @@ node-notifier@^5.4.2: shellwords "^0.1.1" which "^1.3.0" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-releases@^1.1.40, node-releases@^1.1.42: version "1.1.44" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.44.tgz#cd66438a6eb875e3eb012b6a12e48d9f4326ffd7" @@ -7105,14 +7046,6 @@ node-sass@^4.13.0: dependencies: abbrev "1" -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= - dependencies: - abbrev "1" - osenv "^0.1.4" - normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -7155,26 +7088,6 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -npm-bundled@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== - dependencies: - npm-normalize-package-bin "^1.0.1" - -npm-normalize-package-bin@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" - integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== - -npm-packlist@^1.1.6: - version "1.4.7" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.7.tgz#9e954365a06b80b18111ea900945af4f88ed4848" - integrity sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -7182,7 +7095,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -7438,7 +7351,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@0, osenv@^0.1.4: +osenv@0: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== @@ -8719,16 +8632,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - react-app-polyfill@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.5.tgz#59c7377a0b9ed25692eeaca7ad9b12ef2d064709" @@ -9330,7 +9233,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@2, rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -9498,7 +9401,7 @@ selfsigned@^1.10.7: dependencies: node-forge "0.9.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -10104,11 +10007,6 @@ strip-json-comments@^3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - style-loader@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.0.tgz#1d5296f9165e8e2c85d24eee0b7caf9ec8ca1f82" @@ -10203,19 +10101,6 @@ tar@^2.0.0: fstream "^1.0.12" inherits "2" -tar@^4.4.2: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - terser-webpack-plugin@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.2.1.tgz#5569e6c7d8be79e5e43d6da23acc3b6ba77d22bd" @@ -11170,7 +11055,7 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: +yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From 7aaf8ca9a6abdeaac2bf1631648ffc5997af6f38 Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Sun, 5 Jan 2020 19:08:23 +0500 Subject: [PATCH 03/10] Add initial support for datetime editing in editor view --- .eslintrc.json | 2 +- src/helpers/datetime.ts | 9 ++++ src/views/assets/locales.ts | 2 + src/views/components/dateTimePicker.tsx | 67 +++++++++++++++++++++++++ src/views/editorView.tsx | 37 ++++++++++++-- 5 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 src/helpers/datetime.ts create mode 100644 src/views/components/dateTimePicker.tsx diff --git a/.eslintrc.json b/.eslintrc.json index 8566e25..15e8e01 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -110,7 +110,7 @@ "ignoreEnums": true, "enforceConst": true, "ignoreReadonlyClassProperties": true, - "ignore": [0, 1, 2] + "ignore": [0, 1, 2, 1000] } ], "@typescript-eslint/brace-style": ["error", "1tbs"], diff --git a/src/helpers/datetime.ts b/src/helpers/datetime.ts new file mode 100644 index 0000000..d402991 --- /dev/null +++ b/src/helpers/datetime.ts @@ -0,0 +1,9 @@ +export const DateFormat = "yyyy-MM-dd"; +export const TimeFormat = "HH:mm"; + +/** + * Converts a JS date to epoch timestamp. + * @param date Date in JS `Date` class + */ +export const dateTimeToUnixTime = (date: Date) => + parseInt((date.getTime() / 1000).toFixed(0)); diff --git a/src/views/assets/locales.ts b/src/views/assets/locales.ts index 3a12260..abd5276 100644 --- a/src/views/assets/locales.ts +++ b/src/views/assets/locales.ts @@ -34,6 +34,8 @@ export default new LocalizedStrings({ editor_textTp: "Text:", editor_parentTp: "Parent:", editor_parentNoParentVal: "No parent", + editor_deadline: "Deadline:", + editor_reminder: "Reminder:", task_toggleBtnCompleted: "Completed", task_toggleBtnPending: "Pending", app_versionString: `${appFullName} v${appVersion} by ${appAuthor}`, diff --git a/src/views/components/dateTimePicker.tsx b/src/views/components/dateTimePicker.tsx new file mode 100644 index 0000000..0b8ccae --- /dev/null +++ b/src/views/components/dateTimePicker.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import parse from "date-fns/parse"; +import format from "date-fns/format"; + +import { + dateTimeToUnixTime, + DateFormat, + TimeFormat, +} from "../../helpers/datetime"; + +interface Props { + dateRequired?: boolean; + timeRequired?: boolean; + initialValue?: number; + onChange?: (date: number) => void; +} +interface State { + date: Date; +} +export class DateTimePicker extends React.Component { + state = { + date: new Date(this.props.initialValue || 0), + }; + componentDidUpdate = (_p: Props, prevState: State) => { + if (this.props.onChange) { + if (prevState.date !== this.state.date) { + this.props.onChange(dateTimeToUnixTime(this.state.date)); + } + } + }; + updateDate = (e: React.FormEvent) => { + // event input is a string of format like "2020-01-03" + // eslint-disable-next-line react/no-access-state-in-setstate + const date = parse(e.currentTarget.value, DateFormat, this.state.date); + this.setState({ date }); + }; + updateTime = (e: React.FormEvent) => { + // input is a string of format like "13:45" + // eslint-disable-next-line react/no-access-state-in-setstate + const date = parse(e.currentTarget.value, TimeFormat, this.state.date); + this.setState({ date }); + }; + inputInitValue = (date: Date, fmt: string): string => { + if (date.getTime()) { + return format(date, fmt); + } + return ""; + }; + render = () => ( +
+ + +
+ ); +} + +export default DateTimePicker; diff --git a/src/views/editorView.tsx b/src/views/editorView.tsx index d78ad8b..78d0b1d 100644 --- a/src/views/editorView.tsx +++ b/src/views/editorView.tsx @@ -8,6 +8,7 @@ import Input from "./components/bulma/input"; import Field from "./components/bulma/field"; import Level from "./components/bulma/level"; import TaskSelect from "./components/taskSelect"; +import DateTimePicker from "./components/dateTimePicker"; import { deleteTask, updateTask, createTask } from "../actions/tasks"; import { hotkeyHandler, escCode, Hotkey } from "./helpers/keyboard"; @@ -88,6 +89,8 @@ class EditorView extends React.Component { Text: "", ID: 0, PID: 0, + Deadline: 0, + Reminder: 0, } as Task, newTask: false, title: "", @@ -134,18 +137,28 @@ class EditorView extends React.Component { updateText = (e: React.FormEvent) => { const { task } = this.state; task.Text = e.currentTarget.value; - this.setState(() => ({ task })); + this.setState({ task }); }; updateStatus = () => { const { task } = this.state; task.Completed = !task.Completed; - this.setState(() => ({ task })); + this.setState({ task }); }; updateParent = (e: React.FormEvent) => { const newPID = parseInt(e.currentTarget.value); const { task } = this.state; task.PID = newPID; - this.setState(() => ({ task })); + this.setState({ task }); + }; + updateDeadline = (date: number) => { + const { task } = this.state; + task.Deadline = date; + this.setState({ task }); + }; + updateReminder = (date: number) => { + const { task } = this.state; + task.Reminder = date; + this.setState({ task }); }; saveChanges = () => { if (this.state.newTask) { @@ -235,6 +248,24 @@ class EditorView extends React.Component { /> } /> + + } + /> + + } + /> From 8d102d3c4c945e4ff76314bc6ba14ca9a84fe496 Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Tue, 7 Jan 2020 20:14:19 +0500 Subject: [PATCH 04/10] Refactor task selection component so it doesn't use Redux --- src/views/components/taskSelect.tsx | 8 +------- src/views/editorView.tsx | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/views/components/taskSelect.tsx b/src/views/components/taskSelect.tsx index 99f082c..fb1f8c2 100644 --- a/src/views/components/taskSelect.tsx +++ b/src/views/components/taskSelect.tsx @@ -1,15 +1,9 @@ import React, { HTMLAttributes } from "react"; -import { connect } from "react-redux"; -import { Store } from "../../typings/store"; import { Task } from "../../typings/tasks"; import strings from "../assets/locales"; -const mapStateToProps = (state: Store) => ({ - tasks: state.task.tasks, -}); - interface Props extends HTMLAttributes { tasks: Task[]; current: Task; @@ -38,4 +32,4 @@ const TaskSelect: React.FC = (props) => ( ); -export default connect(mapStateToProps)(TaskSelect); +export default TaskSelect; diff --git a/src/views/editorView.tsx b/src/views/editorView.tsx index 78d0b1d..0e83a98 100644 --- a/src/views/editorView.tsx +++ b/src/views/editorView.tsx @@ -242,6 +242,7 @@ class EditorView extends React.Component { control={ Date: Tue, 7 Jan 2020 21:36:31 +0500 Subject: [PATCH 05/10] Add basic datetime rendering support --- src/views/components/dateTimePicker.tsx | 1 + src/views/components/taskLine.tsx | 35 ++++++++++++++++++++----- src/views/styles/common.scss | 11 ++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/views/components/dateTimePicker.tsx b/src/views/components/dateTimePicker.tsx index 0b8ccae..826b9fc 100644 --- a/src/views/components/dateTimePicker.tsx +++ b/src/views/components/dateTimePicker.tsx @@ -46,6 +46,7 @@ export class DateTimePicker extends React.Component { } return ""; }; + // TODO: add an "Unset" button render = () => (
({ + update: (task: Task) => dispatch(updateTask(task)), +}); + +interface Props extends ReturnType { level: number; task: Task; - dispatch: ThunkDispatch; } interface State { task: Task; @@ -35,19 +38,37 @@ class TaskLine extends React.Component { toggleTask = (): void => { const task: Task = { ...this.state.task }; task.Completed = !task.Completed; - this.props.dispatch(updateTask(task)); + this.props.update(task); }; + // Pp expands to a localized date string that looks like this: + // "05/29/1453, 12:00 AM" + fmtDate = (date: number) => format(new Date(date), "Pp"); render = () => { - const { ID, Completed, ToRemove, Text } = this.state.task; + const { + ID, + Completed, + ToRemove, + Text, + Deadline, + Reminder, + } = this.state.task; let classNames: string[] = ["taskLine"]; Completed && classNames.push("taskCompleted"); ToRemove && classNames.push("taskToRemove"); + Deadline && classNames.push("taskHasDeadline"); + Reminder && classNames.push("taskHasReminder"); return ( {`#${ID} - `} {Text} + + {` - (D: ${this.fmtDate(Deadline)})`} + + + {` - (R: ${this.fmtDate(Reminder)})`} + @@ -65,4 +86,4 @@ class TaskLine extends React.Component { }; } -export default connect()(TaskLine); +export default connect(null, mapDispatchToProps)(TaskLine); diff --git a/src/views/styles/common.scss b/src/views/styles/common.scss index b54c000..da545ce 100644 --- a/src/views/styles/common.scss +++ b/src/views/styles/common.scss @@ -120,6 +120,17 @@ div.taskList { div.taskSubtree { margin-left: $spacing * 8; div.taskLine { + // a hackish way to hide dates for tasks that do not have them set + .taskDeadline, + .taskReminder { + display: none; + } + &.taskHasDeadline .taskDeadline { + display: unset !important; + } + &.taskHasReminder .taskReminder { + display: unset !important; + } border: 1px solid $borderColor; border-radius: 3px; margin: $spacing * 3 0; From 9f31a7946327373fa1b2dee14e4f6278a61b9838 Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Tue, 7 Jan 2020 21:37:01 +0500 Subject: [PATCH 06/10] Remove unneeded division from the epoch time helper --- src/helpers/datetime.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/helpers/datetime.ts b/src/helpers/datetime.ts index d402991..19644c7 100644 --- a/src/helpers/datetime.ts +++ b/src/helpers/datetime.ts @@ -5,5 +5,4 @@ export const TimeFormat = "HH:mm"; * Converts a JS date to epoch timestamp. * @param date Date in JS `Date` class */ -export const dateTimeToUnixTime = (date: Date) => - parseInt((date.getTime() / 1000).toFixed(0)); +export const dateTimeToUnixTime = (date: Date) => date.getTime(); From 44a07a5712b0ca76d487c0b8d827f6a5f0099a9a Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Fri, 10 Jan 2020 19:05:47 +0500 Subject: [PATCH 07/10] Make tasks that have reminders associated with them look better on the main screen --- src/views/components/taskLine.tsx | 35 +++++++++++++++++++++---------- src/views/styles/common.scss | 19 +++++++---------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/views/components/taskLine.tsx b/src/views/components/taskLine.tsx index 885f46a..e0eec3e 100644 --- a/src/views/components/taskLine.tsx +++ b/src/views/components/taskLine.tsx @@ -1,23 +1,23 @@ import React from "react"; import { connect } from "react-redux"; +import { withRouter, RouteComponentProps as RCP } from "react-router"; import format from "date-fns/format"; import Level from "../components/bulma/level"; import Button from "../components/bulma/button"; -import Link from "../components/link"; import { Task } from "../../typings/tasks"; +import { Dispatch } from "../../typings/react"; import { updateTask } from "../../actions/tasks"; import strings from "../assets/locales"; -import { Dispatch } from "../../typings/react"; const mapDispatchToProps = (dispatch: Dispatch) => ({ update: (task: Task) => dispatch(updateTask(task)), }); -interface Props extends ReturnType { +interface Props extends ReturnType, RCP { level: number; task: Task; } @@ -43,6 +43,7 @@ class TaskLine extends React.Component { // Pp expands to a localized date string that looks like this: // "05/29/1453, 12:00 AM" fmtDate = (date: number) => format(new Date(date), "Pp"); + gotoEditor = () => this.props.history.push(`/task/${this.state.task.ID}`); render = () => { const { ID, @@ -59,19 +60,31 @@ class TaskLine extends React.Component { Reminder && classNames.push("taskHasReminder"); return ( - - - {`#${ID} - `} + + + {`#${ID}`} + {/* eslint-disable react/jsx-no-literals */} +   {Text} + + - {` - (D: ${this.fmtDate(Deadline)})`} + {`(D: ${this.fmtDate(Deadline)})`} + {/* eslint-disable react/jsx-no-literals */} +   - {` - (R: ${this.fmtDate(Reminder)})`} + {`(R: ${this.fmtDate(Reminder)})`} - + - +