✨ Edit profile
This commit is contained in:
parent
ed2e44cc54
commit
abc1d5a9d7
@ -11,6 +11,7 @@
|
|||||||
"screenAccountPublishers": "Publishers",
|
"screenAccountPublishers": "Publishers",
|
||||||
"screenAccountPublisherNew": "New Publisher",
|
"screenAccountPublisherNew": "New Publisher",
|
||||||
"screenAccountPublisherEdit": "Edit Publisher",
|
"screenAccountPublisherEdit": "Edit Publisher",
|
||||||
|
"screenAccountProfileEdit": "Edit Profile",
|
||||||
"dialogOkay": "Okay",
|
"dialogOkay": "Okay",
|
||||||
"dialogCancel": "Cancel",
|
"dialogCancel": "Cancel",
|
||||||
"dialogConfirm": "Confirm",
|
"dialogConfirm": "Confirm",
|
||||||
@ -36,6 +37,10 @@
|
|||||||
"fieldDescription": "Description",
|
"fieldDescription": "Description",
|
||||||
"fieldUsernameCannotEditHint": "Username cannot be edited after created",
|
"fieldUsernameCannotEditHint": "Username cannot be edited after created",
|
||||||
"fieldUsernameLookupHint": "You can use username, phone number or email to login",
|
"fieldUsernameLookupHint": "You can use username, phone number or email to login",
|
||||||
|
"fieldFirstName": "First name",
|
||||||
|
"fieldLastName": "Last name",
|
||||||
|
"fieldBirthday": "Birthday",
|
||||||
|
"fieldImageHint": "You can click those profile pictures to edit them.",
|
||||||
"forgotPassword": "Forgot password",
|
"forgotPassword": "Forgot password",
|
||||||
"loginPickFactor": "Pick a factor",
|
"loginPickFactor": "Pick a factor",
|
||||||
"loginMultiFactor": {
|
"loginMultiFactor": {
|
||||||
@ -54,6 +59,9 @@
|
|||||||
"accountLogoutConfirm": "You will need to re-enter your account password, even if you have already done so. This is required to login again.",
|
"accountLogoutConfirm": "You will need to re-enter your account password, even if you have already done so. This is required to login again.",
|
||||||
"accountPublishers": "Your publishers",
|
"accountPublishers": "Your publishers",
|
||||||
"accountPublishersSubtitle": "Manage your publish identities.",
|
"accountPublishersSubtitle": "Manage your publish identities.",
|
||||||
|
"accountProfileEdit": "Edit your profile",
|
||||||
|
"accountProfileEditSubtitle": "Make your Solarpass account more looks like you.",
|
||||||
|
"accountProfileEditApplied": "Profile modification applied.",
|
||||||
"publishersNew": "New Publisher",
|
"publishersNew": "New Publisher",
|
||||||
"publisherNewSubtitle": "Create a new publisher identity.",
|
"publisherNewSubtitle": "Create a new publisher identity.",
|
||||||
"publisherSyncWithAccount": "Sync with account"
|
"publisherSyncWithAccount": "Sync with account"
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"screenAccountPublishers": "发布者",
|
"screenAccountPublishers": "发布者",
|
||||||
"screenAccountPublisherNew": "新建发布者",
|
"screenAccountPublisherNew": "新建发布者",
|
||||||
"screenAccountPublisherEdit": "编辑发布者",
|
"screenAccountPublisherEdit": "编辑发布者",
|
||||||
|
"screenAccountProfileEdit": "编辑资料",
|
||||||
"dialogOkay": "好的",
|
"dialogOkay": "好的",
|
||||||
"dialogCancel": "取消",
|
"dialogCancel": "取消",
|
||||||
"dialogConfirm": "确认",
|
"dialogConfirm": "确认",
|
||||||
@ -35,6 +36,10 @@
|
|||||||
"fieldPassword": "密码",
|
"fieldPassword": "密码",
|
||||||
"fieldUsernameCannotEditHint": "用户名在创建后无法修改",
|
"fieldUsernameCannotEditHint": "用户名在创建后无法修改",
|
||||||
"fieldUsernameLookupHint": "支持用户名、电话号码或邮箱地址",
|
"fieldUsernameLookupHint": "支持用户名、电话号码或邮箱地址",
|
||||||
|
"fieldFirstName": "名",
|
||||||
|
"fieldLastName": "姓",
|
||||||
|
"fieldBirthday": "生日",
|
||||||
|
"fieldImageHint": "你可以点击这些个人头像来编辑它们。",
|
||||||
"fieldDescription": "简介",
|
"fieldDescription": "简介",
|
||||||
"forgotPassword": "忘记密码",
|
"forgotPassword": "忘记密码",
|
||||||
"loginPickFactor": "选择方式验证",
|
"loginPickFactor": "选择方式验证",
|
||||||
@ -54,6 +59,9 @@
|
|||||||
"accountLogoutConfirm": "您需要重新输入账号密码,甚至可能需要多步验证来再次登陆。",
|
"accountLogoutConfirm": "您需要重新输入账号密码,甚至可能需要多步验证来再次登陆。",
|
||||||
"accountPublishers": "你的发布者",
|
"accountPublishers": "你的发布者",
|
||||||
"accountPublishersSubtitle": "管理你的公共形象。",
|
"accountPublishersSubtitle": "管理你的公共形象。",
|
||||||
|
"accountProfileEdit": "编辑资料",
|
||||||
|
"accountProfileEditSubtitle": "使你的 Solarpass 账户更像你。",
|
||||||
|
"accountProfileEditApplied": "个人资料修改已被应用。",
|
||||||
"publishersNew": "新发布者",
|
"publishersNew": "新发布者",
|
||||||
"publisherNewSubtitle": "创建一个新的公共身份。",
|
"publisherNewSubtitle": "创建一个新的公共身份。",
|
||||||
"publisherSyncWithAccount": "同步账户信息"
|
"publisherSyncWithAccount": "同步账户信息"
|
||||||
|
@ -2,47 +2,134 @@ PODS:
|
|||||||
- connectivity_plus (0.0.1):
|
- connectivity_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- croppy (0.0.1):
|
||||||
|
- Flutter
|
||||||
- cupertino_http (0.0.1):
|
- cupertino_http (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- DKImagePickerController/Core (4.3.9):
|
||||||
|
- DKImagePickerController/ImageDataManager
|
||||||
|
- DKImagePickerController/Resource
|
||||||
|
- DKImagePickerController/ImageDataManager (4.3.9)
|
||||||
|
- DKImagePickerController/PhotoGallery (4.3.9):
|
||||||
|
- DKImagePickerController/Core
|
||||||
|
- DKPhotoGallery
|
||||||
|
- DKImagePickerController/Resource (4.3.9)
|
||||||
|
- DKPhotoGallery (0.0.19):
|
||||||
|
- DKPhotoGallery/Core (= 0.0.19)
|
||||||
|
- DKPhotoGallery/Model (= 0.0.19)
|
||||||
|
- DKPhotoGallery/Preview (= 0.0.19)
|
||||||
|
- DKPhotoGallery/Resource (= 0.0.19)
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Core (0.0.19):
|
||||||
|
- DKPhotoGallery/Model
|
||||||
|
- DKPhotoGallery/Preview
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Model (0.0.19):
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Preview (0.0.19):
|
||||||
|
- DKPhotoGallery/Model
|
||||||
|
- DKPhotoGallery/Resource
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Resource (0.0.19):
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- file_picker (0.0.1):
|
||||||
|
- DKImagePickerController/PhotoGallery
|
||||||
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_image_compress_common (1.0.0):
|
||||||
|
- Flutter
|
||||||
|
- Mantle
|
||||||
|
- SDWebImage
|
||||||
|
- SDWebImageWebPCoder
|
||||||
- flutter_native_splash (0.0.1):
|
- flutter_native_splash (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_secure_storage (3.3.1):
|
- flutter_secure_storage (3.3.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- image_picker_ios (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- libwebp (1.3.2):
|
||||||
|
- libwebp/demux (= 1.3.2)
|
||||||
|
- libwebp/mux (= 1.3.2)
|
||||||
|
- libwebp/sharpyuv (= 1.3.2)
|
||||||
|
- libwebp/webp (= 1.3.2)
|
||||||
|
- libwebp/demux (1.3.2):
|
||||||
|
- libwebp/webp
|
||||||
|
- libwebp/mux (1.3.2):
|
||||||
|
- libwebp/demux
|
||||||
|
- libwebp/sharpyuv (1.3.2)
|
||||||
|
- libwebp/webp (1.3.2):
|
||||||
|
- libwebp/sharpyuv
|
||||||
|
- Mantle (2.2.0):
|
||||||
|
- Mantle/extobjc (= 2.2.0)
|
||||||
|
- Mantle/extobjc (2.2.0)
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- SDWebImage (5.19.7):
|
||||||
|
- SDWebImage/Core (= 5.19.7)
|
||||||
|
- SDWebImage/Core (5.19.7)
|
||||||
|
- SDWebImageWebPCoder (0.14.6):
|
||||||
|
- libwebp (~> 1.0)
|
||||||
|
- SDWebImage/Core (~> 5.17)
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqflite_darwin (0.0.4):
|
- sqflite_darwin (0.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- SwiftyGif (5.4.5)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
|
||||||
|
- croppy (from `.symlinks/plugins/croppy/ios`)
|
||||||
- cupertino_http (from `.symlinks/plugins/cupertino_http/ios`)
|
- cupertino_http (from `.symlinks/plugins/cupertino_http/ios`)
|
||||||
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||||
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- DKImagePickerController
|
||||||
|
- DKPhotoGallery
|
||||||
|
- libwebp
|
||||||
|
- Mantle
|
||||||
|
- SDWebImage
|
||||||
|
- SDWebImageWebPCoder
|
||||||
|
- SwiftyGif
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
:path: ".symlinks/plugins/connectivity_plus/darwin"
|
:path: ".symlinks/plugins/connectivity_plus/darwin"
|
||||||
|
croppy:
|
||||||
|
:path: ".symlinks/plugins/croppy/ios"
|
||||||
cupertino_http:
|
cupertino_http:
|
||||||
:path: ".symlinks/plugins/cupertino_http/ios"
|
:path: ".symlinks/plugins/cupertino_http/ios"
|
||||||
|
file_picker:
|
||||||
|
:path: ".symlinks/plugins/file_picker/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_image_compress_common:
|
||||||
|
:path: ".symlinks/plugins/flutter_image_compress_common/ios"
|
||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||||
|
image_picker_ios:
|
||||||
|
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
@ -54,13 +141,24 @@ EXTERNAL SOURCES:
|
|||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
connectivity_plus: 4c41c08fc6d7c91f63bc7aec70ffe3730b04f563
|
connectivity_plus: 4c41c08fc6d7c91f63bc7aec70ffe3730b04f563
|
||||||
|
croppy: b6199bc8d56bd2e03cc11609d1c47ad9875c1321
|
||||||
cupertino_http: 1a3a0f163c1b26e7f1a293b33d476e0fde7a64ec
|
cupertino_http: 1a3a0f163c1b26e7f1a293b33d476e0fde7a64ec
|
||||||
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
|
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
|
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
||||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||||
|
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||||
|
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
||||||
|
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
|
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
|
||||||
|
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||||
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
|
|
||||||
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
@ -50,7 +50,15 @@
|
|||||||
<string>en</string>
|
<string>en</string>
|
||||||
<string>zh_CN</string>
|
<string>zh_CN</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>Grant access to Photo Library will allow Solian upload photo or video for your post.</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Grant access to Photo Library will allow Solian take photo or video for your post.</string>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>Grant access to Photo Library will allow Solian record audio for your post.</string>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
<key>UIStatusBarHidden</key>
|
<key>UIStatusBarHidden</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:croppy/croppy.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:easy_localization_loader/easy_localization_loader.dart';
|
import 'package:easy_localization_loader/easy_localization_loader.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -48,6 +49,7 @@ class SolianApp extends StatelessWidget {
|
|||||||
locale: context.locale,
|
locale: context.locale,
|
||||||
supportedLocales: context.supportedLocales,
|
supportedLocales: context.supportedLocales,
|
||||||
localizationsDelegates: [
|
localizationsDelegates: [
|
||||||
|
CroppyLocalizations.delegate,
|
||||||
RelativeTimeLocalizations.delegate,
|
RelativeTimeLocalizations.delegate,
|
||||||
...context.localizationDelegates,
|
...context.localizationDelegates,
|
||||||
],
|
],
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:cross_file/cross_file.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
|
|
||||||
|
const kConcurrentUploadChunks = 5;
|
||||||
|
|
||||||
class SnAttachmentProvider {
|
class SnAttachmentProvider {
|
||||||
late final SnNetworkProvider _sn;
|
late final SnNetworkProvider _sn;
|
||||||
final Map<String, SnAttachment> _cache = {};
|
final Map<String, SnAttachment> _cache = {};
|
||||||
@ -43,4 +50,157 @@ class SnAttachmentProvider {
|
|||||||
}
|
}
|
||||||
return rids.map((rid) => _cache[rid]!).toList();
|
return rids.map((rid) => _cache[rid]!).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Map<String, String> mimetypeOverrides = {
|
||||||
|
'mov': 'video/quicktime',
|
||||||
|
'mp4': 'video/mp4'
|
||||||
|
};
|
||||||
|
|
||||||
|
Future<SnAttachment> directUploadOne(
|
||||||
|
Uint8List data,
|
||||||
|
String filename,
|
||||||
|
String pool,
|
||||||
|
Map<String, dynamic>? metadata, {
|
||||||
|
String? mimetype,
|
||||||
|
Function(double progress)? onProgress,
|
||||||
|
}) async {
|
||||||
|
final filePayload = MultipartFile.fromBytes(data, filename: filename);
|
||||||
|
final fileAlt = filename.contains('.')
|
||||||
|
? filename.substring(0, filename.lastIndexOf('.'))
|
||||||
|
: filename;
|
||||||
|
final fileExt =
|
||||||
|
filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||||
|
|
||||||
|
String? mimetypeOverride;
|
||||||
|
if (mimetype != null) {
|
||||||
|
mimetypeOverride = mimetype;
|
||||||
|
} else if (mimetypeOverrides.keys.contains(fileExt)) {
|
||||||
|
mimetypeOverride = mimetypeOverrides[fileExt];
|
||||||
|
}
|
||||||
|
|
||||||
|
final formData = FormData.fromMap({
|
||||||
|
'alt': fileAlt,
|
||||||
|
'file': filePayload,
|
||||||
|
'pool': pool,
|
||||||
|
'metadata': metadata,
|
||||||
|
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
||||||
|
});
|
||||||
|
final resp = await _sn.client.post(
|
||||||
|
'/cgi/uc/attachments',
|
||||||
|
data: formData,
|
||||||
|
onSendProgress: (count, total) {
|
||||||
|
if (onProgress != null) {
|
||||||
|
onProgress(count / total);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return SnAttachment.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<(SnAttachment, int)> chunkedUploadInitialize(
|
||||||
|
int size,
|
||||||
|
String filename,
|
||||||
|
String pool,
|
||||||
|
Map<String, dynamic>? metadata,
|
||||||
|
) async {
|
||||||
|
final fileAlt = filename.contains('.')
|
||||||
|
? filename.substring(0, filename.lastIndexOf('.'))
|
||||||
|
: filename;
|
||||||
|
final fileExt =
|
||||||
|
filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||||
|
|
||||||
|
String? mimetypeOverride;
|
||||||
|
if (mimetypeOverrides.keys.contains(fileExt)) {
|
||||||
|
mimetypeOverride = mimetypeOverrides[fileExt];
|
||||||
|
}
|
||||||
|
|
||||||
|
final resp = await _sn.client.post('/cgi/uc/attachments/multipart', data: {
|
||||||
|
'alt': fileAlt,
|
||||||
|
'name': filename,
|
||||||
|
'pool': pool,
|
||||||
|
'metadata': metadata,
|
||||||
|
'size': size,
|
||||||
|
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
SnAttachment.fromJson(resp.data['meta']),
|
||||||
|
resp.data['chunk_size'] as int
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SnAttachment> _chunkedUploadOnePart(
|
||||||
|
Uint8List data,
|
||||||
|
String rid,
|
||||||
|
String cid, {
|
||||||
|
Function(double progress)? onProgress,
|
||||||
|
}) async {
|
||||||
|
final resp = await _sn.client.post(
|
||||||
|
'/cgi/uc/attachments/multipart/$rid/$cid',
|
||||||
|
data: data,
|
||||||
|
options: Options(headers: {'Content-Type': 'application/octet-stream'}),
|
||||||
|
onSendProgress: (count, total) {
|
||||||
|
if (onProgress != null) {
|
||||||
|
onProgress(count / total);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return SnAttachment.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SnAttachment> chunkedUploadParts(
|
||||||
|
XFile file,
|
||||||
|
SnAttachment place,
|
||||||
|
int chunkSize, {
|
||||||
|
Function(double progress)? onProgress,
|
||||||
|
}) async {
|
||||||
|
final Map<String, dynamic> chunks = place.fileChunks ?? {};
|
||||||
|
var currentTask = 0;
|
||||||
|
|
||||||
|
final queue = Queue<Future<void>>();
|
||||||
|
final activeTasks = <Future<void>>[];
|
||||||
|
|
||||||
|
for (final entry in chunks.entries) {
|
||||||
|
queue.add(() async {
|
||||||
|
final beginCursor = entry.value * chunkSize;
|
||||||
|
final endCursor = (entry.value + 1) * chunkSize;
|
||||||
|
final data = Uint8List.fromList(await file
|
||||||
|
.openRead(beginCursor, endCursor)
|
||||||
|
.expand((chunk) => chunk)
|
||||||
|
.toList());
|
||||||
|
|
||||||
|
place = await _chunkedUploadOnePart(
|
||||||
|
data,
|
||||||
|
place.rid,
|
||||||
|
entry.key,
|
||||||
|
onProgress: (chunkProgress) {
|
||||||
|
final overallProgress =
|
||||||
|
(currentTask + chunkProgress) / chunks.length;
|
||||||
|
if (onProgress != null) {
|
||||||
|
onProgress(overallProgress);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
currentTask++;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
while (queue.isNotEmpty || activeTasks.isNotEmpty) {
|
||||||
|
while (activeTasks.length < kConcurrentUploadChunks && queue.isNotEmpty) {
|
||||||
|
final task = queue.removeFirst();
|
||||||
|
activeTasks.add(task);
|
||||||
|
|
||||||
|
task.then((_) => activeTasks.remove(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTasks.isNotEmpty) {
|
||||||
|
await Future.any(activeTasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return place;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:surface/screens/account.dart';
|
import 'package:surface/screens/account.dart';
|
||||||
import 'package:surface/screens/account/publisher_edit.dart';
|
import 'package:surface/screens/account/profile_edit.dart';
|
||||||
import 'package:surface/screens/account/publisher_new.dart';
|
import 'package:surface/screens/account/publishers/publisher_edit.dart';
|
||||||
import 'package:surface/screens/account/publishers.dart';
|
import 'package:surface/screens/account/publishers/publisher_new.dart';
|
||||||
|
import 'package:surface/screens/account/publishers/publishers.dart';
|
||||||
import 'package:surface/screens/auth/login.dart';
|
import 'package:surface/screens/auth/login.dart';
|
||||||
import 'package:surface/screens/auth/register.dart';
|
import 'package:surface/screens/auth/register.dart';
|
||||||
import 'package:surface/screens/explore.dart';
|
import 'package:surface/screens/explore.dart';
|
||||||
@ -50,6 +51,11 @@ final appRouter = GoRouter(
|
|||||||
name: 'authRegister',
|
name: 'authRegister',
|
||||||
builder: (context, state) => const RegisterScreen(),
|
builder: (context, state) => const RegisterScreen(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/account/profile/edit',
|
||||||
|
name: 'accountProfileEdit',
|
||||||
|
builder: (context, state) => const ProfileEditScreen(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/account/publishers',
|
path: '/account/publishers',
|
||||||
name: 'accountPublishers',
|
name: 'accountPublishers',
|
||||||
|
@ -74,6 +74,16 @@ class _AuthorizedAccountScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}).padding(all: 20),
|
}).padding(all: 20),
|
||||||
).padding(horizontal: 8, top: 16, bottom: 4),
|
).padding(horizontal: 8, top: 16, bottom: 4),
|
||||||
|
ListTile(
|
||||||
|
title: Text('accountProfileEdit').tr(),
|
||||||
|
subtitle: Text('accountProfileEditSubtitle').tr(),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
leading: const Icon(Symbols.contact_page),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).pushNamed('accountProfileEdit');
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('accountPublishers').tr(),
|
title: Text('accountPublishers').tr(),
|
||||||
subtitle: Text('accountPublishersSubtitle').tr(),
|
subtitle: Text('accountPublishersSubtitle').tr(),
|
||||||
|
348
lib/screens/account/profile_edit.dart
Normal file
348
lib/screens/account/profile_edit.dart
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:croppy/croppy.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:path/path.dart' show basename;
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/sn_attachment.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
|
|
||||||
|
class ProfileEditScreen extends StatefulWidget {
|
||||||
|
const ProfileEditScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProfileEditScreen> createState() => _ProfileEditScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
||||||
|
final _imagePicker = ImagePicker();
|
||||||
|
|
||||||
|
final _usernameController = TextEditingController();
|
||||||
|
final _nicknameController = TextEditingController();
|
||||||
|
final _firstNameController = TextEditingController();
|
||||||
|
final _lastNameController = TextEditingController();
|
||||||
|
final _descriptionController = TextEditingController();
|
||||||
|
final _birthdayController = TextEditingController();
|
||||||
|
|
||||||
|
String? _avatar;
|
||||||
|
String? _banner;
|
||||||
|
DateTime? _birthday;
|
||||||
|
|
||||||
|
bool _isBusy = false;
|
||||||
|
|
||||||
|
static const _kDateFormat = 'y/M/d';
|
||||||
|
|
||||||
|
void _syncWidget() async {
|
||||||
|
final ua = context.read<UserProvider>();
|
||||||
|
final prof = ua.user!;
|
||||||
|
_usernameController.text = prof.name;
|
||||||
|
_nicknameController.text = prof.nick;
|
||||||
|
_descriptionController.text = prof.description;
|
||||||
|
_firstNameController.text = prof.profile!.firstName;
|
||||||
|
_lastNameController.text = prof.profile!.lastName;
|
||||||
|
_avatar = prof.avatar;
|
||||||
|
_banner = prof.banner;
|
||||||
|
if (prof.profile!.birthday != null) {
|
||||||
|
_birthdayController.text = DateFormat(_kDateFormat).format(
|
||||||
|
prof.profile!.birthday!.toLocal(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _selectBirthday() async {
|
||||||
|
await showCupertinoModalPopup<DateTime?>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => Container(
|
||||||
|
height: 216,
|
||||||
|
padding: const EdgeInsets.only(top: 6.0),
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||||
|
),
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: SafeArea(
|
||||||
|
top: false,
|
||||||
|
child: CupertinoDatePicker(
|
||||||
|
initialDateTime: _birthday?.toLocal(),
|
||||||
|
mode: CupertinoDatePickerMode.date,
|
||||||
|
use24hFormat: true,
|
||||||
|
onDateTimeChanged: (DateTime newDate) {
|
||||||
|
setState(() {
|
||||||
|
_birthday = newDate;
|
||||||
|
_birthdayController.text =
|
||||||
|
DateFormat(_kDateFormat).format(_birthday!);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateImage(String place) async {
|
||||||
|
final image = await _imagePicker.pickImage(source: ImageSource.gallery);
|
||||||
|
if (image == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
final ImageProvider imageProvider =
|
||||||
|
kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
||||||
|
final aspectRatios = place == 'banner'
|
||||||
|
? [CropAspectRatio(width: 16, height: 7)]
|
||||||
|
: [CropAspectRatio(width: 1, height: 1)];
|
||||||
|
final result = (!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
||||||
|
? await showCupertinoImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
context,
|
||||||
|
allowedAspectRatios: aspectRatios,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
)
|
||||||
|
: await showMaterialImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
context,
|
||||||
|
allowedAspectRatios: aspectRatios,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == null) return;
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
final attach = context.read<SnAttachmentProvider>();
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
final rawBytes =
|
||||||
|
(await result.uiImage.toByteData(format: ImageByteFormat.png))!
|
||||||
|
.buffer
|
||||||
|
.asUint8List();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final attachment = await attach.directUploadOne(
|
||||||
|
rawBytes,
|
||||||
|
basename(image.path),
|
||||||
|
'avatar',
|
||||||
|
null,
|
||||||
|
mimetype: 'image/png',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
await sn.client.put(
|
||||||
|
'/cgi/id/users/me/$place',
|
||||||
|
data: {'attachment': attachment.rid},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
final ua = context.read<UserProvider>();
|
||||||
|
await ua.refreshUser();
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showSnackbar('accountProfileEditApplied'.tr());
|
||||||
|
_syncWidget();
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateUserInfo() async {
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sn.client.put(
|
||||||
|
'/cgi/id/users/me',
|
||||||
|
data: {
|
||||||
|
'nick': _nicknameController.value.text,
|
||||||
|
'description': _descriptionController.value.text,
|
||||||
|
'first_name': _firstNameController.value.text,
|
||||||
|
'last_name': _lastNameController.value.text,
|
||||||
|
'birthday': _birthday?.toUtc().toIso8601String(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
final ua = context.read<UserProvider>();
|
||||||
|
await ua.refreshUser();
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showSnackbar('accountProfileEditApplied'.tr());
|
||||||
|
_syncWidget();
|
||||||
|
} catch (err) {
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_syncWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_usernameController.dispose();
|
||||||
|
_nicknameController.dispose();
|
||||||
|
_firstNameController.dispose();
|
||||||
|
_lastNameController.dispose();
|
||||||
|
_descriptionController.dispose();
|
||||||
|
_birthdayController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
const double padding = 24;
|
||||||
|
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
LoadingIndicator(isActive: _isBusy),
|
||||||
|
const Gap(24),
|
||||||
|
Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
Material(
|
||||||
|
elevation: 0,
|
||||||
|
child: InkWell(
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
child: Container(
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
|
child: _banner != null
|
||||||
|
? UniversalImage(
|
||||||
|
sn.getAttachmentUrl(_banner!),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_updateImage('banner');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: -28,
|
||||||
|
left: 16,
|
||||||
|
child: Material(
|
||||||
|
elevation: 2,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(40)),
|
||||||
|
child: InkWell(
|
||||||
|
child: AccountImage(content: _avatar, radius: 40),
|
||||||
|
onTap: () {
|
||||||
|
_updateImage('avatar');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: padding),
|
||||||
|
const Gap(8 + 28),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
readOnly: true,
|
||||||
|
controller: _usernameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldUsername'.tr(),
|
||||||
|
helperText: 'fieldUsernameCannotEditHint'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: _nicknameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldNickname'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
flex: 1,
|
||||||
|
child: TextField(
|
||||||
|
controller: _firstNameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldFirstName'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Flexible(
|
||||||
|
flex: 1,
|
||||||
|
child: TextField(
|
||||||
|
controller: _lastNameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldLastName'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: _descriptionController,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
maxLines: null,
|
||||||
|
minLines: 3,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldDescription'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: _birthdayController,
|
||||||
|
readOnly: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldBirthday'.tr(),
|
||||||
|
),
|
||||||
|
onTap: () => _selectBirthday(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: padding + 8),
|
||||||
|
const Gap(12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: _isBusy ? null : _updateUserInfo,
|
||||||
|
icon: const Icon(Symbols.save),
|
||||||
|
label: Text('apply').tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: padding),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -134,7 +134,7 @@ class _AccountPublisherEditScreenState
|
|||||||
const Gap(4),
|
const Gap(4),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _descriptionController,
|
controller: _descriptionController,
|
||||||
maxLines: 3,
|
maxLines: null,
|
||||||
minLines: 3,
|
minLines: 3,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'fieldDescription'.tr(),
|
labelText: 'fieldDescription'.tr(),
|
@ -4,7 +4,7 @@ import 'package:surface/providers/sn_network.dart';
|
|||||||
import 'package:surface/widgets/universal_image.dart';
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
|
|
||||||
class AccountImage extends StatelessWidget {
|
class AccountImage extends StatelessWidget {
|
||||||
final String content;
|
final String? content;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
final Color? foregroundColor;
|
final Color? foregroundColor;
|
||||||
final double? radius;
|
final double? radius;
|
||||||
@ -22,21 +22,21 @@ class AccountImage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final url = sn.getAttachmentUrl(content);
|
final url = sn.getAttachmentUrl(content ?? '');
|
||||||
|
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
key: Key('attachment-${content.hashCode}'),
|
key: Key('attachment-${content.hashCode}'),
|
||||||
radius: radius,
|
radius: radius,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
backgroundImage: content.isNotEmpty
|
backgroundImage: (content?.isNotEmpty ?? false)
|
||||||
? ResizeImage(
|
? ResizeImage(
|
||||||
UniversalImage.provider(url),
|
UniversalImage.provider(url),
|
||||||
width: ((radius ?? 20) * devicePixelRatio * 2).round(),
|
width: ((radius ?? 20) * devicePixelRatio * 2).round(),
|
||||||
height: ((radius ?? 20) * devicePixelRatio * 2).round(),
|
height: ((radius ?? 20) * devicePixelRatio * 2).round(),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
child: content.isEmpty
|
child: (content?.isEmpty ?? true)
|
||||||
? (fallbackWidget ??
|
? (fallbackWidget ??
|
||||||
Icon(
|
Icon(
|
||||||
Icons.account_circle,
|
Icons.account_circle,
|
||||||
|
@ -6,10 +6,14 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
#include <flutter_secure_storage/flutter_secure_storage_plugin.h>
|
#include <flutter_secure_storage/flutter_secure_storage_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
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_secure_storage_registrar =
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStoragePlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStoragePlugin");
|
||||||
flutter_secure_storage_plugin_register_with_registrar(flutter_secure_storage_registrar);
|
flutter_secure_storage_plugin_register_with_registrar(flutter_secure_storage_registrar);
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
file_selector_linux
|
||||||
flutter_secure_storage
|
flutter_secure_storage
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
croppy
|
||||||
jni
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import FlutterMacOS
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import connectivity_plus
|
import connectivity_plus
|
||||||
|
import file_selector_macos
|
||||||
|
import flutter_image_compress_macos
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import sqflite_darwin
|
import sqflite_darwin
|
||||||
@ -13,6 +15,8 @@ import url_launcher_macos
|
|||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||||
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
|
FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
|
200
pubspec.lock
200
pubspec.lock
@ -158,6 +158,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
cassowary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cassowary
|
||||||
|
sha256: f304452beaf93b9349daaeeda23f853578c9dd8674c06c6100fda0319c46b967
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.3"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -230,6 +238,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
|
croppy:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: croppy
|
||||||
|
sha256: "14bb40fd6c1771b093a907ddbf24df9aa49a4e6e379dd630602eb446e30ec629"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
cross_file:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cross_file
|
||||||
|
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.4+2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -334,6 +358,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.2"
|
version: "0.0.2"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -358,6 +390,46 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
|
file_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: file_picker
|
||||||
|
sha256: aac85f20436608e01a6ffd1fdd4e746a7f33c93a2c83752e626bdfaea139b877
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.1.3"
|
||||||
|
file_selector_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_linux
|
||||||
|
sha256: "712ce7fab537ba532c8febdb1a8f167b32441e74acd68c3ccb2e36dcb52c4ab2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.3"
|
||||||
|
file_selector_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_macos
|
||||||
|
sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.4+2"
|
||||||
|
file_selector_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_platform_interface
|
||||||
|
sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.2"
|
||||||
|
file_selector_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_windows
|
||||||
|
sha256: "8f5d2f6590d51ecd9179ba39c64f722edc15226cc93dcc8698466ad36a4a85a4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.3+3"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -387,6 +459,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.4.1"
|
||||||
|
flutter_image_compress:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress
|
||||||
|
sha256: "45a3071868092a61b11044c70422b04d39d4d9f2ef536f3c5b11fb65a1e7dd90"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
flutter_image_compress_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_common
|
||||||
|
sha256: "7f79bc6c8a363063620b4e372fa86bc691e1cb28e58048cd38e030692fbd99ee"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
|
flutter_image_compress_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_macos
|
||||||
|
sha256: "26df6385512e92b3789dc76b613b54b55c457a7f1532e59078b04bf189782d47"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
|
flutter_image_compress_ohos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_ohos
|
||||||
|
sha256: e76b92bbc830ee08f5b05962fc78a532011fcd2041f620b5400a593e96da3f51
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.3"
|
||||||
|
flutter_image_compress_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_platform_interface
|
||||||
|
sha256: "579cb3947fd4309103afe6442a01ca01e1e6f93dc53bb4cbd090e8ce34a41889"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
|
flutter_image_compress_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_web
|
||||||
|
sha256: f02fe352b17f82b72f481de45add240db062a2585850bea1667e82cc4cd6c311
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.4+1"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -416,6 +536,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.4.2"
|
||||||
|
flutter_plugin_android_lifecycle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_plugin_android_lifecycle
|
||||||
|
sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.23"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -562,6 +690,70 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.0"
|
version: "4.3.0"
|
||||||
|
image_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: image_picker
|
||||||
|
sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
image_picker_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_android
|
||||||
|
sha256: "8faba09ba361d4b246dc0a17cb4289b3324c2b9f6db7b3d457ee69106a86bd32"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.12+17"
|
||||||
|
image_picker_for_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_for_web
|
||||||
|
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.6"
|
||||||
|
image_picker_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_ios
|
||||||
|
sha256: "4f0568120c6fcc0aaa04511cb9f9f4d29fc3d0139884b1d06be88dcec7641d6b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.12+1"
|
||||||
|
image_picker_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_linux
|
||||||
|
sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1+1"
|
||||||
|
image_picker_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_macos
|
||||||
|
sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1+1"
|
||||||
|
image_picker_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_platform_interface
|
||||||
|
sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.10.0"
|
||||||
|
image_picker_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_windows
|
||||||
|
sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1+1"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1263,6 +1455,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.8.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -59,6 +59,11 @@ dependencies:
|
|||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
relative_time: ^5.0.0
|
relative_time: ^5.0.0
|
||||||
flutter_secure_storage: ^4.2.1
|
flutter_secure_storage: ^4.2.1
|
||||||
|
image_picker: ^1.1.2
|
||||||
|
cross_file: ^0.3.4+2
|
||||||
|
file_picker: ^8.1.3
|
||||||
|
flutter_image_compress: ^2.3.0
|
||||||
|
croppy: ^1.3.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -7,11 +7,14 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||||
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,12 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
|
file_selector_windows
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
croppy
|
||||||
jni
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user