voices
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Admin Panel / Deploy Admin Panel (push) Waiting to run
Deploy Admin Panel / Admin Panel Verification (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Admin Panel / Deploy Admin Panel (push) Waiting to run
Deploy Admin Panel / Admin Panel Verification (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
This commit is contained in:
parent
d8a29eb84a
commit
47005a357a
7 changed files with 80 additions and 18 deletions
|
|
@ -11,17 +11,49 @@ export async function upsertCardWithOptionalVoice(
|
||||||
card: GameCardDto,
|
card: GameCardDto,
|
||||||
voice?: PendingVoiceUpload,
|
voice?: PendingVoiceUpload,
|
||||||
): Promise<{ success: boolean; card: GameCardDto }> {
|
): Promise<{ success: boolean; card: GameCardDto }> {
|
||||||
|
console.log('🔍 upsertCardWithOptionalVoice: Starting', {
|
||||||
|
cardId: card.id,
|
||||||
|
hasVoice: !!voice,
|
||||||
|
voiceUrl: voice?.voiceUrl,
|
||||||
|
language: voice?.language,
|
||||||
|
})
|
||||||
|
|
||||||
const response = await cardsApi.upsertCard(card)
|
const response = await cardsApi.upsertCard(card)
|
||||||
|
|
||||||
const responseCardId = response.card?.id != null ? String(response.card.id) : ''
|
const responseCardId = response.card?.id != null ? String(response.card.id) : ''
|
||||||
const requestCardId = card.id != null ? String(card.id) : ''
|
const requestCardId = card.id != null ? String(card.id) : ''
|
||||||
const cardId = responseCardId || requestCardId
|
const cardId = responseCardId || requestCardId
|
||||||
|
|
||||||
|
console.log('🔍 upsertCardWithOptionalVoice: After upsertCard', {
|
||||||
|
responseCardId,
|
||||||
|
requestCardId,
|
||||||
|
cardId,
|
||||||
|
hasVoice: !!voice,
|
||||||
|
voiceUrl: voice?.voiceUrl,
|
||||||
|
})
|
||||||
|
|
||||||
if (!voice || !voice.voiceUrl || !cardId) {
|
if (!voice || !voice.voiceUrl || !cardId) {
|
||||||
|
console.log('⚠️ upsertCardWithOptionalVoice: Skipping voice link', {
|
||||||
|
hasVoice: !!voice,
|
||||||
|
voiceUrl: voice?.voiceUrl,
|
||||||
|
cardId,
|
||||||
|
})
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
await voicesApi.addCardVoice(cardId, voice.voiceUrl, voice.language)
|
console.log('✅ upsertCardWithOptionalVoice: Calling addCardVoice', {
|
||||||
|
cardId,
|
||||||
|
voiceUrl: voice.voiceUrl,
|
||||||
|
language: voice.language,
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
await voicesApi.addCardVoice(cardId, voice.voiceUrl, voice.language)
|
||||||
|
console.log('✅ upsertCardWithOptionalVoice: Successfully added voice')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ upsertCardWithOptionalVoice: Error adding voice', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,16 @@ export function CardVoicesManager({ cardId, disabled = false }: CardVoicesManage
|
||||||
|
|
||||||
// Add voice mutation
|
// Add voice mutation
|
||||||
const addVoiceMutation = useMutation({
|
const addVoiceMutation = useMutation({
|
||||||
mutationFn: ({ voiceUrl, language }: { voiceUrl: string; language: string }) =>
|
mutationFn: ({ voiceUrl, language }: { voiceUrl: string; language: string }) => {
|
||||||
voicesApi.addCardVoice(cardId, voiceUrl, language),
|
console.log('🔍 CardVoicesManager: addVoiceMutation.mutationFn called', {
|
||||||
|
cardId,
|
||||||
|
voiceUrl,
|
||||||
|
language,
|
||||||
|
})
|
||||||
|
return voicesApi.addCardVoice(cardId, voiceUrl, language)
|
||||||
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
console.log('✅ CardVoicesManager: addVoiceMutation.onSuccess')
|
||||||
queryClient.invalidateQueries({ queryKey: ['cardVoices', cardId] })
|
queryClient.invalidateQueries({ queryKey: ['cardVoices', cardId] })
|
||||||
toast.success('Voice added successfully')
|
toast.success('Voice added successfully')
|
||||||
setShowAddForm(false)
|
setShowAddForm(false)
|
||||||
|
|
@ -40,6 +47,7 @@ export function CardVoicesManager({ cardId, disabled = false }: CardVoicesManage
|
||||||
setNewLanguage('en')
|
setNewLanguage('en')
|
||||||
},
|
},
|
||||||
onError: (error: unknown) => {
|
onError: (error: unknown) => {
|
||||||
|
console.error('❌ CardVoicesManager: addVoiceMutation.onError', error)
|
||||||
const axiosError = error as AxiosError<{ message?: string }>
|
const axiosError = error as AxiosError<{ message?: string }>
|
||||||
toast.error(axiosError.response?.data?.message || 'Failed to add voice')
|
toast.error(axiosError.response?.data?.message || 'Failed to add voice')
|
||||||
},
|
},
|
||||||
|
|
@ -59,11 +67,24 @@ export function CardVoicesManager({ cardId, disabled = false }: CardVoicesManage
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleAddVoice = () => {
|
const handleAddVoice = () => {
|
||||||
|
console.log('🔍 CardVoicesManager: handleAddVoice called', {
|
||||||
|
newAudio,
|
||||||
|
newLanguage,
|
||||||
|
cardId,
|
||||||
|
})
|
||||||
|
|
||||||
if (!newAudio) {
|
if (!newAudio) {
|
||||||
|
console.warn('⚠️ CardVoicesManager: newAudio is empty')
|
||||||
toast.error('Please upload an audio file')
|
toast.error('Please upload an audio file')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('✅ CardVoicesManager: Calling addVoiceMutation.mutate', {
|
||||||
|
cardId,
|
||||||
|
voiceUrl: newAudio,
|
||||||
|
language: newLanguage,
|
||||||
|
})
|
||||||
|
|
||||||
addVoiceMutation.mutate({
|
addVoiceMutation.mutate({
|
||||||
voiceUrl: newAudio,
|
voiceUrl: newAudio,
|
||||||
language: newLanguage,
|
language: newLanguage,
|
||||||
|
|
@ -103,7 +124,10 @@ export function CardVoicesManager({ cardId, disabled = false }: CardVoicesManage
|
||||||
<AudioUpload
|
<AudioUpload
|
||||||
label="Audio File"
|
label="Audio File"
|
||||||
value={newAudio}
|
value={newAudio}
|
||||||
onChange={setNewAudio}
|
onChange={(value) => {
|
||||||
|
console.log('🔍 CardVoicesManager: AudioUpload onChange', { value, cardId })
|
||||||
|
setNewAudio(value)
|
||||||
|
}}
|
||||||
language={newLanguage}
|
language={newLanguage}
|
||||||
onLanguageChange={setNewLanguage}
|
onLanguageChange={setNewLanguage}
|
||||||
disabled={disabled || addVoiceMutation.isPending}
|
disabled={disabled || addVoiceMutation.isPending}
|
||||||
|
|
|
||||||
|
|
@ -868,6 +868,8 @@ class AdminCardsApiV2 {
|
||||||
|
|
||||||
// Create voice record with object ID
|
// Create voice record with object ID
|
||||||
print('🔍 addCardVoice: Creating voice record with objectId $objectId for card $cardId');
|
print('🔍 addCardVoice: Creating voice record with objectId $objectId for card $cardId');
|
||||||
|
print('🔍 addCardVoice: VoiceModelsCompanion.insert params: cardId=$cardId, voiceUrl=$objectId, language=${requestDto.language ?? 'en'}');
|
||||||
|
|
||||||
final voiceId = await _db.packDao.createVoice(
|
final voiceId = await _db.packDao.createVoice(
|
||||||
VoiceModelsCompanion.insert(
|
VoiceModelsCompanion.insert(
|
||||||
cardId: cardId,
|
cardId: cardId,
|
||||||
|
|
@ -881,6 +883,10 @@ class AdminCardsApiV2 {
|
||||||
print('🔍 addCardVoice: Linking voice $voiceId to card $cardId');
|
print('🔍 addCardVoice: Linking voice $voiceId to card $cardId');
|
||||||
await _db.packDao.addVoiceToCard(cardId, voiceId);
|
await _db.packDao.addVoiceToCard(cardId, voiceId);
|
||||||
print('✅ addCardVoice: Successfully linked voice $voiceId to card $cardId');
|
print('✅ addCardVoice: Successfully linked voice $voiceId to card $cardId');
|
||||||
|
|
||||||
|
// Verify the link was created
|
||||||
|
final verifyVoices = await _db.packDao.getCardVoices(cardId);
|
||||||
|
print('🔍 addCardVoice: Verification - found ${verifyVoices.length} voices for card $cardId');
|
||||||
|
|
||||||
final voice = await _db.packDao.getVoiceById(voiceId);
|
final voice = await _db.packDao.getVoiceById(voiceId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
import 'package:yx_scope_flutter/yx_scope_flutter.dart';
|
import 'package:yx_scope_flutter/yx_scope_flutter.dart';
|
||||||
|
|
||||||
import '../../../di/app_scope/app_scope_container.dart';
|
import '../../../di/app_scope/app_scope_container.dart';
|
||||||
import '../../../domain/config/api_config_v2.dart';
|
|
||||||
import '../../../presentation/theme/app_colors.dart';
|
import '../../../presentation/theme/app_colors.dart';
|
||||||
import '../../../presentation/widgets/pack_card_item.dart';
|
import '../../../presentation/widgets/pack_card_item.dart';
|
||||||
import '../../../presentation/widgets/pack_details_header.dart';
|
import '../../../presentation/widgets/pack_details_header.dart';
|
||||||
|
|
@ -20,6 +19,7 @@ import '../../../presentation/widgets/card_viewer.dart';
|
||||||
import '../../../presentation/widgets/shuffle_animated_switcher.dart';
|
import '../../../presentation/widgets/shuffle_animated_switcher.dart';
|
||||||
import '../../../presentation/widgets/shuffle_movement_wrapper.dart';
|
import '../../../presentation/widgets/shuffle_movement_wrapper.dart';
|
||||||
import '../../../presentation/widgets/utils/card_movement_utils.dart';
|
import '../../../presentation/widgets/utils/card_movement_utils.dart';
|
||||||
|
import '../../../utils/card_image_utils.dart';
|
||||||
import '../../../utils/color_extension.dart';
|
import '../../../utils/color_extension.dart';
|
||||||
import '../../../utils/responsive.dart';
|
import '../../../utils/responsive.dart';
|
||||||
|
|
||||||
|
|
@ -823,8 +823,8 @@ class _PackDetailsPageState extends State<PackDetailsPage> {
|
||||||
|
|
||||||
/// Изображение карточки (вынесено в отдельный метод для переиспользования)
|
/// Изображение карточки (вынесено в отдельный метод для переиспользования)
|
||||||
Widget _buildCardImage(GameCardDto card, double width, double height) {
|
Widget _buildCardImage(GameCardDto card, double width, double height) {
|
||||||
// Use presigned URL from DTO
|
// Use helper to get image URL (handles imageUrl, image URL, and filename)
|
||||||
final imageUrl = card.imageUrl;
|
final imageUrl = CardImageUtils.getCardImageUrl(card, widget.packId);
|
||||||
|
|
||||||
if (imageUrl == null || imageUrl.isEmpty) {
|
if (imageUrl == null || imageUrl.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import 'package:mnemo_cards_web_v2/di/user_scope/user_scope.dart';
|
||||||
import 'package:yx_scope_flutter/yx_scope_flutter.dart';
|
import 'package:yx_scope_flutter/yx_scope_flutter.dart';
|
||||||
import 'package:yx_state_flutter/yx_state_flutter.dart';
|
import 'package:yx_state_flutter/yx_state_flutter.dart';
|
||||||
|
|
||||||
import '../../../domain/config/api_config_v2.dart';
|
|
||||||
import '../../../domain/state/card_flipper_state_manager.dart';
|
import '../../../domain/state/card_flipper_state_manager.dart';
|
||||||
import '../../../presentation/widgets/card_voice_controls.dart';
|
import '../../../presentation/widgets/card_voice_controls.dart';
|
||||||
import '../../../presentation/widgets/mnemo_text.dart';
|
import '../../../presentation/widgets/mnemo_text.dart';
|
||||||
|
import '../../../utils/card_image_utils.dart';
|
||||||
|
|
||||||
/// Card flipper widget for studying cards
|
/// Card flipper widget for studying cards
|
||||||
///
|
///
|
||||||
|
|
@ -720,8 +720,8 @@ class _CardSide extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildImage() {
|
Widget _buildImage() {
|
||||||
// Use presigned URL from DTO
|
// Use helper to get image URL (handles imageUrl, image URL, and filename)
|
||||||
final imageUrl = card.imageUrl;
|
final imageUrl = CardImageUtils.getCardImageUrl(card, packId);
|
||||||
|
|
||||||
if (imageUrl == null || imageUrl.isEmpty) {
|
if (imageUrl == null || imageUrl.isEmpty) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
|
|
||||||
import '../../domain/config/api_config_v2.dart';
|
|
||||||
import '../../presentation/theme/app_colors.dart';
|
import '../../presentation/theme/app_colors.dart';
|
||||||
|
import '../../utils/card_image_utils.dart';
|
||||||
import 'card_favorite_button.dart';
|
import 'card_favorite_button.dart';
|
||||||
import 'card_voice_controls.dart';
|
import 'card_voice_controls.dart';
|
||||||
import 'mnemo_text.dart';
|
import 'mnemo_text.dart';
|
||||||
|
|
@ -565,8 +565,8 @@ class _CardSide extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildImage() {
|
Widget _buildImage() {
|
||||||
// Use presigned URL from DTO
|
// Use helper to get image URL (handles imageUrl, image URL, and filename)
|
||||||
final imageUrl = card.imageUrl;
|
final imageUrl = CardImageUtils.getCardImageUrl(card, packId);
|
||||||
|
|
||||||
if (imageUrl == null || imageUrl.isEmpty) {
|
if (imageUrl == null || imageUrl.isEmpty) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|
@ -600,8 +600,8 @@ class _CardSide extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildImageBack() {
|
Widget _buildImageBack() {
|
||||||
// Use presigned URL from DTO
|
// Use helper to get back image URL (handles imageBackUrl, imageBack URL, and filename)
|
||||||
final imageBackUrl = card.imageBackUrl;
|
final imageBackUrl = CardImageUtils.getCardImageBackUrl(card, packId);
|
||||||
|
|
||||||
if (imageBackUrl == null || imageBackUrl.isEmpty) {
|
if (imageBackUrl == null || imageBackUrl.isEmpty) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
|
|
||||||
import '../../domain/config/api_config_v2.dart';
|
import '../../utils/card_image_utils.dart';
|
||||||
import 'mnemo_text.dart';
|
import 'mnemo_text.dart';
|
||||||
|
|
||||||
/// Виджет для отображения отдельной карточки в сетке
|
/// Виджет для отображения отдельной карточки в сетке
|
||||||
|
|
@ -260,8 +260,8 @@ class PackCardItem extends StatelessWidget {
|
||||||
/// Отображает изображение карточки
|
/// Отображает изображение карточки
|
||||||
/// Загружает изображение с бэкенда по URL
|
/// Загружает изображение с бэкенда по URL
|
||||||
Widget _buildImage(BuildContext context, double cardWidth, double cardHeight) {
|
Widget _buildImage(BuildContext context, double cardWidth, double cardHeight) {
|
||||||
// Use presigned URL from DTO
|
// Use helper to get image URL (handles imageUrl, image URL, and filename)
|
||||||
final imageUrl = card.imageUrl;
|
final imageUrl = CardImageUtils.getCardImageUrl(card, packId);
|
||||||
|
|
||||||
if (imageUrl == null || imageUrl.isEmpty) {
|
if (imageUrl == null || imageUrl.isEmpty) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue