From d27167ce8681b9687e18a7d63112ab3b8aaf379c Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 21 Jan 2021 01:51:00 +0100 Subject: [PATCH 01/16] Add first GraphPreferences which are not hooked to backend yet --- ts/graphs/BUILD.bazel | 1 + ts/graphs/CardCounts.svelte | 6 +++--- ts/graphs/SeparateInactiveCheckbox.svelte | 7 +++++-- ts/graphs/preferences.ts | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 ts/graphs/preferences.ts diff --git a/ts/graphs/BUILD.bazel b/ts/graphs/BUILD.bazel index 0db44c94d..34d8083bc 100644 --- a/ts/graphs/BUILD.bazel +++ b/ts/graphs/BUILD.bazel @@ -71,6 +71,7 @@ ts_library( "@npm//d3-transition", "@npm//lodash.debounce", "@npm//lodash.throttle", + "@npm//svelte", ], ) diff --git a/ts/graphs/CardCounts.svelte b/ts/graphs/CardCounts.svelte index 3a8489632..5f6675138 100644 --- a/ts/graphs/CardCounts.svelte +++ b/ts/graphs/CardCounts.svelte @@ -5,11 +5,11 @@ import type pb from "anki/backend_proto"; import type { I18n } from "anki/i18n"; import SeparateInactiveCheckbox from "./SeparateInactiveCheckbox.svelte"; + import { cardCountsSeparateInactive } from "./preferences"; export let sourceData: pb.BackendProto.GraphsOut; export let i18n: I18n; - let separateInactive = true; let svg = null as HTMLElement | SVGElement | null; let bounds = defaultGraphBounds(); @@ -20,7 +20,7 @@ let tableData = (null as unknown) as TableDatum[]; $: { - graphData = gatherData(sourceData, separateInactive, i18n); + graphData = gatherData(sourceData, $cardCountsSeparateInactive, i18n); tableData = renderCards(svg as any, bounds, graphData); } @@ -56,7 +56,7 @@

{graphData.title}

- +
diff --git a/ts/graphs/SeparateInactiveCheckbox.svelte b/ts/graphs/SeparateInactiveCheckbox.svelte index 24fac6072..405a58f83 100644 --- a/ts/graphs/SeparateInactiveCheckbox.svelte +++ b/ts/graphs/SeparateInactiveCheckbox.svelte @@ -1,10 +1,13 @@ - + diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts new file mode 100644 index 000000000..8d03bfe59 --- /dev/null +++ b/ts/graphs/preferences.ts @@ -0,0 +1,15 @@ +import { writable } from "svelte/store"; + +function createPreference(initialValue: unknown) { + const { subscribe, set } = writable(initialValue); + + return { + subscribe, + set: (v: unknown) => { + set(v); + }, + }; +} + +export const calendarFirstDayOfWeek = createPreference(0); +export const cardCountsSeparateInactive = createPreference(false); From 665a13e378a66de074c61e36f82aaf3c50512c3a Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 21 Jan 2021 03:25:14 +0100 Subject: [PATCH 02/16] Add GraphsPreferences endpoint to backend --- pylib/tools/genbackend.py | 2 +- qt/aqt/mediasrv.py | 5 +++++ rslib/backend.proto | 13 +++++++++++++ rslib/src/backend/mod.rs | 4 ++++ rslib/src/stats/graphs.rs | 7 +++++++ ts/graphs/GraphsPage.svelte | 2 +- ts/graphs/graph-helpers.ts | 6 ++++++ 7 files changed, 37 insertions(+), 2 deletions(-) diff --git a/pylib/tools/genbackend.py b/pylib/tools/genbackend.py index 5ed89f86b..e2ad3ced2 100755 --- a/pylib/tools/genbackend.py +++ b/pylib/tools/genbackend.py @@ -34,7 +34,7 @@ LABEL_REPEATED = 3 # messages we don't want to unroll in codegen SKIP_UNROLL_INPUT = {"TranslateString"} -SKIP_DECODE = {"Graphs"} +SKIP_DECODE = {"Graphs", "GraphsPreferences"} def python_type(field): diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index aecfc79d8..c58c19747 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -255,6 +255,10 @@ def graph_data() -> bytes: return aqt.mw.col.backend.graphs(search=args["search"], days=args["days"]) +def graph_preferences() -> bytes: + return aqt.mw.col.backend.graphs_preferences() + + def congrats_info() -> bytes: info = aqt.mw.col.backend.congrats_info() return info.SerializeToString() @@ -262,6 +266,7 @@ def congrats_info() -> bytes: post_handlers = dict( graphData=graph_data, + graphPreferences=graph_preferences, # pylint: disable=unnecessary-lambda i18nResources=lambda: aqt.mw.col.backend.i18n_resources(), congratsInfo=congrats_info, diff --git a/rslib/backend.proto b/rslib/backend.proto index 848a201b9..60a81eec9 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -116,6 +116,7 @@ service BackendService { rpc CardStats(CardID) returns (String); rpc Graphs(GraphsIn) returns (GraphsOut); + rpc GraphsPreferences(Empty) returns (GraphsPreferencesOut); // media @@ -1099,6 +1100,18 @@ message GraphsOut { Weekday first_weekday = 8; } +message GraphsPreferencesOut { + enum Weekday { + SUNDAY = 0; + MONDAY = 1; + FRIDAY = 5; + SATURDAY = 6; + } + Weekday calendar_first_day_of_week = 1; + bool card_counts_separate_inactive = 2; + +} + message RevlogEntry { enum ReviewKind { LEARNING = 0; diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 92080e62c..17ef064d3 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -676,6 +676,10 @@ impl BackendService for Backend { self.with_col(|col| col.graph_data_for_search(&input.search, input.days)) } + fn graphs_preferences(&self, _input: pb::Empty) -> BackendResult { + self.with_col(|col| col.graphs_preferences()) + } + // decks //----------------------------------------------- diff --git a/rslib/src/stats/graphs.rs b/rslib/src/stats/graphs.rs index 59243e13f..3e0fbff64 100644 --- a/rslib/src/stats/graphs.rs +++ b/rslib/src/stats/graphs.rs @@ -45,6 +45,13 @@ impl Collection { first_weekday: self.get_first_weekday() as i32, }) } + + pub(crate) fn graphs_preferences(&self) -> Result { + Ok(pb::GraphsPreferencesOut { + calendar_first_day_of_week: self.get_first_weekday() as i32, + card_counts_separate_inactive: true, + }) + } } impl From for pb::RevlogEntry { diff --git a/ts/graphs/GraphsPage.svelte b/ts/graphs/GraphsPage.svelte index 47b566175..6a461f6a8 100644 --- a/ts/graphs/GraphsPage.svelte +++ b/ts/graphs/GraphsPage.svelte @@ -5,7 +5,7 @@ import type { SvelteComponent } from "svelte/internal"; import type { I18n } from "anki/i18n"; import type pb from "anki/backend_proto"; - import { getGraphData, RevlogRange } from "./graph-helpers"; + import { getGraphData, getGraphPreferences, RevlogRange } from "./graph-helpers"; export let i18n: I18n; export let nightMode: boolean; diff --git a/ts/graphs/graph-helpers.ts b/ts/graphs/graph-helpers.ts index bed36f6d1..9e94fdbf8 100644 --- a/ts/graphs/graph-helpers.ts +++ b/ts/graphs/graph-helpers.ts @@ -19,6 +19,12 @@ export async function getGraphData( ); } +export async function getGraphPreferences(): Promise { + return pb.BackendProto.GraphsPreferencesOut.decode( + await postRequest("/_anki/graphPreferences", JSON.stringify({})) + ); +} + // amount of data to fetch from backend export enum RevlogRange { Year = 1, From 5fc8b1965aeaf1137710581c4f0a98b400f9ed1f Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 21 Jan 2021 13:45:49 +0100 Subject: [PATCH 03/16] Add PreferenceStore with gettable/settable preferences * setting is not yet hooked up to rslib --- ts/graphs/CardCounts.svelte | 6 +-- ts/graphs/GraphsPage.svelte | 12 ++++- ts/graphs/SeparateInactiveCheckbox.svelte | 4 +- ts/graphs/preferences.ts | 62 +++++++++++++++++++++-- ts/tsconfig.json | 2 +- 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/ts/graphs/CardCounts.svelte b/ts/graphs/CardCounts.svelte index 5f6675138..4c753c13c 100644 --- a/ts/graphs/CardCounts.svelte +++ b/ts/graphs/CardCounts.svelte @@ -5,7 +5,6 @@ import type pb from "anki/backend_proto"; import type { I18n } from "anki/i18n"; import SeparateInactiveCheckbox from "./SeparateInactiveCheckbox.svelte"; - import { cardCountsSeparateInactive } from "./preferences"; export let sourceData: pb.BackendProto.GraphsOut; export let i18n: I18n; @@ -18,9 +17,10 @@ let graphData = (null as unknown) as GraphData; let tableData = (null as unknown) as TableDatum[]; + let cardCountsSeparateInactive = false; $: { - graphData = gatherData(sourceData, $cardCountsSeparateInactive, i18n); + graphData = gatherData(sourceData, cardCountsSeparateInactive, i18n); tableData = renderCards(svg as any, bounds, graphData); } @@ -56,7 +56,7 @@

