mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 12:14:11 +00:00 
			
		
		
		
	Can upload new account image
This commit is contained in:
		
							
								
								
									
										38
									
								
								lib/helpers/settings_helper.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/helpers/settings_helper.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import 'dart:io';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:comunic/models/account_image_settings.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/api_request.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Settings helper
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const _APIAccountImageVisibilityAPILevels = {
 | 
				
			||||||
 | 
					  "open": AccountImageVisibilityLevels.EVERYONE,
 | 
				
			||||||
 | 
					  "public": AccountImageVisibilityLevels.COMUNIC_USERS,
 | 
				
			||||||
 | 
					  "friends": AccountImageVisibilityLevels.FRIENDS_ONLY,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SettingsHelper {
 | 
				
			||||||
 | 
					  /// Get & return account image settings
 | 
				
			||||||
 | 
					  static Future<AccountImageSettings> getAccountImageSettings() async {
 | 
				
			||||||
 | 
					    final response =
 | 
				
			||||||
 | 
					        (await APIRequest(uri: "settings/get_account_image", needLogin: true)
 | 
				
			||||||
 | 
					                .exec())
 | 
				
			||||||
 | 
					            .assertOk()
 | 
				
			||||||
 | 
					            .getObject();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return AccountImageSettings(
 | 
				
			||||||
 | 
					        hasImage: response["has_image"],
 | 
				
			||||||
 | 
					        imageURL: response["image_url"],
 | 
				
			||||||
 | 
					        visibility:
 | 
				
			||||||
 | 
					            _APIAccountImageVisibilityAPILevels[response["visibility"]]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Upload a new account image
 | 
				
			||||||
 | 
					  static Future<bool> uploadAccountImage(File newImage) async =>
 | 
				
			||||||
 | 
					      (await APIRequest(uri: "settings/upload_account_image", needLogin: true)
 | 
				
			||||||
 | 
					              .addFile("picture", newImage)
 | 
				
			||||||
 | 
					              .execWithFiles())
 | 
				
			||||||
 | 
					          .isOK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								lib/models/account_image_settings.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/models/account_image_settings.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/widgets.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Account image settings
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum AccountImageVisibilityLevels { EVERYONE, COMUNIC_USERS, FRIENDS_ONLY }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AccountImageSettings {
 | 
				
			||||||
 | 
					  final bool hasImage;
 | 
				
			||||||
 | 
					  final String imageURL;
 | 
				
			||||||
 | 
					  final AccountImageVisibilityLevels visibility;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const AccountImageSettings({
 | 
				
			||||||
 | 
					    @required this.hasImage,
 | 
				
			||||||
 | 
					    @required this.imageURL,
 | 
				
			||||||
 | 
					    @required this.visibility,
 | 
				
			||||||
 | 
					  })  : assert(hasImage != null),
 | 
				
			||||||
 | 
					        assert(imageURL != null),
 | 
				
			||||||
 | 
					        assert(visibility != null);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -22,14 +22,25 @@ class APIRequest {
 | 
				
			|||||||
    if (this.args == null) this.args = Map();
 | 
					    if (this.args == null) this.args = Map();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void addString(String name, String value) => args[name] = value;
 | 
					  APIRequest addString(String name, String value) {
 | 
				
			||||||
 | 
					    args[name] = value;
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void addInt(String name, int value) => args[name] = value.toString();
 | 
					  APIRequest addInt(String name, int value) {
 | 
				
			||||||
 | 
					    args[name] = value.toString();
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void addBool(String name, bool value) =>
 | 
					  APIRequest addBool(String name, bool value) {
 | 
				
			||||||
      args[name] = value ? "true" : "false";
 | 
					    args[name] = value ? "true" : "false";
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void addFile(String name, File file) => files[name] = file;
 | 
					  APIRequest addFile(String name, File file) {
 | 
				
			||||||
 | 
					    files[name] = file;
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void addArgs(Map<String, String> newArgs) => args.addAll(newArgs);
 | 
					  void addArgs(Map<String, String> newArgs) => args.addAll(newArgs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,18 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/helpers/settings_helper.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/account_image_settings.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/widgets/async_screen_widget.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/widgets/network_image_widget.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/files_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/intl_utils.dart';
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/ui_utils.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:settings_ui/settings_ui.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Account image settings section
 | 
					/// Account image settings section
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// @author Pierre Hubert
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AccountImageSettings extends StatelessWidget {
 | 
					class AccountImageSettingsScreen extends StatelessWidget {
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return Scaffold(
 | 
					    return Scaffold(
 | 
				
			||||||
@@ -17,15 +24,75 @@ class AccountImageSettings extends StatelessWidget {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class _AccountImageSettingsBody extends StatefulWidget {
 | 
					class _AccountImageSettingsBody extends StatefulWidget {
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  __AccountImageSettingsBodyState createState() => __AccountImageSettingsBodyState();
 | 
					  __AccountImageSettingsBodyState createState() =>
 | 
				
			||||||
 | 
					      __AccountImageSettingsBodyState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class __AccountImageSettingsBodyState extends State<_AccountImageSettingsBody> {
 | 
					class __AccountImageSettingsBodyState extends State<_AccountImageSettingsBody> {
 | 
				
			||||||
 | 
					  AccountImageSettings _settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final _key = GlobalKey<AsyncScreenWidgetState>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return Container();
 | 
					    return AsyncScreenWidget(
 | 
				
			||||||
 | 
					      key: _key,
 | 
				
			||||||
 | 
					      onReload: () async =>
 | 
				
			||||||
 | 
					          _settings = await SettingsHelper.getAccountImageSettings(),
 | 
				
			||||||
 | 
					      onBuild: () => _buildLayout(),
 | 
				
			||||||
 | 
					      errorMessage: tr("Could not get account image settings!"),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget _buildLayout() {
 | 
				
			||||||
 | 
					    return SettingsList(
 | 
				
			||||||
 | 
					      sections: [
 | 
				
			||||||
 | 
					        SettingsSection(
 | 
				
			||||||
 | 
					          title: tr("General"),
 | 
				
			||||||
 | 
					          tiles: _settings.hasImage
 | 
				
			||||||
 | 
					              ? _buildHasAccountImageTiles()
 | 
				
			||||||
 | 
					              : _buildNoAccountImageTiles(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// When user has no account image yet
 | 
				
			||||||
 | 
					  List<SettingsTile> _buildNoAccountImageTiles() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// When the user has an account image
 | 
				
			||||||
 | 
					  List<SettingsTile> _buildHasAccountImageTiles() {
 | 
				
			||||||
 | 
					    return [
 | 
				
			||||||
 | 
					      // Current account image (if any)
 | 
				
			||||||
 | 
					      SettingsTile(
 | 
				
			||||||
 | 
					        title: tr("Current account image"),
 | 
				
			||||||
 | 
					        leading: NetworkImageWidget(
 | 
				
			||||||
 | 
					          url: _settings.imageURL,
 | 
				
			||||||
 | 
					          width: 40,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      SettingsTile(
 | 
				
			||||||
 | 
					        title: tr("Upload new account image"),
 | 
				
			||||||
 | 
					        onTap: () => _uploadAccountImage(),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      SettingsTile(title: tr("Change account image visibility")),
 | 
				
			||||||
 | 
					      SettingsTile(title: tr("Delete account image"))
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Upload a new account image
 | 
				
			||||||
 | 
					  void _uploadAccountImage() async {
 | 
				
			||||||
 | 
					    final image = await pickImage(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (image == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!await SettingsHelper.uploadAccountImage(image)) {
 | 
				
			||||||
 | 
					      showSimpleSnack(context, tr("Could not upload your account image!"));
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _key.currentState.refresh();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ class __AccountSettingsBodyState extends State<_AccountSettingsBody> {
 | 
				
			|||||||
              title: tr("Account image"),
 | 
					              title: tr("Account image"),
 | 
				
			||||||
              subtitle: tr("Customize your account image"),
 | 
					              subtitle: tr("Customize your account image"),
 | 
				
			||||||
              leading: Icon(Icons.account_circle),
 | 
					              leading: Icon(Icons.account_circle),
 | 
				
			||||||
              onTap: () => _openSection(AccountImageSettings()),
 | 
					              onTap: () => _openSection(AccountImageSettingsScreen()),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										96
									
								
								lib/ui/widgets/async_screen_widget.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								lib/ui/widgets/async_screen_widget.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/ui/widgets/safe_state.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/ui_utils.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Widget that can be used to easily implement fetch of remote ressources
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AsyncScreenWidget extends StatefulWidget {
 | 
				
			||||||
 | 
					  /// Reload function
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Can be an asynchronous function that takes no arguments and throw an
 | 
				
			||||||
 | 
					  /// [Exception] in case of failure
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// You do not need to call [State.setState] on this function, it is
 | 
				
			||||||
 | 
					  /// automatically done by this widget
 | 
				
			||||||
 | 
					  final Future<void> Function() onReload;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Build function
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// This function will be called whenever [isReady] becomes true
 | 
				
			||||||
 | 
					  final Widget Function() onBuild;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Error message that will be shown in case of error
 | 
				
			||||||
 | 
					  final String errorMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const AsyncScreenWidget({
 | 
				
			||||||
 | 
					    Key key,
 | 
				
			||||||
 | 
					    @required this.onReload,
 | 
				
			||||||
 | 
					    @required this.onBuild,
 | 
				
			||||||
 | 
					    @required this.errorMessage,
 | 
				
			||||||
 | 
					  })  : assert(onReload != null),
 | 
				
			||||||
 | 
					        assert(onBuild != null),
 | 
				
			||||||
 | 
					        assert(errorMessage != null),
 | 
				
			||||||
 | 
					        super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  AsyncScreenWidgetState createState() => AsyncScreenWidgetState();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AsyncScreenWidgetState extends SafeState<AsyncScreenWidget> {
 | 
				
			||||||
 | 
					  bool error = false;
 | 
				
			||||||
 | 
					  bool ready = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    refresh();
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    // In case of error
 | 
				
			||||||
 | 
					    if (error)
 | 
				
			||||||
 | 
					      return buildErrorCard(widget.errorMessage, actions: [
 | 
				
			||||||
 | 
					        MaterialButton(
 | 
				
			||||||
 | 
					          onPressed: () => refresh(),
 | 
				
			||||||
 | 
					          child: Text(tr("Try again").toUpperCase()),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Show loading states
 | 
				
			||||||
 | 
					    if (!ready) return buildCenteredProgressBar();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The widget is ready, show it
 | 
				
			||||||
 | 
					    return RefreshIndicator(
 | 
				
			||||||
 | 
					      child: widget.onBuild(),
 | 
				
			||||||
 | 
					      onRefresh: () => refresh(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Refresh this screen
 | 
				
			||||||
 | 
					  Future<void> refresh() async {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        error = false;
 | 
				
			||||||
 | 
					        ready = false;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Call parent method
 | 
				
			||||||
 | 
					      await widget.onReload();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        ready = true;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } catch (e, stack) {
 | 
				
			||||||
 | 
					      print(e);
 | 
				
			||||||
 | 
					      print(stack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        error = true;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user