Enhanced profile edit

This commit is contained in:
LittleSheep 2025-03-02 20:37:30 +08:00
parent 66aef44281
commit 72e6a6a1f6
22 changed files with 717 additions and 304 deletions

View File

@ -736,5 +736,12 @@
"cacheDelete": "Clean Cache", "cacheDelete": "Clean Cache",
"cacheDeleteDescription": "Remove the cached images and other resources from your disk, the content will be downloaded from server again.", "cacheDeleteDescription": "Remove the cached images and other resources from your disk, the content will be downloaded from server again.",
"cacheDeleted": "All cache has been cleaned up.", "cacheDeleted": "All cache has been cleaned up.",
"userNoDescription": "No description." "userNoDescription": "No description.",
"fieldTimeZone": "Time Zone",
"fieldGender": "Gender",
"fieldPronouns": "Pronouns",
"fieldLocation": "Location",
"fieldLinks": "Links",
"fieldLinkName": "Name",
"fieldLinkUrl": "URL"
} }

View File

@ -734,5 +734,12 @@
"cacheDelete": "清除缓存", "cacheDelete": "清除缓存",
"cacheDeleteDescription": "从磁盘中移除缓存的图片和其他资源,内容将从服务器重新下载。", "cacheDeleteDescription": "从磁盘中移除缓存的图片和其他资源,内容将从服务器重新下载。",
"cacheDeleted": "所有缓存已被清除。", "cacheDeleted": "所有缓存已被清除。",
"userNoDescription": "这个人很懒,没有留下什么……" "userNoDescription": "这个人很懒,没有留下什么……",
"fieldTimeZone": "时区",
"fieldGender": "性别",
"fieldPronouns": "人称代词",
"fieldLocation": "位置",
"fieldLinks": "链接",
"fieldLinkName": "名称",
"fieldLinkUrl": "链接"
} }

View File

@ -733,5 +733,13 @@
"cacheSize": "緩存資源大小", "cacheSize": "緩存資源大小",
"cacheDelete": "清除緩存", "cacheDelete": "清除緩存",
"cacheDeleteDescription": "從磁盤中移除緩存的圖片和其他資源,內容將從服務器重新下載。", "cacheDeleteDescription": "從磁盤中移除緩存的圖片和其他資源,內容將從服務器重新下載。",
"cacheDeleted": "所有緩存已被清除。" "cacheDeleted": "所有緩存已被清除。",
"userNoDescription": "這個人很懶,沒有留下什麼……",
"fieldTimeZone": "時區",
"fieldGender": "性別",
"fieldPronouns": "人稱代詞",
"fieldLocation": "位置",
"fieldLinks": "鏈接",
"fieldLinkName": "名稱",
"fieldLinkUrl": "鏈接"
} }

View File

@ -733,5 +733,13 @@
"cacheSize": "緩存資源大小", "cacheSize": "緩存資源大小",
"cacheDelete": "清除緩存", "cacheDelete": "清除緩存",
"cacheDeleteDescription": "從磁盤中移除緩存的圖片和其他資源,內容將從服務器重新下載。", "cacheDeleteDescription": "從磁盤中移除緩存的圖片和其他資源,內容將從服務器重新下載。",
"cacheDeleted": "所有緩存已被清除。" "cacheDeleted": "所有緩存已被清除。",
"userNoDescription": "這個人很懶,沒有留下什麼……",
"fieldTimeZone": "時區",
"fieldGender": "性別",
"fieldPronouns": "人稱代詞",
"fieldLocation": "位置",
"fieldLinks": "鏈接",
"fieldLinkName": "名稱",
"fieldLinkUrl": "鏈接"
} }

View File