{graphData.title}

- +
diff --git a/ts/graphs/GraphsPage.svelte b/ts/graphs/GraphsPage.svelte index 6a461f6a8..a38d20f15 100644 --- a/ts/graphs/GraphsPage.svelte +++ b/ts/graphs/GraphsPage.svelte @@ -4,8 +4,10 @@ diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts index 8d03bfe59..a4e37ac90 100644 --- a/ts/graphs/preferences.ts +++ b/ts/graphs/preferences.ts @@ -1,15 +1,69 @@ +import { getGraphPreferences } from "./graph-helpers"; import { writable } from "svelte/store"; +import type pb from "anki/backend_proto"; -function createPreference(initialValue: unknown) { +interface CustomStore { + subscribe: (getter: (value: T) => void) => () => void; + set: (value: T) => void; + get: () => T; +} + +export type PreferenceStore = { + [K in keyof pb.BackendProto.GraphsPreferencesOut]: CustomStore< + pb.BackendProto.GraphsPreferencesOut[K] + >; +}; + +function createPreference( + initialValue: T, + savePreferences: () => void +): CustomStore { const { subscribe, set } = writable(initialValue); return { subscribe, - set: (v: unknown) => { + set: (v: T): void => { set(v); + savePreferences(); + }, + get: (): T => { + let result: any /* T */; + subscribe((value: T) => (result = value))(); + return result; }, }; } -export const calendarFirstDayOfWeek = createPreference(0); -export const cardCountsSeparateInactive = createPreference(false); +function preparePreferences( + graphsPreferences: pb.BackendProto.GraphsPreferencesOut, + save: (prefs: pb.BackendProto.GraphsPreferencesOut) => void +): PreferenceStore { + const preferences: Partial = {}; + + function constructPreferences(): pb.BackendProto.GraphsPreferencesOut { + const payload: Partial = {}; + for (const [key, pref] of Object.entries(preferences as PreferenceStore)) { + payload[key] = pref.get(); + } + return payload as pb.BackendProto.GraphsPreferencesOut; + } + + function savePreferences(): void { + const preferences = constructPreferences(); + save(preferences); + } + + for (const [key, value] of Object.entries(graphsPreferences)) { + preferences[key] = createPreference(value, savePreferences); + } + + return preferences as PreferenceStore; +} + +export async function getPreferences() { + const initialPreferences = await getGraphPreferences(); + + return preparePreferences(initialPreferences, (prefs) => { + console.log("preferences to save", prefs); + }); +} diff --git a/ts/tsconfig.json b/ts/tsconfig.json index 4e8f96d8c..883e2130b 100644 --- a/ts/tsconfig.json +++ b/ts/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "target": "es6", "module": "es6", - "lib": ["es2016", "es2019.array", "dom", "dom.iterable"], + "lib": ["es2017", "es2019.array", "dom", "dom.iterable"], "baseUrl": ".", "paths": { "anki/*": ["../bazel-bin/ts/lib/*"] From 64352ce0d5e0767a007d3024198993c850209f7a Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 21 Jan 2021 14:04:33 +0100 Subject: [PATCH 04/16] Hook up cardCountsSeparateInactive to PreferenceStore --- ts/graphs/CardCounts.svelte | 8 +++++--- ts/graphs/GraphsPage.svelte | 1 + ts/graphs/SeparateInactiveCheckbox.svelte | 5 +++-- ts/graphs/preferences.ts | 12 +++--------- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/ts/graphs/CardCounts.svelte b/ts/graphs/CardCounts.svelte index 4c753c13c..cab4908ea 100644 --- a/ts/graphs/CardCounts.svelte +++ b/ts/graphs/CardCounts.svelte @@ -2,13 +2,16 @@ import { defaultGraphBounds } from "./graph-helpers"; import { gatherData, renderCards } from "./card-counts"; import type { GraphData, TableDatum } from "./card-counts"; + import type { PreferenceStore } from "./preferences"; import type pb from "anki/backend_proto"; import type { I18n } from "anki/i18n"; import SeparateInactiveCheckbox from "./SeparateInactiveCheckbox.svelte"; export let sourceData: pb.BackendProto.GraphsOut; export let i18n: I18n; + export let preferences: PreferenceStore; + let { cardCountsSeparateInactive } = preferences; let svg = null as HTMLElement | SVGElement | null; let bounds = defaultGraphBounds(); @@ -17,10 +20,9 @@ let graphData = (null as unknown) as GraphData; let tableData = (null as unknown) as TableDatum[]; - let cardCountsSeparateInactive = false; $: { - graphData = gatherData(sourceData, cardCountsSeparateInactive, i18n); + graphData = gatherData(sourceData, $cardCountsSeparateInactive, i18n); tableData = renderCards(svg as any, bounds, graphData); } @@ -56,7 +58,7 @@

{graphData.title}

- +
diff --git a/ts/graphs/GraphsPage.svelte b/ts/graphs/GraphsPage.svelte index a38d20f15..8615c28e5 100644 --- a/ts/graphs/GraphsPage.svelte +++ b/ts/graphs/GraphsPage.svelte @@ -62,6 +62,7 @@ diff --git a/ts/graphs/SeparateInactiveCheckbox.svelte b/ts/graphs/SeparateInactiveCheckbox.svelte index f998c5ceb..51ef6529f 100644 --- a/ts/graphs/SeparateInactiveCheckbox.svelte +++ b/ts/graphs/SeparateInactiveCheckbox.svelte @@ -1,13 +1,14 @@ diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts index a4e37ac90..cc3109fc5 100644 --- a/ts/graphs/preferences.ts +++ b/ts/graphs/preferences.ts @@ -1,11 +1,10 @@ import { getGraphPreferences } from "./graph-helpers"; -import { writable } from "svelte/store"; +import { writable, get } from "svelte/store"; import type pb from "anki/backend_proto"; -interface CustomStore { +export interface CustomStore { subscribe: (getter: (value: T) => void) => () => void; set: (value: T) => void; - get: () => T; } export type PreferenceStore = { @@ -26,11 +25,6 @@ function createPreference( set(v); savePreferences(); }, - get: (): T => { - let result: any /* T */; - subscribe((value: T) => (result = value))(); - return result; - }, }; } @@ -43,7 +37,7 @@ function preparePreferences( function constructPreferences(): pb.BackendProto.GraphsPreferencesOut { const payload: Partial = {}; for (const [key, pref] of Object.entries(preferences as PreferenceStore)) { - payload[key] = pref.get(); + payload[key] = get(pref as any); } return payload as pb.BackendProto.GraphsPreferencesOut; } From 054c30a695bdad7406d5c5d0628999ff6674b176 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 21 Jan 2021 16:16:58 +0100 Subject: [PATCH 05/16] Add non-functioning logic for settings graphs preferences --- qt/aqt/mediasrv.py | 14 ++++++++++++++ rslib/backend.proto | 1 + rslib/src/backend/mod.rs | 5 +++++ rslib/src/stats/graphs.rs | 5 +++++ ts/graphs/graph-helpers.ts | 6 ++++++ ts/graphs/preferences.ts | 18 ++++++------------ 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index c58c19747..d50168d2a 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -19,6 +19,7 @@ from flask import Response, request from waitress.server import create_server import aqt +import anki.backend_pb2 as pb from anki import hooks from anki.rsbackend import from_json_bytes from anki.utils import devMode @@ -259,6 +260,18 @@ def graph_preferences() -> bytes: return aqt.mw.col.backend.graphs_preferences() +def set_graph_preferences() -> bytes: + data = from_json_bytes(request.data) + + underscore_data = { + "calendar_first_day_of_week": data["calendarFirstDayOfWeek"], + "card_counts_separate_inactive": data["cardCountsSeparateInactive"], + } + + data = pb.GraphsPreferencesOut(**underscore_data) + return aqt.mw.col.backend.set_graphs_preferences(input=data) + + def congrats_info() -> bytes: info = aqt.mw.col.backend.congrats_info() return info.SerializeToString() @@ -267,6 +280,7 @@ def congrats_info() -> bytes: post_handlers = dict( graphData=graph_data, graphPreferences=graph_preferences, + setGraphPreferences=set_graph_preferences, # pylint: disable=unnecessary-lambda i18nResources=lambda: aqt.mw.col.backend.i18n_resources(), congratsInfo=congrats_info, diff --git a/rslib/backend.proto b/rslib/backend.proto index 60a81eec9..2e6a9a0a3 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -117,6 +117,7 @@ service BackendService { rpc CardStats(CardID) returns (String); rpc Graphs(GraphsIn) returns (GraphsOut); rpc GraphsPreferences(Empty) returns (GraphsPreferencesOut); + rpc SetGraphsPreferences(GraphsPreferencesOut) returns (Empty); // media diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 17ef064d3..3444f8358 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -680,6 +680,11 @@ impl BackendService for Backend { self.with_col(|col| col.graphs_preferences()) } + fn set_graphs_preferences(&self, input: pb::GraphsPreferencesOut) -> BackendResult { + self.with_col(|col| col.set_graphs_preferences(input)) + .map(Into::into) + } + // decks //----------------------------------------------- diff --git a/rslib/src/stats/graphs.rs b/rslib/src/stats/graphs.rs index 3e0fbff64..eeddf68fd 100644 --- a/rslib/src/stats/graphs.rs +++ b/rslib/src/stats/graphs.rs @@ -52,6 +52,11 @@ impl Collection { card_counts_separate_inactive: true, }) } + + pub(crate) fn set_graphs_preferences(&self, prefs: pb::GraphsPreferencesOut) -> Result<()> { + // self.set_first_weekday(prefs.calendar_first_day_of_week); + Ok(()) + } } impl From for pb::RevlogEntry { diff --git a/ts/graphs/graph-helpers.ts b/ts/graphs/graph-helpers.ts index 9e94fdbf8..546f0816e 100644 --- a/ts/graphs/graph-helpers.ts +++ b/ts/graphs/graph-helpers.ts @@ -25,6 +25,12 @@ export async function getGraphPreferences(): Promise { + return (async () => { + await postRequest("/_anki/setGraphPreferences", JSON.stringify({ ...prefs })) + })() +} + // amount of data to fetch from backend export enum RevlogRange { Year = 1, diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts index cc3109fc5..8906cced5 100644 --- a/ts/graphs/preferences.ts +++ b/ts/graphs/preferences.ts @@ -1,6 +1,7 @@ -import { getGraphPreferences } from "./graph-helpers"; -import { writable, get } from "svelte/store"; import type pb from "anki/backend_proto"; +import { getGraphPreferences, setGraphPreferences } from "./graph-helpers"; +import { writable, get } from "svelte/store"; + export interface CustomStore { subscribe: (getter: (value: T) => void) => () => void; @@ -28,10 +29,7 @@ function createPreference( }; } -function preparePreferences( - graphsPreferences: pb.BackendProto.GraphsPreferencesOut, - save: (prefs: pb.BackendProto.GraphsPreferencesOut) => void -): PreferenceStore { +function preparePreferences(graphsPreferences: pb.BackendProto.GraphsPreferencesOut): PreferenceStore { const preferences: Partial = {}; function constructPreferences(): pb.BackendProto.GraphsPreferencesOut { @@ -43,8 +41,7 @@ function preparePreferences( } function savePreferences(): void { - const preferences = constructPreferences(); - save(preferences); + setGraphPreferences(constructPreferences()); } for (const [key, value] of Object.entries(graphsPreferences)) { @@ -56,8 +53,5 @@ function preparePreferences( export async function getPreferences() { const initialPreferences = await getGraphPreferences(); - - return preparePreferences(initialPreferences, (prefs) => { - console.log("preferences to save", prefs); - }); + return preparePreferences(initialPreferences) } From d1ada886573f6d719379332bd6a9e725c4686062 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 14:37:24 +0100 Subject: [PATCH 06/16] Successfully send post request to rust --- qt/aqt/mediasrv.py | 43 ++++++++++++++++++-------------------- ts/graphs/graph-helpers.ts | 2 +- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index d50168d2a..e48ba9661 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -18,8 +18,8 @@ import flask_cors # type: ignore from flask import Response, request from waitress.server import create_server -import aqt import anki.backend_pb2 as pb +import aqt from anki import hooks from anki.rsbackend import from_json_bytes from anki.utils import devMode @@ -260,16 +260,10 @@ def graph_preferences() -> bytes: return aqt.mw.col.backend.graphs_preferences() -def set_graph_preferences() -> bytes: - data = from_json_bytes(request.data) - - underscore_data = { - "calendar_first_day_of_week": data["calendarFirstDayOfWeek"], - "card_counts_separate_inactive": data["cardCountsSeparateInactive"], - } - - data = pb.GraphsPreferencesOut(**underscore_data) - return aqt.mw.col.backend.set_graphs_preferences(input=data) +def set_graph_preferences() -> None: + input = pb.GraphsPreferencesOut() + input.ParseFromString(request.data) + aqt.mw.col.backend.set_graphs_preferences(input=input) def congrats_info() -> bytes: @@ -277,14 +271,14 @@ def congrats_info() -> bytes: return info.SerializeToString() -post_handlers = dict( - graphData=graph_data, - graphPreferences=graph_preferences, - setGraphPreferences=set_graph_preferences, +post_handlers = { + "graphData": graph_data, + "graphPreferences": graph_preferences, + "setGraphPreferences": set_graph_preferences, # pylint: disable=unnecessary-lambda - i18nResources=lambda: aqt.mw.col.backend.i18n_resources(), - congratsInfo=congrats_info, -) + "i18nResources": lambda: aqt.mw.col.backend.i18n_resources(), + "congratsInfo": congrats_info, +} def handle_post(path: str) -> Response: @@ -293,12 +287,15 @@ def handle_post(path: str) -> Response: return flask.make_response("Collection not open", HTTPStatus.NOT_FOUND) if path in post_handlers: - data = post_handlers[path]() - response = flask.make_response(data) - response.headers["Content-Type"] = "application/binary" - return response + if data := post_handlers[path](): + response = flask.make_response(data) + response.headers["Content-Type"] = "application/binary" + else: + response = flask.make_response("", HTTPStatus.NO_CONTENT) else: - return flask.make_response( + response = flask.make_response( f"Unhandled post to {path}", HTTPStatus.FORBIDDEN, ) + + return response diff --git a/ts/graphs/graph-helpers.ts b/ts/graphs/graph-helpers.ts index 546f0816e..456273c1b 100644 --- a/ts/graphs/graph-helpers.ts +++ b/ts/graphs/graph-helpers.ts @@ -27,7 +27,7 @@ export async function getGraphPreferences(): Promise { return (async () => { - await postRequest("/_anki/setGraphPreferences", JSON.stringify({ ...prefs })) + await postRequest("/_anki/setGraphPreferences", pb.BackendProto.GraphsPreferencesOut.encode(prefs).finish()) })() } From b0c2e8c99cdd7bc0256a4a56e7642619024a8827 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 14:56:41 +0100 Subject: [PATCH 07/16] Remve SeparateInactiveCheckbox and correctly use preferences values * Expose issue with non-existing calendar value in GraphPreferences --- ts/graphs/CalendarGraph.svelte | 4 +++- ts/graphs/CardCounts.svelte | 7 +++++-- ts/graphs/SeparateInactiveCheckbox.svelte | 14 -------------- ts/graphs/calendar.ts | 18 ++++++++++++------ ts/graphs/graph-helpers.ts | 11 ++++++++--- ts/graphs/preferences.ts | 7 ++++--- 6 files changed, 32 insertions(+), 29 deletions(-) delete mode 100644 ts/graphs/SeparateInactiveCheckbox.svelte diff --git a/ts/graphs/CalendarGraph.svelte b/ts/graphs/CalendarGraph.svelte index c11606c09..307bf1f5b 100644 --- a/ts/graphs/CalendarGraph.svelte +++ b/ts/graphs/CalendarGraph.svelte @@ -8,10 +8,12 @@ import type { I18n } from "anki/i18n"; export let sourceData: pb.BackendProto.GraphsOut | null = null; + export let preferences: pb.BackendProto.GraphsPreferencesOut | null = null; export let revlogRange: RevlogRange; export let i18n: I18n; export let nightMode: boolean; + let { calendarFirstDayOfWeek } = preferences; let graphData: GraphData | null = null; let bounds = defaultGraphBounds(); @@ -25,7 +27,7 @@ let targetYear = maxYear; $: if (sourceData) { - graphData = gatherData(sourceData); + graphData = gatherData(sourceData, $calendarFirstDayOfWeek); } $: { diff --git a/ts/graphs/CardCounts.svelte b/ts/graphs/CardCounts.svelte index cab4908ea..7835f7e10 100644 --- a/ts/graphs/CardCounts.svelte +++ b/ts/graphs/CardCounts.svelte @@ -5,7 +5,6 @@ import type { PreferenceStore } from "./preferences"; import type pb from "anki/backend_proto"; import type { I18n } from "anki/i18n"; - import SeparateInactiveCheckbox from "./SeparateInactiveCheckbox.svelte"; export let sourceData: pb.BackendProto.GraphsOut; export let i18n: I18n; @@ -26,6 +25,7 @@ tableData = renderCards(svg as any, bounds, graphData); } + const label = i18n.tr(i18n.TR.STATISTICS_COUNTS_SEPARATE_SUSPENDED_BURIED_CARDS); const total = i18n.tr(i18n.TR.STATISTICS_COUNTS_TOTAL_CARDS); @@ -58,7 +58,10 @@

{graphData.title}

- +
diff --git a/ts/graphs/SeparateInactiveCheckbox.svelte b/ts/graphs/SeparateInactiveCheckbox.svelte deleted file mode 100644 index 51ef6529f..000000000 --- a/ts/graphs/SeparateInactiveCheckbox.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index 49069b320..b01ede2d1 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -6,7 +6,7 @@ @typescript-eslint/no-explicit-any: "off", */ -import type pb from "anki/backend_proto"; +import pb from "anki/backend_proto"; import { interpolateBlues } from "d3-scale-chromatic"; import "d3-transition"; import { select, mouse } from "d3-selection"; @@ -41,7 +41,13 @@ interface DayDatum { date: Date; } -export function gatherData(data: pb.BackendProto.GraphsOut): GraphData { +type WeekdayType = pb.BackendProto.GraphsPreferencesOut.Weekday; +const Weekday = pb.BackendProto.GraphsPreferencesOut.Weekday; /* enum */ + +export function gatherData( + data: pb.BackendProto.GraphsOut, + firstDayOfWeek: WeekdayType +): GraphData { const reviewCount = new Map(); for (const review of data.revlog as pb.BackendProto.RevlogEntry[]) { @@ -56,17 +62,17 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData { } const timeFunction = - data.firstWeekday === 1 + firstDayOfWeek === Weekday.MONDAY ? timeMonday - : data.firstWeekday === 5 + : data.firstWeekday === Weekday.FRIDAY ? timeFriday - : data.firstWeekday === 6 + : data.firstWeekday === Weekday.SATURDAY ? timeSaturday : timeSunday; const weekdayLabels: number[] = []; for (let i = 0; i < 7; i++) { - weekdayLabels.push((data.firstWeekday + i) % 7); + weekdayLabels.push((firstDayOfWeek + i) % 7); } return { reviewCount, timeFunction, weekdayLabels }; diff --git a/ts/graphs/graph-helpers.ts b/ts/graphs/graph-helpers.ts index 456273c1b..fe54cfdc7 100644 --- a/ts/graphs/graph-helpers.ts +++ b/ts/graphs/graph-helpers.ts @@ -25,10 +25,15 @@ export async function getGraphPreferences(): Promise { +export async function setGraphPreferences( + prefs: pb.BackendProto.GraphsPreferencesOut +): Promise { return (async () => { - await postRequest("/_anki/setGraphPreferences", pb.BackendProto.GraphsPreferencesOut.encode(prefs).finish()) - })() + await postRequest( + "/_anki/setGraphPreferences", + pb.BackendProto.GraphsPreferencesOut.encode(prefs).finish() + ); + })(); } // amount of data to fetch from backend diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts index 8906cced5..8b64a4b06 100644 --- a/ts/graphs/preferences.ts +++ b/ts/graphs/preferences.ts @@ -2,7 +2,6 @@ import type pb from "anki/backend_proto"; import { getGraphPreferences, setGraphPreferences } from "./graph-helpers"; import { writable, get } from "svelte/store"; - export interface CustomStore { subscribe: (getter: (value: T) => void) => () => void; set: (value: T) => void; @@ -29,7 +28,9 @@ function createPreference( }; } -function preparePreferences(graphsPreferences: pb.BackendProto.GraphsPreferencesOut): PreferenceStore { +function preparePreferences( + graphsPreferences: pb.BackendProto.GraphsPreferencesOut +): PreferenceStore { const preferences: Partial = {}; function constructPreferences(): pb.BackendProto.GraphsPreferencesOut { @@ -53,5 +54,5 @@ function preparePreferences(graphsPreferences: pb.BackendProto.GraphsPreferences export async function getPreferences() { const initialPreferences = await getGraphPreferences(); - return preparePreferences(initialPreferences) + return preparePreferences(initialPreferences); } From aebaa046522df80e20fd411f1b0172eafeb2282e Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 16:53:33 +0100 Subject: [PATCH 08/16] Uniformly rename firstWeekday to firstDayOfWeek --- rslib/backend.proto | 7 ------- rslib/src/config.rs | 12 ++++++++---- rslib/src/stats/graphs.rs | 12 ++++++++---- ts/graphs/calendar.ts | 4 ++-- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/rslib/backend.proto b/rslib/backend.proto index 2e6a9a0a3..4f31670b2 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -1084,12 +1084,6 @@ message GraphsIn { } message GraphsOut { - enum Weekday { - SUNDAY = 0; - MONDAY = 1; - FRIDAY = 5; - SATURDAY = 6; - } repeated Card cards = 1; repeated RevlogEntry revlog = 2; uint32 days_elapsed = 3; @@ -1098,7 +1092,6 @@ message GraphsOut { uint32 scheduler_version = 5; /// Seconds to add to UTC timestamps to get local time. int32 local_offset_secs = 7; - Weekday first_weekday = 8; } message GraphsPreferencesOut { diff --git a/rslib/src/config.rs b/rslib/src/config.rs index 0495015dd..eb2b88b1a 100644 --- a/rslib/src/config.rs +++ b/rslib/src/config.rs @@ -47,7 +47,7 @@ pub(crate) enum ConfigKey { ShowRemainingDueCountsInStudy, ShowIntervalsAboveAnswerButtons, NewReviewMix, - FirstWeekday, + FirstDayOfWeek, AnswerTimeLimitSecs, ShowDayLearningCardsFirst, LastUnburiedDay, @@ -76,7 +76,7 @@ impl From for &'static str { ConfigKey::ShowRemainingDueCountsInStudy => "dueCounts", ConfigKey::ShowIntervalsAboveAnswerButtons => "estTimes", ConfigKey::NewReviewMix => "newSpread", - ConfigKey::FirstWeekday => "firstWeekday", + ConfigKey::FirstDayOfWeek => "firstDayOfWeek", ConfigKey::AnswerTimeLimitSecs => "timeLim", ConfigKey::ShowDayLearningCardsFirst => "dayLearnFirst", ConfigKey::LastUnburiedDay => "lastUnburied", @@ -229,11 +229,15 @@ impl Collection { self.set_config(ConfigKey::NewReviewMix, &(mix as u8)) } - pub(crate) fn get_first_weekday(&self) -> Weekday { - self.get_config_optional(ConfigKey::FirstWeekday) + pub(crate) fn get_first_day_of_week(&self) -> Weekday { + self.get_config_optional(ConfigKey::FirstDayOfWeek) .unwrap_or(Weekday::Sunday) } + pub(crate) fn set_first_day_of_week(&self, weekday: Weekday) -> Result<()> { + self.set_config(ConfigKey::FirstDayOfWeek, &weekday) + } + pub(crate) fn get_show_due_counts(&self) -> bool { self.get_config_optional(ConfigKey::ShowRemainingDueCountsInStudy) .unwrap_or(true) diff --git a/rslib/src/stats/graphs.rs b/rslib/src/stats/graphs.rs index eeddf68fd..dcfe4a2a3 100644 --- a/rslib/src/stats/graphs.rs +++ b/rslib/src/stats/graphs.rs @@ -1,7 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use crate::{backend_proto as pb, prelude::*, revlog::RevlogEntry, search::SortMode}; +use crate::{backend_proto as pb, prelude::*, revlog::RevlogEntry, search::SortMode, config::Weekday}; impl Collection { pub(crate) fn graph_data_for_search( @@ -42,19 +42,23 @@ impl Collection { next_day_at_secs: timing.next_day_at as u32, scheduler_version: self.sched_ver() as u32, local_offset_secs: local_offset_secs as i32, - first_weekday: self.get_first_weekday() as i32, }) } pub(crate) fn graphs_preferences(&self) -> Result { Ok(pb::GraphsPreferencesOut { - calendar_first_day_of_week: self.get_first_weekday() as i32, + calendar_first_day_of_week: self.get_first_day_of_week() as i32, card_counts_separate_inactive: true, }) } pub(crate) fn set_graphs_preferences(&self, prefs: pb::GraphsPreferencesOut) -> Result<()> { - // self.set_first_weekday(prefs.calendar_first_day_of_week); + self.set_first_day_of_week(match prefs.calendar_first_day_of_week { + 1 => Weekday::Monday, + 5 => Weekday::Friday, + 6 => Weekday::Saturday, + _ => Weekday::Sunday, + })?; Ok(()) } } diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index b01ede2d1..7fa24bdce 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -64,9 +64,9 @@ export function gatherData( const timeFunction = firstDayOfWeek === Weekday.MONDAY ? timeMonday - : data.firstWeekday === Weekday.FRIDAY + : firstDayOfWeek === Weekday.FRIDAY ? timeFriday - : data.firstWeekday === Weekday.SATURDAY + : firstDayOfWeek === Weekday.SATURDAY ? timeSaturday : timeSunday; From 806e52d6db86ab10bd8cbc594235bfaa91154bb5 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 17:51:15 +0100 Subject: [PATCH 09/16] Make first day of week settable through UI --- ts/graphs/CalendarGraph.svelte | 5 +++-- ts/graphs/calendar.ts | 16 +++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/ts/graphs/CalendarGraph.svelte b/ts/graphs/CalendarGraph.svelte index 307bf1f5b..6b43590da 100644 --- a/ts/graphs/CalendarGraph.svelte +++ b/ts/graphs/CalendarGraph.svelte @@ -51,8 +51,9 @@ targetYear, i18n, nightMode, - revlogRange - ); + revlogRange, + calendarFirstDayOfWeek.set, + ) } const title = i18n.tr(i18n.TR.STATISTICS_CALENDAR_TITLE); diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index 7fa24bdce..62957bcad 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -85,7 +85,8 @@ export function renderCalendar( targetYear: number, i18n: I18n, nightMode: boolean, - revlogRange: RevlogRange + revlogRange: RevlogRange, + setFirstDayOfWeek: (d: number) => void, ): void { const svg = select(svgElem); const now = new Date(); @@ -181,7 +182,10 @@ export function renderCalendar( .attr("text-anchor", "end") .attr("font-size", "small") .attr("font-family", "monospace") - .style("user-select", "none"); + .style("user-select", "none") + .on("click", null) + .filter((d: number) => [Weekday.SUNDAY, Weekday.MONDAY, Weekday.FRIDAY, Weekday.SATURDAY].includes(d)) + .on("click", setFirstDayOfWeek); svg.select("g.days") .selectAll("rect") @@ -199,11 +203,5 @@ export function renderCalendar( .on("mouseout", hideTooltip) .transition() .duration(800) - .attr("fill", (d) => { - if (d.count === 0) { - return emptyColour; - } else { - return blues(d.count)!; - } - }); + .attr("fill", (d) => d.count === 0 ? emptyColour : blues(d.count)!); } From 834f2de99bf0213cdf23f42ecba5ea63b2c353cb Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 18:03:58 +0100 Subject: [PATCH 10/16] Make cardCountsSeparateInactive settable --- rslib/src/config.rs | 11 +++++++++++ rslib/src/stats/graphs.rs | 7 +++++-- ts/graphs/CalendarGraph.svelte | 4 ++-- ts/graphs/calendar.ts | 10 +++++++--- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/rslib/src/config.rs b/rslib/src/config.rs index eb2b88b1a..c76147100 100644 --- a/rslib/src/config.rs +++ b/rslib/src/config.rs @@ -48,6 +48,7 @@ pub(crate) enum ConfigKey { ShowIntervalsAboveAnswerButtons, NewReviewMix, FirstDayOfWeek, + CardCountsSeparateInactive, AnswerTimeLimitSecs, ShowDayLearningCardsFirst, LastUnburiedDay, @@ -77,6 +78,7 @@ impl From for &'static str { ConfigKey::ShowIntervalsAboveAnswerButtons => "estTimes", ConfigKey::NewReviewMix => "newSpread", ConfigKey::FirstDayOfWeek => "firstDayOfWeek", + ConfigKey::CardCountsSeparateInactive => "cardCountsSeparateInactive", ConfigKey::AnswerTimeLimitSecs => "timeLim", ConfigKey::ShowDayLearningCardsFirst => "dayLearnFirst", ConfigKey::LastUnburiedDay => "lastUnburied", @@ -238,6 +240,15 @@ impl Collection { self.set_config(ConfigKey::FirstDayOfWeek, &weekday) } + pub(crate) fn get_card_counts_separate_inactive(&self) -> bool { + self.get_config_optional(ConfigKey::CardCountsSeparateInactive) + .unwrap_or(true) + } + + pub(crate) fn set_card_counts_separate_inactive(&self, separate: bool) -> Result<()> { + self.set_config(ConfigKey::CardCountsSeparateInactive, &separate) + } + pub(crate) fn get_show_due_counts(&self) -> bool { self.get_config_optional(ConfigKey::ShowRemainingDueCountsInStudy) .unwrap_or(true) diff --git a/rslib/src/stats/graphs.rs b/rslib/src/stats/graphs.rs index dcfe4a2a3..ed4a73eda 100644 --- a/rslib/src/stats/graphs.rs +++ b/rslib/src/stats/graphs.rs @@ -1,7 +1,9 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use crate::{backend_proto as pb, prelude::*, revlog::RevlogEntry, search::SortMode, config::Weekday}; +use crate::{ + backend_proto as pb, config::Weekday, prelude::*, revlog::RevlogEntry, search::SortMode, +}; impl Collection { pub(crate) fn graph_data_for_search( @@ -48,7 +50,7 @@ impl Collection { pub(crate) fn graphs_preferences(&self) -> Result { Ok(pb::GraphsPreferencesOut { calendar_first_day_of_week: self.get_first_day_of_week() as i32, - card_counts_separate_inactive: true, + card_counts_separate_inactive: self.get_card_counts_separate_inactive(), }) } @@ -59,6 +61,7 @@ impl Collection { 6 => Weekday::Saturday, _ => Weekday::Sunday, })?; + self.set_card_counts_separate_inactive(prefs.card_counts_separate_inactive)?; Ok(()) } } diff --git a/ts/graphs/CalendarGraph.svelte b/ts/graphs/CalendarGraph.svelte index 6b43590da..8e88cdf55 100644 --- a/ts/graphs/CalendarGraph.svelte +++ b/ts/graphs/CalendarGraph.svelte @@ -52,8 +52,8 @@ i18n, nightMode, revlogRange, - calendarFirstDayOfWeek.set, - ) + calendarFirstDayOfWeek.set + ); } const title = i18n.tr(i18n.TR.STATISTICS_CALENDAR_TITLE); diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index 62957bcad..142d56763 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -86,7 +86,7 @@ export function renderCalendar( i18n: I18n, nightMode: boolean, revlogRange: RevlogRange, - setFirstDayOfWeek: (d: number) => void, + setFirstDayOfWeek: (d: number) => void ): void { const svg = select(svgElem); const now = new Date(); @@ -184,7 +184,11 @@ export function renderCalendar( .attr("font-family", "monospace") .style("user-select", "none") .on("click", null) - .filter((d: number) => [Weekday.SUNDAY, Weekday.MONDAY, Weekday.FRIDAY, Weekday.SATURDAY].includes(d)) + .filter((d: number) => + [Weekday.SUNDAY, Weekday.MONDAY, Weekday.FRIDAY, Weekday.SATURDAY].includes( + d + ) + ) .on("click", setFirstDayOfWeek); svg.select("g.days") @@ -203,5 +207,5 @@ export function renderCalendar( .on("mouseout", hideTooltip) .transition() .duration(800) - .attr("fill", (d) => d.count === 0 ? emptyColour : blues(d.count)!); + .attr("fill", (d) => (d.count === 0 ? emptyColour : blues(d.count)!)); } From ebd3ca8a8f88bb53300b65dfe45ad0f6868cce89 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 18:09:57 +0100 Subject: [PATCH 11/16] Set calendar labels to emptyColour --- rslib/backend.proto | 1 - ts/graphs/calendar.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/rslib/backend.proto b/rslib/backend.proto index 4f31670b2..2af60ab6e 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -1103,7 +1103,6 @@ message GraphsPreferencesOut { } Weekday calendar_first_day_of_week = 1; bool card_counts_separate_inactive = 2; - } message RevlogEntry { diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index 142d56763..49d127bcc 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -178,6 +178,7 @@ export function renderCalendar( .attr("height", height - 2) .attr("x", x(1)! - 3) .attr("y", (_d, index) => bounds.marginTop + index * height) + .attr("fill", emptyColour) .attr("dominant-baseline", "hanging") .attr("text-anchor", "end") .attr("font-size", "small") From 17ebb69151b096f8cf460008e056f43257d73b83 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 19:02:05 +0100 Subject: [PATCH 12/16] Make code typecheck, fix issue with day labels in nightMode --- ts/graphs/CalendarGraph.svelte | 3 ++- ts/graphs/calendar.ts | 2 +- ts/graphs/graph-helpers.ts | 7 +++---- ts/graphs/preferences.ts | 37 +++++++++++++++++++++++----------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/ts/graphs/CalendarGraph.svelte b/ts/graphs/CalendarGraph.svelte index 8e88cdf55..0ad08d0b8 100644 --- a/ts/graphs/CalendarGraph.svelte +++ b/ts/graphs/CalendarGraph.svelte @@ -3,12 +3,13 @@ import AxisTicks from "./AxisTicks.svelte"; import { defaultGraphBounds, RevlogRange } from "./graph-helpers"; import { gatherData, renderCalendar } from "./calendar"; + import type { PreferenceStore } from "./preferences"; import type { GraphData } from "./calendar"; import type pb from "anki/backend_proto"; import type { I18n } from "anki/i18n"; export let sourceData: pb.BackendProto.GraphsOut | null = null; - export let preferences: pb.BackendProto.GraphsPreferencesOut | null = null; + export let preferences: PreferenceStore | null = null; export let revlogRange: RevlogRange; export let i18n: I18n; export let nightMode: boolean; diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index 49d127bcc..3d6553480 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -178,7 +178,7 @@ export function renderCalendar( .attr("height", height - 2) .attr("x", x(1)! - 3) .attr("y", (_d, index) => bounds.marginTop + index * height) - .attr("fill", emptyColour) + .attr("fill", nightMode ? "#ddd" : "black") .attr("dominant-baseline", "hanging") .attr("text-anchor", "end") .attr("font-size", "small") diff --git a/ts/graphs/graph-helpers.ts b/ts/graphs/graph-helpers.ts index fe54cfdc7..8047e45f0 100644 --- a/ts/graphs/graph-helpers.ts +++ b/ts/graphs/graph-helpers.ts @@ -8,6 +8,7 @@ import pb from "anki/backend_proto"; import type { Selection } from "d3-selection"; +import type { PreferencePayload } from "./preferences"; import { postRequest } from "anki/postrequest"; export async function getGraphData( @@ -25,10 +26,8 @@ export async function getGraphPreferences(): Promise { - return (async () => { +export async function setGraphPreferences(prefs: PreferencePayload): Promise { + return (async (): Promise => { await postRequest( "/_anki/setGraphPreferences", pb.BackendProto.GraphsPreferencesOut.encode(prefs).finish() diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts index 8b64a4b06..9a235b2b8 100644 --- a/ts/graphs/preferences.ts +++ b/ts/graphs/preferences.ts @@ -1,28 +1,39 @@ import type pb from "anki/backend_proto"; import { getGraphPreferences, setGraphPreferences } from "./graph-helpers"; -import { writable, get } from "svelte/store"; +import { Writable, writable, get } from "svelte/store"; -export interface CustomStore { +export interface CustomStore extends Writable { subscribe: (getter: (value: T) => void) => () => void; set: (value: T) => void; } export type PreferenceStore = { - [K in keyof pb.BackendProto.GraphsPreferencesOut]: CustomStore< + [K in keyof Omit]: CustomStore< pb.BackendProto.GraphsPreferencesOut[K] >; }; +export type PreferencePayload = { + [K in keyof Omit< + pb.BackendProto.GraphsPreferencesOut, + "toJSON" + >]: pb.BackendProto.GraphsPreferencesOut[K]; +}; + function createPreference( initialValue: T, savePreferences: () => void ): CustomStore { - const { subscribe, set } = writable(initialValue); + const { subscribe, set, update } = writable(initialValue); return { subscribe, - set: (v: T): void => { - set(v); + set: (value: T): void => { + set(value); + savePreferences(); + }, + update: (updater: (value: T) => T): void => { + update(updater); savePreferences(); }, }; @@ -33,12 +44,14 @@ function preparePreferences( ): PreferenceStore { const preferences: Partial = {}; - function constructPreferences(): pb.BackendProto.GraphsPreferencesOut { - const payload: Partial = {}; - for (const [key, pref] of Object.entries(preferences as PreferenceStore)) { - payload[key] = get(pref as any); + function constructPreferences(): PreferencePayload { + const payload: Partial = {}; + + for (const key in preferences as PreferenceStore) { + payload[key] = get(preferences[key]); } - return payload as pb.BackendProto.GraphsPreferencesOut; + + return payload as PreferencePayload; } function savePreferences(): void { @@ -52,7 +65,7 @@ function preparePreferences( return preferences as PreferenceStore; } -export async function getPreferences() { +export async function getPreferences(): Promise { const initialPreferences = await getGraphPreferences(); return preparePreferences(initialPreferences); } From de71123ab484ed12a6dd6c17f5484b61b8b17c67 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 19:22:36 +0100 Subject: [PATCH 13/16] Rename GraphsPreferencesOut to simply GraphsPreferences --- qt/aqt/mediasrv.py | 6 +++--- rslib/backend.proto | 6 +++--- rslib/src/backend/mod.rs | 6 +++--- rslib/src/stats/graphs.rs | 6 +++--- ts/graphs/calendar.ts | 4 ++-- ts/graphs/graph-helpers.ts | 6 +++--- ts/graphs/preferences.ts | 10 +++++----- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index e48ba9661..f91506a71 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -256,12 +256,12 @@ def graph_data() -> bytes: return aqt.mw.col.backend.graphs(search=args["search"], days=args["days"]) -def graph_preferences() -> bytes: - return aqt.mw.col.backend.graphs_preferences() +def graph_preferences() -> pb.GraphsPreferences: + return aqt.mw.col.backend.get_graphs_preferences() def set_graph_preferences() -> None: - input = pb.GraphsPreferencesOut() + input = pb.GraphsPreferences() input.ParseFromString(request.data) aqt.mw.col.backend.set_graphs_preferences(input=input) diff --git a/rslib/backend.proto b/rslib/backend.proto index 2af60ab6e..3f2e74f14 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -116,8 +116,8 @@ service BackendService { rpc CardStats(CardID) returns (String); rpc Graphs(GraphsIn) returns (GraphsOut); - rpc GraphsPreferences(Empty) returns (GraphsPreferencesOut); - rpc SetGraphsPreferences(GraphsPreferencesOut) returns (Empty); + rpc GetGraphsPreferences(Empty) returns (GraphsPreferences); + rpc SetGraphsPreferences(GraphsPreferences) returns (Empty); // media @@ -1094,7 +1094,7 @@ message GraphsOut { int32 local_offset_secs = 7; } -message GraphsPreferencesOut { +message GraphsPreferences { enum Weekday { SUNDAY = 0; MONDAY = 1; diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 3444f8358..d502f9f8b 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -676,11 +676,11 @@ impl BackendService for Backend { self.with_col(|col| col.graph_data_for_search(&input.search, input.days)) } - fn graphs_preferences(&self, _input: pb::Empty) -> BackendResult { - self.with_col(|col| col.graphs_preferences()) + fn get_graphs_preferences(&self, _input: pb::Empty) -> BackendResult { + self.with_col(|col| col.get_graphs_preferences()) } - fn set_graphs_preferences(&self, input: pb::GraphsPreferencesOut) -> BackendResult { + fn set_graphs_preferences(&self, input: pb::GraphsPreferences) -> BackendResult { self.with_col(|col| col.set_graphs_preferences(input)) .map(Into::into) } diff --git a/rslib/src/stats/graphs.rs b/rslib/src/stats/graphs.rs index ed4a73eda..bf4c6d62b 100644 --- a/rslib/src/stats/graphs.rs +++ b/rslib/src/stats/graphs.rs @@ -47,14 +47,14 @@ impl Collection { }) } - pub(crate) fn graphs_preferences(&self) -> Result { - Ok(pb::GraphsPreferencesOut { + pub(crate) fn get_graphs_preferences(&self) -> Result { + Ok(pb::GraphsPreferences { calendar_first_day_of_week: self.get_first_day_of_week() as i32, card_counts_separate_inactive: self.get_card_counts_separate_inactive(), }) } - pub(crate) fn set_graphs_preferences(&self, prefs: pb::GraphsPreferencesOut) -> Result<()> { + pub(crate) fn set_graphs_preferences(&self, prefs: pb::GraphsPreferences) -> Result<()> { self.set_first_day_of_week(match prefs.calendar_first_day_of_week { 1 => Weekday::Monday, 5 => Weekday::Friday, diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index 3d6553480..5f080fdab 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -41,8 +41,8 @@ interface DayDatum { date: Date; } -type WeekdayType = pb.BackendProto.GraphsPreferencesOut.Weekday; -const Weekday = pb.BackendProto.GraphsPreferencesOut.Weekday; /* enum */ +type WeekdayType = pb.BackendProto.GraphsPreferences.Weekday; +const Weekday = pb.BackendProto.GraphsPreferences.Weekday; /* enum */ export function gatherData( data: pb.BackendProto.GraphsOut, diff --git a/ts/graphs/graph-helpers.ts b/ts/graphs/graph-helpers.ts index 8047e45f0..5e9fd1e8a 100644 --- a/ts/graphs/graph-helpers.ts +++ b/ts/graphs/graph-helpers.ts @@ -20,8 +20,8 @@ export async function getGraphData( ); } -export async function getGraphPreferences(): Promise { - return pb.BackendProto.GraphsPreferencesOut.decode( +export async function getGraphPreferences(): Promise { + return pb.BackendProto.GraphsPreferences.decode( await postRequest("/_anki/graphPreferences", JSON.stringify({})) ); } @@ -30,7 +30,7 @@ export async function setGraphPreferences(prefs: PreferencePayload): Promise => { await postRequest( "/_anki/setGraphPreferences", - pb.BackendProto.GraphsPreferencesOut.encode(prefs).finish() + pb.BackendProto.GraphsPreferences.encode(prefs).finish() ); })(); } diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts index 9a235b2b8..affa77c99 100644 --- a/ts/graphs/preferences.ts +++ b/ts/graphs/preferences.ts @@ -8,16 +8,16 @@ export interface CustomStore extends Writable { } export type PreferenceStore = { - [K in keyof Omit]: CustomStore< - pb.BackendProto.GraphsPreferencesOut[K] + [K in keyof Omit]: CustomStore< + pb.BackendProto.GraphsPreferences[K] >; }; export type PreferencePayload = { [K in keyof Omit< - pb.BackendProto.GraphsPreferencesOut, + pb.BackendProto.GraphsPreferences, "toJSON" - >]: pb.BackendProto.GraphsPreferencesOut[K]; + >]: pb.BackendProto.GraphsPreferences[K]; }; function createPreference( @@ -40,7 +40,7 @@ function createPreference( } function preparePreferences( - graphsPreferences: pb.BackendProto.GraphsPreferencesOut + graphsPreferences: pb.BackendProto.GraphsPreferences ): PreferenceStore { const preferences: Partial = {}; From bf130d1da0bda9164e7398423c089da447d0844b Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 22 Jan 2021 20:05:28 +0100 Subject: [PATCH 14/16] Change the function name in genbackend.py --- pylib/tools/genbackend.py | 2 +- qt/aqt/mediasrv.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pylib/tools/genbackend.py b/pylib/tools/genbackend.py index e2ad3ced2..4a2c75e06 100755 --- a/pylib/tools/genbackend.py +++ b/pylib/tools/genbackend.py @@ -34,7 +34,7 @@ LABEL_REPEATED = 3 # messages we don't want to unroll in codegen SKIP_UNROLL_INPUT = {"TranslateString"} -SKIP_DECODE = {"Graphs", "GraphsPreferences"} +SKIP_DECODE = {"Graphs", "GetGraphsPreferences"} def python_type(field): diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index f91506a71..94082814b 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -256,7 +256,7 @@ def graph_data() -> bytes: return aqt.mw.col.backend.graphs(search=args["search"], days=args["days"]) -def graph_preferences() -> pb.GraphsPreferences: +def graph_preferences() -> bytes: return aqt.mw.col.backend.get_graphs_preferences() From 6f798930a21fdd6849afb1f76f7b263eb7cfc34d Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 23 Jan 2021 20:40:43 +1000 Subject: [PATCH 15/16] fix graphs failing to load until preferences set The protobuf object will be missing keys that have the default value, so we need to fill the defaults in. --- ts/graphs/preferences.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts index affa77c99..7da866527 100644 --- a/ts/graphs/preferences.ts +++ b/ts/graphs/preferences.ts @@ -1,4 +1,4 @@ -import type pb from "anki/backend_proto"; +import pb from "anki/backend_proto"; import { getGraphPreferences, setGraphPreferences } from "./graph-helpers"; import { Writable, writable, get } from "svelte/store"; @@ -58,7 +58,11 @@ function preparePreferences( setGraphPreferences(constructPreferences()); } - for (const [key, value] of Object.entries(graphsPreferences)) { + for (const [key, value] of Object.entries( + pb.BackendProto.GraphsPreferences.toObject(graphsPreferences, { + defaults: true, + }) + )) { preferences[key] = createPreference(value, savePreferences); } From 37ca8afaf6b1c1ba5385e860cf0bdbf3b0f503b5 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 23 Jan 2021 20:47:45 +1000 Subject: [PATCH 16/16] minor wording tweak: GraphsPreferences -> GraphPreferences --- pylib/tools/genbackend.py | 2 +- qt/aqt/mediasrv.py | 6 +++--- rslib/backend.proto | 6 +++--- rslib/src/backend/mod.rs | 8 ++++---- rslib/src/stats/graphs.rs | 6 +++--- ts/graphs/calendar.ts | 4 ++-- ts/graphs/graph-helpers.ts | 6 +++--- ts/graphs/preferences.ts | 12 ++++++------ 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pylib/tools/genbackend.py b/pylib/tools/genbackend.py index 4a2c75e06..3074bde63 100755 --- a/pylib/tools/genbackend.py +++ b/pylib/tools/genbackend.py @@ -34,7 +34,7 @@ LABEL_REPEATED = 3 # messages we don't want to unroll in codegen SKIP_UNROLL_INPUT = {"TranslateString"} -SKIP_DECODE = {"Graphs", "GetGraphsPreferences"} +SKIP_DECODE = {"Graphs", "GetGraphPreferences"} def python_type(field): diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index 94082814b..5482b849f 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -257,13 +257,13 @@ def graph_data() -> bytes: def graph_preferences() -> bytes: - return aqt.mw.col.backend.get_graphs_preferences() + return aqt.mw.col.backend.get_graph_preferences() def set_graph_preferences() -> None: - input = pb.GraphsPreferences() + input = pb.GraphPreferences() input.ParseFromString(request.data) - aqt.mw.col.backend.set_graphs_preferences(input=input) + aqt.mw.col.backend.set_graph_preferences(input=input) def congrats_info() -> bytes: diff --git a/rslib/backend.proto b/rslib/backend.proto index 3f2e74f14..56d38dd96 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -116,8 +116,8 @@ service BackendService { rpc CardStats(CardID) returns (String); rpc Graphs(GraphsIn) returns (GraphsOut); - rpc GetGraphsPreferences(Empty) returns (GraphsPreferences); - rpc SetGraphsPreferences(GraphsPreferences) returns (Empty); + rpc GetGraphPreferences(Empty) returns (GraphPreferences); + rpc SetGraphPreferences(GraphPreferences) returns (Empty); // media @@ -1094,7 +1094,7 @@ message GraphsOut { int32 local_offset_secs = 7; } -message GraphsPreferences { +message GraphPreferences { enum Weekday { SUNDAY = 0; MONDAY = 1; diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index d502f9f8b..71e1595de 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -676,12 +676,12 @@ impl BackendService for Backend { self.with_col(|col| col.graph_data_for_search(&input.search, input.days)) } - fn get_graphs_preferences(&self, _input: pb::Empty) -> BackendResult { - self.with_col(|col| col.get_graphs_preferences()) + fn get_graph_preferences(&self, _input: pb::Empty) -> BackendResult { + self.with_col(|col| col.get_graph_preferences()) } - fn set_graphs_preferences(&self, input: pb::GraphsPreferences) -> BackendResult { - self.with_col(|col| col.set_graphs_preferences(input)) + fn set_graph_preferences(&self, input: pb::GraphPreferences) -> BackendResult { + self.with_col(|col| col.set_graph_preferences(input)) .map(Into::into) } diff --git a/rslib/src/stats/graphs.rs b/rslib/src/stats/graphs.rs index bf4c6d62b..093aecb73 100644 --- a/rslib/src/stats/graphs.rs +++ b/rslib/src/stats/graphs.rs @@ -47,14 +47,14 @@ impl Collection { }) } - pub(crate) fn get_graphs_preferences(&self) -> Result { - Ok(pb::GraphsPreferences { + pub(crate) fn get_graph_preferences(&self) -> Result { + Ok(pb::GraphPreferences { calendar_first_day_of_week: self.get_first_day_of_week() as i32, card_counts_separate_inactive: self.get_card_counts_separate_inactive(), }) } - pub(crate) fn set_graphs_preferences(&self, prefs: pb::GraphsPreferences) -> Result<()> { + pub(crate) fn set_graph_preferences(&self, prefs: pb::GraphPreferences) -> Result<()> { self.set_first_day_of_week(match prefs.calendar_first_day_of_week { 1 => Weekday::Monday, 5 => Weekday::Friday, diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index 5f080fdab..b933ca411 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -41,8 +41,8 @@ interface DayDatum { date: Date; } -type WeekdayType = pb.BackendProto.GraphsPreferences.Weekday; -const Weekday = pb.BackendProto.GraphsPreferences.Weekday; /* enum */ +type WeekdayType = pb.BackendProto.GraphPreferences.Weekday; +const Weekday = pb.BackendProto.GraphPreferences.Weekday; /* enum */ export function gatherData( data: pb.BackendProto.GraphsOut, diff --git a/ts/graphs/graph-helpers.ts b/ts/graphs/graph-helpers.ts index 5e9fd1e8a..44934827f 100644 --- a/ts/graphs/graph-helpers.ts +++ b/ts/graphs/graph-helpers.ts @@ -20,8 +20,8 @@ export async function getGraphData( ); } -export async function getGraphPreferences(): Promise { - return pb.BackendProto.GraphsPreferences.decode( +export async function getGraphPreferences(): Promise { + return pb.BackendProto.GraphPreferences.decode( await postRequest("/_anki/graphPreferences", JSON.stringify({})) ); } @@ -30,7 +30,7 @@ export async function setGraphPreferences(prefs: PreferencePayload): Promise => { await postRequest( "/_anki/setGraphPreferences", - pb.BackendProto.GraphsPreferences.encode(prefs).finish() + pb.BackendProto.GraphPreferences.encode(prefs).finish() ); })(); } diff --git a/ts/graphs/preferences.ts b/ts/graphs/preferences.ts index 7da866527..5dad878a2 100644 --- a/ts/graphs/preferences.ts +++ b/ts/graphs/preferences.ts @@ -8,16 +8,16 @@ export interface CustomStore extends Writable { } export type PreferenceStore = { - [K in keyof Omit]: CustomStore< - pb.BackendProto.GraphsPreferences[K] + [K in keyof Omit]: CustomStore< + pb.BackendProto.GraphPreferences[K] >; }; export type PreferencePayload = { [K in keyof Omit< - pb.BackendProto.GraphsPreferences, + pb.BackendProto.GraphPreferences, "toJSON" - >]: pb.BackendProto.GraphsPreferences[K]; + >]: pb.BackendProto.GraphPreferences[K]; }; function createPreference( @@ -40,7 +40,7 @@ function createPreference( } function preparePreferences( - graphsPreferences: pb.BackendProto.GraphsPreferences + GraphPreferences: pb.BackendProto.GraphPreferences ): PreferenceStore { const preferences: Partial = {}; @@ -59,7 +59,7 @@ function preparePreferences( } for (const [key, value] of Object.entries( - pb.BackendProto.GraphsPreferences.toObject(graphsPreferences, { + pb.BackendProto.GraphPreferences.toObject(GraphPreferences, { defaults: true, }) )) {