feat(diary): fixes

This commit is contained in:
mso7nueh
2026-05-10 20:08:51 +03:00
parent 2859607f4d
commit 63fc824449
17 changed files with 628 additions and 48 deletions

View File

@@ -174,12 +174,22 @@ class DiaryCubit extends Cubit<DiaryState> {
_lastFetchWasOffline = true;
return result;
} catch (_) {
// Кэша нет — попробуем сеть на случай ложного "offline"
log('[getDiary] OFFLINE: no cache, trying network as fallback...');
// Кэша нет — создаём день локально, чтобы он был видим
log('[getDiary] OFFLINE: no cache, creating day locally...');
try {
await diaryRepository.createDiaryDay(day: dayStr);
final result = await diaryRepository.getDiaryDayV2(day: dayStr);
log('[getDiary] OFFLINE: local day created and loaded OK');
_lastFetchWasOffline = true;
return result;
} catch (e2) {
log('[getDiary] OFFLINE: local day creation failed: $e2');
return null;
}
}
}
// Онлайн (или fallback после неудачного offline): создаём день + грузим с сервера
// Онлайн: создаём день + грузим с сервера
try {
log('[getDiary] ONLINE: creating diary day...');
await diaryRepository.createDiaryDay(day: dayStr);

View File

@@ -29,6 +29,10 @@ class GlossarySyncCubit extends Cubit<GlossarySyncState> {
_connectionCubit = connectionCubit,
super(const GlossarySyncState.idle());
/// Готов ли глоссарий к использованию (есть ли хотя бы 1 запись в локальном кэше).
bool get isGlossaryReady => _glossaryReady;
bool _glossaryReady = false;
/// Начать прослушивание сети и автозапуск синхронизации.
void start() {
_connectionSub = _connectionCubit.stream.listen((state) {
@@ -56,6 +60,8 @@ class GlossarySyncCubit extends Cubit<GlossarySyncState> {
onProcessingProgress: _onProcessingProgress,
);
_glossaryReady = true;
if (count == 0) {
emit(const GlossarySyncState.upToDate());
} else {
@@ -68,10 +74,13 @@ class GlossarySyncCubit extends Cubit<GlossarySyncState> {
});
} catch (e) {
emit(GlossarySyncState.error(message: _humanReadableError(e)));
// Скрыть ошибку через 6 секунд
_hideTimer = Timer(const Duration(seconds: 6), () {
if (!isClosed) emit(const GlossarySyncState.idle());
});
// Если глоссарий ещё не синхронизирован — НЕ скрываем ошибку,
// чтобы пользователь мог нажать «Повторить».
if (_glossaryReady) {
_hideTimer = Timer(const Duration(seconds: 6), () {
if (!isClosed) emit(const GlossarySyncState.idle());
});
}
} finally {
_isSyncing = false;
}

View File

@@ -56,6 +56,24 @@ class GlossarySyncBannerWidget extends StatelessWidget {
color: const Color(0xFFDC2626),
icon: Icons.error_outline_rounded,
text: message,
action: TextButton(
onPressed: () => context.read<GlossarySyncCubit>().syncNow(),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: const Text(
'Повторить',
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w700,
decoration: TextDecoration.underline,
decorationColor: Colors.white,
),
),
),
),
);
},
@@ -68,6 +86,7 @@ class GlossarySyncBannerWidget extends StatelessWidget {
required String text,
bool showSpinner = false,
double? progress,
Widget? action,
}) {
return _AnimatedBanner(
color: color,
@@ -75,6 +94,7 @@ class GlossarySyncBannerWidget extends StatelessWidget {
text: text,
showSpinner: showSpinner,
progress: progress,
action: action,
);
}
@@ -95,6 +115,7 @@ class _AnimatedBanner extends StatefulWidget {
final String text;
final bool showSpinner;
final double? progress;
final Widget? action;
const _AnimatedBanner({
required this.color,
@@ -102,6 +123,7 @@ class _AnimatedBanner extends StatefulWidget {
required this.text,
this.showSpinner = false,
this.progress,
this.action,
});
@override
@@ -182,6 +204,7 @@ class _AnimatedBannerState extends State<_AnimatedBanner>
),
),
),
if (widget.action != null) widget.action!,
],
),
),