Compare commits
	
		
			4 Commits
		
	
	
		
			9311bfc3b5
			...
			9cc577adbe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9cc577adbe | |||
| dd196b7754 | |||
| 16c07c2133 | |||
| 6bcb658d44 | 
@@ -897,5 +897,23 @@
 | 
			
		||||
  "albumDescription": "View albums and manage attachments.",
 | 
			
		||||
  "stickers": "Stickers",
 | 
			
		||||
  "stickersDescription": "View sticker packs and manage stickers.",
 | 
			
		||||
  "navBottomUnauthorizedCaption": "Or create an account"
 | 
			
		||||
  "navBottomUnauthorizedCaption": "Or create an account",
 | 
			
		||||
  "walletCurrencyGoldenShort": "GDP",
 | 
			
		||||
  "walletCurrencyGolden": {
 | 
			
		||||
    "one": "{} Golden Point",
 | 
			
		||||
    "other": "{} Golden Points"
 | 
			
		||||
  },
 | 
			
		||||
  "walletTransactionTypeNormal": "Source Point",
 | 
			
		||||
  "walletTransactionTypeGolden": "Golden Point",
 | 
			
		||||
  "accountProgram": "Programs",
 | 
			
		||||
  "accountProgramDescription": "Explore the available member programs.",
 | 
			
		||||
  "accountProgramJoin": "Join Program",
 | 
			
		||||
  "accountProgramJoinRequirements": "Requirements",
 | 
			
		||||
  "accountProgramJoinPricing": "Pricing",
 | 
			
		||||
  "accountProgramJoinPricingHint": "Billed every (30 days) month.",
 | 
			
		||||
  "accountProgramLeaveHint": "After leaving the program, the source points will not be refunded.",
 | 
			
		||||
  "accountProgramJoined": "Joined Program.",
 | 
			
		||||
  "accountProgramAlreadyJoined": "Joined",
 | 
			
		||||
  "accountProgramLeft": "Left Program.",
 | 
			
		||||
  "leave": "Leave"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -895,5 +895,23 @@
 | 
			
		||||
  "albumDescription": "查看相册与管理上传附件。",
 | 
			
		||||
  "stickers": "贴图",
 | 
			
		||||
  "stickersDescription": "查看贴图包与管理贴图。",
 | 
			
		||||
  "navBottomUnauthorizedCaption": "或者注册一个账号"
 | 
			
		||||
  "navBottomUnauthorizedCaption": "或者注册一个账号",
 | 
			
		||||
  "walletCurrencyGoldenShort": "金点",
 | 
			
		||||
  "walletCurrencyGolden": {
 | 
			
		||||
    "one": "{} 金点",
 | 
			
		||||
    "other": "{} 金点"
 | 
			
		||||
  },
 | 
			
		||||
  "walletTransactionTypeNormal": "源点",
 | 
			
		||||
  "walletTransactionTypeGolden": "金点",
 | 
			
		||||
  "accountProgram": "计划",
 | 
			
		||||
  "accountProgramDescription": "了解可用的成员计划。",
 | 
			
		||||
  "accountProgramJoin": "加入计划",
 | 
			
		||||
  "accountProgramJoinRequirements": "要求",
 | 
			
		||||
  "accountProgramJoinPricing": "价格",
 | 
			
		||||
  "accountProgramJoinPricingHint": "按月(30 天)收费",
 | 
			
		||||
  "accountProgramLeaveHint": "离开计划后,之前花费的源点不会退款。",
 | 
			
		||||
  "accountProgramJoined": "已加入计划。",
 | 
			
		||||
  "accountProgramLeft": "已离开计划。",
 | 
			
		||||
  "accountProgramAlreadyJoined": "已加入",
 | 
			
		||||
  "leave": "离开"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import 'package:surface/screens/account/prefs/notify.dart';
 | 
			
		||||
import 'package:surface/screens/account/prefs/security.dart';
 | 
			
		||||
import 'package:surface/screens/account/profile_page.dart';
 | 
			
		||||
import 'package:surface/screens/account/profile_edit.dart';
 | 
			
		||||
import 'package:surface/screens/account/programs.dart';
 | 
			
		||||
import 'package:surface/screens/account/publishers/publisher_edit.dart';
 | 
			
		||||
import 'package:surface/screens/account/publishers/publisher_new.dart';
 | 
			
		||||
import 'package:surface/screens/account/publishers/publishers.dart';
 | 
			
		||||
