Merge pull request #28 from fancy-flashcard/limit-learning-session
Limit learning session
This commit is contained in:
commit
49e6778e17
15 changed files with 256 additions and 39 deletions
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "ffc",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"author": "Niko Lockenvitz & Rene-Pascal Fischer",
|
||||
"license": "MIT",
|
||||
"license": "GNU GPLv2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
|
|
16
src/App.vue
16
src/App.vue
|
@ -10,7 +10,11 @@
|
|||
:decks="decks"
|
||||
:numberOfSelectedDecks="numberOfSelectedDecks"
|
||||
></NavigationBar>
|
||||
<router-view :decks="decks" :numberOfSelectedDecks="numberOfSelectedDecks" />
|
||||
<router-view
|
||||
:decks="decks"
|
||||
:numberOfSelectedDecks="numberOfSelectedDecks"
|
||||
:cardLimit="cardLimit"
|
||||
/>
|
||||
<v-snackbar app v-model="snackbar.snackbar" :timeout="snackbar.timeout">
|
||||
{{ snackbar.text }}
|
||||
<template v-slot:action="{ attrs }">
|
||||
|
@ -60,8 +64,10 @@ const AppProps = Vue.extend({
|
|||
})
|
||||
export default class App extends AppProps {
|
||||
propertiesToSyncWithLocalStorage = [
|
||||
{ key: "decks", defaultValue: [] }
|
||||
{ key: "decks", defaultValue: [] },
|
||||
{ key: "cardLimit", defaultValue: "" }
|
||||
] as SyncItem[];
|
||||
cardLimit = "";
|
||||
decks = [] as Deck[];
|
||||
snackbar = {
|
||||
text: "",
|
||||
|
@ -96,7 +102,11 @@ export default class App extends AppProps {
|
|||
mounted() {
|
||||
readFromLocalStorage(this);
|
||||
this.setSelectedStatusForAllDecks(false);
|
||||
continueCurrentLearningSessionIfPresent(this.$eventHub, this.$router, this.decks);
|
||||
continueCurrentLearningSessionIfPresent(
|
||||
this.$eventHub,
|
||||
this.$router,
|
||||
this.decks
|
||||
);
|
||||
}
|
||||
|
||||
get numberOfSelectedDecks() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="settings">
|
||||
<div>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<ThirdPartyDeckCard />
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<v-list-item-title v-text="deck.name"></v-list-item-title>
|
||||
</v-list-item-content>
|
||||
<v-list-item-icon
|
||||
v-bind:class="{ hidden: numberOfSelectedDecks===0, visible: numberOfSelectedDecks>0 }"
|
||||
:class="{ hidden: numberOfSelectedDecks===0, visible: numberOfSelectedDecks>0 }"
|
||||
>
|
||||
<v-icon v-if="deck.selected">mdi-check-box-outline</v-icon>
|
||||
<v-icon v-else>mdi-checkbox-blank-outline</v-icon>
|
||||
|
|
|
@ -50,7 +50,8 @@ import {
|
|||
const LearnProps = Vue.extend({
|
||||
props: {
|
||||
decks: { type: Array as () => Deck[] },
|
||||
numberOfSelectedDecks: Number
|
||||
numberOfSelectedDecks: Number,
|
||||
cardLimit: String
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -136,11 +137,15 @@ export default class Learn extends LearnProps {
|
|||
}
|
||||
|
||||
checkIfCardIsEndOfSession(): boolean {
|
||||
return (
|
||||
this.learningSessionManager.learningSession.currentElementIndex ===
|
||||
const endOfSession =
|
||||
(this.learningSessionManager.learningSession.currentElementIndex ===
|
||||
this.learningSessionManager.learningSession.elements.length - 1 &&
|
||||
this.learningSessionManager.cardsToSelectFrom.length === 0
|
||||
);
|
||||
this.learningSessionManager.cardsToSelectFrom.length === 0) ||
|
||||
(this.cardLimit === "0"
|
||||
? false
|
||||
: this.learningSessionManager.learningSession.currentElementIndex ===
|
||||
parseInt(this.cardLimit) - 1);
|
||||
return endOfSession;
|
||||
}
|
||||
|
||||
moveToNext() {
|
||||
|
|
97
src/components/settings/CardLimit.vue
Normal file
97
src/components/settings/CardLimit.vue
Normal file
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<v-col cols="12" sm="12" md="6" lg="6" xl="6">
|
||||
<v-card height="100%" raised>
|
||||
<v-card-title>Card Limit</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="paragraph">
|
||||
Limit the number of cards that will be in each learning session.
|
||||
<v-icon
|
||||
size="1em"
|
||||
@click="showHelpText = !showHelpText"
|
||||
class="mx-1"
|
||||
>mdi-help-circle-outline</v-icon>
|
||||
</p>
|
||||
<p
|
||||
class="description"
|
||||
v-show="showHelpText"
|
||||
>If the given deck(s) has (have) less cards it will default to the number of cards in the given deck(s).</p>
|
||||
<v-text-field
|
||||
v-model="curCardLimit"
|
||||
type="number"
|
||||
min="1"
|
||||
:label="label"
|
||||
hide-details="auto"
|
||||
outlined
|
||||
></v-text-field>
|
||||
</v-card-text>
|
||||
<v-card-actions class="button-padding">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="red" @click="deactivatecardLimit" right>Deactivate Limit</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from "vue";
|
||||
import Component from "vue-class-component";
|
||||
|
||||
import { Event } from "../../types";
|
||||
|
||||
const CardLimitProps = Vue.extend({
|
||||
props: {
|
||||
cardLimit: String
|
||||
}
|
||||
});
|
||||
|
||||
@Component
|
||||
export default class CardLimit extends CardLimitProps {
|
||||
noLimitString = "Currently no limit";
|
||||
defaultLabel = "Card Limit";
|
||||
showHelpText = false;
|
||||
|
||||
get label() {
|
||||
return this.curCardLimit ? this.defaultLabel : this.noLimitString;
|
||||
}
|
||||
|
||||
get curCardLimit() {
|
||||
return this.cardLimit === "0" || !this.cardLimit ? null : this.cardLimit;
|
||||
}
|
||||
|
||||
set curCardLimit(newValue) {
|
||||
this.$eventHub.$emit(Event.UPDATE_CARD_LIMIT, newValue);
|
||||
}
|
||||
|
||||
deactivatecardLimit() {
|
||||
this.$eventHub.$emit(Event.UPDATE_CARD_LIMIT, "0");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-padding {
|
||||
padding: 16px;
|
||||
}
|
||||
.description {
|
||||
align-items: center;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
p .v-icon {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Disable stepper in number input */
|
||||
::v-deep input,
|
||||
::v-deep input::-webkit-outer-spin-button,
|
||||
::v-deep input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
</style>
|
42
src/components/settings/ClearLocalStorage.vue
Normal file
42
src/components/settings/ClearLocalStorage.vue
Normal file
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<v-col cols="12" sm="12" md="6" lg="6" xl="6">
|
||||
<v-card height="100%" raised>
|
||||
<v-card-title>Clear Local Storage</v-card-title>
|
||||
<v-card-text>
|
||||
<p>Delete all imported decks as well as your learning progess in the app.</p>
|
||||
</v-card-text>
|
||||
<div class="horizontal-spacer"></div>
|
||||
<v-card-actions class="button-padding">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="red" @click="clearLocalStorage" right>Clear Local Storage</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from "vue";
|
||||
import Component from "vue-class-component";
|
||||
|
||||
import * as clearStorageDialogHelper from "../../helpers/clearStorageDialogHelper";
|
||||
|
||||
@Component
|
||||
export default class ClearLocalStorage extends Vue {
|
||||
clearLocalStorage() {
|
||||
clearStorageDialogHelper.clearLocalStorageDialog(this);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-padding {
|
||||
padding: 16px;
|
||||
}
|
||||
.v-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.horizontal-spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
35
src/components/settings/Settings.vue
Normal file
35
src/components/settings/Settings.vue
Normal file
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<CardLimit :cardLimit="cardLimit" />
|
||||
<ClearLocalStorage />
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from "vue";
|
||||
import Component from "vue-class-component";
|
||||
|
||||
import CardLimit from "./CardLimit.vue";
|
||||
import ClearLocalStorage from "./ClearLocalStorage.vue";
|
||||
|
||||
const SettingProps = Vue.extend({
|
||||
props: {
|
||||
cardLimit: String
|
||||
}
|
||||
});
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
CardLimit,
|
||||
ClearLocalStorage
|
||||
}
|
||||
})
|
||||
export default class Settings extends SettingProps {}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
24
src/helpers/clearStorageDialogHelper.ts
Normal file
24
src/helpers/clearStorageDialogHelper.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Event } from "../types";
|
||||
|
||||
export function clearLocalStorageDialog(context: any) {
|
||||
context.$eventHub.$emit("showCustomDialog", {
|
||||
title: "Clear Storage?",
|
||||
message:
|
||||
"Do you really want to clear your local storage? Every deck and learning progress will be lost.",
|
||||
buttons: [
|
||||
{
|
||||
name: "Cancel",
|
||||
color: "grey",
|
||||
},
|
||||
{
|
||||
name: "Clear Storage",
|
||||
color: "orange darken-1",
|
||||
callback: () => {
|
||||
context.$eventHub.$emit(
|
||||
Event.CLEAR_LOCAL_STORAGE
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
|
@ -22,9 +22,12 @@ export function registerEventListenerForMainApp(context: any) {
|
|||
context.$eventHub.$on(Event.ADD_DECKS_FROM_FILE, (fileContent: string) => {
|
||||
addDecksFromFile(context, fileContent);
|
||||
});
|
||||
context.$eventHub.$on(Event.ADD_DECKS_FROM_JSON, (fileContent: FFCFile, url?: string) => {
|
||||
addDecksFromJSON(context, fileContent, url);
|
||||
});
|
||||
context.$eventHub.$on(
|
||||
Event.ADD_DECKS_FROM_JSON,
|
||||
(fileContent: FFCFile, url?: string) => {
|
||||
addDecksFromJSON(context, fileContent, url);
|
||||
}
|
||||
);
|
||||
context.$eventHub.$on(Event.SNACKBAR_EVENT, (message: string) => {
|
||||
showSnackbar(context, message);
|
||||
});
|
||||
|
@ -43,4 +46,7 @@ export function registerEventListenerForMainApp(context: any) {
|
|||
}
|
||||
context.$router.replace("/");
|
||||
});
|
||||
context.$eventHub.$on(Event.UPDATE_CARD_LIMIT, (newValue: string) => {
|
||||
context.cardLimit = newValue;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { showSnackbar } from './snackbarHelper';
|
||||
import { showSnackbar } from "./snackbarHelper";
|
||||
|
||||
const LOCAL_STORAGE_APP_CONTEXT = "ffc_";
|
||||
|
||||
|
@ -25,9 +25,9 @@ export function remove(key: string): void {
|
|||
}
|
||||
|
||||
function clearAppData(): void {
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i) || "";
|
||||
if (key.startsWith(LOCAL_STORAGE_APP_CONTEXT)) {
|
||||
for (let i = localStorage.length - 1; i >= 0; i--) {
|
||||
const key = localStorage.key(i);
|
||||
if (key && key.startsWith(LOCAL_STORAGE_APP_CONTEXT)) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ export enum Event {
|
|||
DESELECT_ALL_DECKS = "deselectAllDecks",
|
||||
SWIPE_LEFT_IN_LEARN = "swipeLeftInLearn",
|
||||
SWIPE_RIGHT_IN_LEARN = "swipeRightInLearn",
|
||||
UPDATE_CARD_LIMIT = "updateCardLimit",
|
||||
}
|
||||
|
||||
export interface NavBarConfigItem {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="deckselection">
|
||||
<DeckSelection v-bind:decks="decks" v-bind:numberOfSelectedDecks="numberOfSelectedDecks"></DeckSelection>
|
||||
<DeckSelection :decks="decks" :numberOfSelectedDecks="numberOfSelectedDecks"></DeckSelection>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
:decks="decks"
|
||||
:numberOfSelectedDecks="numberOfSelectedDecks"
|
||||
:learningSession="learningSession"
|
||||
:cardLimit="cardLimit"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -20,7 +21,8 @@ const LearnProps = Vue.extend({
|
|||
props: {
|
||||
decks: { type: Array as () => Deck[] },
|
||||
learningSession: { type: Object as () => LearningSession },
|
||||
numberOfSelectedDecks: Number
|
||||
numberOfSelectedDecks: Number,
|
||||
cardLimit: String
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
<template>
|
||||
<div class="settings">
|
||||
<v-container fluid>
|
||||
<v-row align="center" justify="center">
|
||||
<v-col cols="10">
|
||||
<span class="title">Settings</span>
|
||||
<br />
|
||||
<v-btn color="red" @click="clearLocalStorage" class="my-4">Clear Local Storage</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<SettingsComponent :cardLimit="cardLimit" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -16,18 +8,21 @@
|
|||
import Vue from "vue";
|
||||
import Component from "vue-class-component";
|
||||
|
||||
import { Event } from "../types";
|
||||
import SettingsComponent from "../components/settings/Settings.vue";
|
||||
|
||||
@Component
|
||||
export default class Settings extends Vue {
|
||||
clearLocalStorage() {
|
||||
this.$eventHub.$emit(Event.CLEAR_LOCAL_STORAGE);
|
||||
const SettingProps = Vue.extend({
|
||||
props: {
|
||||
cardLimit: String
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
SettingsComponent
|
||||
}
|
||||
})
|
||||
export default class Settings extends SettingProps {}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.settings {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue