diff --git a/app/Http/Controllers/DeckController.php b/app/Http/Controllers/DeckController.php
index 5b761c0..386bc42 100644
--- a/app/Http/Controllers/DeckController.php
+++ b/app/Http/Controllers/DeckController.php
@@ -35,11 +35,13 @@ public function store(Request $request)
$validated = $request->validate([
'name' => 'required|string',
'description' => 'string|nullable',
+ 'is_tts_enabled' => 'boolean|nullable',
]);
$deck = Deck::create([
'name' => $validated['name'],
'description' => $validated['description'] ?? null,
+ 'is_tts_enabled' => $validated['is_tts_enabled'] ?? false,
]);
$deck->memberships()->create([
@@ -96,11 +98,13 @@ public function update(Request $request, Deck $deck)
$validated = $request->validate([
'name' => 'string',
'description' => 'string|nullable',
+ 'is_tts_enabled' => 'boolean|nullable',
]);
$deck->update([
'name' => $validated['name'] ?? $deck->name,
'description' => $validated['description'] ?? $deck->description,
+ 'is_tts_enabled' => $validated['is_tts_enabled'] ?? $deck->is_tts_enabled,
]);
return DeckResource::make($deck->fresh());
diff --git a/app/Http/Resources/DeckResource.php b/app/Http/Resources/DeckResource.php
index 1ec7e43..064cdba 100644
--- a/app/Http/Resources/DeckResource.php
+++ b/app/Http/Resources/DeckResource.php
@@ -19,6 +19,7 @@ public function toArray(Request $request): array
'name' => $this->name,
'description' => $this->description,
'is_public' => $this->is_public,
+ 'is_tts_enabled' => $this->is_tts_enabled,
'cards_count' => $this->when(isset($this->cards_count), $this->cards_count),
diff --git a/app/Models/Deck.php b/app/Models/Deck.php
index fc05796..cb83847 100644
--- a/app/Models/Deck.php
+++ b/app/Models/Deck.php
@@ -19,12 +19,14 @@ class Deck extends Model implements AuditableContract
'avg_score' => 'float',
'is_public' => 'boolean',
'current_user_xp' => 'integer',
+ 'is_tts_enabled' => 'boolean',
];
protected $fillable = [
'name',
'description',
'is_public',
+ 'is_tts_enabled',
];
public function users()
diff --git a/database/migrations/2024_12_05_215120_add_is_tts_enabled_column_to_decks_table.php b/database/migrations/2024_12_05_215120_add_is_tts_enabled_column_to_decks_table.php
new file mode 100644
index 0000000..d7876e5
--- /dev/null
+++ b/database/migrations/2024_12_05_215120_add_is_tts_enabled_column_to_decks_table.php
@@ -0,0 +1,31 @@
+boolean('is_tts_enabled')
+ ->default(false)
+ ->after('is_public');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('decks', function (Blueprint $table) {
+ $table->dropColumn('is_tts_enabled');
+ });
+ }
+};
diff --git a/resources/client/api/index.ts b/resources/client/api/index.ts
index 2bc1ed5..0b0e2e2 100644
--- a/resources/client/api/index.ts
+++ b/resources/client/api/index.ts
@@ -68,11 +68,18 @@ export async function getDeckById(deckId: number) {
}
export async function createDeck(
- deck: { name: string; description: string },
+ deck: { name: string; description: string; isTTSEnabled: boolean },
customConfig: T.CustomAxiosRequestConfig = {},
) {
await csrf();
- const res = await axios.post<{ data: T.Deck }>(`/decks`, deck, customConfig);
+ const res = await axios.post<{ data: T.Deck }>(
+ `/decks`,
+ {
+ ...deck,
+ is_tts_enabled: deck.isTTSEnabled,
+ },
+ customConfig,
+ );
return res.data.data;
}
@@ -88,14 +95,17 @@ export async function updateDeck({
id,
name,
description,
+ isTTSEnabled,
}: {
id: number;
name: string;
description: string;
+ isTTSEnabled: boolean;
}) {
const res = await axios.put<{ data: T.Deck }>(`/decks/${id}`, {
name,
description,
+ is_tts_enabled: isTTSEnabled,
});
return res.data.data;
}
diff --git a/resources/client/components/BlockEditor/TextBlockInput.vue b/resources/client/components/BlockEditor/TextBlockInput.vue
index a0777c4..93c1d29 100644
--- a/resources/client/components/BlockEditor/TextBlockInput.vue
+++ b/resources/client/components/BlockEditor/TextBlockInput.vue
@@ -5,7 +5,7 @@
:selectedLanguage="selectedLanguage"
class="top-1 right-1 absolute z-10"
isIdleClass="bg-brand-oatmeal-50"
- v-if="featureFlags?.text_to_speech && charCount < MAX_TTS_CHARS"
+ v-if="isDeckTTSEnabled && charCount < MAX_TTS_CHARS"
/>
-
+
diff --git a/resources/client/pages/Decks/CreateOrEditDeckPage.vue b/resources/client/pages/Decks/CreateOrEditDeckPage.vue
index 71840d7..9300c90 100644
--- a/resources/client/pages/Decks/CreateOrEditDeckPage.vue
+++ b/resources/client/pages/Decks/CreateOrEditDeckPage.vue
@@ -14,11 +14,26 @@
v-model="form.description"
id="description"
/>
+
+
+
+
-
@@ -38,6 +53,10 @@ import { useRouter } from "vue-router";
import InputGroup from "@/components/InputGroup.vue";
import PageHeader from "@/components/PageHeader.vue";
import { Button } from "@/components/ui/button";
+import { Switch } from "@/components/ui/switch";
+import HintTooltip from "@/components/HintTooltip.vue";
+import { Label } from "@/components/ui/label";
+import { Deck } from "@/types";
const props = defineProps<{
deckId: number | null;
@@ -46,6 +65,7 @@ const props = defineProps<{
const form = reactive({
name: "",
description: "",
+ isTTSEnabled: false,
});
const isCreateMode = computed(() => props.deckId === null);
@@ -60,7 +80,8 @@ watch(
() => {
if (deck.value) {
form.name = deck.value.name;
- form.description = deck.value.description;
+ form.description = deck.value.description ?? "";
+ form.isTTSEnabled = deck.value.is_tts_enabled;
}
},
{ immediate: true },
@@ -69,8 +90,8 @@ watch(
async function handleSubmit() {
if (isCreateMode.value) {
createDeck(form, {
- onSuccess: () => {
- router.push({ name: "decks.index" });
+ onSuccess: (newDeck) => {
+ router.push({ name: "decks.show", params: { deckId: newDeck.id } });
},
});
return;
@@ -83,8 +104,8 @@ async function handleSubmit() {
updateDeck(
{ id: props.deckId, ...form },
{
- onSuccess: () => {
- router.push({ name: "decks.index" });
+ onSuccess: (updatedDeck: Deck) => {
+ router.push({ name: "decks.show", params: { deckId: updatedDeck.id } });
},
},
);
diff --git a/resources/client/pages/Decks/DeckIndexPage/MoreDeckActions.vue b/resources/client/pages/Decks/DeckIndexPage/MoreDeckActions.vue
index 7dae7de..df81ca0 100644
--- a/resources/client/pages/Decks/DeckIndexPage/MoreDeckActions.vue
+++ b/resources/client/pages/Decks/DeckIndexPage/MoreDeckActions.vue
@@ -9,8 +9,8 @@
-
- Edit Name
+
+ Settings
@@ -80,6 +80,7 @@ import {
IconTrash,
IconUpload,
IconCirclePlay,
+ IconSettings,
} from "@/components/icons";
import {
DropdownMenu,
diff --git a/resources/client/pages/Decks/DeckShowPage/DeckShowPage.vue b/resources/client/pages/Decks/DeckShowPage/DeckShowPage.vue
index 4662cec..ef2a49e 100644
--- a/resources/client/pages/Decks/DeckShowPage/DeckShowPage.vue
+++ b/resources/client/pages/Decks/DeckShowPage/DeckShowPage.vue
@@ -114,7 +114,7 @@ import { useDeleteCardMutation } from "@/queries/cards";
import { useDeckByIdQuery } from "@/queries/decks";
import * as T from "@/types";
import { RouterLink } from "vue-router";
-import { computed } from "vue";
+import { computed, provide } from "vue";
import { Button } from "@/components/ui/button";
import MoreDeckActions from "@/pages/Decks/DeckIndexPage/MoreDeckActions.vue";
import PageHeader from "@/components/PageHeader.vue";
@@ -125,7 +125,8 @@ import MoreCardActions from "./MoreCardActions.vue";
import { ref } from "vue";
import LevelProgress from "@/components/LevelProgress.vue";
import { useActivityTypesQuery } from "@/queries/activityTypes/useActivityTypesQuery";
-import { match } from "ramda";
+import { useIsDeckTTSEnabled } from "@/composables/useIsDeckTTSEnabled";
+import { IS_DECK_TTS_ENABLED_INJECTION_KEY } from "@/constants";
const props = defineProps<{
deckId: number;
@@ -175,5 +176,9 @@ const initialCardSide = ref("front");
function flipAllCards() {
initialCardSide.value = initialCardSide.value === "front" ? "back" : "front";
}
+
+// provide info about TTS to any card blocks that need it
+const { isDeckTTSEnabled } = useIsDeckTTSEnabled(deck);
+provide(IS_DECK_TTS_ENABLED_INJECTION_KEY, isDeckTTSEnabled);
diff --git a/resources/client/pages/Decks/PracticeDeckPage/PracticeDeckPage.vue b/resources/client/pages/Decks/PracticeDeckPage/PracticeDeckPage.vue
index 9812e62..dc738c2 100644
--- a/resources/client/pages/Decks/PracticeDeckPage/PracticeDeckPage.vue
+++ b/resources/client/pages/Decks/PracticeDeckPage/PracticeDeckPage.vue
@@ -79,7 +79,7 @@