mirror of
https://github.com/ankitects/anki.git
synced 2025-11-06 12:47:11 -05:00
Re-implement answer buttons
This commit is contained in:
parent
85ebdefec0
commit
953e6c9360
7 changed files with 57 additions and 27 deletions
|
|
@ -291,12 +291,18 @@ message NextCardDataRequest {
|
|||
}
|
||||
|
||||
message NextCardDataResponse {
|
||||
message AnswerButton {
|
||||
CardAnswer.Rating rating = 1;
|
||||
string due = 2;
|
||||
}
|
||||
|
||||
message NextCardData {
|
||||
int64 card_id = 1;
|
||||
string front = 2;
|
||||
string back = 3;
|
||||
|
||||
SchedulingStates states = 4;
|
||||
repeated AnswerButton answer_buttons = 5;
|
||||
}
|
||||
|
||||
optional NextCardData next_card = 1;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ mod states;
|
|||
use anki_proto::cards;
|
||||
use anki_proto::generic;
|
||||
use anki_proto::scheduler;
|
||||
use anki_proto::scheduler::next_card_data_response::AnswerButton;
|
||||
use anki_proto::scheduler::next_card_data_response::NextCardData;
|
||||
use anki_proto::scheduler::ComputeFsrsParamsResponse;
|
||||
use anki_proto::scheduler::ComputeMemoryStateResponse;
|
||||
|
|
@ -398,6 +399,16 @@ impl crate::services::SchedulerService for Collection {
|
|||
let render = self.render_existing_card(cid, false, false)?;
|
||||
let style = format!("<style>{}</style>", render.css);
|
||||
|
||||
let answer_buttons = self
|
||||
.describe_next_states(&next_card.states)?
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, due)| AnswerButton {
|
||||
rating: i as i32,
|
||||
due,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(NextCardDataResponse {
|
||||
next_card: Some(NextCardData {
|
||||
card_id: cid.0,
|
||||
|
|
@ -405,6 +416,7 @@ impl crate::services::SchedulerService for Collection {
|
|||
back: [style, render.answer().to_string()].concat(),
|
||||
|
||||
states: Some(next_card.states.clone().into()),
|
||||
answer_buttons,
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import ReviewerBottom from "./reviewer-bottom/ReviewerBottom.svelte";
|
||||
import Reviewer from "./Reviewer.svelte";
|
||||
|
||||
let state = new ReviewerState
|
||||
const state = new ReviewerState();
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
<script lang="ts">
|
||||
import type { ReviewerState } from "./reviewer";
|
||||
|
||||
|
||||
let iframe: HTMLIFrameElement;
|
||||
export let state: ReviewerState
|
||||
|
||||
$: if (iframe) state.registerIFrame(iframe)
|
||||
export let state: ReviewerState;
|
||||
|
||||
$: if (iframe) {
|
||||
state.registerIFrame(iframe);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="qa">
|
||||
|
|
|
|||
|
|
@ -3,10 +3,16 @@ Copyright: Ankitects Pty Ltd and contributors
|
|||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { bridgeCommand } from "@tslib/bridgecommand";
|
||||
import type { AnswerButtonInfo } from "./types";
|
||||
import type { NextCardDataResponse_AnswerButton } from "@generated/anki/scheduler_pb";
|
||||
import * as tr from "@generated/ftl";
|
||||
import type { ReviewerState } from "../reviewer";
|
||||
|
||||
export let info: NextCardDataResponse_AnswerButton;
|
||||
export let state: ReviewerState;
|
||||
|
||||
const labels = [tr.studyingAgain(), tr.studyingHard(), tr.studyingGood(), tr.studyingEasy()]
|
||||
$: label = labels[info.rating];
|
||||
|
||||
export let info: AnswerButtonInfo;
|
||||
</script>
|
||||
|
||||
<span>
|
||||
|
|
@ -16,8 +22,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
{/if}
|
||||
</span>
|
||||
<button on:click={() => bridgeCommand(`ease${info.i}`)}>
|
||||
{info.label}
|
||||
<button on:click={() => state.easeButtonPressed(info.rating)}>
|
||||
{label}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
|
|||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import "./index.scss";
|
||||
|
||||
import AnswerButton from "./AnswerButton.svelte";
|
||||
import { bridgeCommand } from "@tslib/bridgecommand";
|
||||
import * as tr from "@generated/ftl";
|
||||
|
|
@ -10,18 +12,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import type { ReviewerState } from "../reviewer";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export let state: ReviewerState
|
||||
export let state: ReviewerState;
|
||||
|
||||
const answerButtons = state.answerButtons;
|
||||
const answerShown = state.answerShown;
|
||||
// Placeholders
|
||||
let answerButtons = writable([]);
|
||||
let remaining = writable([0, 0, 0]);
|
||||
let remainingIndex = writable(0);
|
||||
const remaining = writable([0, 0, 0]);
|
||||
const remainingIndex = writable(0);
|
||||
|
||||
$: button_count = $answerShown ? $answerButtons.length : 1;
|
||||
|
||||
$: answerShown = $answerButtons.length;
|
||||
</script>
|
||||
|
||||
<div id="outer" class="fancy">
|
||||
<div id="tableinner" style="--answer-button-count: {$answerButtons.length || 1}">
|
||||
<div id="tableinner" style="--answer-button-count: {button_count}">
|
||||
<span class="disappearing"></span>
|
||||
<div class="disappearing edit">
|
||||
<button
|
||||
|
|
@ -31,9 +35,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
{tr.studyingEdit()}
|
||||
</button>
|
||||
</div>
|
||||
{#if answerShown}
|
||||
{#if $answerShown}
|
||||
{#each $answerButtons as answerButton}
|
||||
<AnswerButton info={answerButton}></AnswerButton>
|
||||
<AnswerButton {state} info={answerButton}></AnswerButton>
|
||||
{/each}
|
||||
{:else}
|
||||
<span class="remaining-count">
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
import {
|
||||
CardAnswer,
|
||||
type NextCardDataResponse_NextCardData,
|
||||
} from "@generated/anki/scheduler_pb";
|
||||
import { CardAnswer, NextCardDataResponse_AnswerButton, type NextCardDataResponse_NextCardData } from "@generated/anki/scheduler_pb";
|
||||
import { nextCardData } from "@generated/backend";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export class ReviewerState {
|
||||
answerHtml: string = ""
|
||||
answerHtml = "";
|
||||
cardData: NextCardDataResponse_NextCardData | undefined = undefined;
|
||||
beginAnsweringMs = Date.now();
|
||||
readonly cardClass = writable("");
|
||||
readonly answerButtons = writable<NextCardDataResponse_AnswerButton[]>([]);
|
||||
readonly answerShown = writable(false)
|
||||
|
||||
iframe: HTMLIFrameElement | undefined = undefined;
|
||||
|
||||
|
|
@ -34,19 +33,22 @@ export class ReviewerState {
|
|||
answer: answer || undefined,
|
||||
});
|
||||
// TODO: "Congratulation screen" logic
|
||||
this.cardData = resp.nextCard
|
||||
this.cardData = resp.nextCard;
|
||||
this.answerButtons.set(this.cardData?.answerButtons ?? []);
|
||||
const question = resp.nextCard?.front || "";
|
||||
this.answerShown.set(false);
|
||||
this.updateHtml(question);
|
||||
}
|
||||
|
||||
public showAnswer() {
|
||||
this.answerShown.set(true);
|
||||
this.updateHtml(this.cardData?.back || "");
|
||||
}
|
||||
|
||||
public easeButtonPressed(rating: number) {
|
||||
const states = this.cardData!.states!;
|
||||
|
||||
let newState = ({
|
||||
const newState = ({
|
||||
[1]: states.again!,
|
||||
[2]: states.hard!,
|
||||
[3]: states.good!,
|
||||
|
|
|
|||
Loading…
Reference in a new issue