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

View File

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

View File

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

View File

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

View File

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

View File

@ -45,8 +45,7 @@ class AccountScreen extends StatelessWidget {
? Stack(
fit: StackFit.expand,
children: [
AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner),
fit: BoxFit.cover),
AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), fit: BoxFit.cover),
Positioned(
top: 0,
left: 0,
@ -80,9 +79,7 @@ class AccountScreen extends StatelessWidget {
],
),
body: SingleChildScrollView(
child: ua.isAuthorized
? _AuthorizedAccountScreen()
: _UnauthorizedAccountScreen(),
child: ua.isAuthorized ? _AuthorizedAccountScreen() : _UnauthorizedAccountScreen(),
),
);
}
@ -118,15 +115,19 @@ class _AuthorizedAccountScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(ua.user!.nick)
.textStyle(Theme.of(context).textTheme.titleLarge!),
Text(ua.user!.nick).textStyle(Theme.of(context).textTheme.titleLarge!),
const Gap(4),
Text('@${ua.user!.name}')
.textStyle(Theme.of(context).textTheme.bodySmall!),
Text('@${ua.user!.name}').textStyle(Theme.of(context).textTheme.bodySmall!),
],
),
Text(ua.user!.description)
.textStyle(Theme.of(context).textTheme.bodyMedium!),
Text(
(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),
),
const Gap(8),
Text('accountIntroTitle')
.tr()
.textStyle(Theme.of(context).textTheme.titleLarge!),
Text('accountIntroTitle').tr().textStyle(Theme.of(context).textTheme.titleLarge!),
Text('accountIntroSubtitle').tr(),
],
).padding(all: 20),

View File

@ -6,6 +6,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:gap/gap.dart';
import 'package:image_picker/image_picker.dart';
import 'package:material_symbols_icons/symbols.dart';
@ -36,11 +37,16 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
final _firstNameController = TextEditingController();
final _lastNameController = TextEditingController();
final _descriptionController = TextEditingController();
final _timezoneController = TextEditingController();
final _genderController = TextEditingController();
final _pronounsController = TextEditingController();
final _locationController = TextEditingController();
final _birthdayController = TextEditingController();
String? _avatar;
String? _banner;
DateTime? _birthday;
List<(String, String)>? _links;
bool _isBusy = false;
@ -51,15 +57,21 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
final prof = ua.user!;
_usernameController.text = prof.name;
_nicknameController.text = prof.nick;
_descriptionController.text = prof.description;
_descriptionController.text = prof.profile!.description;
_firstNameController.text = prof.profile!.firstName;
_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;
_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(
prof.profile!.birthday!.toLocal(),
);
prof.profile!.birthday!.toLocal(),
);
}
}
@ -166,7 +178,14 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
'description': _descriptionController.value.text,
'first_name': _firstNameController.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(),
'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();
_lastNameController.dispose();
_descriptionController.dispose();
_timezoneController.dispose();
_genderController.dispose();
_pronounsController.dispose();
_locationController.dispose();
_birthdayController.dispose();
super.dispose();
}
@ -262,6 +285,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
).padding(horizontal: padding),
const Gap(8 + 28),
Column(
spacing: 4,
children: [
TextField(
readOnly: true,
@ -271,16 +295,16 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
labelText: 'fieldUsername'.tr(),
helperText: 'fieldUsernameCannotEditHint'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const Gap(4),
TextField(
controller: _nicknameController,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
labelText: 'fieldNickname'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const Gap(4),
Row(
children: [
Flexible(
@ -291,6 +315,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
border: const UnderlineInputBorder(),
labelText: 'fieldFirstName'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
),
const Gap(8),
@ -302,11 +327,38 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
border: const UnderlineInputBorder(),
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(
controller: _descriptionController,
keyboardType: TextInputType.multiline,
@ -316,8 +368,51 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
border: const UnderlineInputBorder(),
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(
controller: _birthdayController,
readOnly: true,
@ -327,6 +422,75 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
),
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),
const Gap(12),
@ -340,6 +504,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
),
],
).padding(horizontal: padding),
Gap(MediaQuery.of(context).padding.bottom),
],
),
),

View File

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

View File

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

View File

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

View File

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

View File

