mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 04:04:18 +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();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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) =>
 | 
			
		||||
      args[name] = value ? "true" : "false";
 | 
			
		||||
  APIRequest addBool(String name, bool value) {
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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/ui_utils.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:settings_ui/settings_ui.dart';
 | 
			
		||||
 | 
			
		||||
/// Account image settings section
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre Hubert
 | 
			
		||||
 | 
			
		||||
class AccountImageSettings extends StatelessWidget {
 | 
			
		||||
class AccountImageSettingsScreen extends StatelessWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
@@ -17,15 +24,75 @@ class AccountImageSettings extends StatelessWidget {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _AccountImageSettingsBody extends StatefulWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  __AccountImageSettingsBodyState createState() => __AccountImageSettingsBodyState();
 | 
			
		||||
  __AccountImageSettingsBodyState createState() =>
 | 
			
		||||
      __AccountImageSettingsBodyState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class __AccountImageSettingsBodyState extends State<_AccountImageSettingsBody> {
 | 
			
		||||
  AccountImageSettings _settings;
 | 
			
		||||
 | 
			
		||||
  final _key = GlobalKey<AsyncScreenWidgetState>();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  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"),
 | 
			
		||||
              subtitle: tr("Customize your account image"),
 | 
			
		||||
              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