229 lines
8.7 KiB
Dart
229 lines
8.7 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:please_pay_me/core/branding/app_brand.dart';
|
|
import 'package:please_pay_me/core/legal/legal_links.dart';
|
|
import 'package:please_pay_me/features/auth/session_controller.dart';
|
|
import 'package:please_pay_me/features/budgets/budgets_controller.dart';
|
|
import 'package:please_pay_me/features/journal/journal_controller.dart';
|
|
import 'package:please_pay_me/features/work/jobs_controller.dart';
|
|
import 'package:please_pay_me/theme/theme.dart';
|
|
import 'package:please_pay_me/ui/ui.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
class ProfileScreen extends StatelessWidget {
|
|
const ProfileScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final session = context.watch<SessionController>();
|
|
final user = session.user;
|
|
|
|
return CupertinoPageScaffold(
|
|
backgroundColor: AppColors.of(context, AppColors.groupedBackground),
|
|
child: CustomScrollView(
|
|
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
|
slivers: [
|
|
const AppLargeNavBar(title: 'Профиль'),
|
|
SliverToBoxAdapter(
|
|
child: Padding(
|
|
padding: const EdgeInsets.fromLTRB(
|
|
AppSpacing.gutter,
|
|
AppSpacing.s2,
|
|
AppSpacing.gutter,
|
|
AppSpacing.s5,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
AppAvatar(
|
|
initials: user?.initials,
|
|
imageUrl: user?.photoUrl,
|
|
radius: 32,
|
|
),
|
|
const SizedBox(width: AppSpacing.s4),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
AppText.title(user?.displayName ?? 'Гость'),
|
|
AppText.subhead(user?.handle ?? 'не авторизован'),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
SliverToBoxAdapter(
|
|
child: AppListSection(
|
|
header: 'Подключение',
|
|
footer: session.isDemo
|
|
? 'Демо-режим: данные живут только в памяти устройства.'
|
|
: 'Данные синхронизируются с веб-кабинетом «${AppBrand.name}».',
|
|
separatorIndent: 60,
|
|
children: [
|
|
AppListTile(
|
|
leading: AppIconBadge(
|
|
icon: session.isDemo
|
|
? CupertinoIcons.wrench_fill
|
|
: CupertinoIcons.cloud_fill,
|
|
color: session.isDemo ? AppColors.systemOrange : AppColors.systemGreen,
|
|
),
|
|
title: 'Режим',
|
|
value: session.isDemo ? 'Демо' : 'Сервер',
|
|
showChevron: false,
|
|
),
|
|
if (!session.isDemo)
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.link,
|
|
color: AppColors.systemGray,
|
|
),
|
|
title: 'Адрес API',
|
|
subtitle: session.baseUrl,
|
|
showChevron: false,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SliverToBoxAdapter(child: SizedBox(height: AppSpacing.s5)),
|
|
SliverToBoxAdapter(
|
|
child: AppListSection(
|
|
header: 'Оформление',
|
|
footer: 'Системная повторяет тему устройства.',
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(
|
|
AppSpacing.s3,
|
|
AppSpacing.s3,
|
|
AppSpacing.s3,
|
|
AppSpacing.s3,
|
|
),
|
|
child: AppSegmentedControl(
|
|
padding: EdgeInsets.zero,
|
|
labels: const ['Системная', 'Светлая', 'Тёмная'],
|
|
index: context.watch<ThemeController>().preference.index,
|
|
onChanged: (index) {
|
|
context.read<ThemeController>().setPreference(
|
|
ThemePreference.values[index],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SliverToBoxAdapter(child: SizedBox(height: AppSpacing.s5)),
|
|
SliverToBoxAdapter(
|
|
child: AppListSection(
|
|
separatorIndent: 60,
|
|
children: [
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.arrow_clockwise,
|
|
color: AppColors.accent,
|
|
),
|
|
title: 'Обновить данные',
|
|
onTap: () => _refreshAll(context),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SliverToBoxAdapter(child: SizedBox(height: AppSpacing.s5)),
|
|
SliverToBoxAdapter(
|
|
child: AppListSection(
|
|
header: 'Правовая информация',
|
|
footer: 'Открывается веб-версия на please-pay-me.ru.',
|
|
separatorIndent: 60,
|
|
children: [
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.doc_text,
|
|
color: AppColors.accent,
|
|
),
|
|
title: 'Пользовательское соглашение',
|
|
onTap: () => openLegalDocument(
|
|
context,
|
|
LegalLinks.resolve(session.webCabinetUrl, LegalLinks.offer),
|
|
),
|
|
),
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.lock_shield,
|
|
color: AppColors.systemGray,
|
|
),
|
|
title: 'Политика конфиденциальности',
|
|
onTap: () => openLegalDocument(
|
|
context,
|
|
LegalLinks.resolve(session.webCabinetUrl, LegalLinks.privacy),
|
|
),
|
|
),
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.checkmark_shield,
|
|
color: AppColors.systemGray,
|
|
),
|
|
title: 'Согласие на обработку данных',
|
|
onTap: () => openLegalDocument(
|
|
context,
|
|
LegalLinks.resolve(session.webCabinetUrl, LegalLinks.consent),
|
|
),
|
|
),
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.circle_grid_hex,
|
|
color: AppColors.systemGray,
|
|
),
|
|
title: 'Политика cookie',
|
|
onTap: () => openLegalDocument(
|
|
context,
|
|
LegalLinks.resolve(session.webCabinetUrl, LegalLinks.cookies),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SliverToBoxAdapter(child: SizedBox(height: AppSpacing.s5)),
|
|
SliverToBoxAdapter(
|
|
child: AppListSection(
|
|
footer: '${AppBrand.name} · версия 0.1.0',
|
|
children: [
|
|
AppListTile(
|
|
title: 'Выйти',
|
|
destructive: true,
|
|
showChevron: false,
|
|
onTap: () => _signOut(context, session),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SliverToBoxAdapter(child: SizedBox(height: AppSpacing.s7)),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _refreshAll(BuildContext context) async {
|
|
await Future.wait([
|
|
context.read<BudgetsController>().load(silent: true),
|
|
context.read<JournalController>().load(silent: true),
|
|
context.read<JobsController>().load(silent: true),
|
|
]);
|
|
|
|
if (context.mounted) {
|
|
await showAppToast(context, message: 'Данные обновлены');
|
|
}
|
|
}
|
|
|
|
Future<void> _signOut(BuildContext context, SessionController session) async {
|
|
final confirmed = await showAppAlert(
|
|
context: context,
|
|
title: 'Выйти из аккаунта?',
|
|
message: 'Токен будет удалён с устройства.',
|
|
confirmLabel: 'Выйти',
|
|
cancelLabel: 'Отмена',
|
|
destructive: true,
|
|
);
|
|
|
|
if (confirmed == true) await session.signOut();
|
|
}
|
|
}
|