@ -23,7 +23,6 @@ mixin _$SnAccount {
List<SnAccountContact>? get contacts;
String get avatar;
String get banner;
String get description;
String get name;
String get nick;
Map<String, dynamic> get permNodes;
@ -63,8 +62,6 @@ mixin _$SnAccount {
const DeepCollectionEquality().equals(other.contacts, contacts) &&
(identical(other.avatar, avatar) || other.avatar == avatar) &&
(identical(other.banner, banner) || other.banner == banner) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.nick, nick) || other.nick == nick) &&
const DeepCollectionEquality().equals(other.permNodes, permNodes) &&
@ -96,7 +93,6 @@ mixin _$SnAccount {
const DeepCollectionEquality().hash(contacts),
avatar,
banner,
description,
name,
nick,
const DeepCollectionEquality().hash(permNodes),
@ -112,7 +108,7 @@ mixin _$SnAccount {
@override
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,
String avatar,
String banner,
String description,
String name,
String nick,
Map<String, dynamic> permNodes,
@ -166,7 +161,6 @@ class _$SnAccountCopyWithImpl<$Res> implements $SnAccountCopyWith<$Res> {
Object? contacts = freezed,
Object? avatar = null,
Object? banner = null,
Object? description = null,
Object? name = null,
Object? nick = null,
Object? permNodes = null,
@ -212,10 +206,6 @@ class _$SnAccountCopyWithImpl<$Res> implements $SnAccountCopyWith<$Res> {
? _self.banner
: banner // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _self.description
: description // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _self.name
: name // ignore: cast_nullable_to_non_nullable
@ -290,7 +280,6 @@ class _SnAccount extends SnAccount {
required final List<SnAccountContact>? contacts,
this.avatar = "",
this.banner = "",
required this.description,
required this.name,
required this.nick,
final Map<String, dynamic> permNodes = const {},
@ -336,8 +325,6 @@ class _SnAccount extends SnAccount {
@JsonKey()
final String banner;
@override
final String description;
@override
final String name;
@override
final String nick;
@ -406,8 +393,6 @@ class _SnAccount extends SnAccount {
const DeepCollectionEquality().equals(other._contacts, _contacts) &&
(identical(other.avatar, avatar) || other.avatar == avatar) &&
(identical(other.banner, banner) || other.banner == banner) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.nick, nick) || other.nick == nick) &&
const DeepCollectionEquality()
@ -440,7 +425,6 @@ class _SnAccount extends SnAccount {
const DeepCollectionEquality().hash(_contacts),
avatar,
banner,
description,
name,
nick,
const DeepCollectionEquality().hash(_permNodes),
@ -456,7 +440,7 @@ class _SnAccount extends SnAccount {
@override
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,
String avatar,
String banner,
String description,
String name,
String nick,
Map<String, dynamic> permNodes,
@ -514,7 +497,6 @@ class __$SnAccountCopyWithImpl<$Res> implements _$SnAccountCopyWith<$Res> {
Object? contacts = freezed,
Object? avatar = null,
Object? banner = null,
Object? description = null,
Object? name = null,
Object? nick = null,
Object? permNodes = null,
@ -560,10 +542,6 @@ class __$SnAccountCopyWithImpl<$Res> implements _$SnAccountCopyWith<$Res> {
? _self.banner
: banner // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _self.description
: description // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _self.name
: name // ignore: cast_nullable_to_non_nullable
@ -954,15 +932,21 @@ class __$SnAccountContactCopyWithImpl<$Res>
/// @nodoc
mixin _$SnAccountProfile {
int get id;
int get accountId;
DateTime? get birthday;
DateTime get createdAt;
DateTime get updatedAt;
DateTime? get deletedAt;
int get experience;
String get firstName;
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 updatedAt;
DateTime? get birthday;
int get accountId;
/// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values.
@ -981,24 +965,34 @@ mixin _$SnAccountProfile {
(other.runtimeType == runtimeType &&
other is SnAccountProfile &&
(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) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt) &&
(identical(other.deletedAt, deletedAt) ||
other.deletedAt == deletedAt) &&
(identical(other.experience, experience) ||
other.experience == experience) &&
(identical(other.firstName, firstName) ||
other.firstName == firstName) &&
(identical(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) ||
other.lastSeenAt == lastSeenAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt));
(identical(other.birthday, birthday) ||
other.birthday == birthday) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@ -1006,19 +1000,25 @@ mixin _$SnAccountProfile {
int get hashCode => Object.hash(
runtimeType,
id,
accountId,
birthday,
createdAt,
updatedAt,
deletedAt,
experience,
firstName,
lastName,
description,
timeZone,
location,
pronouns,
gender,
const DeepCollectionEquality().hash(links),
experience,
lastSeenAt,
updatedAt);
birthday,
accountId);
@override
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
$Res call(
{int id,
int accountId,
DateTime? birthday,
DateTime createdAt,
DateTime updatedAt,
DateTime? deletedAt,
int experience,
String firstName,
String lastName,
String description,
String timeZone,
String location,
String pronouns,
String gender,
Map<String, String> links,
int experience,
DateTime? lastSeenAt,
DateTime updatedAt});
DateTime? birthday,
int accountId});
}
/// @nodoc
@ -1055,41 +1061,39 @@ class _$SnAccountProfileCopyWithImpl<$Res>
@override
$Res call({
Object? id = null,
Object? accountId = null,
Object? birthday = freezed,
Object? createdAt = null,
Object? updatedAt = null,
Object? deletedAt = freezed,
Object? experience = null,
Object? firstName = 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? updatedAt = null,
Object? birthday = freezed,
Object? accountId = null,
}) {
return _then(_self.copyWith(
id: null == id
? _self.id
: id // ignore: cast_nullable_to_non_nullable
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
? _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?,
experience: null == experience
? _self.experience
: experience // ignore: cast_nullable_to_non_nullable
as int,
firstName: null == firstName
? _self.firstName
: firstName // ignore: cast_nullable_to_non_nullable
@ -1098,14 +1102,46 @@ class _$SnAccountProfileCopyWithImpl<$Res>
? _self.lastName
: lastName // ignore: cast_nullable_to_non_nullable
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
? _self.lastSeenAt
: lastSeenAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
updatedAt: null == updatedAt
? _self.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
birthday: freezed == birthday
? _self.birthday
: birthday // ignore: cast_nullable_to_non_nullable
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 {
const _SnAccountProfile(
{required this.id,
required this.accountId,
required this.birthday,
required this.createdAt,
required this.updatedAt,
required this.deletedAt,
required this.experience,
required this.firstName,
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.updatedAt});
required this.birthday,
required this.accountId})
: _links = links;
factory _SnAccountProfile.fromJson(Map<String, dynamic> json) =>
_$SnAccountProfileFromJson(json);
@override
final int id;
@override
final int accountId;
@override
final DateTime? birthday;
@override
final DateTime createdAt;
@override
final DateTime? deletedAt;
final DateTime updatedAt;
@override
final int experience;
final DateTime? deletedAt;
@override
final String firstName;
@override
final String lastName;
@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;
@override
final DateTime updatedAt;
final DateTime? birthday;
@override
final int accountId;
/// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values.
@ -1169,24 +1231,34 @@ class _SnAccountProfile implements SnAccountProfile {
(other.runtimeType == runtimeType &&
other is _SnAccountProfile &&
(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) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt) &&
(identical(other.deletedAt, deletedAt) ||
other.deletedAt == deletedAt) &&
(identical(other.experience, experience) ||
other.experience == experience) &&
(identical(other.firstName, firstName) ||
other.firstName == firstName) &&
(identical(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) ||
other.lastSeenAt == lastSeenAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt));
(identical(other.birthday, birthday) ||
other.birthday == birthday) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@ -1194,19 +1266,25 @@ class _SnAccountProfile implements SnAccountProfile {
int get hashCode => Object.hash(
runtimeType,
id,
accountId,
birthday,
createdAt,
updatedAt,
deletedAt,
experience,
firstName,
lastName,
description,
timeZone,
location,
pronouns,
gender,
const DeepCollectionEquality().hash(_links),
experience,
lastSeenAt,
updatedAt);
birthday,
accountId);
@override
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
$Res call(
{int id,
int accountId,
DateTime? birthday,
DateTime createdAt,
DateTime updatedAt,
DateTime? deletedAt,
int experience,
String firstName,
String lastName,
String description,
String timeZone,
String location,
String pronouns,
String gender,
Map<String, String> links,
int experience,
DateTime? lastSeenAt,
DateTime updatedAt});
DateTime? birthday,
int accountId});
}
/// @nodoc
@ -1245,41 +1329,39 @@ class __$SnAccountProfileCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
$Res call({
Object? id = null,
Object? accountId = null,
Object? birthday = freezed,
Object? createdAt = null,
Object? updatedAt = null,
Object? deletedAt = freezed,
Object? experience = null,
Object? firstName = 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? updatedAt = null,
Object? birthday = freezed,
Object? accountId = null,
}) {
return _then(_SnAccountProfile(
id: null == id
? _self.id
: id // ignore: cast_nullable_to_non_nullable
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
? _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?,
experience: null == experience
? _self.experience
: experience // ignore: cast_nullable_to_non_nullable
as int,
firstName: null == firstName
? _self.firstName
: firstName // ignore: cast_nullable_to_non_nullable
@ -1288,14 +1370,46 @@ class __$SnAccountProfileCopyWithImpl<$Res>
? _self.lastName
: lastName // ignore: cast_nullable_to_non_nullable
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
? _self.lastSeenAt
: lastSeenAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
updatedAt: null == updatedAt
? _self.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
birthday: freezed == birthday
? _self.birthday
: birthday // ignore: cast_nullable_to_non_nullable
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(),
avatar: json['avatar'] as String? ?? "",
banner: json['banner'] as String? ?? "",
description: json['description'] as String,
name: json['name'] as String,
nick: json['nick'] as String,
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(),
'avatar': instance.avatar,
'banner': instance.banner,
'description': instance.description,
'name': instance.name,
'nick': instance.nick,
'perm_nodes': instance.permNodes,
@ -101,35 +99,50 @@ Map<String, dynamic> _$SnAccountContactToJson(_SnAccountContact instance) =>
_SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
_SnAccountProfile(
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),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
experience: (json['experience'] as num).toInt(),
firstName: json['first_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
? null
: 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) =>
<String, dynamic>{
'id': instance.id,
'account_id': instance.accountId,
'birthday': instance.birthday?.toIso8601String(),
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'experience': instance.experience,
'first_name': instance.firstName,
'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(),
'updated_at': instance.updatedAt.toIso8601String(),
'birthday': instance.birthday?.toIso8601String(),
'account_id': instance.accountId,
};
_SnRelationship _$SnRelationshipFromJson(Map<String, dynamic> json) =>

View File

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

View File

@ -9,6 +9,7 @@
#include <bitsdojo_window_linux/bitsdojo_window_plugin.h>
#include <file_saver/file_saver_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_webrtc/flutter_web_r_t_c_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 =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
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 =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterUdidPlugin");
flutter_udid_plugin_register_with_registrar(flutter_udid_registrar);

View File

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

View File

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

View File

@ -345,6 +345,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -803,6 +811,14 @@ packages:
description: flutter
source: sdk
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:
dependency: "direct main"
description:
@ -872,6 +888,14 @@ packages:
description: flutter
source: sdk
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:
dependency: "direct main"
description:
@ -933,6 +957,54 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -1197,6 +1269,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.9.4"
latlong2:
dependency: transitive
description:
name: latlong2
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
url: "https://pub.dev"
source: hosted
version: "0.9.1"
leak_tracker:
dependency: transitive
description:
@ -1237,6 +1317,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.1.1"
lists:
dependency: transitive
description:
name: lists
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
livekit_client:
dependency: "direct main"
description:
@ -1253,6 +1341,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.6"
logger:
dependency: transitive
description:
name: logger
sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1
url: "https://pub.dev"
source: hosted
version: "2.5.0"
logging:
dependency: transitive
description:
@ -1389,6 +1485,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: "direct main"
description:
@ -1605,6 +1709,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
polylabel:
dependency: transitive
description:
name: polylabel
sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
pool:
dependency: transitive
description:
@ -1629,6 +1741,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.1"
proj4dart:
dependency: transitive
description:
name: proj4dart
sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
url: "https://pub.dev"
source: hosted
version: "2.1.0"
protobuf:
dependency: transitive
description:
@ -2146,6 +2266,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.3"
unicode:
dependency: transitive
description:
name: unicode
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
universal_io:
dependency: transitive
description:
@ -2386,6 +2514,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: "direct main"
description:

View File

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

View File

@ -12,9 +12,11 @@
#include <file_selector_windows/file_selector_windows.h>
#include <firebase_core/firebase_core_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_webrtc/flutter_web_r_t_c_plugin.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 <livekit_client/live_kit_plugin.h>
#include <local_notifier/local_notifier_plugin.h>
@ -41,12 +43,16 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
FlutterTimezonePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterTimezonePluginCApi"));
FlutterUdidPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterUdidPluginCApi"));
FlutterWebRTCPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterWebRTCPlugin"));
GalPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GalPluginCApi"));
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
HotkeyManagerWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("HotkeyManagerWindowsPluginCApi"));
LiveKitPluginRegisterWithRegistrar(

View File

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