Compare commits
	
		
			4 Commits
		
	
	
		
			9311bfc3b5
			...
			9cc577adbe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9cc577adbe | |||
| dd196b7754 | |||
| 16c07c2133 | |||
| 6bcb658d44 | 
| @@ -897,5 +897,23 @@ | |||||||
|   "albumDescription": "View albums and manage attachments.", |   "albumDescription": "View albums and manage attachments.", | ||||||
|   "stickers": "Stickers", |   "stickers": "Stickers", | ||||||
|   "stickersDescription": "View sticker packs and manage 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": "查看相册与管理上传附件。", |   "albumDescription": "查看相册与管理上传附件。", | ||||||
|   "stickers": "贴图", |   "stickers": "贴图", | ||||||
|   "stickersDescription": "查看贴图包与管理贴图。", |   "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/prefs/security.dart'; | ||||||
| import 'package:surface/screens/account/profile_page.dart'; | import 'package:surface/screens/account/profile_page.dart'; | ||||||
| import 'package:surface/screens/account/profile_edit.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_edit.dart'; | ||||||
| import 'package:surface/screens/account/publishers/publisher_new.dart'; | import 'package:surface/screens/account/publishers/publisher_new.dart'; | ||||||
| import 'package:surface/screens/account/publishers/publishers.dart'; | import 'package:surface/screens/account/publishers/publishers.dart'; | ||||||
| @@ -130,6 +131,11 @@ final _appRoutes = [ | |||||||
|     name: 'account', |     name: 'account', | ||||||
|     builder: (context, state) => const AccountScreen(), |     builder: (context, state) => const AccountScreen(), | ||||||
|     routes: [ |     routes: [ | ||||||
|  |       GoRoute( | ||||||
|  |         path: '/programs', | ||||||
|  |         name: 'accountProgram', | ||||||
|  |         builder: (context, state) => const AccountProgramScreen(), | ||||||
|  |       ), | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/contacts', |         path: '/contacts', | ||||||
|         name: 'accountContactMethods', |         name: 'accountContactMethods', | ||||||
|   | |||||||
| @@ -145,6 +145,16 @@ class _AuthorizedAccountScreen extends StatelessWidget { | |||||||
|             GoRouter.of(context).pushNamed('accountPublishers'); |             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( |         ListTile( | ||||||
|           title: Text('friends').tr(), |           title: Text('friends').tr(), | ||||||
|           subtitle: Text('friendsDescription').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:provider/provider.dart'; | ||||||
| import 'package:styled_widget/styled_widget.dart'; | import 'package:styled_widget/styled_widget.dart'; | ||||||
| import 'package:surface/providers/sn_network.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/dialog.dart'; | ||||||
| import 'package:surface/widgets/navigation/app_scaffold.dart'; | import 'package:surface/widgets/navigation/app_scaffold.dart'; | ||||||
| import 'package:url_launcher/url_launcher_string.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:html' as html; | ||||||
| import 'dart:ui_web' as ui; | import 'dart:ui_web' as ui; | ||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
| import 'package:flutter/foundation.dart' show kIsWeb; |  | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter_inappwebview/flutter_inappwebview.dart'; |  | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| import 'package:surface/providers/config.dart'; | import 'package:surface/providers/config.dart'; | ||||||
| import 'package:surface/widgets/navigation/app_scaffold.dart'; | import 'package:surface/widgets/navigation/app_scaffold.dart'; | ||||||
| 
 | 
 | ||||||
| class CaptchaScreen extends StatefulWidget { | class CaptchaScreen extends StatefulWidget { | ||||||
|   const CaptchaScreen({ |   const CaptchaScreen({super.key}); | ||||||
|     super.key, |  | ||||||
|   }); |  | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   State<CaptchaScreen> createState() => _CaptchaScreenState(); |   State<CaptchaScreen> createState() => _CaptchaScreenState(); | ||||||
| @@ -21,9 +17,7 @@ class _CaptchaScreenState extends State<CaptchaScreen> { | |||||||
|   @override |   @override | ||||||
|   void initState() { |   void initState() { | ||||||
|     super.initState(); |     super.initState(); | ||||||
|     if (kIsWeb) { |     _setupWebListener(); | ||||||
|       _setupWebListener(); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void _setupWebListener() { |   void _setupWebListener() { | ||||||
| @@ -37,9 +31,8 @@ class _CaptchaScreenState extends State<CaptchaScreen> { | |||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // Create an iframe for the captcha page |  | ||||||
|     final iframe = html.IFrameElement() |     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' |       ..style.border = 'none' | ||||||
|       ..width = '100%' |       ..width = '100%' | ||||||
|       ..height = '100%'; |       ..height = '100%'; | ||||||
| @@ -47,36 +40,15 @@ class _CaptchaScreenState extends State<CaptchaScreen> { | |||||||
|     html.document.body!.append(iframe); |     html.document.body!.append(iframe); | ||||||
|     ui.platformViewRegistry.registerViewFactory( |     ui.platformViewRegistry.registerViewFactory( | ||||||
|       'captcha-iframe', |       'captcha-iframe', | ||||||
|       (int viewId) => iframe, |           (int viewId) => iframe, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   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( |     return AppScaffold( | ||||||
|       appBar: AppBar(title: Text("reCaptcha").tr()), |       appBar: AppBar(title: Text("reCaptcha").tr()), | ||||||
|       body: InAppWebView( |       body: HtmlElementView(viewType: 'captcha-iframe'), | ||||||
|         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; |  | ||||||
|         }, |  | ||||||
|       ), |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -46,9 +46,7 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|     try { |     try { | ||||||
|       final sn = context.read<SnNetworkProvider>(); |       final sn = context.read<SnNetworkProvider>(); | ||||||
|       final resp = await sn.client.get('/cgi/id/users/me/relations?status=1'); |       final resp = await sn.client.get('/cgi/id/users/me/relations?status=1'); | ||||||
|       _relations = List<SnRelationship>.from( |       _relations = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []); | ||||||
|         resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [], |  | ||||||
|       ); |  | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
|       context.showErrorDialog(err); |       context.showErrorDialog(err); | ||||||
| @@ -66,9 +64,7 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|     try { |     try { | ||||||
|       final sn = context.read<SnNetworkProvider>(); |       final sn = context.read<SnNetworkProvider>(); | ||||||
|       final resp = await sn.client.get('/cgi/id/users/me/relations?status=0,3'); |       final resp = await sn.client.get('/cgi/id/users/me/relations?status=0,3'); | ||||||
|       _requests = List<SnRelationship>.from( |       _requests = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []); | ||||||
|         resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [], |  | ||||||
|       ); |  | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
|       context.showErrorDialog(err); |       context.showErrorDialog(err); | ||||||
| @@ -86,9 +82,7 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|     try { |     try { | ||||||
|       final sn = context.read<SnNetworkProvider>(); |       final sn = context.read<SnNetworkProvider>(); | ||||||
|       final resp = await sn.client.get('/cgi/id/users/me/relations?status=2'); |       final resp = await sn.client.get('/cgi/id/users/me/relations?status=2'); | ||||||
|       _blocks = List<SnRelationship>.from( |       _blocks = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []); | ||||||
|         resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [], |  | ||||||
|       ); |  | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
|       context.showErrorDialog(err); |       context.showErrorDialog(err); | ||||||
| @@ -104,11 +98,7 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       final rel = context.read<SnRelationshipProvider>(); |       final rel = context.read<SnRelationshipProvider>(); | ||||||
|       await rel.updateRelationship( |       await rel.updateRelationship(relation.relatedId, dstStatus, relation.permNodes); | ||||||
|         relation.relatedId, |  | ||||||
|         dstStatus, |  | ||||||
|         relation.permNodes, |  | ||||||
|       ); |  | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
|       _fetchRelations(); |       _fetchRelations(); | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
| @@ -122,9 +112,7 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|   Future<void> _deleteRelation(SnRelationship relation) async { |   Future<void> _deleteRelation(SnRelationship relation) async { | ||||||
|     final confirm = await context.showConfirmDialog( |     final confirm = await context.showConfirmDialog( | ||||||
|       'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]), |       'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]), | ||||||
|       'friendDeleteDescription'.tr(args: [ |       'friendDeleteDescription'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]), | ||||||
|         relation.related?.nick ?? 'unknown'.tr(), |  | ||||||
|       ]), |  | ||||||
|     ); |     ); | ||||||
|     if (!confirm) return; |     if (!confirm) return; | ||||||
|     if (!mounted) return; |     if (!mounted) return; | ||||||
| @@ -145,10 +133,9 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   void _showRequests() { |   void _showRequests() { | ||||||
|     showModalBottomSheet( |     showModalBottomSheet(context: context, builder: (context) => _FriendshipListWidget(relations: _requests)).then(( | ||||||
|       context: context, |       value, | ||||||
|       builder: (context) => _FriendshipListWidget(relations: _requests), |     ) { | ||||||
|     ).then((value) { |  | ||||||
|       if (value != null) { |       if (value != null) { | ||||||
|         _fetchRequests(); |         _fetchRequests(); | ||||||
|         _fetchRelations(); |         _fetchRelations(); | ||||||
| @@ -157,10 +144,9 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   void _showBlocks() { |   void _showBlocks() { | ||||||
|     showModalBottomSheet( |     showModalBottomSheet(context: context, builder: (context) => _FriendshipListWidget(relations: _blocks)).then(( | ||||||
|       context: context, |       value, | ||||||
|       builder: (context) => _FriendshipListWidget(relations: _blocks), |     ) { | ||||||
|     ).then((value) { |  | ||||||
|       if (value != null) { |       if (value != null) { | ||||||
|         _fetchBlocks(); |         _fetchBlocks(); | ||||||
|         _fetchRelations(); |         _fetchRelations(); | ||||||
| @@ -173,9 +159,7 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       final sn = context.read<SnNetworkProvider>(); |       final sn = context.read<SnNetworkProvider>(); | ||||||
|       await sn.client.post('/cgi/id/users/me/relations', data: { |       await sn.client.post('/cgi/id/users/me/relations', data: {'related': user.name}); | ||||||
|         'related': user.name, |  | ||||||
|       }); |  | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
|       context.showSnackbar('friendRequestSent'.tr()); |       context.showSnackbar('friendRequestSent'.tr()); | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
| @@ -200,29 +184,19 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|  |  | ||||||
|     if (!ua.isAuthorized) { |     if (!ua.isAuthorized) { | ||||||
|       return AppScaffold( |       return AppScaffold( | ||||||
|         appBar: AppBar( |         appBar: AppBar(leading: PageBackButton(), title: Text('screenFriend').tr()), | ||||||
|           leading: PageBackButton(), |         body: Center(child: UnauthorizedHint()), | ||||||
|           title: Text('screenFriend').tr(), |  | ||||||
|         ), |  | ||||||
|         body: Center( |  | ||||||
|           child: UnauthorizedHint(), |  | ||||||
|         ), |  | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return AppScaffold( |     return AppScaffold( | ||||||
|       appBar: AppBar( |       appBar: AppBar(leading: AutoAppBarLeading(), title: Text('screenFriend').tr()), | ||||||
|         leading: AutoAppBarLeading(), |  | ||||||
|         title: Text('screenFriend').tr(), |  | ||||||
|       ), |  | ||||||
|       floatingActionButton: FloatingActionButton( |       floatingActionButton: FloatingActionButton( | ||||||
|         child: const Icon(Symbols.add), |         child: const Icon(Symbols.add), | ||||||
|         onPressed: () async { |         onPressed: () async { | ||||||
|           final user = await showModalBottomSheet<SnAccount?>( |           final user = await showModalBottomSheet<SnAccount?>( | ||||||
|             context: context, |             context: context, | ||||||
|             builder: (context) => AccountSelect( |             builder: (context) => AccountSelect(title: 'friendNew'.tr()), | ||||||
|               title: 'friendNew'.tr(), |  | ||||||
|             ), |  | ||||||
|           ); |           ); | ||||||
|           if (!mounted) return; |           if (!mounted) return; | ||||||
|           if (user == null) return; |           if (user == null) return; | ||||||
| @@ -235,9 +209,7 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|           if (_requests.isNotEmpty) |           if (_requests.isNotEmpty) | ||||||
|             ListTile( |             ListTile( | ||||||
|               title: Text('friendRequests').tr(), |               title: Text('friendRequests').tr(), | ||||||
|               subtitle: Text( |               subtitle: Text('friendRequestsDescription').plural(_requests.length), | ||||||
|                 'friendRequestsDescription', |  | ||||||
|               ).plural(_requests.length), |  | ||||||
|               contentPadding: const EdgeInsets.symmetric(horizontal: 24), |               contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||||
|               leading: const Icon(Symbols.group_add), |               leading: const Icon(Symbols.group_add), | ||||||
|               trailing: const Icon(Symbols.chevron_right), |               trailing: const Icon(Symbols.chevron_right), | ||||||
| @@ -246,25 +218,19 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|           if (_blocks.isNotEmpty) |           if (_blocks.isNotEmpty) | ||||||
|             ListTile( |             ListTile( | ||||||
|               title: Text('friendBlocklist').tr(), |               title: Text('friendBlocklist').tr(), | ||||||
|               subtitle: Text( |               subtitle: Text('friendBlocklistDescription').plural(_blocks.length), | ||||||
|                 'friendBlocklistDescription', |  | ||||||
|               ).plural(_blocks.length), |  | ||||||
|               contentPadding: const EdgeInsets.symmetric(horizontal: 24), |               contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||||
|               leading: const Icon(Symbols.block), |               leading: const Icon(Symbols.block), | ||||||
|               trailing: const Icon(Symbols.chevron_right), |               trailing: const Icon(Symbols.chevron_right), | ||||||
|               onTap: _showBlocks, |               onTap: _showBlocks, | ||||||
|             ), |             ), | ||||||
|           if (_requests.isNotEmpty || _blocks.isNotEmpty) |           if (_requests.isNotEmpty || _blocks.isNotEmpty) const Divider(height: 1), | ||||||
|             const Divider(height: 1), |  | ||||||
|           Expanded( |           Expanded( | ||||||
|             child: MediaQuery.removePadding( |             child: MediaQuery.removePadding( | ||||||
|               context: context, |               context: context, | ||||||
|               removeTop: true, |               removeTop: true, | ||||||
|               child: RefreshIndicator( |               child: RefreshIndicator( | ||||||
|                 onRefresh: () => Future.wait([ |                 onRefresh: () => Future.wait([_fetchRelations(), _fetchRequests()]), | ||||||
|                   _fetchRelations(), |  | ||||||
|                   _fetchRequests(), |  | ||||||
|                 ]), |  | ||||||
|                 child: ListView.builder( |                 child: ListView.builder( | ||||||
|                   itemCount: _relations.length, |                   itemCount: _relations.length, | ||||||
|                   itemBuilder: (context, index) { |                   itemBuilder: (context, index) { | ||||||
| @@ -288,16 +254,12 @@ class _FriendScreenState extends State<FriendScreen> { | |||||||
|                               mainAxisAlignment: MainAxisAlignment.end, |                               mainAxisAlignment: MainAxisAlignment.end, | ||||||
|                               children: [ |                               children: [ | ||||||
|                                 InkWell( |                                 InkWell( | ||||||
|                                   onTap: _isUpdating |                                   onTap: _isUpdating ? null : () => _changeRelation(relation, 2), | ||||||
|                                       ? null |  | ||||||
|                                       : () => _changeRelation(relation, 2), |  | ||||||
|                                   child: Text('friendBlock').tr(), |                                   child: Text('friendBlock').tr(), | ||||||
|                                 ), |                                 ), | ||||||
|                                 const Gap(8), |                                 const Gap(8), | ||||||
|                                 InkWell( |                                 InkWell( | ||||||
|                                   onTap: _isUpdating |                                   onTap: _isUpdating ? null : () => _deleteRelation(relation), | ||||||
|                                       ? null |  | ||||||
|                                       : () => _deleteRelation(relation), |  | ||||||
|                                   child: Text('friendDeleteAction').tr(), |                                   child: Text('friendDeleteAction').tr(), | ||||||
|                                 ), |                                 ), | ||||||
|                               ], |                               ], | ||||||
| @@ -366,11 +328,7 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> { | |||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       final rel = context.read<SnRelationshipProvider>(); |       final rel = context.read<SnRelationshipProvider>(); | ||||||
|       await rel.updateRelationship( |       await rel.updateRelationship(relation.relatedId, dstStatus, relation.permNodes); | ||||||
|         relation.relatedId, |  | ||||||
|         dstStatus, |  | ||||||
|         relation.permNodes, |  | ||||||
|       ); |  | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
|       Navigator.pop(context, true); |       Navigator.pop(context, true); | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
| @@ -384,9 +342,7 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> { | |||||||
|   Future<void> _deleteRelation(SnRelationship relation) async { |   Future<void> _deleteRelation(SnRelationship relation) async { | ||||||
|     final confirm = await context.showConfirmDialog( |     final confirm = await context.showConfirmDialog( | ||||||
|       'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]), |       'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]), | ||||||
|       'friendDeleteDescription'.tr(args: [ |       'friendDeleteDescription'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]), | ||||||
|         relation.related?.nick ?? 'unknown'.tr(), |  | ||||||
|       ]), |  | ||||||
|     ); |     ); | ||||||
|     if (!confirm) return; |     if (!confirm) return; | ||||||
|     if (!mounted) return; |     if (!mounted) return; | ||||||
| @@ -426,9 +382,7 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> { | |||||||
|               mainAxisAlignment: MainAxisAlignment.center, |               mainAxisAlignment: MainAxisAlignment.center, | ||||||
|               crossAxisAlignment: CrossAxisAlignment.end, |               crossAxisAlignment: CrossAxisAlignment.end, | ||||||
|               children: [ |               children: [ | ||||||
|                 Text(kFriendStatus[relation.status] ?? 'unknown') |                 Text(kFriendStatus[relation.status] ?? 'unknown').tr().opacity(0.75), | ||||||
|                     .tr() |  | ||||||
|                     .opacity(0.75), |  | ||||||
|                 if (relation.status == 0) |                 if (relation.status == 0) | ||||||
|                   Row( |                   Row( | ||||||
|                     mainAxisAlignment: MainAxisAlignment.end, |                     mainAxisAlignment: MainAxisAlignment.end, | ||||||
| @@ -449,8 +403,7 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> { | |||||||
|                     mainAxisAlignment: MainAxisAlignment.end, |                     mainAxisAlignment: MainAxisAlignment.end, | ||||||
|                     children: [ |                     children: [ | ||||||
|                       InkWell( |                       InkWell( | ||||||
|                         onTap: |                         onTap: _isBusy ? null : () => _changeRelation(relation, 1), | ||||||
|                             _isBusy ? null : () => _changeRelation(relation, 1), |  | ||||||
|                         child: Text('friendUnblock').tr(), |                         child: Text('friendUnblock').tr(), | ||||||
|                       ), |                       ), | ||||||
|                       const Gap(8), |                       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/special_day.dart'; | ||||||
| import 'package:surface/providers/userinfo.dart'; | import 'package:surface/providers/userinfo.dart'; | ||||||
| import 'package:surface/providers/widget.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/check_in.dart'; | ||||||
| import 'package:surface/types/post.dart'; | import 'package:surface/types/post.dart'; | ||||||
| import 'package:surface/widgets/app_bar_leading.dart'; | import 'package:surface/widgets/app_bar_leading.dart'; | ||||||
|   | |||||||
| @@ -28,11 +28,8 @@ class _PostShuffleScreenState extends State<PostShuffleScreen> { | |||||||
|     setState(() => _isBusy = true); |     setState(() => _isBusy = true); | ||||||
|     try { |     try { | ||||||
|       final pt = context.read<SnPostContentProvider>(); |       final pt = context.read<SnPostContentProvider>(); | ||||||
|       final result = await pt.listPosts( |       final result = | ||||||
|         take: 10, |           await pt.listPosts(take: 10, offset: _posts.length, isShuffle: true); | ||||||
|         offset: _posts.length, |  | ||||||
|         isShuffle: true, |  | ||||||
|       ); |  | ||||||
|       _posts.addAll(result.$1); |       _posts.addAll(result.$1); | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
| @@ -57,19 +54,14 @@ class _PostShuffleScreenState extends State<PostShuffleScreen> { | |||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return AppScaffold( |     return AppScaffold( | ||||||
|       appBar: AppBar( |       appBar: AppBar(title: Text('postShuffle').tr()), | ||||||
|         title: Text('postShuffle').tr(), |  | ||||||
|       ), |  | ||||||
|       body: Stack( |       body: Stack( | ||||||
|         children: [ |         children: [ | ||||||
|           Column( |           Column( | ||||||
|             children: [ |             children: [ | ||||||
|               if (_isBusy || _posts.isEmpty) |               if (_isBusy || _posts.isEmpty) | ||||||
|                 const Expanded( |                 const Expanded( | ||||||
|                   child: Center( |                     child: Center(child: CircularProgressIndicator())) | ||||||
|                     child: CircularProgressIndicator(), |  | ||||||
|                   ), |  | ||||||
|                 ) |  | ||||||
|               else |               else | ||||||
|                 Expanded( |                 Expanded( | ||||||
|                   child: CardSwiper( |                   child: CardSwiper( | ||||||
| @@ -81,17 +73,20 @@ class _PostShuffleScreenState extends State<PostShuffleScreen> { | |||||||
|                       final ele = _posts[idx]; |                       final ele = _posts[idx]; | ||||||
|                       return SingleChildScrollView( |                       return SingleChildScrollView( | ||||||
|                         child: Center( |                         child: Center( | ||||||
|                           child: OpenablePostItem( |                           child: Card( | ||||||
|                             key: ValueKey(ele), |                             color: Theme.of(context).colorScheme.surface, | ||||||
|                             data: ele, |                             child: OpenablePostItem( | ||||||
|                             maxWidth: 640, |                               key: ValueKey(ele), | ||||||
|                             onChanged: (ele) { |                               data: ele, | ||||||
|                               _posts[idx] = ele; |                               maxWidth: 640, | ||||||
|                               setState(() {}); |                               onChanged: (ele) { | ||||||
|                             }, |                                 _posts[idx] = ele; | ||||||
|                             onDeleted: () { |                                 setState(() {}); | ||||||
|                               _fetchPosts(); |                               }, | ||||||
|                             }, |                               onDeleted: () { | ||||||
|  |                                 _fetchPosts(); | ||||||
|  |                               }, | ||||||
|  |                             ).padding(all: 8), | ||||||
|                           ).padding( |                           ).padding( | ||||||
|                             all: 24, |                             all: 24, | ||||||
|                             bottom: |                             bottom: | ||||||
|   | |||||||
| @@ -45,10 +45,7 @@ class _WalletScreenState extends State<WalletScreen> { | |||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return AppScaffold( |     return AppScaffold( | ||||||
|       appBar: AppBar( |       appBar: AppBar(leading: PageBackButton(), title: Text('screenAccountWallet').tr()), | ||||||
|         leading: PageBackButton(), |  | ||||||
|         title: Text('screenAccountWallet').tr(), |  | ||||||
|       ), |  | ||||||
|       body: Column( |       body: Column( | ||||||
|         children: [ |         children: [ | ||||||
|           LoadingIndicator(isActive: _isBusy), |           LoadingIndicator(isActive: _isBusy), | ||||||
| @@ -66,11 +63,6 @@ class _WalletScreenState extends State<WalletScreen> { | |||||||
|                 mainAxisSize: MainAxisSize.min, |                 mainAxisSize: MainAxisSize.min, | ||||||
|                 crossAxisAlignment: CrossAxisAlignment.start, |                 crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|                 children: [ |                 children: [ | ||||||
|                   CircleAvatar( |  | ||||||
|                     radius: 28, |  | ||||||
|                     child: Icon(Symbols.wallet, size: 28), |  | ||||||
|                   ), |  | ||||||
|                   const Gap(12), |  | ||||||
|                   SizedBox(width: double.infinity), |                   SizedBox(width: double.infinity), | ||||||
|                   Text( |                   Text( | ||||||
|                     NumberFormat.compactCurrency( |                     NumberFormat.compactCurrency( | ||||||
| @@ -81,6 +73,16 @@ class _WalletScreenState extends State<WalletScreen> { | |||||||
|                     style: Theme.of(context).textTheme.titleLarge, |                     style: Theme.of(context).textTheme.titleLarge, | ||||||
|                   ), |                   ), | ||||||
|                   Text('walletCurrency'.plural(double.parse(_wallet!.balance))), |                   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: 20, vertical: 24), | ||||||
|             ).padding(horizontal: 8, top: 16, bottom: 4), |             ).padding(horizontal: 8, top: 16, bottom: 4), | ||||||
| @@ -109,14 +111,12 @@ class _WalletTransactionListState extends State<_WalletTransactionList> { | |||||||
|     try { |     try { | ||||||
|       setState(() => _isBusy = true); |       setState(() => _isBusy = true); | ||||||
|       final sn = context.read<SnNetworkProvider>(); |       final sn = context.read<SnNetworkProvider>(); | ||||||
|       final resp = await sn.client.get('/cgi/wa/transactions/me', queryParameters: { |       final resp = await sn.client.get( | ||||||
|         'take': 10, |         '/cgi/wa/transactions/me', | ||||||
|         'offset': _transactions.length, |         queryParameters: {'take': 10, 'offset': _transactions.length}, | ||||||
|       }); |  | ||||||
|       _totalCount = resp.data['count']; |  | ||||||
|       _transactions.addAll( |  | ||||||
|         resp.data['data']?.map((e) => SnTransaction.fromJson(e)).cast<SnTransaction>() ?? [], |  | ||||||
|       ); |       ); | ||||||
|  |       _totalCount = resp.data['count']; | ||||||
|  |       _transactions.addAll(resp.data['data']?.map((e) => SnTransaction.fromJson(e)).cast<SnTransaction>() ?? []); | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
|       context.showErrorDialog(err); |       context.showErrorDialog(err); | ||||||
| @@ -159,12 +159,18 @@ class _WalletTransactionListState extends State<_WalletTransactionList> { | |||||||
|                 children: [ |                 children: [ | ||||||
|                   Text(ele.remark), |                   Text(ele.remark), | ||||||
|                   const Gap(2), |                   const Gap(2), | ||||||
|                   Text( |                   Row( | ||||||
|                     DateFormat( |                     children: [ | ||||||
|                       null, |                       Text( | ||||||
|                       EasyLocalization.of(context)!.currentLocale.toString(), |                         'walletTransactionType${ele.currency.capitalize()}'.tr(), | ||||||
|                     ).format(ele.createdAt), |                         style: Theme.of(context).textTheme.labelSmall, | ||||||
|                     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 TextEditingController passwordController = TextEditingController(); | ||||||
|     final password = await showDialog<String?>( |     final password = await showDialog<String?>( | ||||||
|       context: context, |       context: context, | ||||||
|       builder: (ctx) => AlertDialog( |       builder: | ||||||
|         title: Text('walletCreate').tr(), |           (ctx) => AlertDialog( | ||||||
|         content: Column( |             title: Text('walletCreate').tr(), | ||||||
|           crossAxisAlignment: CrossAxisAlignment.start, |             content: Column( | ||||||
|           mainAxisSize: MainAxisSize.min, |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|           children: [ |               mainAxisSize: MainAxisSize.min, | ||||||
|             Text('walletCreatePassword').tr(), |               children: [ | ||||||
|             const Gap(8), |                 Text('walletCreatePassword').tr(), | ||||||
|             TextField( |                 const Gap(8), | ||||||
|               autofocus: true, |                 TextField( | ||||||
|               obscureText: true, |                   autofocus: true, | ||||||
|               controller: passwordController, |                   obscureText: true, | ||||||
|               decoration: InputDecoration( |                   controller: passwordController, | ||||||
|                 labelText: 'fieldPassword'.tr(), |                   decoration: InputDecoration(labelText: 'fieldPassword'.tr()), | ||||||
|               ), |                 ), | ||||||
|  |               ], | ||||||
|             ), |             ), | ||||||
|           ], |             actions: [ | ||||||
|         ), |               TextButton(onPressed: () => Navigator.of(ctx).pop(), child: Text('cancel').tr()), | ||||||
|         actions: [ |               TextButton( | ||||||
|           TextButton( |                 onPressed: () { | ||||||
|             onPressed: () => Navigator.of(ctx).pop(), |                   Navigator.of(ctx).pop(passwordController.text); | ||||||
|             child: Text('cancel').tr(), |                 }, | ||||||
|  |                 child: Text('next').tr(), | ||||||
|  |               ), | ||||||
|  |             ], | ||||||
|           ), |           ), | ||||||
|           TextButton( |  | ||||||
|             onPressed: () { |  | ||||||
|               Navigator.of(ctx).pop(passwordController.text); |  | ||||||
|             }, |  | ||||||
|             child: Text('next').tr(), |  | ||||||
|           ), |  | ||||||
|         ], |  | ||||||
|       ), |  | ||||||
|     ); |     ); | ||||||
|     WidgetsBinding.instance.addPostFrameCallback((_) { |     WidgetsBinding.instance.addPostFrameCallback((_) { | ||||||
|       passwordController.dispose(); |       passwordController.dispose(); | ||||||
| @@ -234,9 +236,7 @@ class _CreateWalletWidgetState extends State<_CreateWalletWidget> { | |||||||
|     try { |     try { | ||||||
|       setState(() => _isBusy = true); |       setState(() => _isBusy = true); | ||||||
|       final sn = context.read<SnNetworkProvider>(); |       final sn = context.read<SnNetworkProvider>(); | ||||||
|       await sn.client.post('/cgi/wa/wallets/me', data: { |       await sn.client.post('/cgi/wa/wallets/me', data: {'password': password}); | ||||||
|         'password': password, |  | ||||||
|       }); |  | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       if (!mounted) return; |       if (!mounted) return; | ||||||
|       context.showErrorDialog(err); |       context.showErrorDialog(err); | ||||||
| @@ -255,20 +255,14 @@ class _CreateWalletWidgetState extends State<_CreateWalletWidget> { | |||||||
|             mainAxisSize: MainAxisSize.min, |             mainAxisSize: MainAxisSize.min, | ||||||
|             crossAxisAlignment: CrossAxisAlignment.start, |             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|             children: [ |             children: [ | ||||||
|               CircleAvatar( |               CircleAvatar(radius: 28, child: Icon(Symbols.add, size: 28)), | ||||||
|                 radius: 28, |  | ||||||
|                 child: Icon(Symbols.add, size: 28), |  | ||||||
|               ), |  | ||||||
|               const Gap(12), |               const Gap(12), | ||||||
|               Text('walletCreate', style: Theme.of(context).textTheme.titleLarge).tr(), |               Text('walletCreate', style: Theme.of(context).textTheme.titleLarge).tr(), | ||||||
|               Text('walletCreateSubtitle', style: Theme.of(context).textTheme.bodyMedium).tr(), |               Text('walletCreateSubtitle', style: Theme.of(context).textTheme.bodyMedium).tr(), | ||||||
|               const Gap(8), |               const Gap(8), | ||||||
|               Align( |               Align( | ||||||
|                 alignment: Alignment.centerRight, |                 alignment: Alignment.centerRight, | ||||||
|                 child: TextButton( |                 child: TextButton(onPressed: _isBusy ? null : () => _createWallet(), child: Text('next').tr()), | ||||||
|                   onPressed: _isBusy ? null : () => _createWallet(), |  | ||||||
|                   child: Text('next').tr(), |  | ||||||
|                 ), |  | ||||||
|               ), |               ), | ||||||
|             ], |             ], | ||||||
|           ).padding(horizontal: 20, vertical: 24), |           ).padding(horizontal: 20, vertical: 24), | ||||||
|   | |||||||
| @@ -184,3 +184,42 @@ abstract class SnActionEvent with _$SnActionEvent { | |||||||
|   factory SnActionEvent.fromJson(Map<String, Object?> json) => |   factory SnActionEvent.fromJson(Map<String, Object?> json) => | ||||||
|       _$SnActionEventFromJson(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 | // dart format on | ||||||
|   | |||||||
| @@ -319,3 +319,64 @@ Map<String, dynamic> _$SnActionEventToJson(_SnActionEvent instance) => | |||||||
|       'account': instance.account.toJson(), |       'account': instance.account.toJson(), | ||||||
|       'account_id': instance.accountId, |       '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 updatedAt, | ||||||
|     required DateTime? deletedAt, |     required DateTime? deletedAt, | ||||||
|     required String balance, |     required String balance, | ||||||
|  |     required String goldenBalance, | ||||||
|     required String password, |     required String password, | ||||||
|     required int accountId, |     required int accountId, | ||||||
|   }) = _SnWallet; |   }) = _SnWallet; | ||||||
| @@ -27,6 +28,7 @@ abstract class SnTransaction with _$SnTransaction { | |||||||
|     required DateTime? deletedAt, |     required DateTime? deletedAt, | ||||||
|     required String remark, |     required String remark, | ||||||
|     required String amount, |     required String amount, | ||||||
|  |     required String currency, | ||||||
|     required SnWallet? payer, |     required SnWallet? payer, | ||||||
|     required SnWallet? payee, |     required SnWallet? payee, | ||||||
|     required int? payerId, |     required int? payerId, | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ mixin _$SnWallet { | |||||||
|   DateTime get updatedAt; |   DateTime get updatedAt; | ||||||
|   DateTime? get deletedAt; |   DateTime? get deletedAt; | ||||||
|   String get balance; |   String get balance; | ||||||
|  |   String get goldenBalance; | ||||||
|   String get password; |   String get password; | ||||||
|   int get accountId; |   int get accountId; | ||||||
|  |  | ||||||
| @@ -46,6 +47,8 @@ mixin _$SnWallet { | |||||||
|             (identical(other.deletedAt, deletedAt) || |             (identical(other.deletedAt, deletedAt) || | ||||||
|                 other.deletedAt == deletedAt) && |                 other.deletedAt == deletedAt) && | ||||||
|             (identical(other.balance, balance) || other.balance == balance) && |             (identical(other.balance, balance) || other.balance == balance) && | ||||||
|  |             (identical(other.goldenBalance, goldenBalance) || | ||||||
|  |                 other.goldenBalance == goldenBalance) && | ||||||
|             (identical(other.password, password) || |             (identical(other.password, password) || | ||||||
|                 other.password == password) && |                 other.password == password) && | ||||||
|             (identical(other.accountId, accountId) || |             (identical(other.accountId, accountId) || | ||||||
| @@ -55,11 +58,11 @@ mixin _$SnWallet { | |||||||
|   @JsonKey(includeFromJson: false, includeToJson: false) |   @JsonKey(includeFromJson: false, includeToJson: false) | ||||||
|   @override |   @override | ||||||
|   int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt, |   int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt, | ||||||
|       deletedAt, balance, password, accountId); |       deletedAt, balance, goldenBalance, password, accountId); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   String toString() { |   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 updatedAt, | ||||||
|       DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
|       String balance, |       String balance, | ||||||
|  |       String goldenBalance, | ||||||
|       String password, |       String password, | ||||||
|       int accountId}); |       int accountId}); | ||||||
| } | } | ||||||
| @@ -95,6 +99,7 @@ class _$SnWalletCopyWithImpl<$Res> implements $SnWalletCopyWith<$Res> { | |||||||
|     Object? updatedAt = null, |     Object? updatedAt = null, | ||||||
|     Object? deletedAt = freezed, |     Object? deletedAt = freezed, | ||||||
|     Object? balance = null, |     Object? balance = null, | ||||||
|  |     Object? goldenBalance = null, | ||||||
|     Object? password = null, |     Object? password = null, | ||||||
|     Object? accountId = null, |     Object? accountId = null, | ||||||
|   }) { |   }) { | ||||||
| @@ -119,6 +124,10 @@ class _$SnWalletCopyWithImpl<$Res> implements $SnWalletCopyWith<$Res> { | |||||||
|           ? _self.balance |           ? _self.balance | ||||||
|           : balance // ignore: cast_nullable_to_non_nullable |           : balance // ignore: cast_nullable_to_non_nullable | ||||||
|               as String, |               as String, | ||||||
|  |       goldenBalance: null == goldenBalance | ||||||
|  |           ? _self.goldenBalance | ||||||
|  |           : goldenBalance // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|       password: null == password |       password: null == password | ||||||
|           ? _self.password |           ? _self.password | ||||||
|           : password // ignore: cast_nullable_to_non_nullable |           : password // ignore: cast_nullable_to_non_nullable | ||||||
| @@ -140,6 +149,7 @@ class _SnWallet implements SnWallet { | |||||||
|       required this.updatedAt, |       required this.updatedAt, | ||||||
|       required this.deletedAt, |       required this.deletedAt, | ||||||
|       required this.balance, |       required this.balance, | ||||||
|  |       required this.goldenBalance, | ||||||
|       required this.password, |       required this.password, | ||||||
|       required this.accountId}); |       required this.accountId}); | ||||||
|   factory _SnWallet.fromJson(Map<String, dynamic> json) => |   factory _SnWallet.fromJson(Map<String, dynamic> json) => | ||||||
| @@ -156,6 +166,8 @@ class _SnWallet implements SnWallet { | |||||||
|   @override |   @override | ||||||
|   final String balance; |   final String balance; | ||||||
|   @override |   @override | ||||||
|  |   final String goldenBalance; | ||||||
|  |   @override | ||||||
|   final String password; |   final String password; | ||||||
|   @override |   @override | ||||||
|   final int accountId; |   final int accountId; | ||||||
| @@ -188,6 +200,8 @@ class _SnWallet implements SnWallet { | |||||||
|             (identical(other.deletedAt, deletedAt) || |             (identical(other.deletedAt, deletedAt) || | ||||||
|                 other.deletedAt == deletedAt) && |                 other.deletedAt == deletedAt) && | ||||||
|             (identical(other.balance, balance) || other.balance == balance) && |             (identical(other.balance, balance) || other.balance == balance) && | ||||||
|  |             (identical(other.goldenBalance, goldenBalance) || | ||||||
|  |                 other.goldenBalance == goldenBalance) && | ||||||
|             (identical(other.password, password) || |             (identical(other.password, password) || | ||||||
|                 other.password == password) && |                 other.password == password) && | ||||||
|             (identical(other.accountId, accountId) || |             (identical(other.accountId, accountId) || | ||||||
| @@ -197,11 +211,11 @@ class _SnWallet implements SnWallet { | |||||||
|   @JsonKey(includeFromJson: false, includeToJson: false) |   @JsonKey(includeFromJson: false, includeToJson: false) | ||||||
|   @override |   @override | ||||||
|   int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt, |   int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt, | ||||||
|       deletedAt, balance, password, accountId); |       deletedAt, balance, goldenBalance, password, accountId); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   String toString() { |   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 updatedAt, | ||||||
|       DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
|       String balance, |       String balance, | ||||||
|  |       String goldenBalance, | ||||||
|       String password, |       String password, | ||||||
|       int accountId}); |       int accountId}); | ||||||
| } | } | ||||||
| @@ -239,6 +254,7 @@ class __$SnWalletCopyWithImpl<$Res> implements _$SnWalletCopyWith<$Res> { | |||||||
|     Object? updatedAt = null, |     Object? updatedAt = null, | ||||||
|     Object? deletedAt = freezed, |     Object? deletedAt = freezed, | ||||||
|     Object? balance = null, |     Object? balance = null, | ||||||
|  |     Object? goldenBalance = null, | ||||||
|     Object? password = null, |     Object? password = null, | ||||||
|     Object? accountId = null, |     Object? accountId = null, | ||||||
|   }) { |   }) { | ||||||
| @@ -263,6 +279,10 @@ class __$SnWalletCopyWithImpl<$Res> implements _$SnWalletCopyWith<$Res> { | |||||||
|           ? _self.balance |           ? _self.balance | ||||||
|           : balance // ignore: cast_nullable_to_non_nullable |           : balance // ignore: cast_nullable_to_non_nullable | ||||||
|               as String, |               as String, | ||||||
|  |       goldenBalance: null == goldenBalance | ||||||
|  |           ? _self.goldenBalance | ||||||
|  |           : goldenBalance // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|       password: null == password |       password: null == password | ||||||
|           ? _self.password |           ? _self.password | ||||||
|           : password // ignore: cast_nullable_to_non_nullable |           : password // ignore: cast_nullable_to_non_nullable | ||||||
| @@ -283,6 +303,7 @@ mixin _$SnTransaction { | |||||||
|   DateTime? get deletedAt; |   DateTime? get deletedAt; | ||||||
|   String get remark; |   String get remark; | ||||||
|   String get amount; |   String get amount; | ||||||
|  |   String get currency; | ||||||
|   SnWallet? get payer; |   SnWallet? get payer; | ||||||
|   SnWallet? get payee; |   SnWallet? get payee; | ||||||
|   int? get payerId; |   int? get payerId; | ||||||
| @@ -313,6 +334,8 @@ mixin _$SnTransaction { | |||||||
|                 other.deletedAt == deletedAt) && |                 other.deletedAt == deletedAt) && | ||||||
|             (identical(other.remark, remark) || other.remark == remark) && |             (identical(other.remark, remark) || other.remark == remark) && | ||||||
|             (identical(other.amount, amount) || other.amount == amount) && |             (identical(other.amount, amount) || other.amount == amount) && | ||||||
|  |             (identical(other.currency, currency) || | ||||||
|  |                 other.currency == currency) && | ||||||
|             (identical(other.payer, payer) || other.payer == payer) && |             (identical(other.payer, payer) || other.payer == payer) && | ||||||
|             (identical(other.payee, payee) || other.payee == payee) && |             (identical(other.payee, payee) || other.payee == payee) && | ||||||
|             (identical(other.payerId, payerId) || other.payerId == payerId) && |             (identical(other.payerId, payerId) || other.payerId == payerId) && | ||||||
| @@ -322,11 +345,11 @@ mixin _$SnTransaction { | |||||||
|   @JsonKey(includeFromJson: false, includeToJson: false) |   @JsonKey(includeFromJson: false, includeToJson: false) | ||||||
|   @override |   @override | ||||||
|   int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt, |   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 |   @override | ||||||
|   String toString() { |   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, |       DateTime? deletedAt, | ||||||
|       String remark, |       String remark, | ||||||
|       String amount, |       String amount, | ||||||
|  |       String currency, | ||||||
|       SnWallet? payer, |       SnWallet? payer, | ||||||
|       SnWallet? payee, |       SnWallet? payee, | ||||||
|       int? payerId, |       int? payerId, | ||||||
| @@ -371,6 +395,7 @@ class _$SnTransactionCopyWithImpl<$Res> | |||||||
|     Object? deletedAt = freezed, |     Object? deletedAt = freezed, | ||||||
|     Object? remark = null, |     Object? remark = null, | ||||||
|     Object? amount = null, |     Object? amount = null, | ||||||
|  |     Object? currency = null, | ||||||
|     Object? payer = freezed, |     Object? payer = freezed, | ||||||
|     Object? payee = freezed, |     Object? payee = freezed, | ||||||
|     Object? payerId = freezed, |     Object? payerId = freezed, | ||||||
| @@ -401,6 +426,10 @@ class _$SnTransactionCopyWithImpl<$Res> | |||||||
|           ? _self.amount |           ? _self.amount | ||||||
|           : amount // ignore: cast_nullable_to_non_nullable |           : amount // ignore: cast_nullable_to_non_nullable | ||||||
|               as String, |               as String, | ||||||
|  |       currency: null == currency | ||||||
|  |           ? _self.currency | ||||||
|  |           : currency // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|       payer: freezed == payer |       payer: freezed == payer | ||||||
|           ? _self.payer |           ? _self.payer | ||||||
|           : payer // ignore: cast_nullable_to_non_nullable |           : payer // ignore: cast_nullable_to_non_nullable | ||||||
| @@ -459,6 +488,7 @@ class _SnTransaction implements SnTransaction { | |||||||
|       required this.deletedAt, |       required this.deletedAt, | ||||||
|       required this.remark, |       required this.remark, | ||||||
|       required this.amount, |       required this.amount, | ||||||
|  |       required this.currency, | ||||||
|       required this.payer, |       required this.payer, | ||||||
|       required this.payee, |       required this.payee, | ||||||
|       required this.payerId, |       required this.payerId, | ||||||
| @@ -479,6 +509,8 @@ class _SnTransaction implements SnTransaction { | |||||||
|   @override |   @override | ||||||
|   final String amount; |   final String amount; | ||||||
|   @override |   @override | ||||||
|  |   final String currency; | ||||||
|  |   @override | ||||||
|   final SnWallet? payer; |   final SnWallet? payer; | ||||||
|   @override |   @override | ||||||
|   final SnWallet? payee; |   final SnWallet? payee; | ||||||
| @@ -516,6 +548,8 @@ class _SnTransaction implements SnTransaction { | |||||||
|                 other.deletedAt == deletedAt) && |                 other.deletedAt == deletedAt) && | ||||||
|             (identical(other.remark, remark) || other.remark == remark) && |             (identical(other.remark, remark) || other.remark == remark) && | ||||||
|             (identical(other.amount, amount) || other.amount == amount) && |             (identical(other.amount, amount) || other.amount == amount) && | ||||||
|  |             (identical(other.currency, currency) || | ||||||
|  |                 other.currency == currency) && | ||||||
|             (identical(other.payer, payer) || other.payer == payer) && |             (identical(other.payer, payer) || other.payer == payer) && | ||||||
|             (identical(other.payee, payee) || other.payee == payee) && |             (identical(other.payee, payee) || other.payee == payee) && | ||||||
|             (identical(other.payerId, payerId) || other.payerId == payerId) && |             (identical(other.payerId, payerId) || other.payerId == payerId) && | ||||||
| @@ -525,11 +559,11 @@ class _SnTransaction implements SnTransaction { | |||||||
|   @JsonKey(includeFromJson: false, includeToJson: false) |   @JsonKey(includeFromJson: false, includeToJson: false) | ||||||
|   @override |   @override | ||||||
|   int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt, |   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 |   @override | ||||||
|   String toString() { |   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, |       DateTime? deletedAt, | ||||||
|       String remark, |       String remark, | ||||||
|       String amount, |       String amount, | ||||||
|  |       String currency, | ||||||
|       SnWallet? payer, |       SnWallet? payer, | ||||||
|       SnWallet? payee, |       SnWallet? payee, | ||||||
|       int? payerId, |       int? payerId, | ||||||
| @@ -578,6 +613,7 @@ class __$SnTransactionCopyWithImpl<$Res> | |||||||
|     Object? deletedAt = freezed, |     Object? deletedAt = freezed, | ||||||
|     Object? remark = null, |     Object? remark = null, | ||||||
|     Object? amount = null, |     Object? amount = null, | ||||||
|  |     Object? currency = null, | ||||||
|     Object? payer = freezed, |     Object? payer = freezed, | ||||||
|     Object? payee = freezed, |     Object? payee = freezed, | ||||||
|     Object? payerId = freezed, |     Object? payerId = freezed, | ||||||
| @@ -608,6 +644,10 @@ class __$SnTransactionCopyWithImpl<$Res> | |||||||
|           ? _self.amount |           ? _self.amount | ||||||
|           : amount // ignore: cast_nullable_to_non_nullable |           : amount // ignore: cast_nullable_to_non_nullable | ||||||
|               as String, |               as String, | ||||||
|  |       currency: null == currency | ||||||
|  |           ? _self.currency | ||||||
|  |           : currency // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|       payer: freezed == payer |       payer: freezed == payer | ||||||
|           ? _self.payer |           ? _self.payer | ||||||
|           : payer // ignore: cast_nullable_to_non_nullable |           : payer // ignore: cast_nullable_to_non_nullable | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ _SnWallet _$SnWalletFromJson(Map<String, dynamic> json) => _SnWallet( | |||||||
|           ? null |           ? null | ||||||
|           : DateTime.parse(json['deleted_at'] as String), |           : DateTime.parse(json['deleted_at'] as String), | ||||||
|       balance: json['balance'] as String, |       balance: json['balance'] as String, | ||||||
|  |       goldenBalance: json['golden_balance'] as String, | ||||||
|       password: json['password'] as String, |       password: json['password'] as String, | ||||||
|       accountId: (json['account_id'] as num).toInt(), |       accountId: (json['account_id'] as num).toInt(), | ||||||
|     ); |     ); | ||||||
| @@ -24,6 +25,7 @@ Map<String, dynamic> _$SnWalletToJson(_SnWallet instance) => <String, dynamic>{ | |||||||
|       'updated_at': instance.updatedAt.toIso8601String(), |       'updated_at': instance.updatedAt.toIso8601String(), | ||||||
|       'deleted_at': instance.deletedAt?.toIso8601String(), |       'deleted_at': instance.deletedAt?.toIso8601String(), | ||||||
|       'balance': instance.balance, |       'balance': instance.balance, | ||||||
|  |       'golden_balance': instance.goldenBalance, | ||||||
|       'password': instance.password, |       'password': instance.password, | ||||||
|       'account_id': instance.accountId, |       'account_id': instance.accountId, | ||||||
|     }; |     }; | ||||||
| @@ -38,6 +40,7 @@ _SnTransaction _$SnTransactionFromJson(Map<String, dynamic> json) => | |||||||
|           : DateTime.parse(json['deleted_at'] as String), |           : DateTime.parse(json['deleted_at'] as String), | ||||||
|       remark: json['remark'] as String, |       remark: json['remark'] as String, | ||||||
|       amount: json['amount'] as String, |       amount: json['amount'] as String, | ||||||
|  |       currency: json['currency'] as String, | ||||||
|       payer: json['payer'] == null |       payer: json['payer'] == null | ||||||
|           ? null |           ? null | ||||||
|           : SnWallet.fromJson(json['payer'] as Map<String, dynamic>), |           : SnWallet.fromJson(json['payer'] as Map<String, dynamic>), | ||||||
| @@ -56,6 +59,7 @@ Map<String, dynamic> _$SnTransactionToJson(_SnTransaction instance) => | |||||||
|       'deleted_at': instance.deletedAt?.toIso8601String(), |       'deleted_at': instance.deletedAt?.toIso8601String(), | ||||||
|       'remark': instance.remark, |       'remark': instance.remark, | ||||||
|       'amount': instance.amount, |       'amount': instance.amount, | ||||||
|  |       'currency': instance.currency, | ||||||
|       'payer': instance.payer?.toJson(), |       'payer': instance.payer?.toJson(), | ||||||
|       'payee': instance.payee?.toJson(), |       'payee': instance.payee?.toJson(), | ||||||
|       'payer_id': instance.payerId, |       'payer_id': instance.payerId, | ||||||
|   | |||||||
| @@ -2525,10 +2525,11 @@ packages: | |||||||
|   workmanager: |   workmanager: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
|       name: workmanager |       path: workmanager | ||||||
|       sha256: ed13530cccd28c5c9959ad42d657cd0666274ca74c56dea0ca183ddd527d3a00 |       ref: main | ||||||
|       url: "https://pub.dev" |       resolved-ref: "4ce065135dc1b91fee918f81596b42a56850391d" | ||||||
|     source: hosted |       url: "https://github.com/fluttercommunity/flutter_workmanager.git" | ||||||
|  |     source: git | ||||||
|     version: "0.5.2" |     version: "0.5.2" | ||||||
|   xdg_directories: |   xdg_directories: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ dependencies: | |||||||
|   relative_time: ^5.0.0 |   relative_time: ^5.0.0 | ||||||
|   image_picker: ^1.1.2 |   image_picker: ^1.1.2 | ||||||
|   cross_file: ^0.3.4+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 |   croppy: ^1.3.1 | ||||||
|   flutter_expandable_fab: ^2.3.0 |   flutter_expandable_fab: ^2.3.0 | ||||||
|   dropdown_button2: ^2.3.9 |   dropdown_button2: ^2.3.9 | ||||||
| @@ -103,7 +103,11 @@ dependencies: | |||||||
|   flutter_svg: ^2.0.16 |   flutter_svg: ^2.0.16 | ||||||
|   home_widget: ^0.7.0 |   home_widget: ^0.7.0 | ||||||
|   receive_sharing_intent: ^1.8.1 |   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 |   flutter_app_update: ^3.2.2 | ||||||
|   in_app_review: ^2.0.10 |   in_app_review: ^2.0.10 | ||||||
|   version: ^3.0.2 |   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