1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-12-26 04:48:51 +00:00

Can upload new account image

This commit is contained in:
Pierre HUBERT 2020-04-16 19:16:44 +02:00
parent 7071600c3f
commit 8feea380a4
6 changed files with 243 additions and 10 deletions

View 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;
}

View 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);
}

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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()),
)
],
)

View 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;
});
}
}
}