@@ -130,6 +131,11 @@ final _appRoutes = [
 | 
			
		||||
    name: 'account',
 | 
			
		||||
    builder: (context, state) => const AccountScreen(),
 | 
			
		||||
    routes: [
 | 
			
		||||
      GoRoute(
 | 
			
		||||
        path: '/programs',
 | 
			
		||||
        name: 'accountProgram',
 | 
			
		||||
        builder: (context, state) => const AccountProgramScreen(),
 | 
			
		||||
      ),
 | 
			
		||||
      GoRoute(
 | 
			
		||||
        path: '/contacts',
 | 
			
		||||
        name: 'accountContactMethods',
 | 
			
		||||
 
 | 
			
		||||
@@ -145,6 +145,16 @@ class _AuthorizedAccountScreen extends StatelessWidget {
 | 
			
		||||
            GoRouter.of(context).pushNamed('accountPublishers');
 | 
			
		||||
          },
 | 
			
		||||
        ),
 | 
			
		||||
        ListTile(
 | 
			
		||||
          title: Text('accountProgram').tr(),
 | 
			
		||||
          subtitle: Text('accountProgramDescription').tr(),
 | 
			
		||||
          contentPadding: const EdgeInsets.symmetric(horizontal: 24),
 | 
			
		||||
          leading: const Icon(Symbols.communities),
 | 
			
		||||
          trailing: const Icon(Symbols.chevron_right),
 | 
			
		||||
          onTap: () {
 | 
			
		||||
            GoRouter.of(context).pushNamed('accountProgram');
 | 
			
		||||
          },
 | 
			
		||||
        ),
 | 
			
		||||
        ListTile(
 | 
			
		||||
          title: Text('friends').tr(),
 | 
			
		||||
          subtitle: Text('friendsDescription').tr(),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										284
									
								
								lib/screens/account/programs.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								lib/screens/account/programs.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,284 @@
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
import 'package:surface/providers/experience.dart';
 | 
			
		||||
import 'package:surface/providers/sn_network.dart';
 | 
			
		||||
import 'package:surface/types/account.dart';
 | 
			
		||||
import 'package:surface/widgets/dialog.dart';
 | 
			
		||||
import 'package:surface/widgets/loading_indicator.dart';
 | 
			
		||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
 | 
			
		||||
 | 
			
		||||
class AccountProgramScreen extends StatefulWidget {
 | 
			
		||||
  const AccountProgramScreen({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<AccountProgramScreen> createState() => _AccountProgramScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _AccountProgramScreenState extends State<AccountProgramScreen> {
 | 
			
		||||
  bool _isBusy = false;
 | 
			
		||||
  final List<SnProgram> _programs = List.empty(growable: true);
 | 
			
		||||
  final List<SnProgramMember> _programMembers = List.empty(growable: true);
 | 
			
		||||
 | 
			
		||||
  Future<void> _fetchPrograms() async {
 | 
			
		||||
    _programs.clear();
 | 
			
		||||
    setState(() => _isBusy = true);
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      final resp = await sn.client.get('/cgi/id/programs');
 | 
			
		||||
      _programs.addAll(
 | 
			
		||||
        resp.data.map((ele) => SnProgram.fromJson(ele)).cast<SnProgram>(),
 | 
			
		||||
      );
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
    } finally {
 | 
			
		||||
      setState(() => _isBusy = false);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _fetchProgramMembers() async {
 | 
			
		||||
    _programMembers.clear();
 | 
			
		||||
    setState(() => _isBusy = true);
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      final resp = await sn.client.get('/cgi/id/programs/members');
 | 
			
		||||
      _programMembers.addAll(
 | 
			
		||||
        resp.data
 | 
			
		||||
            .map((ele) => SnProgramMember.fromJson(ele))
 | 
			
		||||
            .cast<SnProgramMember>(),
 | 
			
		||||
      );
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
    } finally {
 | 
			
		||||
      setState(() => _isBusy = false);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    _fetchPrograms();
 | 
			
		||||
    _fetchProgramMembers();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: Text('accountProgram').tr(),
 | 
			
		||||
      ),
 | 
			
		||||
      body: Column(
 | 
			
		||||
        children: [
 | 
			
		||||
          LoadingIndicator(isActive: _isBusy),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: ListView.builder(
 | 
			
		||||
              padding: EdgeInsets.zero,
 | 
			
		||||
              itemCount: _programs.length,
 | 
			
		||||
              itemBuilder: (context, idx) {
 | 
			
		||||
                final ele = _programs[idx];
 | 
			
		||||
                return Card(
 | 
			
		||||
                  child: InkWell(
 | 
			
		||||
                    borderRadius: BorderRadius.all(Radius.circular(8)),
 | 
			
		||||
                    onTap: () {
 | 
			
		||||
                      showModalBottomSheet(
 | 
			
		||||
                        context: context,
 | 
			
		||||
                        builder: (context) => _ProgramJoinPopup(
 | 
			
		||||
                          program: ele,
 | 
			
		||||
                          isJoined: _programMembers
 | 
			
		||||
                              .any((ele) => ele.programId == ele.id),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ).then((value) {
 | 
			
		||||
                        _fetchProgramMembers();
 | 
			
		||||
                      });
 | 
			
		||||
                    },
 | 
			
		||||
                    child: Column(
 | 
			
		||||
                      children: [
 | 
			
		||||
                        if (ele.appearance['banner'] != null)
 | 
			
		||||
                          AspectRatio(
 | 
			
		||||
                            aspectRatio: 16 / 5,
 | 
			
		||||
                            child: ClipRRect(
 | 
			
		||||
                              borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                              child: Container(
 | 
			
		||||
                                color: Theme.of(context)
 | 
			
		||||
                                    .colorScheme
 | 
			
		||||
                                    .surfaceVariant,
 | 
			
		||||
                                child: Image.network(
 | 
			
		||||
                                  ele.appearance['banner'],
 | 
			
		||||
                                  color: Theme.of(context)
 | 
			
		||||
                                      .colorScheme
 | 
			
		||||
                                      .onSurfaceVariant,
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        Padding(
 | 
			
		||||
                          padding: const EdgeInsets.all(16),
 | 
			
		||||
                          child: Row(
 | 
			
		||||
                            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              Expanded(
 | 
			
		||||
                                child: Column(
 | 
			
		||||
                                  crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                                  children: [
 | 
			
		||||
                                    Text(
 | 
			
		||||
                                      ele.name,
 | 
			
		||||
                                      style: Theme.of(context)
 | 
			
		||||
                                          .textTheme
 | 
			
		||||
                                          .titleMedium,
 | 
			
		||||
                                    ).bold(),
 | 
			
		||||
                                    Text(
 | 
			
		||||
                                      ele.description,
 | 
			
		||||
                                      maxLines: 3,
 | 
			
		||||
                                      overflow: TextOverflow.ellipsis,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    if (_programMembers
 | 
			
		||||
                                        .any((ele) => ele.programId == ele.id))
 | 
			
		||||
                                      Text('accountProgramAlreadyJoined'.tr())
 | 
			
		||||
                                          .opacity(0.75),
 | 
			
		||||
                                  ],
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ).padding(horizontal: 8);
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ProgramJoinPopup extends StatefulWidget {
 | 
			
		||||
  final SnProgram program;
 | 
			
		||||
  final bool isJoined;
 | 
			
		||||
  const _ProgramJoinPopup({required this.program, required this.isJoined});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<_ProgramJoinPopup> createState() => _ProgramJoinPopupState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ProgramJoinPopupState extends State<_ProgramJoinPopup> {
 | 
			
		||||
  bool _isBusy = false;
 | 
			
		||||
 | 
			
		||||
  Future<void> _joinProgram() async {
 | 
			
		||||
    setState(() => _isBusy = true);
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      await sn.client.post('/cgi/id/programs/${widget.program.id}');
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      Navigator.pop(context, true);
 | 
			
		||||
      context.showSnackbar('accountProgramJoined'.tr());
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
    } finally {
 | 
			
		||||
      setState(() => _isBusy = false);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _leaveProgram() async {
 | 
			
		||||
    setState(() => _isBusy = true);
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      await sn.client.delete('/cgi/id/programs/${widget.program.id}');
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      Navigator.pop(context, true);
 | 
			
		||||
      context.showSnackbar('accountProgramLeft'.tr());
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
    } finally {
 | 
			
		||||
      setState(() => _isBusy = false);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Column(
 | 
			
		||||
      crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
      children: [
 | 
			
		||||
        Row(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.center,
 | 
			
		||||
          children: [
 | 
			
		||||
            const Icon(Symbols.add, size: 24),
 | 
			
		||||
            const Gap(16),
 | 
			
		||||
            Text(
 | 
			
		||||
              'accountProgramJoin',
 | 
			
		||||
              style: Theme.of(context).textTheme.titleLarge,
 | 
			
		||||
            ).tr(),
 | 
			
		||||
          ],
 | 
			
		||||
        ).padding(horizontal: 20, top: 16, bottom: 12),
 | 
			
		||||
        Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            if (widget.program.appearance['banner'] != null)
 | 
			
		||||
              AspectRatio(
 | 
			
		||||
                aspectRatio: 16 / 5,
 | 
			
		||||
                child: ClipRRect(
 | 
			
		||||
                  borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                  child: Container(
 | 
			
		||||
                    color: Theme.of(context).colorScheme.surfaceVariant,
 | 
			
		||||
                    child: Image.network(
 | 
			
		||||
                      widget.program.appearance['banner'],
 | 
			
		||||
                      color: Theme.of(context).colorScheme.onSurfaceVariant,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ).padding(bottom: 12),
 | 
			
		||||
            Text(
 | 
			
		||||
              widget.program.name,
 | 
			
		||||
              style: Theme.of(context).textTheme.titleMedium,
 | 
			
		||||
            ).bold(),
 | 
			
		||||
            Text(
 | 
			
		||||
              widget.program.description,
 | 
			
		||||
              maxLines: 3,
 | 
			
		||||
              overflow: TextOverflow.ellipsis,
 | 
			
		||||
            ),
 | 
			
		||||
            const Gap(8),
 | 
			
		||||
            Text(
 | 
			
		||||
              'accountProgramJoinRequirements',
 | 
			
		||||
              style: Theme.of(context).textTheme.titleMedium,
 | 
			
		||||
            ).tr().bold(),
 | 
			
		||||
            Text('≥EXP ${widget.program.expRequirement}'),
 | 
			
		||||
            Text('≥Lv${getLevelFromExp(widget.program.expRequirement)}'),
 | 
			
		||||
            const Gap(8),
 | 
			
		||||
            Text(
 | 
			
		||||
              'accountProgramJoinPricing',
 | 
			
		||||
              style: Theme.of(context).textTheme.titleMedium,
 | 
			
		||||
            ).tr().bold(),
 | 
			
		||||
            Text('walletCurrency${widget.program.price['currency'].toString().capitalize().replaceFirst('Normal', '')}')
 | 
			
		||||
                .plural(widget.program.price['amount'].toDouble()),
 | 
			
		||||
            Text('accountProgramJoinPricingHint').tr().opacity(0.75),
 | 
			
		||||
            const Gap(8),
 | 
			
		||||
            if (widget.isJoined)
 | 
			
		||||
              Text('accountProgramLeaveHint')
 | 
			
		||||
                  .tr()
 | 
			
		||||
                  .opacity(0.75)
 | 
			
		||||
                  .padding(bottom: 8),
 | 
			
		||||
            if (!widget.isJoined)
 | 
			
		||||
              ElevatedButton(
 | 
			
		||||
                onPressed: _isBusy ? null : _joinProgram,
 | 
			
		||||
                child: Text('join').tr(),
 | 
			
		||||
              )
 | 
			
		||||
            else
 | 
			
		||||
              ElevatedButton(
 | 
			
		||||
                onPressed: _isBusy ? null : _leaveProgram,
 | 
			
		||||
                child: Text('leave').tr(),
 | 
			
		||||
              ),
 | 
			
		||||
          ],
 | 
			
		||||
        ).padding(horizontal: 24),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -7,7 +7,7 @@ import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
import 'package:surface/providers/sn_network.dart';
 | 
			
		||||
import 'package:surface/screens/captcha.dart';
 | 
			
		||||
import 'package:surface/screens/captcha/captcha.dart';
 | 
			
		||||
import 'package:surface/widgets/dialog.dart';
 | 
			
		||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher_string.dart';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								lib/screens/captcha/captcha.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/screens/captcha/captcha.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
import 'package:flutter/foundation.dart' show kIsWeb;
 | 
			
		||||
 | 
			
		||||
export 'captcha_native.dart' if (kIsWeb) 'captcha_web.dart';
 | 
			
		||||
							
								
								
									
										37
									
								
								lib/screens/captcha/captcha_native.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								lib/screens/captcha/captcha_native.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
import 'package:surface/providers/config.dart';
 | 
			
		||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
 | 
			
		||||
 | 
			
		||||
class CaptchaScreen extends StatefulWidget {
 | 
			
		||||
  const CaptchaScreen({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<CaptchaScreen> createState() => _CaptchaScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _CaptchaScreenState extends State<CaptchaScreen> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final cfg = context.read<ConfigProvider>();
 | 
			
		||||
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(title: Text("reCaptcha").tr()),
 | 
			
		||||
      body: InAppWebView(
 | 
			
		||||
        initialUrlRequest: URLRequest(
 | 
			
		||||
          url: WebUri('${cfg.serverUrl}/captcha?redirect_uri=solink://captcha'),
 | 
			
		||||
        ),
 | 
			
		||||
        shouldOverrideUrlLoading: (controller, navigationAction) async {
 | 
			
		||||
          Uri? url = navigationAction.request.url;
 | 
			
		||||
          if (url != null && url.queryParameters.containsKey('captcha_tk')) {
 | 
			
		||||
            Navigator.pop(context, url.queryParameters['captcha_tk']!);
 | 
			
		||||
            return NavigationActionPolicy.CANCEL;
 | 
			
		||||
          }
 | 
			
		||||
          return NavigationActionPolicy.ALLOW;
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +1,13 @@
 | 
			
		||||
import 'dart:html' as html;
 | 
			
		||||
import 'dart:ui_web' as ui;
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/foundation.dart' show kIsWeb;
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
import 'package:surface/providers/config.dart';
 | 
			
		||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
 | 
			
		||||
 | 
			
		||||
class CaptchaScreen extends StatefulWidget {
 | 
			
		||||
  const CaptchaScreen({
 | 
			
		||||
    super.key,
 | 
			
		||||
  });
 | 
			
		||||
  const CaptchaScreen({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<CaptchaScreen> createState() => _CaptchaScreenState();
 | 
			
		||||
@@ -21,9 +17,7 @@ class _CaptchaScreenState extends State<CaptchaScreen> {
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    if (kIsWeb) {
 | 
			
		||||
      _setupWebListener();
 | 
			
		||||
    }
 | 
			
		||||
    _setupWebListener();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _setupWebListener() {
 | 
			
		||||
@@ -37,9 +31,8 @@ class _CaptchaScreenState extends State<CaptchaScreen> {
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Create an iframe for the captcha page
 | 
			
		||||
    final iframe = html.IFrameElement()
 | 
			
		||||
      ..src = '${context.read<ConfigProvider>().serverUrl}/captcha?redirect_uri=solink://captcha'
 | 
			
		||||
      ..src = '${context.read<ConfigProvider>().serverUrl}/captcha?redirect_uri=web'
 | 
			
		||||
      ..style.border = 'none'
 | 
			
		||||
      ..width = '100%'
 | 
			
		||||
      ..height = '100%';
 | 
			
		||||
@@ -47,36 +40,15 @@ class _CaptchaScreenState extends State<CaptchaScreen> {
 | 
			
		||||
    html.document.body!.append(iframe);
 | 
			
		||||
    ui.platformViewRegistry.registerViewFactory(
 | 
			
		||||
      'captcha-iframe',
 | 
			
		||||
      (int viewId) => iframe,
 | 
			
		||||
          (int viewId) => iframe,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final cfg = context.read<ConfigProvider>();
 | 
			
		||||
 | 
			
		||||
    if (kIsWeb) {
 | 
			
		||||
      return AppScaffold(
 | 
			
		||||
        appBar: AppBar(title: Text("reCaptcha").tr()),
 | 
			
		||||
        body: HtmlElementView(viewType: 'captcha-iframe'),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(title: Text("reCaptcha").tr()),
 | 
			
		||||
      body: InAppWebView(
 | 
			
		||||
        initialUrlRequest: URLRequest(
 | 
			
		||||
          url: WebUri('${cfg.serverUrl}/captcha?redirect_uri=solink://captcha'),
 | 
			
		||||
        ),
 | 
			
		||||
        shouldOverrideUrlLoading: (controller, navigationAction) async {
 | 
			
		||||
          Uri? url = navigationAction.request.url;
 | 
			
		||||
          if (url != null && url.queryParameters.containsKey('captcha_tk')) {
 | 
			
		||||
            Navigator.pop(context, url.queryParameters['captcha_tk']!);
 | 
			
		||||
            return NavigationActionPolicy.CANCEL;
 | 
			
		||||
          }
 | 
			
		||||
          return NavigationActionPolicy.ALLOW;
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
      body: HtmlElementView(viewType: 'captcha-iframe'),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -46,9 +46,7 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      final resp = await sn.client.get('/cgi/id/users/me/relations?status=1');
 | 
			
		||||
      _relations = List<SnRelationship>.from(
 | 
			
		||||
        resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [],
 | 
			
		||||
      );
 | 
			
		||||
      _relations = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
@@ -66,9 +64,7 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      final resp = await sn.client.get('/cgi/id/users/me/relations?status=0,3');
 | 
			
		||||
      _requests = List<SnRelationship>.from(
 | 
			
		||||
        resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [],
 | 
			
		||||
      );
 | 
			
		||||
      _requests = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
@@ -86,9 +82,7 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      final resp = await sn.client.get('/cgi/id/users/me/relations?status=2');
 | 
			
		||||
      _blocks = List<SnRelationship>.from(
 | 
			
		||||
        resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [],
 | 
			
		||||
      );
 | 
			
		||||
      _blocks = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
@@ -104,11 +98,7 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final rel = context.read<SnRelationshipProvider>();
 | 
			
		||||
      await rel.updateRelationship(
 | 
			
		||||
        relation.relatedId,
 | 
			
		||||
        dstStatus,
 | 
			
		||||
        relation.permNodes,
 | 
			
		||||
      );
 | 
			
		||||
      await rel.updateRelationship(relation.relatedId, dstStatus, relation.permNodes);
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      _fetchRelations();
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
@@ -122,9 +112,7 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
  Future<void> _deleteRelation(SnRelationship relation) async {
 | 
			
		||||
    final confirm = await context.showConfirmDialog(
 | 
			
		||||
      'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
 | 
			
		||||
      'friendDeleteDescription'.tr(args: [
 | 
			
		||||
        relation.related?.nick ?? 'unknown'.tr(),
 | 
			
		||||
      ]),
 | 
			
		||||
      'friendDeleteDescription'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
 | 
			
		||||
    );
 | 
			
		||||
    if (!confirm) return;
 | 
			
		||||
    if (!mounted) return;
 | 
			
		||||
@@ -145,10 +133,9 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _showRequests() {
 | 
			
		||||
    showModalBottomSheet(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (context) => _FriendshipListWidget(relations: _requests),
 | 
			
		||||
    ).then((value) {
 | 
			
		||||
    showModalBottomSheet(context: context, builder: (context) => _FriendshipListWidget(relations: _requests)).then((
 | 
			
		||||
      value,
 | 
			
		||||
    ) {
 | 
			
		||||
      if (value != null) {
 | 
			
		||||
        _fetchRequests();
 | 
			
		||||
        _fetchRelations();
 | 
			
		||||
@@ -157,10 +144,9 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _showBlocks() {
 | 
			
		||||
    showModalBottomSheet(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (context) => _FriendshipListWidget(relations: _blocks),
 | 
			
		||||
    ).then((value) {
 | 
			
		||||
    showModalBottomSheet(context: context, builder: (context) => _FriendshipListWidget(relations: _blocks)).then((
 | 
			
		||||
      value,
 | 
			
		||||
    ) {
 | 
			
		||||
      if (value != null) {
 | 
			
		||||
        _fetchBlocks();
 | 
			
		||||
        _fetchRelations();
 | 
			
		||||
@@ -173,9 +159,7 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      await sn.client.post('/cgi/id/users/me/relations', data: {
 | 
			
		||||
        'related': user.name,
 | 
			
		||||
      });
 | 
			
		||||
      await sn.client.post('/cgi/id/users/me/relations', data: {'related': user.name});
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showSnackbar('friendRequestSent'.tr());
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
@@ -200,29 +184,19 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
 | 
			
		||||
    if (!ua.isAuthorized) {
 | 
			
		||||
      return AppScaffold(
 | 
			
		||||
        appBar: AppBar(
 | 
			
		||||
          leading: PageBackButton(),
 | 
			
		||||
          title: Text('screenFriend').tr(),
 | 
			
		||||
        ),
 | 
			
		||||
        body: Center(
 | 
			
		||||
          child: UnauthorizedHint(),
 | 
			
		||||
        ),
 | 
			
		||||
        appBar: AppBar(leading: PageBackButton(), title: Text('screenFriend').tr()),
 | 
			
		||||
        body: Center(child: UnauthorizedHint()),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: AutoAppBarLeading(),
 | 
			
		||||
        title: Text('screenFriend').tr(),
 | 
			
		||||
      ),
 | 
			
		||||
      appBar: AppBar(leading: AutoAppBarLeading(), title: Text('screenFriend').tr()),
 | 
			
		||||
      floatingActionButton: FloatingActionButton(
 | 
			
		||||
        child: const Icon(Symbols.add),
 | 
			
		||||
        onPressed: () async {
 | 
			
		||||
          final user = await showModalBottomSheet<SnAccount?>(
 | 
			
		||||
            context: context,
 | 
			
		||||
            builder: (context) => AccountSelect(
 | 
			
		||||
              title: 'friendNew'.tr(),
 | 
			
		||||
            ),
 | 
			
		||||
            builder: (context) => AccountSelect(title: 'friendNew'.tr()),
 | 
			
		||||
          );
 | 
			
		||||
          if (!mounted) return;
 | 
			
		||||
          if (user == null) return;
 | 
			
		||||
@@ -235,9 +209,7 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
          if (_requests.isNotEmpty)
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text('friendRequests').tr(),
 | 
			
		||||
              subtitle: Text(
 | 
			
		||||
                'friendRequestsDescription',
 | 
			
		||||
              ).plural(_requests.length),
 | 
			
		||||
              subtitle: Text('friendRequestsDescription').plural(_requests.length),
 | 
			
		||||
              contentPadding: const EdgeInsets.symmetric(horizontal: 24),
 | 
			
		||||
              leading: const Icon(Symbols.group_add),
 | 
			
		||||
              trailing: const Icon(Symbols.chevron_right),
 | 
			
		||||
@@ -246,25 +218,19 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
          if (_blocks.isNotEmpty)
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text('friendBlocklist').tr(),
 | 
			
		||||
              subtitle: Text(
 | 
			
		||||
                'friendBlocklistDescription',
 | 
			
		||||
              ).plural(_blocks.length),
 | 
			
		||||
              subtitle: Text('friendBlocklistDescription').plural(_blocks.length),
 | 
			
		||||
              contentPadding: const EdgeInsets.symmetric(horizontal: 24),
 | 
			
		||||
              leading: const Icon(Symbols.block),
 | 
			
		||||
              trailing: const Icon(Symbols.chevron_right),
 | 
			
		||||
              onTap: _showBlocks,
 | 
			
		||||
            ),
 | 
			
		||||
          if (_requests.isNotEmpty || _blocks.isNotEmpty)
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
          if (_requests.isNotEmpty || _blocks.isNotEmpty) const Divider(height: 1),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: MediaQuery.removePadding(
 | 
			
		||||
              context: context,
 | 
			
		||||
              removeTop: true,
 | 
			
		||||
              child: RefreshIndicator(
 | 
			
		||||
                onRefresh: () => Future.wait([
 | 
			
		||||
                  _fetchRelations(),
 | 
			
		||||
                  _fetchRequests(),
 | 
			
		||||
                ]),
 | 
			
		||||
                onRefresh: () => Future.wait([_fetchRelations(), _fetchRequests()]),
 | 
			
		||||
                child: ListView.builder(
 | 
			
		||||
                  itemCount: _relations.length,
 | 
			
		||||
                  itemBuilder: (context, index) {
 | 
			
		||||
@@ -288,16 +254,12 @@ class _FriendScreenState extends State<FriendScreen> {
 | 
			
		||||
                              mainAxisAlignment: MainAxisAlignment.end,
 | 
			
		||||
                              children: [
 | 
			
		||||
                                InkWell(
 | 
			
		||||
                                  onTap: _isUpdating
 | 
			
		||||
                                      ? null
 | 
			
		||||
                                      : () => _changeRelation(relation, 2),
 | 
			
		||||
                                  onTap: _isUpdating ? null : () => _changeRelation(relation, 2),
 | 
			
		||||
                                  child: Text('friendBlock').tr(),
 | 
			
		||||
                                ),
 | 
			
		||||
                                const Gap(8),
 | 
			
		||||
                                InkWell(
 | 
			
		||||
                                  onTap: _isUpdating
 | 
			
		||||
                                      ? null
 | 
			
		||||
                                      : () => _deleteRelation(relation),
 | 
			
		||||
                                  onTap: _isUpdating ? null : () => _deleteRelation(relation),
 | 
			
		||||
                                  child: Text('friendDeleteAction').tr(),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ],
 | 
			
		||||
@@ -366,11 +328,7 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> {
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final rel = context.read<SnRelationshipProvider>();
 | 
			
		||||
      await rel.updateRelationship(
 | 
			
		||||
        relation.relatedId,
 | 
			
		||||
        dstStatus,
 | 
			
		||||
        relation.permNodes,
 | 
			
		||||
      );
 | 
			
		||||
      await rel.updateRelationship(relation.relatedId, dstStatus, relation.permNodes);
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      Navigator.pop(context, true);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
@@ -384,9 +342,7 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> {
 | 
			
		||||
  Future<void> _deleteRelation(SnRelationship relation) async {
 | 
			
		||||
    final confirm = await context.showConfirmDialog(
 | 
			
		||||
      'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
 | 
			
		||||
      'friendDeleteDescription'.tr(args: [
 | 
			
		||||
        relation.related?.nick ?? 'unknown'.tr(),
 | 
			
		||||
      ]),
 | 
			
		||||
      'friendDeleteDescription'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
 | 
			
		||||
    );
 | 
			
		||||
    if (!confirm) return;
 | 
			
		||||
    if (!mounted) return;
 | 
			
		||||
@@ -426,9 +382,7 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> {
 | 
			
		||||
              mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.end,
 | 
			
		||||
              children: [
 | 
			
		||||
                Text(kFriendStatus[relation.status] ?? 'unknown')
 | 
			
		||||
                    .tr()
 | 
			
		||||
                    .opacity(0.75),
 | 
			
		||||
                Text(kFriendStatus[relation.status] ?? 'unknown').tr().opacity(0.75),
 | 
			
		||||
                if (relation.status == 0)
 | 
			
		||||
                  Row(
 | 
			
		||||
                    mainAxisAlignment: MainAxisAlignment.end,
 | 
			
		||||
@@ -449,8 +403,7 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> {
 | 
			
		||||
                    mainAxisAlignment: MainAxisAlignment.end,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      InkWell(
 | 
			
		||||
                        onTap:
 | 
			
		||||
                            _isBusy ? null : () => _changeRelation(relation, 1),
 | 
			
		||||
                        onTap: _isBusy ? null : () => _changeRelation(relation, 1),
 | 
			
		||||
                        child: Text('friendUnblock').tr(),
 | 
			
		||||
                      ),
 | 
			
		||||
                      const Gap(8),
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ import 'package:surface/providers/sn_network.dart';
 | 
			
		||||
import 'package:surface/providers/special_day.dart';
 | 
			
		||||
import 'package:surface/providers/userinfo.dart';
 | 
			
		||||
import 'package:surface/providers/widget.dart';
 | 
			
		||||
import 'package:surface/screens/captcha.dart';
 | 
			
		||||
import 'package:surface/screens/captcha/captcha.dart';
 | 
			
		||||
import 'package:surface/types/check_in.dart';
 | 
			
		||||
import 'package:surface/types/post.dart';
 | 
			
		||||
import 'package:surface/widgets/app_bar_leading.dart';
 | 
			
		||||
 
 | 
			
		||||
@@ -28,11 +28,8 @@ class _PostShuffleScreenState extends State<PostShuffleScreen> {
 | 
			
		||||
    setState(() => _isBusy = true);
 | 
			
		||||
    try {
 | 
			
		||||
      final pt = context.read<SnPostContentProvider>();
 | 
			
		||||
      final result = await pt.listPosts(
 | 
			
		||||
        take: 10,
 | 
			
		||||
        offset: _posts.length,
 | 
			
		||||
        isShuffle: true,
 | 
			
		||||
      );
 | 
			
		||||
      final result =
 | 
			
		||||
          await pt.listPosts(take: 10, offset: _posts.length, isShuffle: true);
 | 
			
		||||
      _posts.addAll(result.$1);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
@@ -57,19 +54,14 @@ class _PostShuffleScreenState extends State<PostShuffleScreen> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: Text('postShuffle').tr(),
 | 
			
		||||
      ),
 | 
			
		||||
      appBar: AppBar(title: Text('postShuffle').tr()),
 | 
			
		||||
      body: Stack(
 | 
			
		||||
        children: [
 | 
			
		||||
          Column(
 | 
			
		||||
            children: [
 | 
			
		||||
              if (_isBusy || _posts.isEmpty)
 | 
			
		||||
                const Expanded(
 | 
			
		||||
                  child: Center(
 | 
			
		||||
                    child: CircularProgressIndicator(),
 | 
			
		||||
                  ),
 | 
			
		||||
                )
 | 
			
		||||
                    child: Center(child: CircularProgressIndicator()))
 | 
			
		||||
              else
 | 
			
		||||
                Expanded(
 | 
			
		||||
                  child: CardSwiper(
 | 
			
		||||
@@ -81,17 +73,20 @@ class _PostShuffleScreenState extends State<PostShuffleScreen> {
 | 
			
		||||
                      final ele = _posts[idx];
 | 
			
		||||
                      return SingleChildScrollView(
 | 
			
		||||
                        child: Center(
 | 
			
		||||
                          child: OpenablePostItem(
 | 
			
		||||
                            key: ValueKey(ele),
 | 
			
		||||
                            data: ele,
 | 
			
		||||
                            maxWidth: 640,
 | 
			
		||||
                            onChanged: (ele) {
 | 
			
		||||
                              _posts[idx] = ele;
 | 
			
		||||
                              setState(() {});
 | 
			
		||||
                            },
 | 
			
		||||
                            onDeleted: () {
 | 
			
		||||
                              _fetchPosts();
 | 
			
		||||
                            },
 | 
			
		||||
                          child: Card(
 | 
			
		||||
                            color: Theme.of(context).colorScheme.surface,
 | 
			
		||||
                            child: OpenablePostItem(
 | 
			
		||||
                              key: ValueKey(ele),
 | 
			
		||||
                              data: ele,
 | 
			
		||||
                              maxWidth: 640,
 | 
			
		||||
                              onChanged: (ele) {
 | 
			
		||||
                                _posts[idx] = ele;
 | 
			
		||||
                                setState(() {});
 | 
			
		||||
                              },
 | 
			
		||||
                              onDeleted: () {
 | 
			
		||||
                                _fetchPosts();
 | 
			
		||||
                              },
 | 
			
		||||
                            ).padding(all: 8),
 | 
			
		||||
                          ).padding(
 | 
			
		||||
                            all: 24,
 | 
			
		||||
                            bottom:
 | 
			
		||||
 
 | 
			
		||||
@@ -45,10 +45,7 @@ class _WalletScreenState extends State<WalletScreen> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: PageBackButton(),
 | 
			
		||||
        title: Text('screenAccountWallet').tr(),
 | 
			
		||||
      ),
 | 
			
		||||
      appBar: AppBar(leading: PageBackButton(), title: Text('screenAccountWallet').tr()),
 | 
			
		||||
      body: Column(
 | 
			
		||||
        children: [
 | 
			
		||||
          LoadingIndicator(isActive: _isBusy),
 | 
			
		||||
@@ -66,11 +63,6 @@ class _WalletScreenState extends State<WalletScreen> {
 | 
			
		||||
                mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                children: [
 | 
			
		||||
                  CircleAvatar(
 | 
			
		||||
                    radius: 28,
 | 
			
		||||
                    child: Icon(Symbols.wallet, size: 28),
 | 
			
		||||
                  ),
 | 
			
		||||
                  const Gap(12),
 | 
			
		||||
                  SizedBox(width: double.infinity),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    NumberFormat.compactCurrency(
 | 
			
		||||
@@ -81,6 +73,16 @@ class _WalletScreenState extends State<WalletScreen> {
 | 
			
		||||
                    style: Theme.of(context).textTheme.titleLarge,
 | 
			
		||||
                  ),
 | 
			
		||||
                  Text('walletCurrency'.plural(double.parse(_wallet!.balance))),
 | 
			
		||||
                  const Gap(16),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    NumberFormat.compactCurrency(
 | 
			
		||||
                      locale: EasyLocalization.of(context)!.currentLocale.toString(),
 | 
			
		||||
                      symbol: '${'walletCurrencyGoldenShort'.tr()} ',
 | 
			
		||||
                      decimalDigits: 2,
 | 
			
		||||
                    ).format(double.parse(_wallet!.goldenBalance)),
 | 
			
		||||
                    style: Theme.of(context).textTheme.titleLarge,
 | 
			
		||||
                  ),
 | 
			
		||||
                  Text('walletCurrencyGolden'.plural(double.parse(_wallet!.goldenBalance))),
 | 
			
		||||
                ],
 | 
			
		||||
              ).padding(horizontal: 20, vertical: 24),
 | 
			
		||||
            ).padding(horizontal: 8, top: 16, bottom: 4),
 | 
			
		||||
@@ -109,14 +111,12 @@ class _WalletTransactionListState extends State<_WalletTransactionList> {
 | 
			
		||||
    try {
 | 
			
		||||
      setState(() => _isBusy = true);
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      final resp = await sn.client.get('/cgi/wa/transactions/me', queryParameters: {
 | 
			
		||||
        'take': 10,
 | 
			
		||||
        'offset': _transactions.length,
 | 
			
		||||
      });
 | 
			
		||||
      _totalCount = resp.data['count'];
 | 
			
		||||
      _transactions.addAll(
 | 
			
		||||
        resp.data['data']?.map((e) => SnTransaction.fromJson(e)).cast<SnTransaction>() ?? [],
 | 
			
		||||
      final resp = await sn.client.get(
 | 
			
		||||
        '/cgi/wa/transactions/me',
 | 
			
		||||
        queryParameters: {'take': 10, 'offset': _transactions.length},
 | 
			
		||||
      );
 | 
			
		||||
      _totalCount = resp.data['count'];
 | 
			
		||||
      _transactions.addAll(resp.data['data']?.map((e) => SnTransaction.fromJson(e)).cast<SnTransaction>() ?? []);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
@@ -159,12 +159,18 @@ class _WalletTransactionListState extends State<_WalletTransactionList> {
 | 
			
		||||
                children: [
 | 
			
		||||
                  Text(ele.remark),
 | 
			
		||||
                  const Gap(2),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    DateFormat(
 | 
			
		||||
                      null,
 | 
			
		||||
                      EasyLocalization.of(context)!.currentLocale.toString(),
 | 
			
		||||
                    ).format(ele.createdAt),
 | 
			
		||||
                    style: Theme.of(context).textTheme.labelSmall,
 | 
			
		||||
                  Row(
 | 
			
		||||
                    children: [
 | 
			
		||||
                      Text(
 | 
			
		||||
                        'walletTransactionType${ele.currency.capitalize()}'.tr(),
 | 
			
		||||
                        style: Theme.of(context).textTheme.labelSmall,
 | 
			
		||||
                      ),
 | 
			
		||||
                      Text(' · ').textStyle(Theme.of(context).textTheme.labelSmall!).padding(right: 4),
 | 
			
		||||
                      Text(
 | 
			
		||||
                        DateFormat(null, EasyLocalization.of(context)!.currentLocale.toString()).format(ele.createdAt),
 | 
			
		||||
                        style: Theme.of(context).textTheme.labelSmall,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
@@ -193,37 +199,33 @@ class _CreateWalletWidgetState extends State<_CreateWalletWidget> {
 | 
			
		||||
    final TextEditingController passwordController = TextEditingController();
 | 
			
		||||
    final password = await showDialog<String?>(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (ctx) => AlertDialog(
 | 
			
		||||
        title: Text('walletCreate').tr(),
 | 
			
		||||
        content: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: [
 | 
			
		||||
            Text('walletCreatePassword').tr(),
 | 
			
		||||
            const Gap(8),
 | 
			
		||||
            TextField(
 | 
			
		||||
              autofocus: true,
 | 
			
		||||
              obscureText: true,
 | 
			
		||||
              controller: passwordController,
 | 
			
		||||
              decoration: InputDecoration(
 | 
			
		||||
                labelText: 'fieldPassword'.tr(),
 | 
			
		||||
              ),
 | 
			
		||||
      builder:
 | 
			
		||||
          (ctx) => AlertDialog(
 | 
			
		||||
            title: Text('walletCreate').tr(),
 | 
			
		||||
            content: Column(
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
              mainAxisSize: MainAxisSize.min,
 | 
			
		||||
              children: [
 | 
			
		||||
                Text('walletCreatePassword').tr(),
 | 
			
		||||
                const Gap(8),
 | 
			
		||||
                TextField(
 | 
			
		||||
                  autofocus: true,
 | 
			
		||||
                  obscureText: true,
 | 
			
		||||
                  controller: passwordController,
 | 
			
		||||
                  decoration: InputDecoration(labelText: 'fieldPassword'.tr()),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        actions: [
 | 
			
		||||
          TextButton(
 | 
			
		||||
            onPressed: () => Navigator.of(ctx).pop(),
 | 
			
		||||
            child: Text('cancel').tr(),
 | 
			
		||||
            actions: [
 | 
			
		||||
              TextButton(onPressed: () => Navigator.of(ctx).pop(), child: Text('cancel').tr()),
 | 
			
		||||
              TextButton(
 | 
			
		||||
                onPressed: () {
 | 
			
		||||
                  Navigator.of(ctx).pop(passwordController.text);
 | 
			
		||||
                },
 | 
			
		||||
                child: Text('next').tr(),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          TextButton(
 | 
			
		||||
            onPressed: () {
 | 
			
		||||
              Navigator.of(ctx).pop(passwordController.text);
 | 
			
		||||
            },
 | 
			
		||||
            child: Text('next').tr(),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    WidgetsBinding.instance.addPostFrameCallback((_) {
 | 
			
		||||
      passwordController.dispose();
 | 
			
		||||
@@ -234,9 +236,7 @@ class _CreateWalletWidgetState extends State<_CreateWalletWidget> {
 | 
			
		||||
    try {
 | 
			
		||||
      setState(() => _isBusy = true);
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      await sn.client.post('/cgi/wa/wallets/me', data: {
 | 
			
		||||
        'password': password,
 | 
			
		||||
      });
 | 
			
		||||
      await sn.client.post('/cgi/wa/wallets/me', data: {'password': password});
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
@@ -255,20 +255,14 @@ class _CreateWalletWidgetState extends State<_CreateWalletWidget> {
 | 
			
		||||
            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
            children: [
 | 
			
		||||
              CircleAvatar(
 | 
			
		||||
                radius: 28,
 | 
			
		||||
                child: Icon(Symbols.add, size: 28),
 | 
			
		||||
              ),
 | 
			
		||||
              CircleAvatar(radius: 28, child: Icon(Symbols.add, size: 28)),
 | 
			
		||||
              const Gap(12),
 | 
			
		||||
              Text('walletCreate', style: Theme.of(context).textTheme.titleLarge).tr(),
 | 
			
		||||
              Text('walletCreateSubtitle', style: Theme.of(context).textTheme.bodyMedium).tr(),
 | 
			
		||||
              const Gap(8),
 | 
			
		||||
              Align(
 | 
			
		||||
                alignment: Alignment.centerRight,
 | 
			
		||||
                child: TextButton(
 | 
			
		||||
                  onPressed: _isBusy ? null : () => _createWallet(),
 | 
			
		||||
                  child: Text('next').tr(),
 | 
			
		||||
                ),
 | 
			
		||||
                child: TextButton(onPressed: _isBusy ? null : () => _createWallet(), child: Text('next').tr()),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ).padding(horizontal: 20, vertical: 24),
 | 
			
		||||
 
 | 
			
		||||
@@ -184,3 +184,42 @@ abstract class SnActionEvent with _$SnActionEvent {
 | 
			
		||||
  factory SnActionEvent.fromJson(Map<String, Object?> json) =>
 | 
			
		||||
      _$SnActionEventFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
abstract class SnProgram with _$SnProgram {
 | 
			
		||||
  const factory SnProgram({
 | 
			
		||||
    required int id,
 | 
			
		||||
    required DateTime createdAt,
 | 
			
		||||
    required DateTime updatedAt,
 | 
			
		||||
    required DateTime? deletedAt,
 | 
			
		||||
    required String name,
 | 
			
		||||
    required String description,
 | 
			
		||||
    required String alias,
 | 
			
		||||
    required int expRequirement,
 | 
			
		||||
    required Map<String, dynamic> price,
 | 
			
		||||
    required Map<String, dynamic> badge,
 | 
			
		||||
    required Map<String, dynamic> group,
 | 
			
		||||
    required Map<String, dynamic> appearance,
 | 
			
		||||
  }) = _SnProgram;
 | 
			
		||||
 | 
			
		||||
  factory SnProgram.fromJson(Map<String, Object?> json) =>
 | 
			
		||||
      _$SnProgramFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
abstract class SnProgramMember with _$SnProgramMember {
 | 
			
		||||
  const factory SnProgramMember({
 | 
			
		||||
    required int id,
 | 
			
		||||
    required DateTime createdAt,
 | 
			
		||||
    required DateTime updatedAt,
 | 
			
		||||
    required DateTime? deletedAt,
 | 
			
		||||
    required DateTime lastPaid,
 | 
			
		||||
    required SnAccount account,
 | 
			
		||||
    required int accountId,
 | 
			
		||||
    required SnProgram program,
 | 
			
		||||
    required int programId,
 | 
			
		||||
  }) = _SnProgramMember;
 | 
			
		||||
 | 
			
		||||
  factory SnProgramMember.fromJson(Map<String, Object?> json) =>
 | 
			
		||||
      _$SnProgramMemberFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3470,4 +3470,763 @@ class __$SnActionEventCopyWithImpl<$Res>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$SnProgram {
 | 
			
		||||
  int get id;
 | 
			
		||||
  DateTime get createdAt;
 | 
			
		||||
  DateTime get updatedAt;
 | 
			
		||||
  DateTime? get deletedAt;
 | 
			
		||||
  String get name;
 | 
			
		||||
  String get description;
 | 
			
		||||
  String get alias;
 | 
			
		||||
  int get expRequirement;
 | 
			
		||||
  Map<String, dynamic> get price;
 | 
			
		||||
  Map<String, dynamic> get badge;
 | 
			
		||||
  Map<String, dynamic> get group;
 | 
			
		||||
  Map<String, dynamic> get appearance;
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgram
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  $SnProgramCopyWith<SnProgram> get copyWith =>
 | 
			
		||||
      _$SnProgramCopyWithImpl<SnProgram>(this as SnProgram, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this SnProgram to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return identical(this, other) ||
 | 
			
		||||
        (other.runtimeType == runtimeType &&
 | 
			
		||||
            other is SnProgram &&
 | 
			
		||||
            (identical(other.id, id) || other.id == id) &&
 | 
			
		||||
            (identical(other.createdAt, createdAt) ||
 | 
			
		||||
                other.createdAt == createdAt) &&
 | 
			
		||||
            (identical(other.updatedAt, updatedAt) ||
 | 
			
		||||
                other.updatedAt == updatedAt) &&
 | 
			
		||||
            (identical(other.deletedAt, deletedAt) ||
 | 
			
		||||
                other.deletedAt == deletedAt) &&
 | 
			
		||||
            (identical(other.name, name) || other.name == name) &&
 | 
			
		||||
            (identical(other.description, description) ||
 | 
			
		||||
                other.description == description) &&
 | 
			
		||||
            (identical(other.alias, alias) || other.alias == alias) &&
 | 
			
		||||
            (identical(other.expRequirement, expRequirement) ||
 | 
			
		||||
                other.expRequirement == expRequirement) &&
 | 
			
		||||
            const DeepCollectionEquality().equals(other.price, price) &&
 | 
			
		||||
            const DeepCollectionEquality().equals(other.badge, badge) &&
 | 
			
		||||
            const DeepCollectionEquality().equals(other.group, group) &&
 | 
			
		||||
            const DeepCollectionEquality()
 | 
			
		||||
                .equals(other.appearance, appearance));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(
 | 
			
		||||
      runtimeType,
 | 
			
		||||
      id,
 | 
			
		||||
      createdAt,
 | 
			
		||||
      updatedAt,
 | 
			
		||||
      deletedAt,
 | 
			
		||||
      name,
 | 
			
		||||
      description,
 | 
			
		||||
      alias,
 | 
			
		||||
      expRequirement,
 | 
			
		||||
      const DeepCollectionEquality().hash(price),
 | 
			
		||||
      const DeepCollectionEquality().hash(badge),
 | 
			
		||||
      const DeepCollectionEquality().hash(group),
 | 
			
		||||
      const DeepCollectionEquality().hash(appearance));
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return 'SnProgram(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, name: $name, description: $description, alias: $alias, expRequirement: $expRequirement, price: $price, badge: $badge, group: $group, appearance: $appearance)';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $SnProgramCopyWith<$Res> {
 | 
			
		||||
  factory $SnProgramCopyWith(SnProgram value, $Res Function(SnProgram) _then) =
 | 
			
		||||
      _$SnProgramCopyWithImpl;
 | 
			
		||||
  @useResult
 | 
			
		||||
  $Res call(
 | 
			
		||||
      {int id,
 | 
			
		||||
      DateTime createdAt,
 | 
			
		||||
      DateTime updatedAt,
 | 
			
		||||
      DateTime? deletedAt,
 | 
			
		||||
      String name,
 | 
			
		||||
      String description,
 | 
			
		||||
      String alias,
 | 
			
		||||
      int expRequirement,
 | 
			
		||||
      Map<String, dynamic> price,
 | 
			
		||||
      Map<String, dynamic> badge,
 | 
			
		||||
      Map<String, dynamic> group,
 | 
			
		||||
      Map<String, dynamic> appearance});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$SnProgramCopyWithImpl<$Res> implements $SnProgramCopyWith<$Res> {
 | 
			
		||||
  _$SnProgramCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final SnProgram _self;
 | 
			
		||||
  final $Res Function(SnProgram) _then;
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgram
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  @override
 | 
			
		||||
  $Res call({
 | 
			
		||||
    Object? id = null,
 | 
			
		||||
    Object? createdAt = null,
 | 
			
		||||
    Object? updatedAt = null,
 | 
			
		||||
    Object? deletedAt = freezed,
 | 
			
		||||
    Object? name = null,
 | 
			
		||||
    Object? description = null,
 | 
			
		||||
    Object? alias = null,
 | 
			
		||||
    Object? expRequirement = null,
 | 
			
		||||
    Object? price = null,
 | 
			
		||||
    Object? badge = null,
 | 
			
		||||
    Object? group = null,
 | 
			
		||||
    Object? appearance = null,
 | 
			
		||||
  }) {
 | 
			
		||||
    return _then(_self.copyWith(
 | 
			
		||||
      id: null == id
 | 
			
		||||
          ? _self.id
 | 
			
		||||
          : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
      createdAt: null == createdAt
 | 
			
		||||
          ? _self.createdAt
 | 
			
		||||
          : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      updatedAt: null == updatedAt
 | 
			
		||||
          ? _self.updatedAt
 | 
			
		||||
          : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      deletedAt: freezed == deletedAt
 | 
			
		||||
          ? _self.deletedAt
 | 
			
		||||
          : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime?,
 | 
			
		||||
      name: null == name
 | 
			
		||||
          ? _self.name
 | 
			
		||||
          : name // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      description: null == description
 | 
			
		||||
          ? _self.description
 | 
			
		||||
          : description // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      alias: null == alias
 | 
			
		||||
          ? _self.alias
 | 
			
		||||
          : alias // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      expRequirement: null == expRequirement
 | 
			
		||||
          ? _self.expRequirement
 | 
			
		||||
          : expRequirement // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
      price: null == price
 | 
			
		||||
          ? _self.price
 | 
			
		||||
          : price // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as Map<String, dynamic>,
 | 
			
		||||
      badge: null == badge
 | 
			
		||||
          ? _self.badge
 | 
			
		||||
          : badge // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as Map<String, dynamic>,
 | 
			
		||||
      group: null == group
 | 
			
		||||
          ? _self.group
 | 
			
		||||
          : group // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as Map<String, dynamic>,
 | 
			
		||||
      appearance: null == appearance
 | 
			
		||||
          ? _self.appearance
 | 
			
		||||
          : appearance // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as Map<String, dynamic>,
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
class _SnProgram implements SnProgram {
 | 
			
		||||
  const _SnProgram(
 | 
			
		||||
      {required this.id,
 | 
			
		||||
      required this.createdAt,
 | 
			
		||||
      required this.updatedAt,
 | 
			
		||||
      required this.deletedAt,
 | 
			
		||||
      required this.name,
 | 
			
		||||
      required this.description,
 | 
			
		||||
      required this.alias,
 | 
			
		||||
      required this.expRequirement,
 | 
			
		||||
      required final Map<String, dynamic> price,
 | 
			
		||||
      required final Map<String, dynamic> badge,
 | 
			
		||||
      required final Map<String, dynamic> group,
 | 
			
		||||
      required final Map<String, dynamic> appearance})
 | 
			
		||||
      : _price = price,
 | 
			
		||||
        _badge = badge,
 | 
			
		||||
        _group = group,
 | 
			
		||||
        _appearance = appearance;
 | 
			
		||||
  factory _SnProgram.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$SnProgramFromJson(json);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  final int id;
 | 
			
		||||
  @override
 | 
			
		||||
  final DateTime createdAt;
 | 
			
		||||
  @override
 | 
			
		||||
  final DateTime updatedAt;
 | 
			
		||||
  @override
 | 
			
		||||
  final DateTime? deletedAt;
 | 
			
		||||
  @override
 | 
			
		||||
  final String name;
 | 
			
		||||
  @override
 | 
			
		||||
  final String description;
 | 
			
		||||
  @override
 | 
			
		||||
  final String alias;
 | 
			
		||||
  @override
 | 
			
		||||
  final int expRequirement;
 | 
			
		||||
  final Map<String, dynamic> _price;
 | 
			
		||||
  @override
 | 
			
		||||
  Map<String, dynamic> get price {
 | 
			
		||||
    if (_price is EqualUnmodifiableMapView) return _price;
 | 
			
		||||
    // ignore: implicit_dynamic_type
 | 
			
		||||
    return EqualUnmodifiableMapView(_price);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final Map<String, dynamic> _badge;
 | 
			
		||||
  @override
 | 
			
		||||
  Map<String, dynamic> get badge {
 | 
			
		||||
    if (_badge is EqualUnmodifiableMapView) return _badge;
 | 
			
		||||
    // ignore: implicit_dynamic_type
 | 
			
		||||
    return EqualUnmodifiableMapView(_badge);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final Map<String, dynamic> _group;
 | 
			
		||||
  @override
 | 
			
		||||
  Map<String, dynamic> get group {
 | 
			
		||||
    if (_group is EqualUnmodifiableMapView) return _group;
 | 
			
		||||
    // ignore: implicit_dynamic_type
 | 
			
		||||
    return EqualUnmodifiableMapView(_group);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final Map<String, dynamic> _appearance;
 | 
			
		||||
  @override
 | 
			
		||||
  Map<String, dynamic> get appearance {
 | 
			
		||||
    if (_appearance is EqualUnmodifiableMapView) return _appearance;
 | 
			
		||||
    // ignore: implicit_dynamic_type
 | 
			
		||||
    return EqualUnmodifiableMapView(_appearance);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgram
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @override
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  _$SnProgramCopyWith<_SnProgram> get copyWith =>
 | 
			
		||||
      __$SnProgramCopyWithImpl<_SnProgram>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Map<String, dynamic> toJson() {
 | 
			
		||||
    return _$SnProgramToJson(
 | 
			
		||||
      this,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return identical(this, other) ||
 | 
			
		||||
        (other.runtimeType == runtimeType &&
 | 
			
		||||
            other is _SnProgram &&
 | 
			
		||||
            (identical(other.id, id) || other.id == id) &&
 | 
			
		||||
            (identical(other.createdAt, createdAt) ||
 | 
			
		||||
                other.createdAt == createdAt) &&
 | 
			
		||||
            (identical(other.updatedAt, updatedAt) ||
 | 
			
		||||
                other.updatedAt == updatedAt) &&
 | 
			
		||||
            (identical(other.deletedAt, deletedAt) ||
 | 
			
		||||
                other.deletedAt == deletedAt) &&
 | 
			
		||||
            (identical(other.name, name) || other.name == name) &&
 | 
			
		||||
            (identical(other.description, description) ||
 | 
			
		||||
                other.description == description) &&
 | 
			
		||||
            (identical(other.alias, alias) || other.alias == alias) &&
 | 
			
		||||
            (identical(other.expRequirement, expRequirement) ||
 | 
			
		||||
                other.expRequirement == expRequirement) &&
 | 
			
		||||
            const DeepCollectionEquality().equals(other._price, _price) &&
 | 
			
		||||
            const DeepCollectionEquality().equals(other._badge, _badge) &&
 | 
			
		||||
            const DeepCollectionEquality().equals(other._group, _group) &&
 | 
			
		||||
            const DeepCollectionEquality()
 | 
			
		||||
                .equals(other._appearance, _appearance));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(
 | 
			
		||||
      runtimeType,
 | 
			
		||||
      id,
 | 
			
		||||
      createdAt,
 | 
			
		||||
      updatedAt,
 | 
			
		||||
      deletedAt,
 | 
			
		||||
      name,
 | 
			
		||||
      description,
 | 
			
		||||
      alias,
 | 
			
		||||
      expRequirement,
 | 
			
		||||
      const DeepCollectionEquality().hash(_price),
 | 
			
		||||
      const DeepCollectionEquality().hash(_badge),
 | 
			
		||||
      const DeepCollectionEquality().hash(_group),
 | 
			
		||||
      const DeepCollectionEquality().hash(_appearance));
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return 'SnProgram(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, name: $name, description: $description, alias: $alias, expRequirement: $expRequirement, price: $price, badge: $badge, group: $group, appearance: $appearance)';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$SnProgramCopyWith<$Res>
 | 
			
		||||
    implements $SnProgramCopyWith<$Res> {
 | 
			
		||||
  factory _$SnProgramCopyWith(
 | 
			
		||||
          _SnProgram value, $Res Function(_SnProgram) _then) =
 | 
			
		||||
      __$SnProgramCopyWithImpl;
 | 
			
		||||
  @override
 | 
			
		||||
  @useResult
 | 
			
		||||
  $Res call(
 | 
			
		||||
      {int id,
 | 
			
		||||
      DateTime createdAt,
 | 
			
		||||
      DateTime updatedAt,
 | 
			
		||||
      DateTime? deletedAt,
 | 
			
		||||
      String name,
 | 
			
		||||
      String description,
 | 
			
		||||
      String alias,
 | 
			
		||||
      int expRequirement,
 | 
			
		||||
      Map<String, dynamic> price,
 | 
			
		||||
      Map<String, dynamic> badge,
 | 
			
		||||
      Map<String, dynamic> group,
 | 
			
		||||
      Map<String, dynamic> appearance});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$SnProgramCopyWithImpl<$Res> implements _$SnProgramCopyWith<$Res> {
 | 
			
		||||
  __$SnProgramCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _SnProgram _self;
 | 
			
		||||
  final $Res Function(_SnProgram) _then;
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgram
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @override
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  $Res call({
 | 
			
		||||
    Object? id = null,
 | 
			
		||||
    Object? createdAt = null,
 | 
			
		||||
    Object? updatedAt = null,
 | 
			
		||||
    Object? deletedAt = freezed,
 | 
			
		||||
    Object? name = null,
 | 
			
		||||
    Object? description = null,
 | 
			
		||||
    Object? alias = null,
 | 
			
		||||
    Object? expRequirement = null,
 | 
			
		||||
    Object? price = null,
 | 
			
		||||
    Object? badge = null,
 | 
			
		||||
    Object? group = null,
 | 
			
		||||
    Object? appearance = null,
 | 
			
		||||
  }) {
 | 
			
		||||
    return _then(_SnProgram(
 | 
			
		||||
      id: null == id
 | 
			
		||||
          ? _self.id
 | 
			
		||||
          : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
      createdAt: null == createdAt
 | 
			
		||||
          ? _self.createdAt
 | 
			
		||||
          : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      updatedAt: null == updatedAt
 | 
			
		||||
          ? _self.updatedAt
 | 
			
		||||
          : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      deletedAt: freezed == deletedAt
 | 
			
		||||
          ? _self.deletedAt
 | 
			
		||||
          : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime?,
 | 
			
		||||
      name: null == name
 | 
			
		||||
          ? _self.name
 | 
			
		||||
          : name // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      description: null == description
 | 
			
		||||
          ? _self.description
 | 
			
		||||
          : description // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      alias: null == alias
 | 
			
		||||
          ? _self.alias
 | 
			
		||||
          : alias // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      expRequirement: null == expRequirement
 | 
			
		||||
          ? _self.expRequirement
 | 
			
		||||
          : expRequirement // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
      price: null == price
 | 
			
		||||
          ? _self._price
 | 
			
		||||
          : price // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as Map<String, dynamic>,
 | 
			
		||||
      badge: null == badge
 | 
			
		||||
          ? _self._badge
 | 
			
		||||
          : badge // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as Map<String, dynamic>,
 | 
			
		||||
      group: null == group
 | 
			
		||||
          ? _self._group
 | 
			
		||||
          : group // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as Map<String, dynamic>,
 | 
			
		||||
      appearance: null == appearance
 | 
			
		||||
          ? _self._appearance
 | 
			
		||||
          : appearance // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as Map<String, dynamic>,
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$SnProgramMember {
 | 
			
		||||
  int get id;
 | 
			
		||||
  DateTime get createdAt;
 | 
			
		||||
  DateTime get updatedAt;
 | 
			
		||||
  DateTime? get deletedAt;
 | 
			
		||||
  DateTime get lastPaid;
 | 
			
		||||
  SnAccount get account;
 | 
			
		||||
  int get accountId;
 | 
			
		||||
  SnProgram get program;
 | 
			
		||||
  int get programId;
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgramMember
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  $SnProgramMemberCopyWith<SnProgramMember> get copyWith =>
 | 
			
		||||
      _$SnProgramMemberCopyWithImpl<SnProgramMember>(
 | 
			
		||||
          this as SnProgramMember, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this SnProgramMember to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return identical(this, other) ||
 | 
			
		||||
        (other.runtimeType == runtimeType &&
 | 
			
		||||
            other is SnProgramMember &&
 | 
			
		||||
            (identical(other.id, id) || other.id == id) &&
 | 
			
		||||
            (identical(other.createdAt, createdAt) ||
 | 
			
		||||
                other.createdAt == createdAt) &&
 | 
			
		||||
            (identical(other.updatedAt, updatedAt) ||
 | 
			
		||||
                other.updatedAt == updatedAt) &&
 | 
			
		||||
            (identical(other.deletedAt, deletedAt) ||
 | 
			
		||||
                other.deletedAt == deletedAt) &&
 | 
			
		||||
            (identical(other.lastPaid, lastPaid) ||
 | 
			
		||||
                other.lastPaid == lastPaid) &&
 | 
			
		||||
            (identical(other.account, account) || other.account == account) &&
 | 
			
		||||
            (identical(other.accountId, accountId) ||
 | 
			
		||||
                other.accountId == accountId) &&
 | 
			
		||||
            (identical(other.program, program) || other.program == program) &&
 | 
			
		||||
            (identical(other.programId, programId) ||
 | 
			
		||||
                other.programId == programId));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt,
 | 
			
		||||
      deletedAt, lastPaid, account, accountId, program, programId);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return 'SnProgramMember(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, lastPaid: $lastPaid, account: $account, accountId: $accountId, program: $program, programId: $programId)';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $SnProgramMemberCopyWith<$Res> {
 | 
			
		||||
  factory $SnProgramMemberCopyWith(
 | 
			
		||||
          SnProgramMember value, $Res Function(SnProgramMember) _then) =
 | 
			
		||||
      _$SnProgramMemberCopyWithImpl;
 | 
			
		||||
  @useResult
 | 
			
		||||
  $Res call(
 | 
			
		||||
      {int id,
 | 
			
		||||
      DateTime createdAt,
 | 
			
		||||
      DateTime updatedAt,
 | 
			
		||||
      DateTime? deletedAt,
 | 
			
		||||
      DateTime lastPaid,
 | 
			
		||||
      SnAccount account,
 | 
			
		||||
      int accountId,
 | 
			
		||||
      SnProgram program,
 | 
			
		||||
      int programId});
 | 
			
		||||
 | 
			
		||||
  $SnAccountCopyWith<$Res> get account;
 | 
			
		||||
  $SnProgramCopyWith<$Res> get program;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$SnProgramMemberCopyWithImpl<$Res>
 | 
			
		||||
    implements $SnProgramMemberCopyWith<$Res> {
 | 
			
		||||
  _$SnProgramMemberCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final SnProgramMember _self;
 | 
			
		||||
  final $Res Function(SnProgramMember) _then;
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgramMember
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  @override
 | 
			
		||||
  $Res call({
 | 
			
		||||
    Object? id = null,
 | 
			
		||||
    Object? createdAt = null,
 | 
			
		||||
    Object? updatedAt = null,
 | 
			
		||||
    Object? deletedAt = freezed,
 | 
			
		||||
    Object? lastPaid = null,
 | 
			
		||||
    Object? account = null,
 | 
			
		||||
    Object? accountId = null,
 | 
			
		||||
    Object? program = null,
 | 
			
		||||
    Object? programId = null,
 | 
			
		||||
  }) {
 | 
			
		||||
    return _then(_self.copyWith(
 | 
			
		||||
      id: null == id
 | 
			
		||||
          ? _self.id
 | 
			
		||||
          : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
      createdAt: null == createdAt
 | 
			
		||||
          ? _self.createdAt
 | 
			
		||||
          : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      updatedAt: null == updatedAt
 | 
			
		||||
          ? _self.updatedAt
 | 
			
		||||
          : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      deletedAt: freezed == deletedAt
 | 
			
		||||
          ? _self.deletedAt
 | 
			
		||||
          : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime?,
 | 
			
		||||
      lastPaid: null == lastPaid
 | 
			
		||||
          ? _self.lastPaid
 | 
			
		||||
          : lastPaid // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      account: null == account
 | 
			
		||||
          ? _self.account
 | 
			
		||||
          : account // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as SnAccount,
 | 
			
		||||
      accountId: null == accountId
 | 
			
		||||
          ? _self.accountId
 | 
			
		||||
          : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
      program: null == program
 | 
			
		||||
          ? _self.program
 | 
			
		||||
          : program // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as SnProgram,
 | 
			
		||||
      programId: null == programId
 | 
			
		||||
          ? _self.programId
 | 
			
		||||
          : programId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgramMember
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @override
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  $SnAccountCopyWith<$Res> get account {
 | 
			
		||||
    return $SnAccountCopyWith<$Res>(_self.account, (value) {
 | 
			
		||||
      return _then(_self.copyWith(account: value));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgramMember
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @override
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  $SnProgramCopyWith<$Res> get program {
 | 
			
		||||
    return $SnProgramCopyWith<$Res>(_self.program, (value) {
 | 
			
		||||
      return _then(_self.copyWith(program: value));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
class _SnProgramMember implements SnProgramMember {
 | 
			
		||||
  const _SnProgramMember(
 | 
			
		||||
      {required this.id,
 | 
			
		||||
      required this.createdAt,
 | 
			
		||||
      required this.updatedAt,
 | 
			
		||||
      required this.deletedAt,
 | 
			
		||||
      required this.lastPaid,
 | 
			
		||||
      required this.account,
 | 
			
		||||
      required this.accountId,
 | 
			
		||||
      required this.program,
 | 
			
		||||
      required this.programId});
 | 
			
		||||
  factory _SnProgramMember.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$SnProgramMemberFromJson(json);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  final int id;
 | 
			
		||||
  @override
 | 
			
		||||
  final DateTime createdAt;
 | 
			
		||||
  @override
 | 
			
		||||
  final DateTime updatedAt;
 | 
			
		||||
  @override
 | 
			
		||||
  final DateTime? deletedAt;
 | 
			
		||||
  @override
 | 
			
		||||
  final DateTime lastPaid;
 | 
			
		||||
  @override
 | 
			
		||||
  final SnAccount account;
 | 
			
		||||
  @override
 | 
			
		||||
  final int accountId;
 | 
			
		||||
  @override
 | 
			
		||||
  final SnProgram program;
 | 
			
		||||
  @override
 | 
			
		||||
  final int programId;
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgramMember
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @override
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  _$SnProgramMemberCopyWith<_SnProgramMember> get copyWith =>
 | 
			
		||||
      __$SnProgramMemberCopyWithImpl<_SnProgramMember>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Map<String, dynamic> toJson() {
 | 
			
		||||
    return _$SnProgramMemberToJson(
 | 
			
		||||
      this,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return identical(this, other) ||
 | 
			
		||||
        (other.runtimeType == runtimeType &&
 | 
			
		||||
            other is _SnProgramMember &&
 | 
			
		||||
            (identical(other.id, id) || other.id == id) &&
 | 
			
		||||
            (identical(other.createdAt, createdAt) ||
 | 
			
		||||
                other.createdAt == createdAt) &&
 | 
			
		||||
            (identical(other.updatedAt, updatedAt) ||
 | 
			
		||||
                other.updatedAt == updatedAt) &&
 | 
			
		||||
            (identical(other.deletedAt, deletedAt) ||
 | 
			
		||||
                other.deletedAt == deletedAt) &&
 | 
			
		||||
            (identical(other.lastPaid, lastPaid) ||
 | 
			
		||||
                other.lastPaid == lastPaid) &&
 | 
			
		||||
            (identical(other.account, account) || other.account == account) &&
 | 
			
		||||
            (identical(other.accountId, accountId) ||
 | 
			
		||||
                other.accountId == accountId) &&
 | 
			
		||||
            (identical(other.program, program) || other.program == program) &&
 | 
			
		||||
            (identical(other.programId, programId) ||
 | 
			
		||||
                other.programId == programId));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt,
 | 
			
		||||
      deletedAt, lastPaid, account, accountId, program, programId);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return 'SnProgramMember(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, lastPaid: $lastPaid, account: $account, accountId: $accountId, program: $program, programId: $programId)';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$SnProgramMemberCopyWith<$Res>
 | 
			
		||||
    implements $SnProgramMemberCopyWith<$Res> {
 | 
			
		||||
  factory _$SnProgramMemberCopyWith(
 | 
			
		||||
          _SnProgramMember value, $Res Function(_SnProgramMember) _then) =
 | 
			
		||||
      __$SnProgramMemberCopyWithImpl;
 | 
			
		||||
  @override
 | 
			
		||||
  @useResult
 | 
			
		||||
  $Res call(
 | 
			
		||||
      {int id,
 | 
			
		||||
      DateTime createdAt,
 | 
			
		||||
      DateTime updatedAt,
 | 
			
		||||
      DateTime? deletedAt,
 | 
			
		||||
      DateTime lastPaid,
 | 
			
		||||
      SnAccount account,
 | 
			
		||||
      int accountId,
 | 
			
		||||
      SnProgram program,
 | 
			
		||||
      int programId});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  $SnAccountCopyWith<$Res> get account;
 | 
			
		||||
  @override
 | 
			
		||||
  $SnProgramCopyWith<$Res> get program;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$SnProgramMemberCopyWithImpl<$Res>
 | 
			
		||||
    implements _$SnProgramMemberCopyWith<$Res> {
 | 
			
		||||
  __$SnProgramMemberCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _SnProgramMember _self;
 | 
			
		||||
  final $Res Function(_SnProgramMember) _then;
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgramMember
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @override
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  $Res call({
 | 
			
		||||
    Object? id = null,
 | 
			
		||||
    Object? createdAt = null,
 | 
			
		||||
    Object? updatedAt = null,
 | 
			
		||||
    Object? deletedAt = freezed,
 | 
			
		||||
    Object? lastPaid = null,
 | 
			
		||||
    Object? account = null,
 | 
			
		||||
    Object? accountId = null,
 | 
			
		||||
    Object? program = null,
 | 
			
		||||
    Object? programId = null,
 | 
			
		||||
  }) {
 | 
			
		||||
    return _then(_SnProgramMember(
 | 
			
		||||
      id: null == id
 | 
			
		||||
          ? _self.id
 | 
			
		||||
          : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
      createdAt: null == createdAt
 | 
			
		||||
          ? _self.createdAt
 | 
			
		||||
          : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      updatedAt: null == updatedAt
 | 
			
		||||
          ? _self.updatedAt
 | 
			
		||||
          : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      deletedAt: freezed == deletedAt
 | 
			
		||||
          ? _self.deletedAt
 | 
			
		||||
          : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime?,
 | 
			
		||||
      lastPaid: null == lastPaid
 | 
			
		||||
          ? _self.lastPaid
 | 
			
		||||
          : lastPaid // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as DateTime,
 | 
			
		||||
      account: null == account
 | 
			
		||||
          ? _self.account
 | 
			
		||||
          : account // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as SnAccount,
 | 
			
		||||
      accountId: null == accountId
 | 
			
		||||
          ? _self.accountId
 | 
			
		||||
          : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
      program: null == program
 | 
			
		||||
          ? _self.program
 | 
			
		||||
          : program // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as SnProgram,
 | 
			
		||||
      programId: null == programId
 | 
			
		||||
          ? _self.programId
 | 
			
		||||
          : programId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as int,
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgramMember
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @override
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  $SnAccountCopyWith<$Res> get account {
 | 
			
		||||
    return $SnAccountCopyWith<$Res>(_self.account, (value) {
 | 
			
		||||
      return _then(_self.copyWith(account: value));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Create a copy of SnProgramMember
 | 
			
		||||
  /// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
  @override
 | 
			
		||||
  @pragma('vm:prefer-inline')
 | 
			
		||||
  $SnProgramCopyWith<$Res> get program {
 | 
			
		||||
    return $SnProgramCopyWith<$Res>(_self.program, (value) {
 | 
			
		||||
      return _then(_self.copyWith(program: value));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dart format on
 | 
			
		||||
 
 | 
			
		||||
@@ -319,3 +319,64 @@ Map<String, dynamic> _$SnActionEventToJson(_SnActionEvent instance) =>
 | 
			
		||||
      'account': instance.account.toJson(),
 | 
			
		||||
      'account_id': instance.accountId,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
_SnProgram _$SnProgramFromJson(Map<String, dynamic> json) => _SnProgram(
 | 
			
		||||
      id: (json['id'] as num).toInt(),
 | 
			
		||||
      createdAt: DateTime.parse(json['created_at'] as String),
 | 
			
		||||
      updatedAt: DateTime.parse(json['updated_at'] as String),
 | 
			
		||||
      deletedAt: json['deleted_at'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['deleted_at'] as String),
 | 
			
		||||
      name: json['name'] as String,
 | 
			
		||||
      description: json['description'] as String,
 | 
			
		||||
      alias: json['alias'] as String,
 | 
			
		||||
      expRequirement: (json['exp_requirement'] as num).toInt(),
 | 
			
		||||
      price: json['price'] as Map<String, dynamic>,
 | 
			
		||||
      badge: json['badge'] as Map<String, dynamic>,
 | 
			
		||||
      group: json['group'] as Map<String, dynamic>,
 | 
			
		||||
      appearance: json['appearance'] as Map<String, dynamic>,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$SnProgramToJson(_SnProgram instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'id': instance.id,
 | 
			
		||||
      'created_at': instance.createdAt.toIso8601String(),
 | 
			
		||||
      'updated_at': instance.updatedAt.toIso8601String(),
 | 
			
		||||
      'deleted_at': instance.deletedAt?.toIso8601String(),
 | 
			
		||||
      'name': instance.name,
 | 
			
		||||
      'description': instance.description,
 | 
			
		||||
      'alias': instance.alias,
 | 
			
		||||
      'exp_requirement': instance.expRequirement,
 | 
			
		||||
      'price': instance.price,
 | 
			
		||||
      'badge': instance.badge,
 | 
			
		||||
      'group': instance.group,
 | 
			
		||||
      'appearance': instance.appearance,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
_SnProgramMember _$SnProgramMemberFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
    _SnProgramMember(
 | 
			
		||||
      id: (json['id'] as num).toInt(),
 | 
			
		||||
      createdAt: DateTime.parse(json['created_at'] as String),
 | 
			
		||||
      updatedAt: DateTime.parse(json['updated_at'] as String),
 | 
			
		||||
      deletedAt: json['deleted_at'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['deleted_at'] as String),
 | 
			
		||||
      lastPaid: DateTime.parse(json['last_paid'] as String),
 | 
			
		||||
      account: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
 | 
			
		||||
      accountId: (json['account_id'] as num).toInt(),
 | 
			
		||||
      program: SnProgram.fromJson(json['program'] as Map<String, dynamic>),
 | 
			
		||||
      programId: (json['program_id'] as num).toInt(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$SnProgramMemberToJson(_SnProgramMember instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'id': instance.id,
 | 
			
		||||
      'created_at': instance.createdAt.toIso8601String(),
 | 
			
		||||
      'updated_at': instance.updatedAt.toIso8601String(),
 | 
			
		||||
      'deleted_at': instance.deletedAt?.toIso8601String(),
 | 
			
		||||
      'last_paid': instance.lastPaid.toIso8601String(),
 | 
			
		||||
      'account': instance.account.toJson(),
 | 
			
		||||
      'account_id': instance.accountId,
 | 
			
		||||
      'program': instance.program.toJson(),
 | 
			
		||||
      'program_id': instance.programId,
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ abstract class SnWallet with _$SnWallet {
 | 
			
		||||
    required DateTime updatedAt,
 | 
			
		||||
    required DateTime? deletedAt,
 | 
			
		||||
    required String balance,
 | 
			
		||||
    required String goldenBalance,
 | 
			
		||||
    required String password,
 | 
			
		||||
    required int accountId,
 | 
			
		||||
  }) = _SnWallet;
 | 
			
		||||
@@ -27,6 +28,7 @@ abstract class SnTransaction with _$SnTransaction {
 | 
			
		||||
    required DateTime? deletedAt,
 | 
			
		||||
    required String remark,
 | 
			
		||||
    required String amount,
 | 
			
		||||
    required String currency,
 | 
			
		||||
    required SnWallet? payer,
 | 
			
		||||
    required SnWallet? payee,
 | 
			
		||||
    required int? payerId,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ mixin _$SnWallet {
 | 
			
		||||
  DateTime get updatedAt;
 | 
			
		||||
  DateTime? get deletedAt;
 | 
			
		||||
  String get balance;
 | 
			
		||||
  String get goldenBalance;
 | 
			
		||||
  String get password;
 | 
			
		||||
  int get accountId;
 | 
			
		||||
 | 
			
		||||
@@ -46,6 +47,8 @@ mixin _$SnWallet {
 | 
			
		||||
            (identical(other.deletedAt, deletedAt) ||
 | 
			
		||||
                other.deletedAt == deletedAt) &&
 | 
			
		||||
            (identical(other.balance, balance) || other.balance == balance) &&
 | 
			
		||||
            (identical(other.goldenBalance, goldenBalance) ||
 | 
			
		||||
                other.goldenBalance == goldenBalance) &&
 | 
			
		||||
            (identical(other.password, password) ||
 | 
			
		||||
                other.password == password) &&
 | 
			
		||||
            (identical(other.accountId, accountId) ||
 | 
			
		||||
@@ -55,11 +58,11 @@ mixin _$SnWallet {
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt,
 | 
			
		||||
      deletedAt, balance, password, accountId);
 | 
			
		||||
      deletedAt, balance, goldenBalance, password, accountId);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return 'SnWallet(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, balance: $balance, password: $password, accountId: $accountId)';
 | 
			
		||||
    return 'SnWallet(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, balance: $balance, goldenBalance: $goldenBalance, password: $password, accountId: $accountId)';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -74,6 +77,7 @@ abstract mixin class $SnWalletCopyWith<$Res> {
 | 
			
		||||
      DateTime updatedAt,
 | 
			
		||||
      DateTime? deletedAt,
 | 
			
		||||
      String balance,
 | 
			
		||||
      String goldenBalance,
 | 
			
		||||
      String password,
 | 
			
		||||
      int accountId});
 | 
			
		||||
}
 | 
			
		||||
@@ -95,6 +99,7 @@ class _$SnWalletCopyWithImpl<$Res> implements $SnWalletCopyWith<$Res> {
 | 
			
		||||
    Object? updatedAt = null,
 | 
			
		||||
    Object? deletedAt = freezed,
 | 
			
		||||
    Object? balance = null,
 | 
			
		||||
    Object? goldenBalance = null,
 | 
			
		||||
    Object? password = null,
 | 
			
		||||
    Object? accountId = null,
 | 
			
		||||
  }) {
 | 
			
		||||
@@ -119,6 +124,10 @@ class _$SnWalletCopyWithImpl<$Res> implements $SnWalletCopyWith<$Res> {
 | 
			
		||||
          ? _self.balance
 | 
			
		||||
          : balance // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      goldenBalance: null == goldenBalance
 | 
			
		||||
          ? _self.goldenBalance
 | 
			
		||||
          : goldenBalance // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      password: null == password
 | 
			
		||||
          ? _self.password
 | 
			
		||||
          : password // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
@@ -140,6 +149,7 @@ class _SnWallet implements SnWallet {
 | 
			
		||||
      required this.updatedAt,
 | 
			
		||||
      required this.deletedAt,
 | 
			
		||||
      required this.balance,
 | 
			
		||||
      required this.goldenBalance,
 | 
			
		||||
      required this.password,
 | 
			
		||||
      required this.accountId});
 | 
			
		||||
  factory _SnWallet.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
@@ -156,6 +166,8 @@ class _SnWallet implements SnWallet {
 | 
			
		||||
  @override
 | 
			
		||||
  final String balance;
 | 
			
		||||
  @override
 | 
			
		||||
  final String goldenBalance;
 | 
			
		||||
  @override
 | 
			
		||||
  final String password;
 | 
			
		||||
  @override
 | 
			
		||||
  final int accountId;
 | 
			
		||||
@@ -188,6 +200,8 @@ class _SnWallet implements SnWallet {
 | 
			
		||||
            (identical(other.deletedAt, deletedAt) ||
 | 
			
		||||
                other.deletedAt == deletedAt) &&
 | 
			
		||||
            (identical(other.balance, balance) || other.balance == balance) &&
 | 
			
		||||
            (identical(other.goldenBalance, goldenBalance) ||
 | 
			
		||||
                other.goldenBalance == goldenBalance) &&
 | 
			
		||||
            (identical(other.password, password) ||
 | 
			
		||||
                other.password == password) &&
 | 
			
		||||
            (identical(other.accountId, accountId) ||
 | 
			
		||||
@@ -197,11 +211,11 @@ class _SnWallet implements SnWallet {
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt,
 | 
			
		||||
      deletedAt, balance, password, accountId);
 | 
			
		||||
      deletedAt, balance, goldenBalance, password, accountId);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return 'SnWallet(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, balance: $balance, password: $password, accountId: $accountId)';
 | 
			
		||||
    return 'SnWallet(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, balance: $balance, goldenBalance: $goldenBalance, password: $password, accountId: $accountId)';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -218,6 +232,7 @@ abstract mixin class _$SnWalletCopyWith<$Res>
 | 
			
		||||
      DateTime updatedAt,
 | 
			
		||||
      DateTime? deletedAt,
 | 
			
		||||
      String balance,
 | 
			
		||||
      String goldenBalance,
 | 
			
		||||
      String password,
 | 
			
		||||
      int accountId});
 | 
			
		||||
}
 | 
			
		||||
@@ -239,6 +254,7 @@ class __$SnWalletCopyWithImpl<$Res> implements _$SnWalletCopyWith<$Res> {
 | 
			
		||||
    Object? updatedAt = null,
 | 
			
		||||
    Object? deletedAt = freezed,
 | 
			
		||||
    Object? balance = null,
 | 
			
		||||
    Object? goldenBalance = null,
 | 
			
		||||
    Object? password = null,
 | 
			
		||||
    Object? accountId = null,
 | 
			
		||||
  }) {
 | 
			
		||||
@@ -263,6 +279,10 @@ class __$SnWalletCopyWithImpl<$Res> implements _$SnWalletCopyWith<$Res> {
 | 
			
		||||
          ? _self.balance
 | 
			
		||||
          : balance // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      goldenBalance: null == goldenBalance
 | 
			
		||||
          ? _self.goldenBalance
 | 
			
		||||
          : goldenBalance // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      password: null == password
 | 
			
		||||
          ? _self.password
 | 
			
		||||
          : password // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
@@ -283,6 +303,7 @@ mixin _$SnTransaction {
 | 
			
		||||
  DateTime? get deletedAt;
 | 
			
		||||
  String get remark;
 | 
			
		||||
  String get amount;
 | 
			
		||||
  String get currency;
 | 
			
		||||
  SnWallet? get payer;
 | 
			
		||||
  SnWallet? get payee;
 | 
			
		||||
  int? get payerId;
 | 
			
		||||
@@ -313,6 +334,8 @@ mixin _$SnTransaction {
 | 
			
		||||
                other.deletedAt == deletedAt) &&
 | 
			
		||||
            (identical(other.remark, remark) || other.remark == remark) &&
 | 
			
		||||
            (identical(other.amount, amount) || other.amount == amount) &&
 | 
			
		||||
            (identical(other.currency, currency) ||
 | 
			
		||||
                other.currency == currency) &&
 | 
			
		||||
            (identical(other.payer, payer) || other.payer == payer) &&
 | 
			
		||||
            (identical(other.payee, payee) || other.payee == payee) &&
 | 
			
		||||
            (identical(other.payerId, payerId) || other.payerId == payerId) &&
 | 
			
		||||
@@ -322,11 +345,11 @@ mixin _$SnTransaction {
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt,
 | 
			
		||||
      deletedAt, remark, amount, payer, payee, payerId, payeeId);
 | 
			
		||||
      deletedAt, remark, amount, currency, payer, payee, payerId, payeeId);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return 'SnTransaction(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, remark: $remark, amount: $amount, payer: $payer, payee: $payee, payerId: $payerId, payeeId: $payeeId)';
 | 
			
		||||
    return 'SnTransaction(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, remark: $remark, amount: $amount, currency: $currency, payer: $payer, payee: $payee, payerId: $payerId, payeeId: $payeeId)';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -343,6 +366,7 @@ abstract mixin class $SnTransactionCopyWith<$Res> {
 | 
			
		||||
      DateTime? deletedAt,
 | 
			
		||||
      String remark,
 | 
			
		||||
      String amount,
 | 
			
		||||
      String currency,
 | 
			
		||||
      SnWallet? payer,
 | 
			
		||||
      SnWallet? payee,
 | 
			
		||||
      int? payerId,
 | 
			
		||||
@@ -371,6 +395,7 @@ class _$SnTransactionCopyWithImpl<$Res>
 | 
			
		||||
    Object? deletedAt = freezed,
 | 
			
		||||
    Object? remark = null,
 | 
			
		||||
    Object? amount = null,
 | 
			
		||||
    Object? currency = null,
 | 
			
		||||
    Object? payer = freezed,
 | 
			
		||||
    Object? payee = freezed,
 | 
			
		||||
    Object? payerId = freezed,
 | 
			
		||||
@@ -401,6 +426,10 @@ class _$SnTransactionCopyWithImpl<$Res>
 | 
			
		||||
          ? _self.amount
 | 
			
		||||
          : amount // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      currency: null == currency
 | 
			
		||||
          ? _self.currency
 | 
			
		||||
          : currency // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      payer: freezed == payer
 | 
			
		||||
          ? _self.payer
 | 
			
		||||
          : payer // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
@@ -459,6 +488,7 @@ class _SnTransaction implements SnTransaction {
 | 
			
		||||
      required this.deletedAt,
 | 
			
		||||
      required this.remark,
 | 
			
		||||
      required this.amount,
 | 
			
		||||
      required this.currency,
 | 
			
		||||
      required this.payer,
 | 
			
		||||
      required this.payee,
 | 
			
		||||
      required this.payerId,
 | 
			
		||||
@@ -479,6 +509,8 @@ class _SnTransaction implements SnTransaction {
 | 
			
		||||
  @override
 | 
			
		||||
  final String amount;
 | 
			
		||||
  @override
 | 
			
		||||
  final String currency;
 | 
			
		||||
  @override
 | 
			
		||||
  final SnWallet? payer;
 | 
			
		||||
  @override
 | 
			
		||||
  final SnWallet? payee;
 | 
			
		||||
@@ -516,6 +548,8 @@ class _SnTransaction implements SnTransaction {
 | 
			
		||||
                other.deletedAt == deletedAt) &&
 | 
			
		||||
            (identical(other.remark, remark) || other.remark == remark) &&
 | 
			
		||||
            (identical(other.amount, amount) || other.amount == amount) &&
 | 
			
		||||
            (identical(other.currency, currency) ||
 | 
			
		||||
                other.currency == currency) &&
 | 
			
		||||
            (identical(other.payer, payer) || other.payer == payer) &&
 | 
			
		||||
            (identical(other.payee, payee) || other.payee == payee) &&
 | 
			
		||||
            (identical(other.payerId, payerId) || other.payerId == payerId) &&
 | 
			
		||||
@@ -525,11 +559,11 @@ class _SnTransaction implements SnTransaction {
 | 
			
		||||
  @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt,
 | 
			
		||||
      deletedAt, remark, amount, payer, payee, payerId, payeeId);
 | 
			
		||||
      deletedAt, remark, amount, currency, payer, payee, payerId, payeeId);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return 'SnTransaction(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, remark: $remark, amount: $amount, payer: $payer, payee: $payee, payerId: $payerId, payeeId: $payeeId)';
 | 
			
		||||
    return 'SnTransaction(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, remark: $remark, amount: $amount, currency: $currency, payer: $payer, payee: $payee, payerId: $payerId, payeeId: $payeeId)';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -548,6 +582,7 @@ abstract mixin class _$SnTransactionCopyWith<$Res>
 | 
			
		||||
      DateTime? deletedAt,
 | 
			
		||||
      String remark,
 | 
			
		||||
      String amount,
 | 
			
		||||
      String currency,
 | 
			
		||||
      SnWallet? payer,
 | 
			
		||||
      SnWallet? payee,
 | 
			
		||||
      int? payerId,
 | 
			
		||||
@@ -578,6 +613,7 @@ class __$SnTransactionCopyWithImpl<$Res>
 | 
			
		||||
    Object? deletedAt = freezed,
 | 
			
		||||
    Object? remark = null,
 | 
			
		||||
    Object? amount = null,
 | 
			
		||||
    Object? currency = null,
 | 
			
		||||
    Object? payer = freezed,
 | 
			
		||||
    Object? payee = freezed,
 | 
			
		||||
    Object? payerId = freezed,
 | 
			
		||||
@@ -608,6 +644,10 @@ class __$SnTransactionCopyWithImpl<$Res>
 | 
			
		||||
          ? _self.amount
 | 
			
		||||
          : amount // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      currency: null == currency
 | 
			
		||||
          ? _self.currency
 | 
			
		||||
          : currency // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
              as String,
 | 
			
		||||
      payer: freezed == payer
 | 
			
		||||
          ? _self.payer
 | 
			
		||||
          : payer // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ _SnWallet _$SnWalletFromJson(Map<String, dynamic> json) => _SnWallet(
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['deleted_at'] as String),
 | 
			
		||||
      balance: json['balance'] as String,
 | 
			
		||||
      goldenBalance: json['golden_balance'] as String,
 | 
			
		||||
      password: json['password'] as String,
 | 
			
		||||
      accountId: (json['account_id'] as num).toInt(),
 | 
			
		||||
    );
 | 
			
		||||
@@ -24,6 +25,7 @@ Map<String, dynamic> _$SnWalletToJson(_SnWallet instance) => <String, dynamic>{
 | 
			
		||||
      'updated_at': instance.updatedAt.toIso8601String(),
 | 
			
		||||
      'deleted_at': instance.deletedAt?.toIso8601String(),
 | 
			
		||||
      'balance': instance.balance,
 | 
			
		||||
      'golden_balance': instance.goldenBalance,
 | 
			
		||||
      'password': instance.password,
 | 
			
		||||
      'account_id': instance.accountId,
 | 
			
		||||
    };
 | 
			
		||||
@@ -38,6 +40,7 @@ _SnTransaction _$SnTransactionFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
          : DateTime.parse(json['deleted_at'] as String),
 | 
			
		||||
      remark: json['remark'] as String,
 | 
			
		||||
      amount: json['amount'] as String,
 | 
			
		||||
      currency: json['currency'] as String,
 | 
			
		||||
      payer: json['payer'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnWallet.fromJson(json['payer'] as Map<String, dynamic>),
 | 
			
		||||
@@ -56,6 +59,7 @@ Map<String, dynamic> _$SnTransactionToJson(_SnTransaction instance) =>
 | 
			
		||||
      'deleted_at': instance.deletedAt?.toIso8601String(),
 | 
			
		||||
      'remark': instance.remark,
 | 
			
		||||
      'amount': instance.amount,
 | 
			
		||||
      'currency': instance.currency,
 | 
			
		||||
      'payer': instance.payer?.toJson(),
 | 
			
		||||
      'payee': instance.payee?.toJson(),
 | 
			
		||||
      'payer_id': instance.payerId,
 | 
			
		||||
 
 | 
			
		||||
@@ -2525,10 +2525,11 @@ packages:
 | 
			
		||||
  workmanager:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: workmanager
 | 
			
		||||
      sha256: ed13530cccd28c5c9959ad42d657cd0666274ca74c56dea0ca183ddd527d3a00
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
      path: workmanager
 | 
			
		||||
      ref: main
 | 
			
		||||
      resolved-ref: "4ce065135dc1b91fee918f81596b42a56850391d"
 | 
			
		||||
      url: "https://github.com/fluttercommunity/flutter_workmanager.git"
 | 
			
		||||
    source: git
 | 
			
		||||
    version: "0.5.2"
 | 
			
		||||
  xdg_directories:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ dependencies:
 | 
			
		||||
  relative_time: ^5.0.0
 | 
			
		||||
  image_picker: ^1.1.2
 | 
			
		||||
  cross_file: ^0.3.4+2
 | 
			
		||||
  file_picker: ^9.0.0 # pinned due to compile failed on android, https://github.com/miguelpruivo/flutter_file_picker/issues/1643
 | 
			
		||||
  file_picker: ^9.2.1
 | 
			
		||||
  croppy: ^1.3.1
 | 
			
		||||
  flutter_expandable_fab: ^2.3.0
 | 
			
		||||
  dropdown_button2: ^2.3.9
 | 
			
		||||
@@ -103,7 +103,11 @@ dependencies:
 | 
			
		||||
  flutter_svg: ^2.0.16
 | 
			
		||||
  home_widget: ^0.7.0
 | 
			
		||||
  receive_sharing_intent: ^1.8.1
 | 
			
		||||
  workmanager: ^0.5.2
 | 
			
		||||
  workmanager: # use git due to: https://github.com/fluttercommunity/flutter_workmanager/issues/588#issuecomment-2660871645
 | 
			
		||||
    git:
 | 
			
		||||
      url: https://github.com/fluttercommunity/flutter_workmanager.git
 | 
			
		||||
      path: workmanager
 | 
			
		||||
      ref: main
 | 
			
		||||
  flutter_app_update: ^3.2.2
 | 
			
		||||
  in_app_review: ^2.0.10
 | 
			
		||||
  version: ^3.0.2
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								web/_redirects
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								web/_redirects
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
/assets/assets/translations/en.json /assets/assets/translations/en-US.json 301
 | 
			
		||||
/assets/assets/translations/zh.json /assets/assets/translations/zh-CN.json 301
 | 
			
		||||
		Reference in New Issue
	
	Block a user