@ -113,6 +113,8 @@ PODS:
- OrderedSet (~> 6.0.3) - OrderedSet (~> 6.0.3)
- flutter_native_splash (2.4.3): - flutter_native_splash (2.4.3):
- Flutter - Flutter
- flutter_timezone (0.0.1):
- Flutter
- flutter_udid (0.0.1): - flutter_udid (0.0.1):
- Flutter - Flutter
- SAMKeychain - SAMKeychain
@ -122,6 +124,8 @@ PODS:
- gal (1.0.0): - gal (1.0.0):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- geolocator_apple (1.2.0):
- Flutter
- GoogleAppMeasurement (11.8.0): - GoogleAppMeasurement (11.8.0):
- GoogleAppMeasurement/AdIdSupport (= 11.8.0) - GoogleAppMeasurement/AdIdSupport (= 11.8.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0)
@ -267,9 +271,11 @@ DEPENDENCIES:
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`) - flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`) - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`)
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`) - flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`) - flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
- gal (from `.symlinks/plugins/gal/darwin`) - gal (from `.symlinks/plugins/gal/darwin`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`)
- home_widget (from `.symlinks/plugins/home_widget/ios`) - home_widget (from `.symlinks/plugins/home_widget/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- in_app_review (from `.symlinks/plugins/in_app_review/ios`) - in_app_review (from `.symlinks/plugins/in_app_review/ios`)
@ -343,12 +349,16 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios" :path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
flutter_native_splash: flutter_native_splash:
:path: ".symlinks/plugins/flutter_native_splash/ios" :path: ".symlinks/plugins/flutter_native_splash/ios"
flutter_timezone:
:path: ".symlinks/plugins/flutter_timezone/ios"
flutter_udid: flutter_udid:
:path: ".symlinks/plugins/flutter_udid/ios" :path: ".symlinks/plugins/flutter_udid/ios"
flutter_webrtc: flutter_webrtc:
:path: ".symlinks/plugins/flutter_webrtc/ios" :path: ".symlinks/plugins/flutter_webrtc/ios"
gal: gal:
:path: ".symlinks/plugins/gal/darwin" :path: ".symlinks/plugins/gal/darwin"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/ios"
home_widget: home_widget:
:path: ".symlinks/plugins/home_widget/ios" :path: ".symlinks/plugins/home_widget/ios"
image_picker_ios: image_picker_ios:
@ -416,9 +426,11 @@ SPEC CHECKSUMS:
flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
flutter_timezone: ac3da59ac941ff1c98a2e1f0293420e020120282
flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab
flutter_webrtc: 90260f83024b1b96d239a575ea4e3708e79344d1 flutter_webrtc: 90260f83024b1b96d239a575ea4e3708e79344d1
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5 gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
geolocator_apple: 9bcea1918ff7f0062d98345d238ae12718acfbc1
GoogleAppMeasurement: fc0817122bd4d4189164f85374e06773b9561896 GoogleAppMeasurement: fc0817122bd4d4189164f85374e06773b9561896
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d

View File

@ -108,8 +108,7 @@ void main() async {
} }
if (!kIsWeb && Platform.isAndroid) { if (!kIsWeb && Platform.isAndroid) {
final ImagePickerPlatform imagePickerImplementation = final ImagePickerPlatform imagePickerImplementation = ImagePickerPlatform.instance;
ImagePickerPlatform.instance;
if (imagePickerImplementation is ImagePickerAndroid) { if (imagePickerImplementation is ImagePickerAndroid) {
imagePickerImplementation.useAndroidPhotoPicker = true; imagePickerImplementation.useAndroidPhotoPicker = true;
} }
@ -228,8 +227,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
if (prefs.containsKey('first_boot_time')) { if (prefs.containsKey('first_boot_time')) {
final rawTime = prefs.getString('first_boot_time'); final rawTime = prefs.getString('first_boot_time');
final time = DateTime.tryParse(rawTime ?? ''); final time = DateTime.tryParse(rawTime ?? '');
if (time != null && if (time != null && time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) {
time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) {
final inAppReview = InAppReview.instance; final inAppReview = InAppReview.instance;
if (prefs.getBool('rating_requested') == true) return; if (prefs.getBool('rating_requested') == true) return;
if (await inAppReview.isAvailable()) { if (await inAppReview.isAvailable()) {
@ -260,18 +258,12 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
final remoteVersionString = resp.data?['tag_name'] ?? '0.0.0+0'; final remoteVersionString = resp.data?['tag_name'] ?? '0.0.0+0';
final remoteVersion = Version.parse(remoteVersionString.split('+').first); final remoteVersion = Version.parse(remoteVersionString.split('+').first);
final localVersion = Version.parse(localVersionString.split('+').first); final localVersion = Version.parse(localVersionString.split('+').first);
final remoteBuildNumber = final remoteBuildNumber = int.tryParse(remoteVersionString.split('+').last) ?? 0;
int.tryParse(remoteVersionString.split('+').last) ?? 0; final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0;
final localBuildNumber = logging.info("[Update] Local: $localVersionString, Remote: $remoteVersionString");
int.tryParse(localVersionString.split('+').last) ?? 0; if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) {
logging.info(
"[Update] Local: $localVersionString, Remote: $remoteVersionString");
if ((remoteVersion > localVersion ||
remoteBuildNumber > localBuildNumber) &&
mounted) {
final config = context.read<ConfigProvider>(); final config = context.read<ConfigProvider>();
config.setUpdate( config.setUpdate(remoteVersionString, resp.data?['body'] ?? 'No changelog');
remoteVersionString, resp.data?['body'] ?? 'No changelog');
logging.info("[Update] Update available: $remoteVersionString"); logging.info("[Update] Update available: $remoteVersionString");
} }
} catch (e) { } catch (e) {
@ -363,9 +355,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
Future<void> _trayInitialization() async { Future<void> _trayInitialization() async {
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return; if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
final icon = Platform.isWindows final icon = Platform.isWindows ? 'assets/icon/tray-icon.ico' : 'assets/icon/tray-icon.png';
? 'assets/icon/tray-icon.ico'
: 'assets/icon/tray-icon.png';
final appVersion = await PackageInfo.fromPlatform(); final appVersion = await PackageInfo.fromPlatform();
trayManager.addListener(this); trayManager.addListener(this);

View File

@ -45,8 +45,7 @@ class AccountScreen extends StatelessWidget {
? Stack( ? Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), fit: BoxFit.cover),
fit: BoxFit.cover),
Positioned( Positioned(
top: 0, top: 0,
left: 0, left: 0,
@ -80,9 +79,7 @@ class AccountScreen extends StatelessWidget {
], ],
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
child: ua.isAuthorized child: ua.isAuthorized ? _AuthorizedAccountScreen() : _UnauthorizedAccountScreen(),
? _AuthorizedAccountScreen()
: _UnauthorizedAccountScreen(),
), ),
); );
} }
@ -118,15 +115,19 @@ class _AuthorizedAccountScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.baseline, crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic, textBaseline: TextBaseline.alphabetic,
children: [ children: [
Text(ua.user!.nick) Text(ua.user!.nick).textStyle(Theme.of(context).textTheme.titleLarge!),
.textStyle(Theme.of(context).textTheme.titleLarge!),
const Gap(4), const Gap(4),
Text('@${ua.user!.name}') Text('@${ua.user!.name}').textStyle(Theme.of(context).textTheme.bodySmall!),
.textStyle(Theme.of(context).textTheme.bodySmall!),
], ],
), ),
Text(ua.user!.description) Text(
.textStyle(Theme.of(context).textTheme.bodyMedium!), (ua.user!.profile?.description.isNotEmpty ?? false)
? ua.user!.profile!.description
: 'userNoDescription'.tr(),
style: (ua.user!.profile?.description.isEmpty ?? true)
? TextStyle(fontStyle: FontStyle.italic)
: null,
).textStyle(Theme.of(context).textTheme.bodyMedium!),
], ],
), ),
); );
@ -225,9 +226,7 @@ class _UnauthorizedAccountScreen extends StatelessWidget {
child: Icon(Symbols.waving_hand, size: 28), child: Icon(Symbols.waving_hand, size: 28),
), ),
const Gap(8), const Gap(8),
Text('accountIntroTitle') Text('accountIntroTitle').tr().textStyle(Theme.of(context).textTheme.titleLarge!),
.tr()
.textStyle(Theme.of(context).textTheme.titleLarge!),
Text('accountIntroSubtitle').tr(), Text('accountIntroSubtitle').tr(),
], ],
).padding(all: 20), ).padding(all: 20),

View File

@ -6,6 +6,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
@ -36,11 +37,16 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
final _firstNameController = TextEditingController(); final _firstNameController = TextEditingController();
final _lastNameController = TextEditingController(); final _lastNameController = TextEditingController();
final _descriptionController = TextEditingController(); final _descriptionController = TextEditingController();
final _timezoneController = TextEditingController();
final _genderController = TextEditingController();
final _pronounsController = TextEditingController();
final _locationController = TextEditingController();
final _birthdayController = TextEditingController(); final _birthdayController = TextEditingController();
String? _avatar; String? _avatar;
String? _banner; String? _banner;
DateTime? _birthday; DateTime? _birthday;
List<(String, String)>? _links;
bool _isBusy = false; bool _isBusy = false;
@ -51,15 +57,21 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
final prof = ua.user!; final prof = ua.user!;
_usernameController.text = prof.name; _usernameController.text = prof.name;
_nicknameController.text = prof.nick; _nicknameController.text = prof.nick;
_descriptionController.text = prof.description; _descriptionController.text = prof.profile!.description;
_firstNameController.text = prof.profile!.firstName; _firstNameController.text = prof.profile!.firstName;
_lastNameController.text = prof.profile!.lastName; _lastNameController.text = prof.profile!.lastName;
_timezoneController.text = prof.profile!.timeZone;
_genderController.text = prof.profile!.gender;
_pronounsController.text = prof.profile!.pronouns;
_locationController.text = prof.profile!.location;
_avatar = prof.avatar; _avatar = prof.avatar;
_banner = prof.banner; _banner = prof.banner;
if (prof.profile!.birthday != null) { _links = prof.profile!.links.entries.map((ele) => (ele.key, ele.value)).toList();
_birthday = prof.profile!.birthday?.toLocal();
if(_birthday != null) {
_birthdayController.text = DateFormat(_kDateFormat).format( _birthdayController.text = DateFormat(_kDateFormat).format(
prof.profile!.birthday!.toLocal(), prof.profile!.birthday!.toLocal(),
); );
} }
} }
@ -166,7 +178,14 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
'description': _descriptionController.value.text, 'description': _descriptionController.value.text,
'first_name': _firstNameController.value.text, 'first_name': _firstNameController.value.text,
'last_name': _lastNameController.value.text, 'last_name': _lastNameController.value.text,
'time_zone': _timezoneController.value.text,
'gender': _genderController.value.text,
'pronouns': _pronounsController.value.text,
'location': _locationController.value.text,
'birthday': _birthday?.toUtc().toIso8601String(), 'birthday': _birthday?.toUtc().toIso8601String(),
'links': {
for (final link in _links!.where((ele) => ele.$1.isNotEmpty && ele.$2.isNotEmpty)) link.$1: link.$2
},
}, },
); );
@ -197,6 +216,10 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
_firstNameController.dispose(); _firstNameController.dispose();
_lastNameController.dispose(); _lastNameController.dispose();
_descriptionController.dispose(); _descriptionController.dispose();
_timezoneController.dispose();
_genderController.dispose();
_pronounsController.dispose();
_locationController.dispose();
_birthdayController.dispose(); _birthdayController.dispose();
super.dispose(); super.dispose();
} }
@ -262,6 +285,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
).padding(horizontal: padding), ).padding(horizontal: padding),
const Gap(8 + 28), const Gap(8 + 28),
Column( Column(
spacing: 4,
children: [ children: [
TextField( TextField(
readOnly: true, readOnly: true,
@ -271,16 +295,16 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
labelText: 'fieldUsername'.tr(), labelText: 'fieldUsername'.tr(),
helperText: 'fieldUsernameCannotEditHint'.tr(), helperText: 'fieldUsernameCannotEditHint'.tr(),
), ),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
const Gap(4),
TextField( TextField(
controller: _nicknameController, controller: _nicknameController,
decoration: InputDecoration( decoration: InputDecoration(
border: const UnderlineInputBorder(), border: const UnderlineInputBorder(),
labelText: 'fieldNickname'.tr(), labelText: 'fieldNickname'.tr(),
), ),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
const Gap(4),
Row( Row(
children: [ children: [
Flexible( Flexible(
@ -291,6 +315,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
border: const UnderlineInputBorder(), border: const UnderlineInputBorder(),
labelText: 'fieldFirstName'.tr(), labelText: 'fieldFirstName'.tr(),
), ),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
), ),
const Gap(8), const Gap(8),
@ -302,11 +327,38 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
border: const UnderlineInputBorder(), border: const UnderlineInputBorder(),
labelText: 'fieldLastName'.tr(), labelText: 'fieldLastName'.tr(),
), ),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
),
],
),
Row(
children: [
Flexible(
flex: 1,
child: TextField(
controller: _genderController,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
labelText: 'fieldGender'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
),
const Gap(4),
Flexible(
flex: 1,
child: TextField(
controller: _pronounsController,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
labelText: 'fieldPronouns'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
), ),
], ],
), ),
const Gap(4),
TextField( TextField(
controller: _descriptionController, controller: _descriptionController,
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
@ -316,8 +368,51 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
border: const UnderlineInputBorder(), border: const UnderlineInputBorder(),
labelText: 'fieldDescription'.tr(), labelText: 'fieldDescription'.tr(),
), ),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: TextField(
controller: _timezoneController,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
labelText: 'fieldTimeZone'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
),
const Gap(4),
StyledWidget(IconButton(
icon: const Icon(Symbols.calendar_month),
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () async {
_timezoneController.text = await FlutterTimezone.getLocalTimezone();
},
)).padding(top: 6),
const Gap(4),
StyledWidget(IconButton(
icon: const Icon(Symbols.clear),
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () {
_timezoneController.clear();
},
)).padding(top: 6),
],
),
TextField(
controller: _locationController,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
labelText: 'fieldLocation'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
const Gap(4),
TextField( TextField(
controller: _birthdayController, controller: _birthdayController,
readOnly: true, readOnly: true,
@ -327,6 +422,75 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
), ),
onTap: () => _selectBirthday(), onTap: () => _selectBirthday(),
), ),
if (_links != null)
Card(
margin: const EdgeInsets.only(top: 16, bottom: 4),
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
'fieldLinks'.tr(),
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 17),
),
),
IconButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
icon: const Icon(Symbols.add),
onPressed: () {
setState(() => _links!.add(('', '')));
},
),
],
),
const Gap(8),
for (var idx = 0; idx < _links!.length; idx++)
Row(
children: [
Flexible(
flex: 1,
child: TextFormField(
initialValue: _links![idx].$1,
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText: 'fieldLinkName'.tr(),
),
onChanged: (value) {
_links![idx] = (value, _links![idx].$2);
},
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
),
const Gap(8),
Flexible(
flex: 1,
child: TextFormField(
initialValue: _links![idx].$2,
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText: 'fieldLinkUrl'.tr(),
),
onChanged: (value) {
_links![idx] = (_links![idx].$1, value);
},
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
),
],
),
],
),
),
),
], ],
).padding(horizontal: padding + 8), ).padding(horizontal: padding + 8),
const Gap(12), const Gap(12),
@ -340,6 +504,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
), ),
], ],
).padding(horizontal: padding), ).padding(horizontal: padding),
Gap(MediaQuery.of(context).padding.bottom),
], ],
), ),
), ),

View File

@ -406,7 +406,7 @@ class _UserScreenState extends State<UserScreen>
], ],
).padding(right: 8), ).padding(right: 8),
const Gap(12), const Gap(12),
Text(_account!.description).padding(horizontal: 8), Text(_account!.profile!.description).padding(horizontal: 8),
const Gap(4), const Gap(4),
Card( Card(
child: Row( child: Row(

View File

@ -97,7 +97,7 @@ class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen>
_banner = ua.user!.banner; _banner = ua.user!.banner;
_nickController.text = ua.user!.nick; _nickController.text = ua.user!.nick;
_nameController.text = ua.user!.name; _nameController.text = ua.user!.name;
_descriptionController.text = ua.user!.description; _descriptionController.text = ua.user!.profile!.description;
setState(() {}); setState(() {});
} }

View File

@ -109,7 +109,7 @@ class _PublisherNewPersonalState extends State<_PublisherNewPersonal> {
_nameController.text = ua.user!.name; _nameController.text = ua.user!.name;
_nickController.text = ua.user!.nick; _nickController.text = ua.user!.nick;
_descriptionController.text = ua.user!.description; _descriptionController.text = ua.user!.profile!.description;
} }
@override @override

View File

@ -16,7 +16,6 @@ abstract class SnAccount with _$SnAccount {
required List<SnAccountContact>? contacts, required List<SnAccountContact>? contacts,
@Default("") String avatar, @Default("") String avatar,
@Default("") String banner, @Default("") String banner,
required String description,
required String name, required String name,
required String nick, required String nick,
@Default({}) Map<String, dynamic> permNodes, @Default({}) Map<String, dynamic> permNodes,
@ -57,15 +56,21 @@ abstract class SnAccountContact with _$SnAccountContact {
abstract class SnAccountProfile with _$SnAccountProfile { abstract class SnAccountProfile with _$SnAccountProfile {
const factory SnAccountProfile({ const factory SnAccountProfile({
required int id, required int id,
required int accountId,
required DateTime? birthday,
required DateTime createdAt, required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt, required DateTime? deletedAt,
required int experience,
required String firstName, required String firstName,
required String lastName, required String lastName,
required String description,
required String timeZone,
required String location,
required String pronouns,
required String gender,
@Default({}) Map<String, String> links,
required int experience,
required DateTime? lastSeenAt, required DateTime? lastSeenAt,
required DateTime updatedAt, required DateTime? birthday,
required int accountId,
}) = _SnAccountProfile; }) = _SnAccountProfile;
factory SnAccountProfile.fromJson(Map<String, Object?> json) => factory SnAccountProfile.fromJson(Map<String, Object?> json) =>

View File

@ -23,7 +23,6 @@ mixin _$SnAccount {
List<SnAccountContact>? get contacts; List<SnAccountContact>? get contacts;
String get avatar; String get avatar;
String get banner; String get banner;
String get description;
String get name; String get name;
String get nick; String get nick;
Map<String, dynamic> get permNodes; Map<String, dynamic> get permNodes;
@ -63,8 +62,6 @@ mixin _$SnAccount {
const DeepCollectionEquality().equals(other.contacts, contacts) && const DeepCollectionEquality().equals(other.contacts, contacts) &&
(identical(other.avatar, avatar) || other.avatar == avatar) && (identical(other.avatar, avatar) || other.avatar == avatar) &&
(identical(other.banner, banner) || other.banner == banner) && (identical(other.banner, banner) || other.banner == banner) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.name, name) || other.name == name) && (identical(other.name, name) || other.name == name) &&
(identical(other.nick, nick) || other.nick == nick) && (identical(other.nick, nick) || other.nick == nick) &&
const DeepCollectionEquality().equals(other.permNodes, permNodes) && const DeepCollectionEquality().equals(other.permNodes, permNodes) &&
@ -96,7 +93,6 @@ mixin _$SnAccount {
const DeepCollectionEquality().hash(contacts), const DeepCollectionEquality().hash(contacts),
avatar, avatar,
banner, banner,
description,
name, name,
nick, nick,
const DeepCollectionEquality().hash(permNodes), const DeepCollectionEquality().hash(permNodes),
@ -112,7 +108,7 @@ mixin _$SnAccount {
@override @override
String toString() { String toString() {
return 'SnAccount(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, confirmedAt: $confirmedAt, contacts: $contacts, avatar: $avatar, banner: $banner, description: $description, name: $name, nick: $nick, permNodes: $permNodes, language: $language, profile: $profile, badges: $badges, suspendedAt: $suspendedAt, affiliatedId: $affiliatedId, affiliatedTo: $affiliatedTo, automatedBy: $automatedBy, automatedId: $automatedId)'; return 'SnAccount(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, confirmedAt: $confirmedAt, contacts: $contacts, avatar: $avatar, banner: $banner, name: $name, nick: $nick, permNodes: $permNodes, language: $language, profile: $profile, badges: $badges, suspendedAt: $suspendedAt, affiliatedId: $affiliatedId, affiliatedTo: $affiliatedTo, automatedBy: $automatedBy, automatedId: $automatedId)';
} }
} }
@ -130,7 +126,6 @@ abstract mixin class $SnAccountCopyWith<$Res> {
List<SnAccountContact>? contacts, List<SnAccountContact>? contacts,
String avatar, String avatar,
String banner, String banner,
String description,
String name, String name,
String nick, String nick,
Map<String, dynamic> permNodes, Map<String, dynamic> permNodes,
@ -166,7 +161,6 @@ class _$SnAccountCopyWithImpl<$Res> implements $SnAccountCopyWith<$Res> {
Object? contacts = freezed, Object? contacts = freezed,
Object? avatar = null, Object? avatar = null,
Object? banner = null, Object? banner = null,
Object? description = null,
Object? name = null, Object? name = null,
Object? nick = null, Object? nick = null,
Object? permNodes = null, Object? permNodes = null,
@ -212,10 +206,6 @@ class _$SnAccountCopyWithImpl<$Res> implements $SnAccountCopyWith<$Res> {
? _self.banner ? _self.banner
: banner // ignore: cast_nullable_to_non_nullable : banner // ignore: cast_nullable_to_non_nullable
as String, as String,
description: null == description
? _self.description
: description // ignore: cast_nullable_to_non_nullable
as String,
name: null == name name: null == name
? _self.name ? _self.name
: name // ignore: cast_nullable_to_non_nullable : name // ignore: cast_nullable_to_non_nullable
@ -290,7 +280,6 @@ class _SnAccount extends SnAccount {
required final List<SnAccountContact>? contacts, required final List<SnAccountContact>? contacts,
this.avatar = "", this.avatar = "",
this.banner = "", this.banner = "",
required this.description,
required this.name, required this.name,
required this.nick, required this.nick,
final Map<String, dynamic> permNodes = const {}, final Map<String, dynamic> permNodes = const {},
@ -336,8 +325,6 @@ class _SnAccount extends SnAccount {
@JsonKey() @JsonKey()
final String banner; final String banner;
@override @override
final String description;
@override
final String name; final String name;
@override @override
final String nick; final String nick;
@ -406,8 +393,6 @@ class _SnAccount extends SnAccount {
const DeepCollectionEquality().equals(other._contacts, _contacts) && const DeepCollectionEquality().equals(other._contacts, _contacts) &&
(identical(other.avatar, avatar) || other.avatar == avatar) && (identical(other.avatar, avatar) || other.avatar == avatar) &&
(identical(other.banner, banner) || other.banner == banner) && (identical(other.banner, banner) || other.banner == banner) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.name, name) || other.name == name) && (identical(other.name, name) || other.name == name) &&
(identical(other.nick, nick) || other.nick == nick) && (identical(other.nick, nick) || other.nick == nick) &&
const DeepCollectionEquality() const DeepCollectionEquality()
@ -440,7 +425,6 @@ class _SnAccount extends SnAccount {
const DeepCollectionEquality().hash(_contacts), const DeepCollectionEquality().hash(_contacts),
avatar, avatar,
banner, banner,
description,
name, name,
nick, nick,
const DeepCollectionEquality().hash(_permNodes), const DeepCollectionEquality().hash(_permNodes),
@ -456,7 +440,7 @@ class _SnAccount extends SnAccount {
@override @override
String toString() { String toString() {
return 'SnAccount(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, confirmedAt: $confirmedAt, contacts: $contacts, avatar: $avatar, banner: $banner, description: $description, name: $name, nick: $nick, permNodes: $permNodes, language: $language, profile: $profile, badges: $badges, suspendedAt: $suspendedAt, affiliatedId: $affiliatedId, affiliatedTo: $affiliatedTo, automatedBy: $automatedBy, automatedId: $automatedId)'; return 'SnAccount(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, confirmedAt: $confirmedAt, contacts: $contacts, avatar: $avatar, banner: $banner, name: $name, nick: $nick, permNodes: $permNodes, language: $language, profile: $profile, badges: $badges, suspendedAt: $suspendedAt, affiliatedId: $affiliatedId, affiliatedTo: $affiliatedTo, automatedBy: $automatedBy, automatedId: $automatedId)';
} }
} }
@ -477,7 +461,6 @@ abstract mixin class _$SnAccountCopyWith<$Res>
List<SnAccountContact>? contacts, List<SnAccountContact>? contacts,
String avatar, String avatar,
String banner, String banner,
String description,
String name, String name,
String nick, String nick,
Map<String, dynamic> permNodes, Map<String, dynamic> permNodes,
@ -514,7 +497,6 @@ class __$SnAccountCopyWithImpl<$Res> implements _$SnAccountCopyWith<$Res> {
Object? contacts = freezed, Object? contacts = freezed,
Object? avatar = null, Object? avatar = null,
Object? banner = null, Object? banner = null,
Object? description = null,
Object? name = null, Object? name = null,
Object? nick = null, Object? nick = null,
Object? permNodes = null, Object? permNodes = null,
@ -560,10 +542,6 @@ class __$SnAccountCopyWithImpl<$Res> implements _$SnAccountCopyWith<$Res> {
? _self.banner ? _self.banner
: banner // ignore: cast_nullable_to_non_nullable : banner // ignore: cast_nullable_to_non_nullable
as String, as String,
description: null == description
? _self.description
: description // ignore: cast_nullable_to_non_nullable
as String,
name: null == name name: null == name
? _self.name ? _self.name
: name // ignore: cast_nullable_to_non_nullable : name // ignore: cast_nullable_to_non_nullable
@ -954,15 +932,21 @@ class __$SnAccountContactCopyWithImpl<$Res>
/// @nodoc /// @nodoc
mixin _$SnAccountProfile { mixin _$SnAccountProfile {
int get id; int get id;
int get accountId;
DateTime? get birthday;
DateTime get createdAt; DateTime get createdAt;
DateTime get updatedAt;
DateTime? get deletedAt; DateTime? get deletedAt;
int get experience;
String get firstName; String get firstName;
String get lastName; String get lastName;
String get description;
String get timeZone;
String get location;
String get pronouns;
String get gender;
Map<String, String> get links;
int get experience;
DateTime? get lastSeenAt; DateTime? get lastSeenAt;
DateTime get updatedAt; DateTime? get birthday;
int get accountId;
/// Create a copy of SnAccountProfile /// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -981,24 +965,34 @@ mixin _$SnAccountProfile {
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is SnAccountProfile && other is SnAccountProfile &&
(identical(other.id, id) || other.id == id) && (identical(other.id, id) || other.id == id) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId) &&
(identical(other.birthday, birthday) ||
other.birthday == birthday) &&
(identical(other.createdAt, createdAt) || (identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) && other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt) &&
(identical(other.deletedAt, deletedAt) || (identical(other.deletedAt, deletedAt) ||
other.deletedAt == deletedAt) && other.deletedAt == deletedAt) &&
(identical(other.experience, experience) ||
other.experience == experience) &&
(identical(other.firstName, firstName) || (identical(other.firstName, firstName) ||
other.firstName == firstName) && other.firstName == firstName) &&
(identical(other.lastName, lastName) || (identical(other.lastName, lastName) ||
other.lastName == lastName) && other.lastName == lastName) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.timeZone, timeZone) ||
other.timeZone == timeZone) &&
(identical(other.location, location) ||
other.location == location) &&
(identical(other.pronouns, pronouns) ||
other.pronouns == pronouns) &&
(identical(other.gender, gender) || other.gender == gender) &&
const DeepCollectionEquality().equals(other.links, links) &&
(identical(other.experience, experience) ||
other.experience == experience) &&
(identical(other.lastSeenAt, lastSeenAt) || (identical(other.lastSeenAt, lastSeenAt) ||
other.lastSeenAt == lastSeenAt) && other.lastSeenAt == lastSeenAt) &&
(identical(other.updatedAt, updatedAt) || (identical(other.birthday, birthday) ||
other.updatedAt == updatedAt)); other.birthday == birthday) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -1006,19 +1000,25 @@ mixin _$SnAccountProfile {
int get hashCode => Object.hash( int get hashCode => Object.hash(
runtimeType, runtimeType,
id, id,
accountId,
birthday,
createdAt, createdAt,
updatedAt,
deletedAt, deletedAt,
experience,
firstName, firstName,
lastName, lastName,
description,
timeZone,
location,
pronouns,
gender,
const DeepCollectionEquality().hash(links),
experience,
lastSeenAt, lastSeenAt,
updatedAt); birthday,
accountId);
@override @override
String toString() { String toString() {
return 'SnAccountProfile(id: $id, accountId: $accountId, birthday: $birthday, createdAt: $createdAt, deletedAt: $deletedAt, experience: $experience, firstName: $firstName, lastName: $lastName, lastSeenAt: $lastSeenAt, updatedAt: $updatedAt)'; return 'SnAccountProfile(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, firstName: $firstName, lastName: $lastName, description: $description, timeZone: $timeZone, location: $location, pronouns: $pronouns, gender: $gender, links: $links, experience: $experience, lastSeenAt: $lastSeenAt, birthday: $birthday, accountId: $accountId)';
} }
} }
@ -1030,15 +1030,21 @@ abstract mixin class $SnAccountProfileCopyWith<$Res> {
@useResult @useResult
$Res call( $Res call(
{int id, {int id,
int accountId,
DateTime? birthday,
DateTime createdAt, DateTime createdAt,
DateTime updatedAt,
DateTime? deletedAt, DateTime? deletedAt,
int experience,
String firstName, String firstName,
String lastName, String lastName,
String description,
String timeZone,
String location,
String pronouns,
String gender,
Map<String, String> links,
int experience,
DateTime? lastSeenAt, DateTime? lastSeenAt,
DateTime updatedAt}); DateTime? birthday,
int accountId});
} }
/// @nodoc /// @nodoc
@ -1055,41 +1061,39 @@ class _$SnAccountProfileCopyWithImpl<$Res>
@override @override
$Res call({ $Res call({
Object? id = null, Object? id = null,
Object? accountId = null,
Object? birthday = freezed,
Object? createdAt = null, Object? createdAt = null,
Object? updatedAt = null,
Object? deletedAt = freezed, Object? deletedAt = freezed,
Object? experience = null,
Object? firstName = null, Object? firstName = null,
Object? lastName = null, Object? lastName = null,
Object? description = null,
Object? timeZone = null,
Object? location = null,
Object? pronouns = null,
Object? gender = null,
Object? links = null,
Object? experience = null,
Object? lastSeenAt = freezed, Object? lastSeenAt = freezed,
Object? updatedAt = null, Object? birthday = freezed,
Object? accountId = null,
}) { }) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id id: null == id
? _self.id ? _self.id
: id // ignore: cast_nullable_to_non_nullable : id // ignore: cast_nullable_to_non_nullable
as int, as int,
accountId: null == accountId
? _self.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
birthday: freezed == birthday
? _self.birthday
: birthday // ignore: cast_nullable_to_non_nullable
as DateTime?,
createdAt: null == createdAt createdAt: null == createdAt
? _self.createdAt ? _self.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime, as DateTime,
updatedAt: null == updatedAt
? _self.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
deletedAt: freezed == deletedAt deletedAt: freezed == deletedAt
? _self.deletedAt ? _self.deletedAt
: deletedAt // ignore: cast_nullable_to_non_nullable : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,
experience: null == experience
? _self.experience
: experience // ignore: cast_nullable_to_non_nullable
as int,
firstName: null == firstName firstName: null == firstName
? _self.firstName ? _self.firstName
: firstName // ignore: cast_nullable_to_non_nullable : firstName // ignore: cast_nullable_to_non_nullable
@ -1098,14 +1102,46 @@ class _$SnAccountProfileCopyWithImpl<$Res>
? _self.lastName ? _self.lastName
: lastName // ignore: cast_nullable_to_non_nullable : lastName // ignore: cast_nullable_to_non_nullable
as String, as String,
description: null == description
? _self.description
: description // ignore: cast_nullable_to_non_nullable
as String,
timeZone: null == timeZone
? _self.timeZone
: timeZone // ignore: cast_nullable_to_non_nullable
as String,
location: null == location
? _self.location
: location // ignore: cast_nullable_to_non_nullable
as String,
pronouns: null == pronouns
? _self.pronouns
: pronouns // ignore: cast_nullable_to_non_nullable
as String,
gender: null == gender
? _self.gender
: gender // ignore: cast_nullable_to_non_nullable
as String,
links: null == links
? _self.links
: links // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
experience: null == experience
? _self.experience
: experience // ignore: cast_nullable_to_non_nullable
as int,
lastSeenAt: freezed == lastSeenAt lastSeenAt: freezed == lastSeenAt
? _self.lastSeenAt ? _self.lastSeenAt
: lastSeenAt // ignore: cast_nullable_to_non_nullable : lastSeenAt // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,
updatedAt: null == updatedAt birthday: freezed == birthday
? _self.updatedAt ? _self.birthday
: updatedAt // ignore: cast_nullable_to_non_nullable : birthday // ignore: cast_nullable_to_non_nullable
as DateTime, as DateTime?,
accountId: null == accountId
? _self.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
)); ));
} }
} }
@ -1115,38 +1151,64 @@ class _$SnAccountProfileCopyWithImpl<$Res>
class _SnAccountProfile implements SnAccountProfile { class _SnAccountProfile implements SnAccountProfile {
const _SnAccountProfile( const _SnAccountProfile(
{required this.id, {required this.id,
required this.accountId,
required this.birthday,
required this.createdAt, required this.createdAt,
required this.updatedAt,
required this.deletedAt, required this.deletedAt,
required this.experience,
required this.firstName, required this.firstName,
required this.lastName, required this.lastName,
required this.description,
required this.timeZone,
required this.location,
required this.pronouns,
required this.gender,
final Map<String, String> links = const {},
required this.experience,
required this.lastSeenAt, required this.lastSeenAt,
required this.updatedAt}); required this.birthday,
required this.accountId})
: _links = links;
factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => factory _SnAccountProfile.fromJson(Map<String, dynamic> json) =>
_$SnAccountProfileFromJson(json); _$SnAccountProfileFromJson(json);
@override @override
final int id; final int id;
@override @override
final int accountId;
@override
final DateTime? birthday;
@override
final DateTime createdAt; final DateTime createdAt;
@override @override
final DateTime? deletedAt; final DateTime updatedAt;
@override @override
final int experience; final DateTime? deletedAt;
@override @override
final String firstName; final String firstName;
@override @override
final String lastName; final String lastName;
@override @override
final String description;
@override
final String timeZone;
@override
final String location;
@override
final String pronouns;
@override
final String gender;
final Map<String, String> _links;
@override
@JsonKey()
Map<String, String> get links {
if (_links is EqualUnmodifiableMapView) return _links;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_links);
}
@override
final int experience;
@override
final DateTime? lastSeenAt; final DateTime? lastSeenAt;
@override @override
final DateTime updatedAt; final DateTime? birthday;
@override
final int accountId;
/// Create a copy of SnAccountProfile /// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -1169,24 +1231,34 @@ class _SnAccountProfile implements SnAccountProfile {
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _SnAccountProfile && other is _SnAccountProfile &&
(identical(other.id, id) || other.id == id) && (identical(other.id, id) || other.id == id) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId) &&
(identical(other.birthday, birthday) ||
other.birthday == birthday) &&
(identical(other.createdAt, createdAt) || (identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) && other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt) &&
(identical(other.deletedAt, deletedAt) || (identical(other.deletedAt, deletedAt) ||
other.deletedAt == deletedAt) && other.deletedAt == deletedAt) &&
(identical(other.experience, experience) ||
other.experience == experience) &&
(identical(other.firstName, firstName) || (identical(other.firstName, firstName) ||
other.firstName == firstName) && other.firstName == firstName) &&
(identical(other.lastName, lastName) || (identical(other.lastName, lastName) ||
other.lastName == lastName) && other.lastName == lastName) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.timeZone, timeZone) ||
other.timeZone == timeZone) &&
(identical(other.location, location) ||
other.location == location) &&
(identical(other.pronouns, pronouns) ||
other.pronouns == pronouns) &&
(identical(other.gender, gender) || other.gender == gender) &&
const DeepCollectionEquality().equals(other._links, _links) &&
(identical(other.experience, experience) ||
other.experience == experience) &&
(identical(other.lastSeenAt, lastSeenAt) || (identical(other.lastSeenAt, lastSeenAt) ||
other.lastSeenAt == lastSeenAt) && other.lastSeenAt == lastSeenAt) &&
(identical(other.updatedAt, updatedAt) || (identical(other.birthday, birthday) ||
other.updatedAt == updatedAt)); other.birthday == birthday) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -1194,19 +1266,25 @@ class _SnAccountProfile implements SnAccountProfile {
int get hashCode => Object.hash( int get hashCode => Object.hash(
runtimeType, runtimeType,
id, id,
accountId,
birthday,
createdAt, createdAt,
updatedAt,
deletedAt, deletedAt,
experience,
firstName, firstName,
lastName, lastName,
description,
timeZone,
location,
pronouns,
gender,
const DeepCollectionEquality().hash(_links),
experience,
lastSeenAt, lastSeenAt,
updatedAt); birthday,
accountId);
@override @override
String toString() { String toString() {
return 'SnAccountProfile(id: $id, accountId: $accountId, birthday: $birthday, createdAt: $createdAt, deletedAt: $deletedAt, experience: $experience, firstName: $firstName, lastName: $lastName, lastSeenAt: $lastSeenAt, updatedAt: $updatedAt)'; return 'SnAccountProfile(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, firstName: $firstName, lastName: $lastName, description: $description, timeZone: $timeZone, location: $location, pronouns: $pronouns, gender: $gender, links: $links, experience: $experience, lastSeenAt: $lastSeenAt, birthday: $birthday, accountId: $accountId)';
} }
} }
@ -1220,15 +1298,21 @@ abstract mixin class _$SnAccountProfileCopyWith<$Res>
@useResult @useResult
$Res call( $Res call(
{int id, {int id,
int accountId,
DateTime? birthday,
DateTime createdAt, DateTime createdAt,
DateTime updatedAt,
DateTime? deletedAt, DateTime? deletedAt,
int experience,
String firstName, String firstName,
String lastName, String lastName,
String description,
String timeZone,
String location,
String pronouns,
String gender,
Map<String, String> links,
int experience,
DateTime? lastSeenAt, DateTime? lastSeenAt,
DateTime updatedAt}); DateTime? birthday,
int accountId});
} }
/// @nodoc /// @nodoc
@ -1245,41 +1329,39 @@ class __$SnAccountProfileCopyWithImpl<$Res>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
$Res call({ $Res call({
Object? id = null, Object? id = null,
Object? accountId = null,
Object? birthday = freezed,
Object? createdAt = null, Object? createdAt = null,
Object? updatedAt = null,
Object? deletedAt = freezed, Object? deletedAt = freezed,
Object? experience = null,
Object? firstName = null, Object? firstName = null,
Object? lastName = null, Object? lastName = null,
Object? description = null,
Object? timeZone = null,
Object? location = null,
Object? pronouns = null,
Object? gender = null,
Object? links = null,
Object? experience = null,
Object? lastSeenAt = freezed, Object? lastSeenAt = freezed,
Object? updatedAt = null, Object? birthday = freezed,
Object? accountId = null,
}) { }) {
return _then(_SnAccountProfile( return _then(_SnAccountProfile(
id: null == id id: null == id
? _self.id ? _self.id
: id // ignore: cast_nullable_to_non_nullable : id // ignore: cast_nullable_to_non_nullable
as int, as int,
accountId: null == accountId
? _self.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
birthday: freezed == birthday
? _self.birthday
: birthday // ignore: cast_nullable_to_non_nullable
as DateTime?,
createdAt: null == createdAt createdAt: null == createdAt
? _self.createdAt ? _self.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime, as DateTime,
updatedAt: null == updatedAt
? _self.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
deletedAt: freezed == deletedAt deletedAt: freezed == deletedAt
? _self.deletedAt ? _self.deletedAt
: deletedAt // ignore: cast_nullable_to_non_nullable : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,
experience: null == experience
? _self.experience
: experience // ignore: cast_nullable_to_non_nullable
as int,
firstName: null == firstName firstName: null == firstName
? _self.firstName ? _self.firstName
: firstName // ignore: cast_nullable_to_non_nullable : firstName // ignore: cast_nullable_to_non_nullable
@ -1288,14 +1370,46 @@ class __$SnAccountProfileCopyWithImpl<$Res>
? _self.lastName ? _self.lastName
: lastName // ignore: cast_nullable_to_non_nullable : lastName // ignore: cast_nullable_to_non_nullable
as String, as String,
description: null == description
? _self.description
: description // ignore: cast_nullable_to_non_nullable
as String,
timeZone: null == timeZone
? _self.timeZone
: timeZone // ignore: cast_nullable_to_non_nullable
as String,
location: null == location
? _self.location
: location // ignore: cast_nullable_to_non_nullable
as String,
pronouns: null == pronouns
? _self.pronouns
: pronouns // ignore: cast_nullable_to_non_nullable
as String,
gender: null == gender
? _self.gender
: gender // ignore: cast_nullable_to_non_nullable
as String,
links: null == links
? _self._links
: links // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
experience: null == experience
? _self.experience
: experience // ignore: cast_nullable_to_non_nullable
as int,
lastSeenAt: freezed == lastSeenAt lastSeenAt: freezed == lastSeenAt
? _self.lastSeenAt ? _self.lastSeenAt
: lastSeenAt // ignore: cast_nullable_to_non_nullable : lastSeenAt // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,
updatedAt: null == updatedAt birthday: freezed == birthday
? _self.updatedAt ? _self.birthday
: updatedAt // ignore: cast_nullable_to_non_nullable : birthday // ignore: cast_nullable_to_non_nullable
as DateTime, as DateTime?,
accountId: null == accountId
? _self.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
)); ));
} }
} }

View File

@ -21,7 +21,6 @@ _SnAccount _$SnAccountFromJson(Map<String, dynamic> json) => _SnAccount(
.toList(), .toList(),
avatar: json['avatar'] as String? ?? "", avatar: json['avatar'] as String? ?? "",
banner: json['banner'] as String? ?? "", banner: json['banner'] as String? ?? "",
description: json['description'] as String,
name: json['name'] as String, name: json['name'] as String,
nick: json['nick'] as String, nick: json['nick'] as String,
permNodes: json['perm_nodes'] as Map<String, dynamic>? ?? const {}, permNodes: json['perm_nodes'] as Map<String, dynamic>? ?? const {},
@ -52,7 +51,6 @@ Map<String, dynamic> _$SnAccountToJson(_SnAccount instance) =>
'contacts': instance.contacts?.map((e) => e.toJson()).toList(), 'contacts': instance.contacts?.map((e) => e.toJson()).toList(),
'avatar': instance.avatar, 'avatar': instance.avatar,
'banner': instance.banner, 'banner': instance.banner,
'description': instance.description,
'name': instance.name, 'name': instance.name,
'nick': instance.nick, 'nick': instance.nick,
'perm_nodes': instance.permNodes, 'perm_nodes': instance.permNodes,
@ -101,35 +99,50 @@ Map<String, dynamic> _$SnAccountContactToJson(_SnAccountContact instance) =>
_SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) => _SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
_SnAccountProfile( _SnAccountProfile(
id: (json['id'] as num).toInt(), id: (json['id'] as num).toInt(),
accountId: (json['account_id'] as num).toInt(),
birthday: json['birthday'] == null
? null
: DateTime.parse(json['birthday'] as String),
createdAt: DateTime.parse(json['created_at'] as String), createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt: json['deleted_at'] == null deletedAt: json['deleted_at'] == null
? null ? null
: DateTime.parse(json['deleted_at'] as String), : DateTime.parse(json['deleted_at'] as String),
experience: (json['experience'] as num).toInt(),
firstName: json['first_name'] as String, firstName: json['first_name'] as String,
lastName: json['last_name'] as String, lastName: json['last_name'] as String,
description: json['description'] as String,
timeZone: json['time_zone'] as String,
location: json['location'] as String,
pronouns: json['pronouns'] as String,
gender: json['gender'] as String,
links: (json['links'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {},
experience: (json['experience'] as num).toInt(),
lastSeenAt: json['last_seen_at'] == null lastSeenAt: json['last_seen_at'] == null
? null ? null
: DateTime.parse(json['last_seen_at'] as String), : DateTime.parse(json['last_seen_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String), birthday: json['birthday'] == null
? null
: DateTime.parse(json['birthday'] as String),
accountId: (json['account_id'] as num).toInt(),
); );
Map<String, dynamic> _$SnAccountProfileToJson(_SnAccountProfile instance) => Map<String, dynamic> _$SnAccountProfileToJson(_SnAccountProfile instance) =>
<String, dynamic>{ <String, dynamic>{
'id': instance.id, 'id': instance.id,
'account_id': instance.accountId,
'birthday': instance.birthday?.toIso8601String(),
'created_at': instance.createdAt.toIso8601String(), 'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(),
'experience': instance.experience,
'first_name': instance.firstName, 'first_name': instance.firstName,
'last_name': instance.lastName, 'last_name': instance.lastName,
'description': instance.description,
'time_zone': instance.timeZone,
'location': instance.location,
'pronouns': instance.pronouns,
'gender': instance.gender,
'links': instance.links,
'experience': instance.experience,
'last_seen_at': instance.lastSeenAt?.toIso8601String(), 'last_seen_at': instance.lastSeenAt?.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(), 'birthday': instance.birthday?.toIso8601String(),
'account_id': instance.accountId,
}; };
_SnRelationship _$SnRelationshipFromJson(Map<String, dynamic> json) => _SnRelationship _$SnRelationshipFromJson(Map<String, dynamic> json) =>

View File

@ -92,10 +92,9 @@ class OpenablePostItem extends StatelessWidget {
openColor: Colors.transparent, openColor: Colors.transparent,
openElevation: 0, openElevation: 0,
transitionType: ContainerTransitionType.fade, transitionType: ContainerTransitionType.fade,
closedColor: closedColor: Theme.of(context).colorScheme.surfaceContainerLow.withOpacity(
Theme.of(context).colorScheme.surfaceContainerLow.withOpacity( cfg.prefs.getBool(kAppBackgroundStoreKey) == true ? 0.75 : 1,
cfg.prefs.getBool(kAppBackgroundStoreKey) == true ? 0.75 : 1, ),
),
closedShape: const RoundedRectangleBorder( closedShape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16)), borderRadius: BorderRadius.all(Radius.circular(16)),
), ),
@ -136,11 +135,9 @@ class PostItem extends StatelessWidget {
final box = context.findRenderObject() as RenderBox?; final box = context.findRenderObject() as RenderBox?;
final url = 'https://solsynth.dev/posts/${data.id}'; final url = 'https://solsynth.dev/posts/${data.id}';
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) { if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
Share.shareUri(Uri.parse(url), Share.shareUri(Uri.parse(url), sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
} else { } else {
Share.share(url, Share.share(url, sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
} }
} }
@ -158,8 +155,7 @@ class PostItem extends StatelessWidget {
child: MultiProvider( child: MultiProvider(
providers: [ providers: [
Provider<SnNetworkProvider>(create: (_) => context.read()), Provider<SnNetworkProvider>(create: (_) => context.read()),
ChangeNotifierProvider<ConfigProvider>( ChangeNotifierProvider<ConfigProvider>(create: (_) => context.read()),
create: (_) => context.read()),
], ],
child: ResponsiveBreakpoints.builder( child: ResponsiveBreakpoints.builder(
breakpoints: ResponsiveBreakpoints.of(context).breakpoints, breakpoints: ResponsiveBreakpoints.of(context).breakpoints,
@ -187,8 +183,7 @@ class PostItem extends StatelessWidget {
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
); );
} else { } else {
await FileSaver.instance.saveFile( await FileSaver.instance.saveFile(name: 'Solar Network Post #${data.id}.png', file: imageFile);
name: 'Solar Network Post #${data.id}.png', file: imageFile);
} }
await imageFile.delete(); await imageFile.delete();
@ -202,9 +197,7 @@ class PostItem extends StatelessWidget {
final isAuthor = ua.isAuthorized && data.publisher.accountId == ua.user?.id; final isAuthor = ua.isAuthorized && data.publisher.accountId == ua.user?.id;
// Video full view // Video full view
if (showFullPost && if (showFullPost && data.type == 'video' && ResponsiveBreakpoints.of(context).largerThan(TABLET)) {
data.type == 'video' &&
ResponsiveBreakpoints.of(context).largerThan(TABLET)) {
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -224,8 +217,7 @@ class PostItem extends StatelessWidget {
if (onDeleted != null) {} if (onDeleted != null) {}
}, },
).padding(bottom: 8), ).padding(bottom: 8),
if (data.preload?.video != null) if (data.preload?.video != null) _PostVideoPlayer(data: data).padding(bottom: 8),
_PostVideoPlayer(data: data).padding(bottom: 8),
_PostHeadline(data: data).padding(horizontal: 4, bottom: 8), _PostHeadline(data: data).padding(horizontal: 4, bottom: 8),
_PostFeaturedComment(data: data), _PostFeaturedComment(data: data),
_PostBottomAction( _PostBottomAction(
@ -273,8 +265,7 @@ class PostItem extends StatelessWidget {
if (onDeleted != null) {} if (onDeleted != null) {}
}, },
).padding(horizontal: 12, top: 8, bottom: 8), ).padding(horizontal: 12, top: 8, bottom: 8),
if (data.preload?.video != null) if (data.preload?.video != null) _PostVideoPlayer(data: data).padding(horizontal: 12, bottom: 8),
_PostVideoPlayer(data: data).padding(horizontal: 12, bottom: 8),
Container( Container(
width: double.infinity, width: double.infinity,
margin: const EdgeInsets.only(bottom: 4, left: 12, right: 12), margin: const EdgeInsets.only(bottom: 4, left: 12, right: 12),
@ -317,13 +308,8 @@ class PostItem extends StatelessWidget {
], ],
), ),
), ),
Text('postArticle') Text('postArticle').tr().fontSize(13).opacity(0.75).padding(horizontal: 24, bottom: 8),
.tr() _PostFeaturedComment(data: data, maxWidth: maxWidth).padding(horizontal: 12),
.fontSize(13)
.opacity(0.75)
.padding(horizontal: 24, bottom: 8),
_PostFeaturedComment(data: data, maxWidth: maxWidth)
.padding(horizontal: 12),
_PostBottomAction( _PostBottomAction(
data: data, data: data,
showComments: showComments, showComments: showComments,
@ -338,8 +324,7 @@ class PostItem extends StatelessWidget {
} }
final displayableAttachments = data.preload?.attachments final displayableAttachments = data.preload?.attachments
?.where((ele) => ?.where((ele) => ele?.mediaType != SnMediaType.image || data.type != 'article')
ele?.mediaType != SnMediaType.image || data.type != 'article')
.toList(); .toList();
final cfg = context.read<ConfigProvider>(); final cfg = context.read<ConfigProvider>();
@ -364,13 +349,9 @@ class PostItem extends StatelessWidget {
if (onDeleted != null) onDeleted!(); if (onDeleted != null) onDeleted!();
}, },
).padding(horizontal: 12, vertical: 8), ).padding(horizontal: 12, vertical: 8),
if (data.preload?.video != null) if (data.preload?.video != null) _PostVideoPlayer(data: data).padding(horizontal: 12, bottom: 8),
_PostVideoPlayer(data: data).padding(horizontal: 12, bottom: 8), if (data.type == 'question') _PostQuestionHint(data: data).padding(horizontal: 16, bottom: 8),
if (data.type == 'question') if (data.body['title'] != null || data.body['description'] != null)
_PostQuestionHint(data: data)
.padding(horizontal: 16, bottom: 8),
if (data.body['title'] != null ||
data.body['description'] != null)
_PostHeadline( _PostHeadline(
data: data, data: data,
isEnlarge: data.type == 'article' && showFullPost, isEnlarge: data.type == 'article' && showFullPost,
@ -384,8 +365,7 @@ class PostItem extends StatelessWidget {
if (data.repostTo != null) if (data.repostTo != null)
_PostQuoteContent(child: data.repostTo!).padding( _PostQuoteContent(child: data.repostTo!).padding(
horizontal: 12, horizontal: 12,
bottom: bottom: data.preload?.attachments?.isNotEmpty ?? false ? 12 : 0,
data.preload?.attachments?.isNotEmpty ?? false ? 12 : 0,
), ),
if (data.visibility > 0) if (data.visibility > 0)
_PostVisibilityHint(data: data).padding( _PostVisibilityHint(data: data).padding(
@ -397,9 +377,7 @@ class PostItem extends StatelessWidget {
horizontal: 16, horizontal: 16,
vertical: 4, vertical: 4,
), ),
if (data.tags.isNotEmpty) if (data.tags.isNotEmpty) _PostTagsList(data: data).padding(horizontal: 16, top: 4, bottom: 6),
_PostTagsList(data: data)
.padding(horizontal: 16, top: 4, bottom: 6),
], ],
), ),
), ),
@ -412,16 +390,12 @@ class PostItem extends StatelessWidget {
fit: showFullPost ? BoxFit.cover : BoxFit.contain, fit: showFullPost ? BoxFit.cover : BoxFit.contain,
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
), ),
if (data.preload?.poll != null) if (data.preload?.poll != null) PostPoll(poll: data.preload!.poll!).padding(horizontal: 12, vertical: 4),
PostPoll(poll: data.preload!.poll!) if (data.body['content'] != null && (cfg.prefs.getBool(kAppExpandPostLink) ?? true))
.padding(horizontal: 12, vertical: 4),
if (data.body['content'] != null &&
(cfg.prefs.getBool(kAppExpandPostLink) ?? true))
LinkPreviewWidget( LinkPreviewWidget(
text: data.body['content'], text: data.body['content'],
).padding(horizontal: 4), ).padding(horizontal: 4),
_PostFeaturedComment(data: data, maxWidth: maxWidth) _PostFeaturedComment(data: data, maxWidth: maxWidth).padding(horizontal: 12),
.padding(horizontal: 12),
Container( Container(
constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity), constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity),
child: Column( child: Column(
@ -483,8 +457,7 @@ class PostShareImageWidget extends StatelessWidget {
showMenu: false, showMenu: false,
isRelativeDate: false, isRelativeDate: false,
).padding(horizontal: 16, bottom: 8), ).padding(horizontal: 16, bottom: 8),
if (data.type == 'question') if (data.type == 'question') _PostQuestionHint(data: data).padding(horizontal: 16, bottom: 8),
_PostQuestionHint(data: data).padding(horizontal: 16, bottom: 8),
_PostHeadline( _PostHeadline(
data: data, data: data,
isEnlarge: data.type == 'article', isEnlarge: data.type == 'article',
@ -499,8 +472,7 @@ class PostShareImageWidget extends StatelessWidget {
child: data.repostTo!, child: data.repostTo!,
isRelativeDate: false, isRelativeDate: false,
).padding(horizontal: 16, bottom: 8), ).padding(horizontal: 16, bottom: 8),
if (data.type != 'article' && if (data.type != 'article' && (data.preload?.attachments?.isNotEmpty ?? false))
(data.preload?.attachments?.isNotEmpty ?? false))
StyledWidget(AttachmentList( StyledWidget(AttachmentList(
data: data.preload!.attachments!, data: data.preload!.attachments!,
columned: true, columned: true,
@ -509,8 +481,7 @@ class PostShareImageWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (data.visibility > 0) _PostVisibilityHint(data: data), if (data.visibility > 0) _PostVisibilityHint(data: data),
if (data.body['content_truncated'] == true) if (data.body['content_truncated'] == true) _PostTruncatedHint(data: data),
_PostTruncatedHint(data: data),
], ],
).padding(horizontal: 16), ).padding(horizontal: 16),
_PostBottomAction( _PostBottomAction(
@ -570,8 +541,7 @@ class PostShareImageWidget extends StatelessWidget {
version: QrVersions.auto, version: QrVersions.auto,
size: 100, size: 100,
gapless: true, gapless: true,
embeddedImage: embeddedImage: AssetImage('assets/icon/icon-light-radius.png'),
AssetImage('assets/icon/icon-light-radius.png'),
embeddedImageStyle: QrEmbeddedImageStyle( embeddedImageStyle: QrEmbeddedImageStyle(
size: Size(28, 28), size: Size(28, 28),
), ),
@ -602,11 +572,9 @@ class _PostQuestionHint extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
children: [ children: [
Icon(data.body['answer'] == null ? Symbols.help : Symbols.check_circle, Icon(data.body['answer'] == null ? Symbols.help : Symbols.check_circle, size: 20),
size: 20),
const Gap(4), const Gap(4),
if (data.body['answer'] == null && if (data.body['answer'] == null && data.body['reward']?.toDouble() != null)
data.body['reward']?.toDouble() != null)
Text('postQuestionUnansweredWithReward'.tr(args: [ Text('postQuestionUnansweredWithReward'.tr(args: [
'${data.body['reward']}', '${data.body['reward']}',
])).opacity(0.75) ])).opacity(0.75)
@ -642,9 +610,7 @@ class _PostBottomAction extends StatelessWidget {
); );
final String? mostTypicalReaction = data.metric.reactionList.isNotEmpty final String? mostTypicalReaction = data.metric.reactionList.isNotEmpty
? data.metric.reactionList.entries ? data.metric.reactionList.entries.reduce((a, b) => a.value > b.value ? a : b).key
.reduce((a, b) => a.value > b.value ? a : b)
.key
: null; : null;
return Row( return Row(
@ -658,8 +624,7 @@ class _PostBottomAction extends StatelessWidget {
InkWell( InkWell(
child: Row( child: Row(
children: [ children: [
if (mostTypicalReaction == null || if (mostTypicalReaction == null || kTemplateReactions[mostTypicalReaction] == null)
kTemplateReactions[mostTypicalReaction] == null)
Icon(Symbols.add_reaction, size: 20, color: iconColor) Icon(Symbols.add_reaction, size: 20, color: iconColor)
else else
Text( Text(
@ -671,8 +636,7 @@ class _PostBottomAction extends StatelessWidget {
), ),
), ),
const Gap(8), const Gap(8),
if (data.totalUpvote > 0 && if (data.totalUpvote > 0 && data.totalUpvote >= data.totalDownvote)
data.totalUpvote >= data.totalDownvote)
Text('postReactionUpvote').plural( Text('postReactionUpvote').plural(
data.totalUpvote, data.totalUpvote,
) )
@ -691,12 +655,8 @@ class _PostBottomAction extends StatelessWidget {
data: data, data: data,
onChanged: (value, attr, delta) { onChanged: (value, attr, delta) {
onChanged(data.copyWith( onChanged(data.copyWith(
totalUpvote: attr == 1 totalUpvote: attr == 1 ? data.totalUpvote + delta : data.totalUpvote,
? data.totalUpvote + delta totalDownvote: attr == 2 ? data.totalDownvote + delta : data.totalDownvote,
: data.totalUpvote,
totalDownvote: attr == 2
? data.totalDownvote + delta
: data.totalDownvote,
metric: data.metric.copyWith(reactionList: value), metric: data.metric.copyWith(reactionList: value),
)); ));
}, },
@ -803,7 +763,7 @@ class _PostHeadline extends StatelessWidget {
children: [ children: [
Text( Text(
'articleWrittenAt'.tr( 'articleWrittenAt'.tr(
args: [DateFormat('y/M/d HH:mm').format(data.createdAt)], args: [DateFormat('y/M/d HH:mm').format(data.createdAt.toLocal())],
), ),
style: TextStyle(fontSize: 13), style: TextStyle(fontSize: 13),
), ),
@ -811,7 +771,7 @@ class _PostHeadline extends StatelessWidget {
if (data.editedAt != null) if (data.editedAt != null)
Text( Text(
'articleEditedAt'.tr( 'articleEditedAt'.tr(
args: [DateFormat('y/M/d HH:mm').format(data.editedAt!)], args: [DateFormat('y/M/d HH:mm').format(data.editedAt!.toLocal())],
), ),
style: TextStyle(fontSize: 13), style: TextStyle(fontSize: 13),
), ),
@ -944,10 +904,8 @@ class _PostContentHeader extends StatelessWidget {
const Gap(4), const Gap(4),
Text( Text(
isRelativeDate isRelativeDate
? RelativeTime(context) ? RelativeTime(context).format((data.publishedAt ?? data.createdAt).toLocal())
.format(data.publishedAt ?? data.createdAt) : DateFormat('y/M/d HH:mm').format((data.publishedAt ?? data.createdAt).toLocal()),
: DateFormat('y/M/d HH:mm')
.format(data.publishedAt ?? data.createdAt),
).fontSize(13), ).fontSize(13),
], ],
).opacity(0.8), ).opacity(0.8),
@ -965,10 +923,8 @@ class _PostContentHeader extends StatelessWidget {
const Gap(4), const Gap(4),
Text( Text(
isRelativeDate isRelativeDate
? RelativeTime(context) ? RelativeTime(context).format((data.publishedAt ?? data.createdAt).toLocal())
.format(data.publishedAt ?? data.createdAt) : DateFormat('y/M/d HH:mm').format((data.publishedAt ?? data.createdAt).toLocal()),
: DateFormat('y/M/d HH:mm')
.format(data.publishedAt ?? data.createdAt),
).fontSize(13), ).fontSize(13),
], ],
).opacity(0.8), ).opacity(0.8),
@ -1151,8 +1107,7 @@ class _PostContentBody extends StatelessWidget {
if (data.body['content'] == null) return const SizedBox.shrink(); if (data.body['content'] == null) return const SizedBox.shrink();
final content = MarkdownTextContent( final content = MarkdownTextContent(
isAutoWarp: data.type == 'story', isAutoWarp: data.type == 'story',
isEnlargeSticker: isEnlargeSticker: RegExp(r"^:([-\w]+):$").hasMatch(data.body['content'] ?? ''),
RegExp(r"^:([-\w]+):$").hasMatch(data.body['content'] ?? ''),
textScaler: isEnlarge ? TextScaler.linear(1.1) : null, textScaler: isEnlarge ? TextScaler.linear(1.1) : null,
content: data.body['content'], content: data.body['content'],
attachments: data.preload?.attachments, attachments: data.preload?.attachments,
@ -1201,12 +1156,10 @@ class _PostQuoteContent extends StatelessWidget {
onDeleted: () {}, onDeleted: () {},
).padding(bottom: 4), ).padding(bottom: 4),
_PostContentBody(data: child), _PostContentBody(data: child),
if (child.visibility > 0) if (child.visibility > 0) _PostVisibilityHint(data: child).padding(top: 4),
_PostVisibilityHint(data: child).padding(top: 4),
], ],
).padding(horizontal: 16), ).padding(horizontal: 16),
if (child.type != 'article' && if (child.type != 'article' && (child.preload?.attachments?.isNotEmpty ?? false))
(child.preload?.attachments?.isNotEmpty ?? false))
ClipRRect( ClipRRect(
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(8), bottomLeft: Radius.circular(8),
@ -1357,9 +1310,7 @@ class _PostTruncatedHint extends StatelessWidget {
const Gap(4), const Gap(4),
Text('postReadEstimate').tr(args: [ Text('postReadEstimate').tr(args: [
'${Duration( '${Duration(
seconds: (data.body['content_length'] as num).toDouble() * seconds: (data.body['content_length'] as num).toDouble() * 60 ~/ kHumanReadSpeed,
60 ~/
kHumanReadSpeed,
).inSeconds}s', ).inSeconds}s',
]), ]),
], ],
@ -1398,8 +1349,7 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
// If this is a answered question, fetch the answer instead // If this is a answered question, fetch the answer instead
if (widget.data.type == 'question' && widget.data.body['answer'] != null) { if (widget.data.type == 'question' && widget.data.body['answer'] != null) {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
final resp = final resp = await sn.client.get('/cgi/co/posts/${widget.data.body['answer']}');
await sn.client.get('/cgi/co/posts/${widget.data.body['answer']}');
_isAnswer = true; _isAnswer = true;
setState(() => _featuredComment = SnPost.fromJson(resp.data)); setState(() => _featuredComment = SnPost.fromJson(resp.data));
return; return;
@ -1407,11 +1357,9 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
try { try {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
final resp = await sn.client.get( final resp = await sn.client.get('/cgi/co/posts/${widget.data.id}/replies/featured', queryParameters: {
'/cgi/co/posts/${widget.data.id}/replies/featured', 'take': 1,
queryParameters: { });
'take': 1,
});
setState(() => _featuredComment = SnPost.fromJson(resp.data[0])); setState(() => _featuredComment = SnPost.fromJson(resp.data[0]));
} catch (err) { } catch (err) {
if (!mounted) return; if (!mounted) return;
@ -1440,9 +1388,7 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
width: double.infinity, width: double.infinity,
child: Material( child: Material(
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
color: _isAnswer color: _isAnswer ? Colors.green.withOpacity(0.5) : Theme.of(context).colorScheme.surfaceContainerHigh,
? Colors.green.withOpacity(0.5)
: Theme.of(context).colorScheme.surfaceContainerHigh,
child: InkWell( child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
onTap: () { onTap: () {
@ -1462,17 +1408,11 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
const Gap(2), const Gap(2),
Icon(_isAnswer ? Symbols.task_alt : Symbols.prompt_suggestion, Icon(_isAnswer ? Symbols.task_alt : Symbols.prompt_suggestion, size: 20),
size: 20),
const Gap(10), const Gap(10),
Text( Text(
_isAnswer _isAnswer ? 'postQuestionAnswerTitle' : 'postFeaturedComment',
? 'postQuestionAnswerTitle' style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 15),
: 'postFeaturedComment',
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontSize: 15),
).tr(), ).tr(),
], ],
), ),
@ -1610,8 +1550,7 @@ class _PostGetInsightPopupState extends State<_PostGetInsightPopup> {
} }
RegExp cleanThinkingRegExp = RegExp(r'<think>[\s\S]*?</think>'); RegExp cleanThinkingRegExp = RegExp(r'<think>[\s\S]*?</think>');
setState( setState(() => _response = out.replaceAll(cleanThinkingRegExp, '').trim());
() => _response = out.replaceAll(cleanThinkingRegExp, '').trim());
} catch (err) { } catch (err) {
if (!mounted) return; if (!mounted) return;
context.showErrorDialog(err); context.showErrorDialog(err);
@ -1634,16 +1573,11 @@ class _PostGetInsightPopupState extends State<_PostGetInsightPopup> {
children: [ children: [
const Icon(Symbols.book_4_spark, size: 24), const Icon(Symbols.book_4_spark, size: 24),
const Gap(16), const Gap(16),
Text('postGetInsightTitle', Text('postGetInsightTitle', style: Theme.of(context).textTheme.titleLarge).tr(),
style: Theme.of(context).textTheme.titleLarge)
.tr(),
], ],
).padding(horizontal: 20, top: 16, bottom: 12), ).padding(horizontal: 20, top: 16, bottom: 12),
const Gap(4), const Gap(4),
Text('postGetInsightDescription', Text('postGetInsightDescription', style: Theme.of(context).textTheme.bodySmall).tr().padding(horizontal: 20),
style: Theme.of(context).textTheme.bodySmall)
.tr()
.padding(horizontal: 20),
const Gap(4), const Gap(4),
if (_response == null) if (_response == null)
Expanded( Expanded(
@ -1661,16 +1595,12 @@ class _PostGetInsightPopupState extends State<_PostGetInsightPopup> {
leading: const Icon(Symbols.info), leading: const Icon(Symbols.info),
title: Text('aiThinkingProcess'.tr()), title: Text('aiThinkingProcess'.tr()),
tilePadding: const EdgeInsets.symmetric(horizontal: 20), tilePadding: const EdgeInsets.symmetric(horizontal: 20),
collapsedBackgroundColor: collapsedBackgroundColor: Theme.of(context).colorScheme.surfaceContainerHigh,
Theme.of(context).colorScheme.surfaceContainerHigh,
minTileHeight: 32, minTileHeight: 32,
children: [ children: [
SelectableText( SelectableText(
_thinkingProcess!, _thinkingProcess!,
style: Theme.of(context) style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontStyle: FontStyle.italic),
.textTheme
.bodyMedium!
.copyWith(fontStyle: FontStyle.italic),
).padding(horizontal: 20, vertical: 8), ).padding(horizontal: 20, vertical: 8),
], ],
).padding(vertical: 8), ).padding(vertical: 8),
@ -1707,8 +1637,7 @@ class _PostVideoPlayer extends StatelessWidget {
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AttachmentItem( child: AttachmentItem(data: data.preload!.video!, heroTag: 'post-video-${data.id}'),
data: data.preload!.video!, heroTag: 'post-video-${data.id}'),
), ),
), ),
); );

View File

@ -9,6 +9,7 @@
#include <bitsdojo_window_linux/bitsdojo_window_plugin.h> #include <bitsdojo_window_linux/bitsdojo_window_plugin.h>
#include <file_saver/file_saver_plugin.h> #include <file_saver/file_saver_plugin.h>
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <flutter_timezone/flutter_timezone_plugin.h>
#include <flutter_udid/flutter_udid_plugin.h> #include <flutter_udid/flutter_udid_plugin.h>
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h> #include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <hotkey_manager_linux/hotkey_manager_linux_plugin.h> #include <hotkey_manager_linux/hotkey_manager_linux_plugin.h>
@ -30,6 +31,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar); file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) flutter_timezone_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterTimezonePlugin");
flutter_timezone_plugin_register_with_registrar(flutter_timezone_registrar);
g_autoptr(FlPluginRegistrar) flutter_udid_registrar = g_autoptr(FlPluginRegistrar) flutter_udid_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterUdidPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterUdidPlugin");
flutter_udid_plugin_register_with_registrar(flutter_udid_registrar); flutter_udid_plugin_register_with_registrar(flutter_udid_registrar);

View File

@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
bitsdojo_window_linux bitsdojo_window_linux
file_saver file_saver
file_selector_linux file_selector_linux
flutter_timezone
flutter_udid flutter_udid
flutter_webrtc flutter_webrtc
hotkey_manager_linux hotkey_manager_linux

View File

@ -15,9 +15,11 @@ import firebase_analytics
import firebase_core import firebase_core
import firebase_messaging import firebase_messaging
import flutter_inappwebview_macos import flutter_inappwebview_macos
import flutter_timezone
import flutter_udid import flutter_udid
import flutter_webrtc import flutter_webrtc
import gal import gal
import geolocator_apple
import hotkey_manager_macos import hotkey_manager_macos
import in_app_review import in_app_review
import livekit_client import livekit_client
@ -48,9 +50,11 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin"))
FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin")) FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin"))
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin")) FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin")) GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin")) HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin")) LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))

View File

@ -345,6 +345,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.0.8"
dart_earcut:
dependency: transitive
description:
name: dart_earcut
sha256: e485001bfc05dcbc437d7bfb666316182e3522d4c3f9668048e004d0eb2ce43b
url: "https://pub.dev"
source: hosted
version: "1.2.0"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
@ -803,6 +811,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_map:
dependency: "direct main"
description:
name: flutter_map
sha256: bbf145e8220531f2f727608c431871c7457f3b134e513543913afd00fdc1cd47
url: "https://pub.dev"
source: hosted
version: "8.1.0"
flutter_markdown: flutter_markdown:
dependency: "direct main" dependency: "direct main"
description: description:
@ -872,6 +888,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_timezone:
dependency: "direct main"
description:
name: flutter_timezone
sha256: bc286cecb0366d88e6c4644e3962ebd1ce1d233abc658eb1e0cd803389f84b64
url: "https://pub.dev"
source: hosted
version: "4.1.0"
flutter_udid: flutter_udid:
dependency: "direct main" dependency: "direct main"
description: description:
@ -933,6 +957,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
geolocator:
dependency: "direct main"
description:
name: geolocator
sha256: d2ec66329cab29cb297d51d96c067d457ca519dca8589665fa0b82ebacb7dbe4
url: "https://pub.dev"
source: hosted
version: "13.0.2"
geolocator_android:
dependency: transitive
description:
name: geolocator_android
sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47"
url: "https://pub.dev"
source: hosted
version: "4.6.1"
geolocator_apple:
dependency: transitive
description:
name: geolocator_apple
sha256: c4ecead17985ede9634f21500072edfcb3dba0ef7b97f8d7bc556d2d722b3ba3
url: "https://pub.dev"
source: hosted
version: "2.3.9"
geolocator_platform_interface:
dependency: transitive
description:
name: geolocator_platform_interface
sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
geolocator_web:
dependency: transitive
description:
name: geolocator_web
sha256: "2ed69328e05cd94e7eb48bb0535f5fc0c0c44d1c4fa1e9737267484d05c29b5e"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
geolocator_windows:
dependency: transitive
description:
name: geolocator_windows
sha256: "53da08937d07c24b0d9952eb57a3b474e29aae2abf9dd717f7e1230995f13f0e"
url: "https://pub.dev"
source: hosted
version: "0.2.3"
glob: glob:
dependency: transitive dependency: transitive
description: description:
@ -1197,6 +1269,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.9.4" version: "6.9.4"
latlong2:
dependency: transitive
description:
name: latlong2
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
url: "https://pub.dev"
source: hosted
version: "0.9.1"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -1237,6 +1317,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.1" version: "5.1.1"
lists:
dependency: transitive
description:
name: lists
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
livekit_client: livekit_client:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1253,6 +1341,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.6" version: "0.1.6"
logger:
dependency: transitive
description:
name: logger
sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1
url: "https://pub.dev"
source: hosted
version: "2.5.0"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -1389,6 +1485,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.16.0" version: "1.16.0"
mgrs_dart:
dependency: transitive
description:
name: mgrs_dart
sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7
url: "https://pub.dev"
source: hosted
version: "2.0.0"
mime: mime:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1605,6 +1709,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
polylabel:
dependency: transitive
description:
name: polylabel
sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@ -1629,6 +1741,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.1" version: "6.0.1"
proj4dart:
dependency: transitive
description:
name: proj4dart
sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
url: "https://pub.dev"
source: hosted
version: "2.1.0"
protobuf: protobuf:
dependency: transitive dependency: transitive
description: description:
@ -2146,6 +2266,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.3" version: "0.1.3"
unicode:
dependency: transitive
description:
name: unicode
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
universal_io: universal_io:
dependency: transitive dependency: transitive
description: description:
@ -2386,6 +2514,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
wkt_parser:
dependency: transitive
description:
name: wkt_parser
sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
workmanager: workmanager:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -134,6 +134,9 @@ dependencies:
talker_dio_logger: ^4.6.14 talker_dio_logger: ^4.6.14
talker: ^4.6.14 talker: ^4.6.14
flutter_cache_manager: ^3.4.1 flutter_cache_manager: ^3.4.1
flutter_timezone: ^4.1.0
flutter_map: ^8.1.0
geolocator: ^13.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -12,9 +12,11 @@
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <firebase_core/firebase_core_plugin_c_api.h> #include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h> #include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <flutter_timezone/flutter_timezone_plugin_c_api.h>
#include <flutter_udid/flutter_udid_plugin_c_api.h> #include <flutter_udid/flutter_udid_plugin_c_api.h>
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h> #include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <gal/gal_plugin_c_api.h> #include <gal/gal_plugin_c_api.h>
#include <geolocator_windows/geolocator_windows.h>
#include <hotkey_manager_windows/hotkey_manager_windows_plugin_c_api.h> #include <hotkey_manager_windows/hotkey_manager_windows_plugin_c_api.h>
#include <livekit_client/live_kit_plugin.h> #include <livekit_client/live_kit_plugin.h>
#include <local_notifier/local_notifier_plugin.h> #include <local_notifier/local_notifier_plugin.h>
@ -41,12 +43,16 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
FlutterTimezonePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterTimezonePluginCApi"));
FlutterUdidPluginCApiRegisterWithRegistrar( FlutterUdidPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterUdidPluginCApi")); registry->GetRegistrarForPlugin("FlutterUdidPluginCApi"));
FlutterWebRTCPluginRegisterWithRegistrar( FlutterWebRTCPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterWebRTCPlugin")); registry->GetRegistrarForPlugin("FlutterWebRTCPlugin"));
GalPluginCApiRegisterWithRegistrar( GalPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GalPluginCApi")); registry->GetRegistrarForPlugin("GalPluginCApi"));
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
HotkeyManagerWindowsPluginCApiRegisterWithRegistrar( HotkeyManagerWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("HotkeyManagerWindowsPluginCApi")); registry->GetRegistrarForPlugin("HotkeyManagerWindowsPluginCApi"));
LiveKitPluginRegisterWithRegistrar( LiveKitPluginRegisterWithRegistrar(

View File

@ -9,9 +9,11 @@ list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows file_selector_windows
firebase_core firebase_core
flutter_inappwebview_windows flutter_inappwebview_windows
flutter_timezone
flutter_udid flutter_udid
flutter_webrtc flutter_webrtc
gal gal
geolocator_windows
hotkey_manager_windows hotkey_manager_windows
livekit_client livekit_client
local_notifier local_notifier