Added translations using l10n
Translations are provided in *.arb* format. Some keys have descriptions (indicated by leading @-symbol). Descriptions should not be copied into the translation itself. Currently only English is supported (app_en.arb), but German is planned. Apparently weblate merged .arb support at some time, so it would be nice to enable community translations at some point.
This commit is contained in:
parent
90adcc6bb1
commit
8fffafde47
24 changed files with 947 additions and 618 deletions
|
@ -1,3 +0,0 @@
|
||||||
# Official Outbag App
|
|
||||||
Source code of the official outbag app,
|
|
||||||
written in flutter.
|
|
15
README.org
Normal file
15
README.org
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
* Official Outbag App
|
||||||
|
Source code of the official outbag app,
|
||||||
|
written in flutter.
|
||||||
|
|
||||||
|
** Translating
|
||||||
|
This app uses /l10n/ according to the official flutter
|
||||||
|
[[https://docs.flutter.dev/development/accessibility-and-localization/internationalization][internationalization guide]].
|
||||||
|
|
||||||
|
1. Check if there is a ~.arb~ file available for your language,
|
||||||
|
and add missing translations in there,
|
||||||
|
if the file exists
|
||||||
|
2. Otherwise copy the ~app_en.arb~ file
|
||||||
|
and paste it as ~arb_<your-language>.arb~
|
||||||
|
3. Edit the translations in the file
|
||||||
|
4. Run ~flutter gen-l10n~ to generate the required ~.dart~ files
|
3
l10n.yaml
Normal file
3
l10n.yaml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
arb-dir: lib/l10n
|
||||||
|
template-arb-file: app_en.arb
|
||||||
|
output-localization-file: app_localizations.dart
|
|
@ -1,30 +1,32 @@
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tool to automatically generate english text from error
|
* Tool to automatically generate english text from error
|
||||||
*/
|
*/
|
||||||
String errorAsString(Map<String, dynamic> json) {
|
String errorAsString(Map<String, dynamic> json, AppLocalizations trans) {
|
||||||
switch (json['data']) {
|
switch (json['data']) {
|
||||||
case 'notfound':
|
case 'notfound':
|
||||||
return 'Endpoint not found';
|
return trans.errorNotFound;
|
||||||
case 'wrongstate':
|
case 'wrongstate':
|
||||||
return 'Missing data';
|
return trans.errorDataIncomplete;
|
||||||
case 'data':
|
case 'data':
|
||||||
return 'Invalid data';
|
return trans.errorDataInvalid;
|
||||||
case 'roomAdmin':
|
case 'roomAdmin':
|
||||||
case 'right':
|
case 'right':
|
||||||
return 'You are not allowed to perform this action';
|
return trans.errorPermissions;
|
||||||
case 'server':
|
case 'server':
|
||||||
return 'Server error';
|
return trans.errorServer;
|
||||||
case 'closed':
|
case 'closed':
|
||||||
return 'Server cannot be reached';
|
return trans.errorUnreachable;
|
||||||
case 'auth':
|
case 'auth':
|
||||||
return 'Username or password wrong';
|
return trans.errorAuth;
|
||||||
case 'ota':
|
case 'ota':
|
||||||
return 'Invalid OTA';
|
return trans.errorInvalidOTA;
|
||||||
case 'existence':
|
case 'existence':
|
||||||
return 'Username unavailable';
|
return trans.errorUsernameUnavailable;
|
||||||
case 'config':
|
case 'config':
|
||||||
return 'Server reached user limit';
|
return trans.errorServerLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Unknown Error";
|
return trans.errorUnknown;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
// implementation of permission types
|
// implementation of permission types
|
||||||
// according to the server permissions.ts
|
// according to the server permissions.ts
|
||||||
// https://codeberg.org/outbag/server/src/branch/dev/src/server/permissions.ts
|
// https://codeberg.org/outbag/server/src/branch/dev/src/server/permissions.ts
|
||||||
|
@ -100,45 +102,49 @@ class RoomPermission {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static String name(int permission) {
|
static String name(int permission, BuildContext context) {
|
||||||
|
final trans = AppLocalizations.of(context);
|
||||||
|
|
||||||
switch (permission) {
|
switch (permission) {
|
||||||
case 1:
|
case 1:
|
||||||
return 'Add Articles';
|
return trans!.roomPermissionAddArticles;
|
||||||
case 2:
|
case 2:
|
||||||
return 'Remove Articles';
|
return trans!.roomPermissionRemoveArticles;
|
||||||
case 4:
|
case 4:
|
||||||
return 'List Groups and Items';
|
return trans!.roomPermissionList;
|
||||||
case 8:
|
case 8:
|
||||||
return 'Change Room Metadata';
|
return trans!.roomPermissionChangeMeta;
|
||||||
case 16:
|
case 16:
|
||||||
return 'Manage OTAs';
|
return trans!.roomPermissionManageOTA;
|
||||||
case 32:
|
case 32:
|
||||||
return 'Manage Admins';
|
return trans!.roomPermissionManageAdmins;
|
||||||
case 64:
|
case 64:
|
||||||
return 'Manage Members';
|
return trans!.roomPermissionManageMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Unknown permission";
|
return trans!.roomPermissionUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String describe(int permission) {
|
static String describe(int permission, BuildContext context) {
|
||||||
|
final trans = AppLocalizations.of(context);
|
||||||
|
|
||||||
switch (permission) {
|
switch (permission) {
|
||||||
case 1:
|
case 1:
|
||||||
return 'Allows users to add items to the shopping list';
|
return trans!.roomPermissionAddArticlesSubtitle;
|
||||||
case 2:
|
case 2:
|
||||||
return 'Allows users to remove items from the shopping list';
|
return trans!.roomPermissionRemoveArticlesSubtitle;
|
||||||
case 4:
|
case 4:
|
||||||
return 'Allows the user to view groups and products';
|
return trans!.roomPermissionListSubtitle;
|
||||||
case 8:
|
case 8:
|
||||||
return 'Allows the user to edit the room name, description and icon';
|
return trans!.roomPermissionChangeMetaSubtitle;
|
||||||
case 16:
|
case 16:
|
||||||
return 'Alloww the user to create, share and delete authentification tokens';
|
return trans!.roomPermissionManageOTASubtitle;
|
||||||
case 32:
|
case 32:
|
||||||
return 'Allows the user to change the admin status of other members';
|
return trans!.roomPermissionManageAdminsSubtitle;
|
||||||
case 64:
|
case 64:
|
||||||
return 'Allows the user to invite and kick room members';
|
return trans!.roomPermissionManageMembersSubtitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "No description available";
|
return trans!.roomPermissionUnknownSubtitle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:localstore/localstore.dart';
|
import 'package:localstore/localstore.dart';
|
||||||
import 'package:outbag_app/tools/assets.dart';
|
import 'package:outbag_app/tools/assets.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class RoomVisibility {
|
class RoomVisibility {
|
||||||
final int type;
|
final int type;
|
||||||
|
@ -28,14 +29,16 @@ class RoomVisibility {
|
||||||
return Icons.lock;
|
return Icons.lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
String get text {
|
String text(BuildContext context) {
|
||||||
|
final trans = AppLocalizations.of(context);
|
||||||
|
|
||||||
if (type == 2) {
|
if (type == 2) {
|
||||||
return "Global";
|
return trans!.roomVisibilityGlobal;
|
||||||
} else if (type == 1) {
|
} else if (type == 1) {
|
||||||
return "Local";
|
return trans!.roomVisibilityLocal;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Private";
|
return trans!.roomVisibilityPrivate;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<RoomVisibility> list() {
|
static List<RoomVisibility> list() {
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:localstore/localstore.dart';
|
import 'package:localstore/localstore.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class AppTheme {
|
class AppTheme {
|
||||||
ThemeMode mode;
|
ThemeMode mode;
|
||||||
|
|
||||||
AppTheme(this.mode);
|
AppTheme(this.mode);
|
||||||
|
|
||||||
String get name {
|
String name(BuildContext context) {
|
||||||
if (mode == ThemeMode.light) {
|
final trans = AppLocalizations.of(context);
|
||||||
return 'Light';
|
|
||||||
|
if (mode == ThemeMode.light) {
|
||||||
|
return trans!.themeLight;
|
||||||
}
|
}
|
||||||
if (mode == ThemeMode.dark) {
|
if (mode == ThemeMode.dark) {
|
||||||
return 'Dark';
|
return trans!.themeDark;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'System';
|
return trans!.themeSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
IconData get icon {
|
IconData get icon {
|
||||||
|
@ -80,5 +83,4 @@ class AppTheme {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => mode.index;
|
int get hashCode => mode.index;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
287
lib/l10n/app_en.arb
Normal file
287
lib/l10n/app_en.arb
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
{
|
||||||
|
"helloWorld": "Hello World!",
|
||||||
|
"@helloWorld": {
|
||||||
|
"description": "The conventional newborn programmer greeting"
|
||||||
|
},
|
||||||
|
|
||||||
|
"welcomeTitle": "Welcome to Outbag",
|
||||||
|
"@welcomeTitle": {
|
||||||
|
"description": "Title shown on welcome screen"
|
||||||
|
},
|
||||||
|
"welcomeSubtitle": "Shopping lists made easy",
|
||||||
|
"@welcomeSubtitle": {
|
||||||
|
"description": "Subtitle shown on welcome screen"
|
||||||
|
},
|
||||||
|
"userHasAnAccount": "I already have an account",
|
||||||
|
"@userHasAnAccount": {
|
||||||
|
"description": "Button displayed on welcome screen (bottom-center) used to launch sign in screen"
|
||||||
|
},
|
||||||
|
"letsGo": "Let's go",
|
||||||
|
"@letsGo": {
|
||||||
|
"description": "Text for button on welcome screen used to move the page viewer the first time"
|
||||||
|
},
|
||||||
|
"page2Title": "Open. Decentralized",
|
||||||
|
"page2Subtitle": "One account, multiple servers",
|
||||||
|
|
||||||
|
"next": "Next",
|
||||||
|
|
||||||
|
"page3Title": "Made to share",
|
||||||
|
"page3Subtitle": "Collaborate on your shopping lists in real time",
|
||||||
|
|
||||||
|
"continueTour": "Continue Tour",
|
||||||
|
"takeTour": "Take Tour",
|
||||||
|
|
||||||
|
"page4Title": "Pocket-size",
|
||||||
|
"page4Subtitle": "Always have your shopping lists with you",
|
||||||
|
"signUp": "Sign Up",
|
||||||
|
|
||||||
|
|
||||||
|
"signIn": "Sign In",
|
||||||
|
"logIntoAccount": "Log into account",
|
||||||
|
"createNewAccount": "Create new account",
|
||||||
|
|
||||||
|
"inputServerLabel": "Server",
|
||||||
|
"inputServerHint": "Your homeserver URL",
|
||||||
|
"inputServerHelp": "Your data will be stored on your homeserver",
|
||||||
|
|
||||||
|
"inputUsernameLabel": "Username",
|
||||||
|
"inputUsernameHint": "Your username",
|
||||||
|
"inputUsernameHelp": "Your username and server-tag allow others to identify you",
|
||||||
|
|
||||||
|
"inputPasswordLabel": "Password",
|
||||||
|
"inputPasswordHint": "Your password",
|
||||||
|
"inputPasswordHelp": "Password has to be at least six characters long",
|
||||||
|
|
||||||
|
"inputPasswordRepeatLabel": "Repeat Password",
|
||||||
|
"inputPasswordRepeatHint": "Type your password again",
|
||||||
|
"inputPasswordRepeatHelp": "Make sure to type the correct password",
|
||||||
|
|
||||||
|
"inputOTALabel": "OTA",
|
||||||
|
"inputOTAHint": "One-Time-Authorization token",
|
||||||
|
"inputOTAHelp": "This token might be required if the server is rate limited",
|
||||||
|
|
||||||
|
"errorPasswordLength": "Password has to be at least six characters long",
|
||||||
|
"errorPasswordsDoNotMatch": "Passwords do not match",
|
||||||
|
|
||||||
|
"errorInvalidServer": "Unable to find outbag server on {server}",
|
||||||
|
"@errorInvalidServer": {
|
||||||
|
"description": "Error shown when there is no outbag server on the given url",
|
||||||
|
"placeholders": {
|
||||||
|
"server": {
|
||||||
|
"type": "String",
|
||||||
|
"example": "outbag.example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"search": "Search",
|
||||||
|
"settings": "Settings",
|
||||||
|
"about": "About",
|
||||||
|
"serverDashboard": "Server Dashboard",
|
||||||
|
|
||||||
|
"addRoom": "Add Room",
|
||||||
|
"addRoomHint": "Add a new room",
|
||||||
|
|
||||||
|
"noNewRoomsFound": "No new rooms found",
|
||||||
|
"joinRoom": "Join Room",
|
||||||
|
"refresh": "Refresh",
|
||||||
|
"joinRoomInvite": "Join invite-only room",
|
||||||
|
|
||||||
|
"newRoom": "New Room",
|
||||||
|
"newRoomShort": "New",
|
||||||
|
"createRoom": "Create Room",
|
||||||
|
"createRoomShort": "Create",
|
||||||
|
|
||||||
|
"changeRoomIcon": "Change room icon",
|
||||||
|
"chooseRoomIcon": "Choose a room icon",
|
||||||
|
|
||||||
|
"inputRoomIdLabel": "Room ID",
|
||||||
|
"inputRoomIdHint": "Unique room id",
|
||||||
|
"inputRoomIdHelp": "The room id and server tag allow the room to be identified",
|
||||||
|
|
||||||
|
"inputRoomNameLabel": "Room Name",
|
||||||
|
"inputRoomNameHint": "Give your room a name",
|
||||||
|
"inputRoomNameHelp": "Choose a human-readable name to easily identify a room",
|
||||||
|
|
||||||
|
"inputRoomDescriptionLabel": "Room Description",
|
||||||
|
"inputRoomDescriptionHint": "Briefly describe your room",
|
||||||
|
"inputRoomDescriptionHelp": "Make it easier for others to know what this room is used for",
|
||||||
|
|
||||||
|
"roomVisibilityTitle": "Visibility",
|
||||||
|
"roomVisibilitySubtitle": "Specify who has access to your room",
|
||||||
|
"roomVisibilityPrivate": "Private",
|
||||||
|
"roomVisibilityLocal": "Local",
|
||||||
|
"roomVisibilityGlobal": "Global",
|
||||||
|
|
||||||
|
"errorNoRoomId": "Please specify a room ID",
|
||||||
|
"errorRoomIdLength": "Room ID has to be at least three characters long",
|
||||||
|
"errorNoRoomName": "Please specify a room name",
|
||||||
|
|
||||||
|
"errorNetwork": "Network error",
|
||||||
|
"errorUnknown": "Unknown error",
|
||||||
|
"errorServer":"Server error",
|
||||||
|
|
||||||
|
"errorNotFound":"Not found",
|
||||||
|
"errorDataIncomplete":"Missing data",
|
||||||
|
"errorDataInvalid":"Invalid data",
|
||||||
|
"errorPermissions":"You are not allowed to perform this action",
|
||||||
|
"errorUnreachable":"Server cannot be reached",
|
||||||
|
"errorAuth":"Username or password wrong",
|
||||||
|
"errorInvalidOTA":"Invalid OTA",
|
||||||
|
"errorUsernameUnavailable":"A user with that name already exists",
|
||||||
|
"errorServerLimit":"Server reached user limit",
|
||||||
|
|
||||||
|
"themeLight": "Light",
|
||||||
|
"themeDark": "Dark",
|
||||||
|
"themeSystem": "System",
|
||||||
|
|
||||||
|
"roomListTitle": "List",
|
||||||
|
"roomListSubtitle": "View shopping list",
|
||||||
|
"roomProductsTitle": "Products",
|
||||||
|
"roomProductsSubtitle": "View saved items",
|
||||||
|
"roomCategoriesTitle": "Categories",
|
||||||
|
"roomCategoriesSubtitle": "View categories",
|
||||||
|
"roomAboutTitle": "About",
|
||||||
|
"roomAboutSubtitle": "View room info",
|
||||||
|
|
||||||
|
"changeRoomVisibilityTitle": "Change Room Visibility",
|
||||||
|
"changeRoomVisibilitySubtitle": "Do you really want to change the room visibility to: {visibility}",
|
||||||
|
"@changeRoomVisibilitySubtitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"visibility": {
|
||||||
|
"type": "String",
|
||||||
|
"example": "Local"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"editRoomMetadata": "Edit Metadata",
|
||||||
|
"editRoomMetadataShort": "Edit Room",
|
||||||
|
"editRoomMetadataSubtitle": "Edit the room name, description and icon",
|
||||||
|
"showRoomMembers": "Members",
|
||||||
|
"showRoomMembersSubtitle": "Show Member list",
|
||||||
|
"editRoomPermissions": "Edit Permissions",
|
||||||
|
"editRoomPermissionsSubtitle": "Change the default permission-set for all members",
|
||||||
|
"manageRoomOTA": "OTA",
|
||||||
|
"manageRoomOTASubtitle": "Add and delete OTAs",
|
||||||
|
"manageRoomInvites": "Invites",
|
||||||
|
"manageRoomInvitesSubtitle": "Invite people to this room",
|
||||||
|
|
||||||
|
"leaveRoom": "Leave Room",
|
||||||
|
"leaveRoomShort": "Leave",
|
||||||
|
"leaveRoomConfirm": "Do you really want to leave this room?",
|
||||||
|
"deleteRoom": "Delete Room",
|
||||||
|
"deleteRoomShort": "Delete",
|
||||||
|
"deleteRoomConfirm": "Do you really want to delete this room?",
|
||||||
|
|
||||||
|
"updateRoomPermissions": "Edit",
|
||||||
|
"updateRoomPermissionsHint": "Update default permission set",
|
||||||
|
"roomDefaultPermissions": "Default Permissions",
|
||||||
|
|
||||||
|
"roomPermissionAddArticles": "Add Articles",
|
||||||
|
"roomPermissionAddArticlesSubtitle": "Allows users to add items to the shopping list",
|
||||||
|
"roomPermissionRemoveArticles": "Remove Articles",
|
||||||
|
"roomPermissionRemoveArticlesSubtitle": "Allows users to remove items from the shopping list",
|
||||||
|
"roomPermissionList": "List Groups and Items",
|
||||||
|
"roomPermissionListSubtitle": "Allows the user to view groups and products",
|
||||||
|
"roomPermissionChangeMeta": "Change Room Metadata",
|
||||||
|
"roomPermissionChangeMetaSubtitle": "Allows the user to edit the room name, description and icon",
|
||||||
|
"roomPermissionManageOTA": "Manage OTAs",
|
||||||
|
"roomPermissionManageOTASubtitle": "Alloww the user to create, share and delete authentification tokens",
|
||||||
|
"roomPermissionManageAdmins": "Manage Admins",
|
||||||
|
"roomPermissionManageAdminsSubtitle": "Allows the user to change the admin status of other members",
|
||||||
|
"roomPermissionManageMembers": "Manage Members",
|
||||||
|
"roomPermissionManageMembersSubtitle": "Allows the user to invite and kick room members",
|
||||||
|
"roomPermissionUnknown": "Unknown Permission",
|
||||||
|
"roomPermissionUnknownSubtitle": "No information available",
|
||||||
|
|
||||||
|
"roomMembersTitle": "Room Members ({count})",
|
||||||
|
"@roomMembersTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int",
|
||||||
|
"example": "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"roleOwner": "Owner",
|
||||||
|
"roleAdmin": "Admin",
|
||||||
|
"roleMember": "Member",
|
||||||
|
|
||||||
|
"makeAdminTitle": "Make Admin",
|
||||||
|
"makeAdminSubtitle": "Grants the user the permission to do everything",
|
||||||
|
"makeAdminConfirm": "Do you really want to make {user} admin?",
|
||||||
|
"@makeAdminConfirm": {
|
||||||
|
"placeholders": {
|
||||||
|
"user": {
|
||||||
|
"type": "String",
|
||||||
|
"example": "ash@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"removeAdminTitle": "Remove admin privileges",
|
||||||
|
"removeAdminSubtitle": "Revokes admin privileges from the user",
|
||||||
|
"removeAdminConfirm": "Do you really want to remove {user}'s admin privileges",
|
||||||
|
"@removeAdminConfirm": {
|
||||||
|
"placeholders": {
|
||||||
|
"user": {
|
||||||
|
"type": "String",
|
||||||
|
"example": "ash@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kickUserTitle": "Kich User",
|
||||||
|
"kickUserSubtitle": "Temporarily remove user from server (they'll be able to join the room again)",
|
||||||
|
"kichUserConfirm": "Do you really want to kick {user}?",
|
||||||
|
"@kickUserConfirm": {
|
||||||
|
"placeholders": {
|
||||||
|
"user": {
|
||||||
|
"type": "String",
|
||||||
|
"example": "ash@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"limitRoomCount": "Room count limit",
|
||||||
|
"limitRoomCountSubtitle": "How many rooms you are allowed to own",
|
||||||
|
"limitRoomSize": "Room size limit",
|
||||||
|
"limitRoomSizeSubtitle": "How many items/products/categories each room may contain",
|
||||||
|
"limitRoomMemberCount": "Room member limit",
|
||||||
|
"limitRoomMemberCountSubtitle": "How many members each of your rooms may have",
|
||||||
|
"userDiscoverable": "Discoverable",
|
||||||
|
"userDiscoverableSubtitle": "Determines if your account can be discovered by users from other servers",
|
||||||
|
|
||||||
|
"changeThemeTitle": "Change Theme",
|
||||||
|
"changeThemeSubtitle": "Choose your preferred color-scheme",
|
||||||
|
"changePasswordTitle": "Change Password",
|
||||||
|
"changePasswordSubtitle": "Choose a new password for your account",
|
||||||
|
"exportAccountTitle": "Export Account",
|
||||||
|
"exportAccountSubtitle": "Export account data",
|
||||||
|
"deleteAccountTitle": "Delete Account",
|
||||||
|
"deleteAccountSubtitle": "Delete your account from your homeserver",
|
||||||
|
"deleteAccountConfirm": "Do you really want to delete your account?",
|
||||||
|
|
||||||
|
"logOut": "Log out",
|
||||||
|
"logOutConfirm": "Do you really want to log out?",
|
||||||
|
|
||||||
|
"inputOldPasswordLabel": "Old Password",
|
||||||
|
"inputOldPasswordHint": "Your current password",
|
||||||
|
"inputOldPasswordHelp": "Type your current password here",
|
||||||
|
"inputNewPasswordLabel": "New Password",
|
||||||
|
"inputNewPasswordHint": "Your new password",
|
||||||
|
"inputNewPasswordHelp": "Password has to be at least six characters long",
|
||||||
|
"inputNewPasswordRepeatLabel": "Repeat new Password",
|
||||||
|
"inputNewPasswordRepeatHint": "Type your new password again",
|
||||||
|
"inputNewPasswordRepeatHelp": "Make sure this matches your new password",
|
||||||
|
|
||||||
|
"errorPasswordsDontMatch": "New passwords do not match",
|
||||||
|
"errorOldPasswordWrong": "Your old password is wrong",
|
||||||
|
|
||||||
|
"yes": "Yes",
|
||||||
|
"loading": "Loading",
|
||||||
|
"dismiss": "Dismiss",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"ok": "OK",
|
||||||
|
"close": "Close",
|
||||||
|
"update": "Update"
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ import 'package:outbag_app/screens/room/new.dart';
|
||||||
import 'package:outbag_app/screens/settings/main.dart';
|
import 'package:outbag_app/screens/settings/main.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import './screens/home.dart';
|
import './screens/home.dart';
|
||||||
import './screens/welcome.dart';
|
import './screens/welcome.dart';
|
||||||
import './screens/room/main.dart';
|
import './screens/room/main.dart';
|
||||||
|
@ -142,6 +144,13 @@ class _OutbagAppState extends State {
|
||||||
],
|
],
|
||||||
child: MaterialApp.router(
|
child: MaterialApp.router(
|
||||||
title: "Outbag",
|
title: "Outbag",
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
AppLocalizations.delegate
|
||||||
|
],
|
||||||
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
themeMode: theme.mode,
|
themeMode: theme.mode,
|
||||||
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
|
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
|
||||||
darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
|
darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
|
||||||
|
@ -223,10 +232,6 @@ class _OutbagAppState extends State {
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'room',
|
name: 'room',
|
||||||
path: 'r/:server/:id',
|
path: 'r/:server/:id',
|
||||||
redirect: (context, state) {
|
|
||||||
print(state.subloc);
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
builder: (context, state) => RoomPage(
|
builder: (context, state) => RoomPage(
|
||||||
state.params['server'] ?? '',
|
state.params['server'] ?? '',
|
||||||
state.params['id'] ?? ''),
|
state.params['id'] ?? ''),
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:outbag_app/backend/request.dart';
|
||||||
import 'package:outbag_app/backend/user.dart';
|
import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:outbag_app/tools/snackbar.dart';
|
import 'package:outbag_app/tools/snackbar.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import '../backend/resolve_url.dart';
|
import '../backend/resolve_url.dart';
|
||||||
|
|
||||||
enum Mode {
|
enum Mode {
|
||||||
|
@ -31,13 +32,13 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String modeName = "Sign In";
|
String modeName = AppLocalizations.of(context)!.signIn;
|
||||||
if (widget.mode != Mode.signin) {
|
if (widget.mode != Mode.signin) {
|
||||||
modeName = "Sign Up";
|
modeName = AppLocalizations.of(context)!.signUp;
|
||||||
}
|
}
|
||||||
String modeDescription = "Log into account";
|
String modeDescription = AppLocalizations.of(context)!.logIntoAccount;
|
||||||
if (widget.mode != Mode.signin) {
|
if (widget.mode != Mode.signin) {
|
||||||
modeDescription = "Create new account";
|
modeDescription = AppLocalizations.of(context)!.createNewAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
final textTheme = Theme.of(context)
|
final textTheme = Theme.of(context)
|
||||||
|
@ -52,7 +53,7 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const CircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
Text('Loading', style: textTheme.titleLarge),
|
Text(AppLocalizations.of(context)!.loading, style: textTheme.titleLarge),
|
||||||
])))
|
])))
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
@ -70,13 +71,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrServer,
|
controller: _ctrServer,
|
||||||
keyboardType: TextInputType.url,
|
keyboardType: TextInputType.url,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.dns),
|
prefixIcon: const Icon(Icons.dns),
|
||||||
labelText: 'Server',
|
labelText: AppLocalizations.of(context)!.inputServerLabel,
|
||||||
hintText: 'Your homeserver url',
|
hintText: AppLocalizations.of(context)!.inputServerHint,
|
||||||
helperText:
|
helperText:AppLocalizations.of(context)!.inputServerHelp,
|
||||||
'Your data will be stored on your homeserver',
|
border: const OutlineInputBorder(),
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -85,13 +85,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrUsername,
|
controller: _ctrUsername,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.person),
|
prefixIcon: const Icon(Icons.person),
|
||||||
labelText: 'Username',
|
labelText: AppLocalizations.of(context)!.inputUsernameLabel,
|
||||||
hintText: 'Your username',
|
hintText: AppLocalizations.of(context)!.inputUsernameHint,
|
||||||
helperText:
|
helperText:AppLocalizations.of(context)!.inputUsernameHelp,
|
||||||
'your username and server tag allow others to identify you',
|
border: const OutlineInputBorder(),
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -101,13 +100,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
controller: _ctrPassword,
|
controller: _ctrPassword,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.lock),
|
prefixIcon: const Icon(Icons.lock),
|
||||||
labelText: 'Password',
|
labelText: AppLocalizations.of(context)!.inputPasswordLabel,
|
||||||
hintText: 'Your password',
|
hintText: AppLocalizations.of(context)!.inputPasswordHint,
|
||||||
helperText:
|
helperText:AppLocalizations.of(context)!.inputPasswordHelp,
|
||||||
'Password have to be at least six characters long',
|
border: const OutlineInputBorder(),
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -120,13 +118,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
controller: _ctrPasswordRpt,
|
controller: _ctrPasswordRpt,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.lock),
|
prefixIcon: const Icon(Icons.lock),
|
||||||
labelText: 'Repeat Password',
|
labelText: AppLocalizations.of(context)!.inputPasswordRepeatLabel,
|
||||||
hintText: 'Type your password again',
|
hintText: AppLocalizations.of(context)!.inputPasswordRepeatHint,
|
||||||
helperText:
|
helperText:AppLocalizations.of(context)!.inputPasswordRepeatHelp,
|
||||||
'Make sure to type the correct password',
|
border: const OutlineInputBorder(),
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -140,13 +137,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrOTA,
|
controller: _ctrOTA,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.key),
|
prefixIcon: const Icon(Icons.key),
|
||||||
labelText: 'OTA',
|
labelText: AppLocalizations.of(context)!.inputOTALabel,
|
||||||
hintText: 'One-Time-Authorization token',
|
hintText: AppLocalizations.of(context)!.inputOTAHint,
|
||||||
helperText:
|
helperText:AppLocalizations.of(context)!.inputOTAHelp,
|
||||||
'This token might be required if the server is rate limited',
|
border: const OutlineInputBorder(),
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -170,7 +166,8 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
});
|
});
|
||||||
|
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: 'Passwords do not match', action: 'Dismiss');
|
text: AppLocalizations.of(context)!.errorPasswordsDoNotMatch,
|
||||||
|
action: AppLocalizations.of(context)!.dismiss);
|
||||||
|
|
||||||
_ctrPasswordRpt.clear();
|
_ctrPasswordRpt.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -184,8 +181,8 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
});
|
});
|
||||||
|
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: 'Password has to be at least 6 characters longs',
|
text: AppLocalizations.of(context)!.errorPasswordLength,
|
||||||
action: 'Dismiss');
|
action: AppLocalizations.of(context)!.dismiss);
|
||||||
|
|
||||||
_ctrPasswordRpt.clear();
|
_ctrPasswordRpt.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -204,9 +201,8 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
});
|
});
|
||||||
|
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text:
|
text: AppLocalizations.of(context)!.errorInvalidServer(_ctrServer.text),
|
||||||
'Unable to find valid outbag server on ${_ctrServer.text}',
|
action: AppLocalizations.of(context)!.dismiss);
|
||||||
action: 'Dismiss');
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import '../backend/room.dart';
|
import '../backend/room.dart';
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
|
@ -69,7 +70,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
tooltip: "Search",
|
tooltip: AppLocalizations.of(context)!.search,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show searchbar
|
// show searchbar
|
||||||
// NOTE: location currently unknown
|
// NOTE: location currently unknown
|
||||||
|
@ -91,7 +92,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
menuChildren: [
|
menuChildren: [
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(Icons.settings),
|
leadingIcon: const Icon(Icons.settings),
|
||||||
child: const Text('Settings'),
|
child: Text(AppLocalizations.of(context)!.settings),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show settings screen
|
// show settings screen
|
||||||
context.goNamed('settings');
|
context.goNamed('settings');
|
||||||
|
@ -103,7 +104,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
? [
|
? [
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(Icons.dns),
|
leadingIcon: const Icon(Icons.dns),
|
||||||
child: const Text('Server Dashboard'),
|
child: Text(AppLocalizations.of(context)!.serverDashboard),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show settings screen
|
// show settings screen
|
||||||
context.goNamed('dash');
|
context.goNamed('dash');
|
||||||
|
@ -112,7 +113,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
: [],
|
: [],
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(Icons.info_rounded),
|
leadingIcon: const Icon(Icons.info_rounded),
|
||||||
child: const Text('About'),
|
child: Text(AppLocalizations.of(context)!.about),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show about screen
|
// show about screen
|
||||||
context.goNamed('about');
|
context.goNamed('about');
|
||||||
|
@ -154,13 +155,13 @@ class _HomePageState extends State<HomePage> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
label: const Text('Add Room'),
|
label: Text(AppLocalizations.of(context)!.addRoom),
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// create new room
|
// create new room
|
||||||
context.goNamed('add-room');
|
context.goNamed('add-room');
|
||||||
},
|
},
|
||||||
tooltip: 'Add new Room',
|
tooltip: AppLocalizations.of(context)!.addRoomHint,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:outbag_app/backend/errors.dart';
|
|
||||||
import 'package:outbag_app/backend/request.dart';
|
import 'package:outbag_app/backend/request.dart';
|
||||||
import 'package:outbag_app/backend/room.dart';
|
import 'package:outbag_app/backend/room.dart';
|
||||||
import 'package:outbag_app/backend/user.dart';
|
import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
|
import 'package:outbag_app/tools/snackbar.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
class EditRoomPage extends StatefulWidget {
|
class EditRoomPage extends StatefulWidget {
|
||||||
|
@ -69,7 +70,7 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// no room data available
|
// no room data available
|
||||||
// close screen
|
// close screen
|
||||||
router.pushReplacementNamed('homoe');
|
router.pushReplacementNamed('home');
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
return true;
|
return true;
|
||||||
|
@ -77,7 +78,7 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
onServerErr: (json) {
|
onServerErr: (json) {
|
||||||
// user not allowed to be here
|
// user not allowed to be here
|
||||||
// close screen
|
// close screen
|
||||||
router.pushReplacementNamed('homoe');
|
router.pushReplacementNamed('home');
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -127,11 +128,11 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const CircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
Text('Loading', style: textTheme.titleLarge),
|
Text(AppLocalizations.of(context)!.loading, style: textTheme.titleLarge),
|
||||||
])))
|
])))
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Edit Room'),
|
title: Text(AppLocalizations.of(context)!.editRoomMetadataShort),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
|
@ -149,13 +150,13 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
width: smallest * 0.3,
|
width: smallest * 0.3,
|
||||||
height: smallest * 0.3,
|
height: smallest * 0.3,
|
||||||
),
|
),
|
||||||
tooltip: 'Change room icon',
|
tooltip: AppLocalizations.of(context)!.changeRoomIcon,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text(
|
title: Text(
|
||||||
'Choose a room Icon'),
|
AppLocalizations.of(context)!.chooseRoomIcon),
|
||||||
actions: const [],
|
actions: const [],
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: smallest * 0.3 * 3,
|
width: smallest * 0.3 * 3,
|
||||||
|
@ -195,13 +196,15 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
controller: _ctrID,
|
controller: _ctrID,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.fact_check),
|
prefixIcon: const Icon(Icons.fact_check),
|
||||||
labelText: 'Room ID',
|
labelText: AppLocalizations.of(context)!
|
||||||
hintText: 'Unique room id',
|
.inputRoomIdLabel,
|
||||||
helperText:
|
hintText: AppLocalizations.of(context)!
|
||||||
'the room id and server tag allow the room to be identified',
|
.inputRoomIdHint,
|
||||||
border: OutlineInputBorder(),
|
helperText: AppLocalizations.of(context)!
|
||||||
|
.inputRoomIdHelp,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -210,13 +213,15 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrName,
|
controller: _ctrName,
|
||||||
keyboardType: TextInputType.name,
|
keyboardType: TextInputType.name,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.badge),
|
prefixIcon: const Icon(Icons.badge),
|
||||||
labelText: 'Room Name',
|
labelText: AppLocalizations.of(context)!
|
||||||
hintText: 'Give your room a name',
|
.inputRoomNameLabel,
|
||||||
helperText:
|
hintText: AppLocalizations.of(context)!
|
||||||
'Easily identify a room with a human readable name',
|
.inputRoomNameHint,
|
||||||
border: OutlineInputBorder(),
|
helperText: AppLocalizations.of(context)!
|
||||||
|
.inputRoomNameHelp,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -225,13 +230,15 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrDescription,
|
controller: _ctrDescription,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.dns),
|
labelText: AppLocalizations.of(context)!
|
||||||
labelText: 'Room Description',
|
.inputRoomDescriptionLabel,
|
||||||
hintText: 'Briefly describe your Room',
|
hintText: AppLocalizations.of(context)!
|
||||||
helperText:
|
.inputRoomDescriptionHint,
|
||||||
'Make it easier for other to know what this room is used for',
|
helperText: AppLocalizations.of(context)!
|
||||||
border: OutlineInputBorder(),
|
.inputRoomDescriptionHelp,
|
||||||
|
prefixIcon: const Icon(Icons.dns),
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -241,22 +248,12 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final scaffMgr = ScaffoldMessenger.of(context);
|
final scaffMgr = ScaffoldMessenger.of(context);
|
||||||
final nav = Navigator.of(context);
|
final nav = Navigator.of(context);
|
||||||
|
final trans = AppLocalizations.of(context);
|
||||||
|
|
||||||
// name may not be empty
|
// name may not be empty
|
||||||
if (_ctrName.text.isEmpty) {
|
if (_ctrName.text.isEmpty) {
|
||||||
final snackBar = SnackBar(
|
showSimpleSnackbar(scaffMgr,
|
||||||
behavior: SnackBarBehavior.floating,
|
text: trans!.errorNoRoomName, action: trans.ok);
|
||||||
content: const Text('Please specify a room name'),
|
|
||||||
action: SnackBarAction(
|
|
||||||
label: 'Ok',
|
|
||||||
onPressed: () {
|
|
||||||
scaffMgr.hideCurrentSnackBar();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
scaffMgr.hideCurrentSnackBar();
|
|
||||||
scaffMgr.showSnackBar(snackBar);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -275,8 +272,8 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
clone.description = _ctrDescription.text;
|
clone.description = _ctrDescription.text;
|
||||||
clone.icon = _ctrIcon;
|
clone.icon = _ctrIcon;
|
||||||
|
|
||||||
try {
|
doNetworkRequest(scaffMgr,
|
||||||
final resp = await postWithCreadentials(
|
req: ()=>postWithCreadentials(
|
||||||
target: user.server,
|
target: user.server,
|
||||||
credentials: user,
|
credentials: user,
|
||||||
path: 'changeRoomMeta',
|
path: 'changeRoomMeta',
|
||||||
|
@ -286,45 +283,16 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
||||||
'title': clone.name,
|
'title': clone.name,
|
||||||
'description': clone.description,
|
'description': clone.description,
|
||||||
'icon': clone.icon?.type,
|
'icon': clone.icon?.type,
|
||||||
});
|
}),
|
||||||
if (resp.res == Result.ok) {
|
onOK: (_) async {
|
||||||
// room was created
|
// room was created
|
||||||
// save room
|
// save room
|
||||||
await clone.toDisk();
|
await clone.toDisk();
|
||||||
nav.pop();
|
nav.pop();
|
||||||
} else {
|
|
||||||
// error
|
|
||||||
final snackBar = SnackBar(
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
content: Text(errorAsString(resp.body)),
|
|
||||||
action: SnackBarAction(
|
|
||||||
label: 'Dismiss',
|
|
||||||
onPressed: () {
|
|
||||||
scaffMgr.hideCurrentSnackBar();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
scaffMgr.hideCurrentSnackBar();
|
|
||||||
scaffMgr.showSnackBar(snackBar);
|
|
||||||
}
|
}
|
||||||
} catch (_) {
|
);
|
||||||
final snackBar = SnackBar(
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
content: const Text('Network error'),
|
|
||||||
action: SnackBarAction(
|
|
||||||
label: 'Dismiss',
|
|
||||||
onPressed: () {
|
|
||||||
scaffMgr.hideCurrentSnackBar();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
scaffMgr.hideCurrentSnackBar();
|
|
||||||
scaffMgr.showSnackBar(snackBar);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
label: const Text('Update'),
|
label: Text(AppLocalizations.of(context)!.update),
|
||||||
icon: const Icon(Icons.edit)),
|
icon: const Icon(Icons.edit)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:outbag_app/backend/room.dart';
|
||||||
import 'package:outbag_app/backend/user.dart';
|
import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
class JoinRoomPage extends StatefulWidget {
|
class JoinRoomPage extends StatefulWidget {
|
||||||
|
@ -102,7 +103,7 @@ class _JoinRoomPageState extends State {
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
tooltip: "Search",
|
tooltip: AppLocalizations.of(context)!.search,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show searchbar
|
// show searchbar
|
||||||
// NOTE: location currently unknown
|
// NOTE: location currently unknown
|
||||||
|
@ -110,7 +111,7 @@ class _JoinRoomPageState extends State {
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
tooltip: "Refresh",
|
tooltip: AppLocalizations.of(context)!.refresh,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// fetch public rooms again
|
// fetch public rooms again
|
||||||
didChangeDependencies();
|
didChangeDependencies();
|
||||||
|
@ -132,7 +133,7 @@ class _JoinRoomPageState extends State {
|
||||||
menuChildren: [
|
menuChildren: [
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(Icons.drafts),
|
leadingIcon: const Icon(Icons.drafts),
|
||||||
child: const Text('Join invite-only room'),
|
child: Text(AppLocalizations.of(context)!.joinRoomInvite),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show settings screen
|
// show settings screen
|
||||||
context.goNamed('join-room-ota');
|
context.goNamed('join-room-ota');
|
||||||
|
@ -146,7 +147,7 @@ class _JoinRoomPageState extends State {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text('No new Rooms found', style: textTheme.titleLarge),
|
Text(AppLocalizations.of(context)!.noNewRoomsFound, style: textTheme.titleLarge),
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
|
@ -159,7 +160,7 @@ class _JoinRoomPageState extends State {
|
||||||
semanticContainer: true,
|
semanticContainer: true,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// TODO: show modalBottomSheet
|
// show modalBottomSheet
|
||||||
// with room information
|
// with room information
|
||||||
// and join button
|
// and join button
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
|
@ -202,7 +203,7 @@ class _JoinRoomPageState extends State {
|
||||||
children: [
|
children: [
|
||||||
Icon(room.visibility?.icon),
|
Icon(room.visibility?.icon),
|
||||||
Text((room
|
Text((room
|
||||||
.visibility?.text)!),
|
.visibility?.text(context))!),
|
||||||
]),
|
]),
|
||||||
])),
|
])),
|
||||||
// action buttons
|
// action buttons
|
||||||
|
@ -217,7 +218,7 @@ class _JoinRoomPageState extends State {
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
icon:
|
icon:
|
||||||
const Icon(Icons.close),
|
const Icon(Icons.close),
|
||||||
label: const Text('Cancel'),
|
label: Text(AppLocalizations.of(context)!.cancel),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// close sheet
|
// close sheet
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
@ -230,7 +231,7 @@ class _JoinRoomPageState extends State {
|
||||||
child: FilledButton.icon(
|
child: FilledButton.icon(
|
||||||
icon:
|
icon:
|
||||||
const Icon(Icons.check),
|
const Icon(Icons.check),
|
||||||
label: const Text('Join'),
|
label: Text(AppLocalizations.of(context)!.joinRoom),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final scaffMgr =
|
final scaffMgr =
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
|
@ -294,13 +295,13 @@ class _JoinRoomPageState extends State {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
label: const Text('New'),
|
label: Text(AppLocalizations.of(context)!.newRoom),
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// create new room
|
// create new room
|
||||||
context.goNamed('new-room');
|
context.goNamed('new-room');
|
||||||
},
|
},
|
||||||
tooltip: 'Create Room',
|
tooltip: AppLocalizations.of(context)!.createRoomShort,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:outbag_app/screens/room/pages/products.dart';
|
||||||
import 'package:outbag_app/screens/room/pages/list.dart';
|
import 'package:outbag_app/screens/room/pages/list.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class RoomPage extends StatefulWidget {
|
class RoomPage extends StatefulWidget {
|
||||||
final String server;
|
final String server;
|
||||||
|
@ -121,23 +122,27 @@ class _RoomPageState extends State<RoomPage> {
|
||||||
duration: const Duration(milliseconds: 300));
|
duration: const Duration(milliseconds: 300));
|
||||||
},
|
},
|
||||||
selectedIndex: page,
|
selectedIndex: page,
|
||||||
destinations: const [
|
destinations: [
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: Icon(Icons.list),
|
icon: const Icon(Icons.list),
|
||||||
label: "List",
|
label: AppLocalizations.of(context)!.roomListTitle,
|
||||||
tooltip: 'View shopping list'),
|
tooltip: AppLocalizations.of(context)!.roomListSubtitle
|
||||||
|
),
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: Icon(Icons.inventory_2),
|
icon: const Icon(Icons.inventory_2),
|
||||||
label: "Products",
|
label: AppLocalizations.of(context)!.roomProductsTitle,
|
||||||
tooltip: 'View saved items'),
|
tooltip: AppLocalizations.of(context)!.roomProductsSubtitle
|
||||||
|
),
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: Icon(Icons.category),
|
icon: const Icon(Icons.category),
|
||||||
label: "Categories",
|
label: AppLocalizations.of(context)!.roomCategoriesTitle,
|
||||||
tooltip: 'View categories'),
|
tooltip: AppLocalizations.of(context)!.roomCategoriesSubtitle
|
||||||
|
),
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: Icon(Icons.info_rounded),
|
icon: const Icon(Icons.info_rounded),
|
||||||
label: "About",
|
label: AppLocalizations.of(context)!.roomAboutTitle,
|
||||||
tooltip: 'View room info'),
|
tooltip: AppLocalizations.of(context)!.roomAboutSubtitle
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:outbag_app/backend/room.dart';
|
||||||
import 'package:outbag_app/backend/user.dart';
|
import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class ManageRoomMembersPage extends StatefulWidget {
|
class ManageRoomMembersPage extends StatefulWidget {
|
||||||
final String server;
|
final String server;
|
||||||
|
@ -95,15 +96,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Room Members (${list.length})'),
|
title: Text(
|
||||||
leading: IconButton(
|
AppLocalizations.of(context)!.roomMembersTitle(list.length)),
|
||||||
onPressed: () {
|
|
||||||
// go back
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
tooltip: "Go back",
|
|
||||||
),
|
|
||||||
//actions: [
|
//actions: [
|
||||||
// // NOTE: Maybe add a search icon
|
// // NOTE: Maybe add a search icon
|
||||||
// // and general search functionality here
|
// // and general search functionality here
|
||||||
|
@ -113,13 +107,13 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
final item = list[index];
|
final item = list[index];
|
||||||
|
|
||||||
String role = "Member";
|
String role = AppLocalizations.of(context)!.roleMember;
|
||||||
if (info != null &&
|
if (info != null &&
|
||||||
(info?.owner)! == item.id &&
|
(info?.owner)! == item.id &&
|
||||||
widget.server == item.serverTag) {
|
widget.server == item.serverTag) {
|
||||||
role = "Owner";
|
role = AppLocalizations.of(context)!.roleOwner;
|
||||||
} else if (item.isAdmin) {
|
} else if (item.isAdmin) {
|
||||||
role = "Admin";
|
role = AppLocalizations.of(context)!.roleAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool enable = true;
|
bool enable = true;
|
||||||
|
@ -172,11 +166,11 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.supervisor_account),
|
Icons.supervisor_account),
|
||||||
title: Text(item.isAdmin
|
title: Text(item.isAdmin
|
||||||
? 'Remove admin privileges'
|
? AppLocalizations.of(context)!.removeAdminTitle
|
||||||
: 'Make Admin'),
|
: AppLocalizations.of(context)!.makeAdminTitle),
|
||||||
subtitle: Text(item.isAdmin
|
subtitle: Text(item.isAdmin
|
||||||
? 'Revokes admin privileges from the user'
|
? AppLocalizations.of(context)!.removeAdminSubtitle
|
||||||
: 'Grants the user the permission to do everything'),
|
: AppLocalizations.of(context)!.makeAdminSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// make user admin
|
// make user admin
|
||||||
showDialog(
|
showDialog(
|
||||||
|
@ -189,12 +183,12 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
.supervisor_account),
|
.supervisor_account),
|
||||||
title: Text(item
|
title: Text(item
|
||||||
.isAdmin
|
.isAdmin
|
||||||
? 'Remove admin privileges'
|
? AppLocalizations.of(context)!.removeAdminTitle
|
||||||
: 'Make Admin'),
|
: AppLocalizations.of(context)!.makeAdminTitle),
|
||||||
content: Text(item
|
content: Text(item
|
||||||
.isAdmin
|
.isAdmin
|
||||||
? "Do you really want to remove ${item.humanReadableName}'s admin privileges"
|
? AppLocalizations.of(context)!.removeAdminConfirm(item.humanReadableName)
|
||||||
: 'Do you really want to make ${item.humanReadableName} admin?'),
|
: AppLocalizations.of(context)!.makeAdminConfirm(item.humanReadableName)),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
|
@ -205,8 +199,7 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.pop();
|
.pop();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: Text(AppLocalizations.of(context)!.cancel),
|
||||||
'Cancel'),
|
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
|
@ -238,8 +231,7 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: Text(AppLocalizations.of(context)!.ok),
|
||||||
'OK'),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
@ -257,10 +249,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.person_remove),
|
Icons.person_remove),
|
||||||
title:
|
title: Text(AppLocalizations.of(context)!.kickUserTitle),
|
||||||
const Text('Kick User'),
|
subtitle: Text(AppLocalizations.of(context)!.kickUserSubtitle),
|
||||||
subtitle: const Text(
|
|
||||||
"Temporarrily remove user from server (they'll be able to join the room again)"),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// remove user from room
|
// remove user from room
|
||||||
showDialog(
|
showDialog(
|
||||||
|
@ -271,10 +261,10 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons
|
Icons
|
||||||
.person_remove),
|
.person_remove),
|
||||||
title: const Text(
|
title: Text(
|
||||||
'Kick User'),
|
AppLocalizations.of(context)!.kickUserTitle),
|
||||||
content: Text(
|
content: Text(
|
||||||
'Do you really want to kick ${item.humanReadableName}?'),
|
AppLocalizations.of(context)!.kichUserConfirm(item.humanReadableName)),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
|
@ -286,8 +276,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
Navigator.of(ctx)
|
Navigator.of(ctx)
|
||||||
.pop();
|
.pop();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Cancel'),
|
AppLocalizations.of(context)!.cancel),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
|
@ -318,8 +308,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Kick User'),
|
AppLocalizations.of(context)!.ok),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
@ -331,7 +321,7 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
child: const Text('Close'),
|
child: Text(AppLocalizations.of(context)!.close),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:outbag_app/tools/snackbar.dart';
|
import 'package:outbag_app/tools/snackbar.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
class NewRoomPage extends StatefulWidget {
|
class NewRoomPage extends StatefulWidget {
|
||||||
|
@ -28,208 +29,226 @@ class _NewRoomPageState extends State {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final textTheme = Theme.of(context)
|
final textTheme = Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
||||||
|
|
||||||
double width = MediaQuery.of(context).size.width;
|
double width = MediaQuery.of(context).size.width;
|
||||||
double height = MediaQuery.of(context).size.height;
|
double height = MediaQuery.of(context).size.height;
|
||||||
double smallest = min(min(width, height), 400);
|
double smallest = min(min(width, height), 400);
|
||||||
|
|
||||||
return showSpinner
|
return showSpinner
|
||||||
? Scaffold(
|
? Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const CircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
Text('Loading', style: textTheme.titleLarge),
|
Text(AppLocalizations.of(context)!.loading,
|
||||||
])))
|
style: textTheme.titleLarge),
|
||||||
: Scaffold(
|
])))
|
||||||
appBar: AppBar(
|
: Scaffold(
|
||||||
title: const Text('New Room'),
|
appBar: AppBar(
|
||||||
),
|
title: Text(AppLocalizations.of(context)!.newRoom),
|
||||||
body: SingleChildScrollView(
|
),
|
||||||
child: Center(
|
body: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Center(
|
||||||
padding: const EdgeInsets.all(14),
|
child: Padding(
|
||||||
child: ConstrainedBox(
|
padding: const EdgeInsets.all(14),
|
||||||
constraints: const BoxConstraints(maxWidth: 400),
|
child: ConstrainedBox(
|
||||||
child: Column(
|
constraints: const BoxConstraints(maxWidth: 400),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
IconButton(
|
children: [
|
||||||
icon: SvgPicture.asset(
|
IconButton(
|
||||||
_ctrIcon.img,
|
icon: SvgPicture.asset(
|
||||||
width: smallest * 0.3,
|
_ctrIcon.img,
|
||||||
height: smallest * 0.3,
|
width: smallest * 0.3,
|
||||||
),
|
height: smallest * 0.3,
|
||||||
tooltip: 'Change room icon',
|
),
|
||||||
onPressed: () {
|
tooltip: AppLocalizations.of(context)!
|
||||||
showDialog(
|
.changeRoomIcon,
|
||||||
context: context,
|
onPressed: () {
|
||||||
builder: (ctx) => AlertDialog(
|
showDialog(
|
||||||
title: const Text(
|
context: context,
|
||||||
'Choose a room Icon'),
|
builder: (ctx) => AlertDialog(
|
||||||
actions: const [],
|
title: Text(
|
||||||
content: SizedBox(
|
AppLocalizations.of(context)!
|
||||||
width: smallest * 0.3 * 3,
|
.chooseRoomIcon),
|
||||||
height: smallest * 0.3 * 3,
|
actions: const [],
|
||||||
child: GridView.count(
|
content: SizedBox(
|
||||||
crossAxisCount: 3,
|
width: smallest * 0.3 * 3,
|
||||||
children: RoomIcon.list()
|
height: smallest * 0.3 * 3,
|
||||||
.map((icon) {
|
child: GridView.count(
|
||||||
return GridTile(
|
crossAxisCount: 3,
|
||||||
child: IconButton(
|
children: RoomIcon.list()
|
||||||
icon: SvgPicture
|
.map((icon) {
|
||||||
.asset(
|
return GridTile(
|
||||||
icon.img,
|
child: IconButton(
|
||||||
width:
|
icon: SvgPicture
|
||||||
smallest *
|
.asset(
|
||||||
0.3,
|
icon.img,
|
||||||
height:
|
width:
|
||||||
smallest *
|
smallest *
|
||||||
0.3,
|
0.3,
|
||||||
),
|
height:
|
||||||
tooltip:
|
smallest *
|
||||||
icon.text,
|
0.3,
|
||||||
onPressed: () {
|
),
|
||||||
setState(() {
|
// do not display tooltip for now
|
||||||
_ctrIcon =
|
// as it is hard to translate
|
||||||
icon;
|
// and the tooltip prevented the click event,
|
||||||
});
|
// when clicked on the tooltip bar
|
||||||
Navigator.of(
|
// tooltip:icon.text,
|
||||||
context)
|
onPressed: () {
|
||||||
.pop();
|
setState(() {
|
||||||
}));
|
_ctrIcon =
|
||||||
}).toList())),
|
icon;
|
||||||
));
|
});
|
||||||
},
|
Navigator.of(
|
||||||
),
|
context)
|
||||||
Padding(
|
.pop();
|
||||||
padding: const EdgeInsets.all(8),
|
}));
|
||||||
child: TextField(
|
}).toList())),
|
||||||
controller: _ctrID,
|
));
|
||||||
keyboardType: TextInputType.emailAddress,
|
},
|
||||||
decoration: const InputDecoration(
|
),
|
||||||
prefixIcon: Icon(Icons.fact_check),
|
Padding(
|
||||||
labelText: 'Room ID',
|
padding: const EdgeInsets.all(8),
|
||||||
hintText: 'Unique room id',
|
child: TextField(
|
||||||
helperText:
|
controller: _ctrID,
|
||||||
'the room id and server tag allow the room to be identified',
|
keyboardType: TextInputType.emailAddress,
|
||||||
border: OutlineInputBorder(),
|
decoration: InputDecoration(
|
||||||
),
|
prefixIcon: const Icon(Icons.fact_check),
|
||||||
),
|
labelText: AppLocalizations.of(context)!
|
||||||
),
|
.inputRoomIdLabel,
|
||||||
Padding(
|
hintText: AppLocalizations.of(context)!
|
||||||
padding: const EdgeInsets.all(8),
|
.inputRoomIdHint,
|
||||||
child: TextField(
|
helperText: AppLocalizations.of(context)!
|
||||||
controller: _ctrName,
|
.inputRoomIdHelp,
|
||||||
keyboardType: TextInputType.name,
|
border: const OutlineInputBorder(),
|
||||||
decoration: const InputDecoration(
|
),
|
||||||
prefixIcon: Icon(Icons.badge),
|
),
|
||||||
labelText: 'Room Name',
|
),
|
||||||
hintText: 'Give your room a name',
|
Padding(
|
||||||
helperText:
|
padding: const EdgeInsets.all(8),
|
||||||
'Easily identify a room with a human readable name',
|
child: TextField(
|
||||||
border: OutlineInputBorder(),
|
controller: _ctrName,
|
||||||
),
|
keyboardType: TextInputType.name,
|
||||||
),
|
decoration: InputDecoration(
|
||||||
),
|
prefixIcon: const Icon(Icons.badge),
|
||||||
Padding(
|
labelText: AppLocalizations.of(context)!
|
||||||
padding: const EdgeInsets.all(8),
|
.inputRoomNameLabel,
|
||||||
child: TextField(
|
hintText: AppLocalizations.of(context)!
|
||||||
controller: _ctrDescription,
|
.inputRoomNameHint,
|
||||||
keyboardType: TextInputType.text,
|
helperText: AppLocalizations.of(context)!
|
||||||
decoration: const InputDecoration(
|
.inputRoomNameHelp,
|
||||||
prefixIcon: Icon(Icons.dns),
|
border: const OutlineInputBorder(),
|
||||||
labelText: 'Room Description',
|
),
|
||||||
hintText: 'Briefly describe your Room',
|
),
|
||||||
helperText:
|
),
|
||||||
'Make it easier for other to know what this room is used for',
|
Padding(
|
||||||
border: OutlineInputBorder(),
|
padding: const EdgeInsets.all(8),
|
||||||
),
|
child: TextField(
|
||||||
),
|
controller: _ctrDescription,
|
||||||
),
|
keyboardType: TextInputType.text,
|
||||||
Text('Visibility', style: textTheme.labelLarge),
|
decoration: InputDecoration(
|
||||||
Text('Specify who has access to your room',
|
labelText: AppLocalizations.of(context)!
|
||||||
style: textTheme.bodySmall),
|
.inputRoomDescriptionLabel,
|
||||||
SegmentedButton<RoomVisibility>(
|
hintText: AppLocalizations.of(context)!
|
||||||
showSelectedIcon: true,
|
.inputRoomDescriptionHint,
|
||||||
multiSelectionEnabled: false,
|
helperText: AppLocalizations.of(context)!
|
||||||
emptySelectionAllowed: false,
|
.inputRoomDescriptionHelp,
|
||||||
segments: RoomVisibility.list().map((vis) {
|
prefixIcon: const Icon(Icons.dns),
|
||||||
return ButtonSegment<RoomVisibility>(
|
border: const OutlineInputBorder(),
|
||||||
value: vis,
|
),
|
||||||
label: Text(vis.text),
|
),
|
||||||
icon: Icon(vis.icon));
|
),
|
||||||
}).toList(),
|
Text(
|
||||||
onSelectionChanged: ((vset) {
|
AppLocalizations.of(context)!
|
||||||
setState(() {
|
.roomVisibilityTitle,
|
||||||
_ctrVis = vset.single;
|
style: textTheme.labelLarge),
|
||||||
});
|
Text(
|
||||||
}),
|
AppLocalizations.of(context)!
|
||||||
selected: {_ctrVis},
|
.roomVisibilitySubtitle,
|
||||||
selectedIcon: Icon(_ctrVis.icon),
|
style: textTheme.bodySmall),
|
||||||
),
|
SegmentedButton<RoomVisibility>(
|
||||||
],
|
showSelectedIcon: true,
|
||||||
))))),
|
multiSelectionEnabled: false,
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
emptySelectionAllowed: false,
|
||||||
onPressed: () async {
|
segments: RoomVisibility.list().map((vis) {
|
||||||
final scaffMgr = ScaffoldMessenger.of(context);
|
return ButtonSegment<RoomVisibility>(
|
||||||
final router = GoRouter.of(context);
|
value: vis,
|
||||||
|
label: Text(vis.text(context)),
|
||||||
|
icon: Icon(vis.icon));
|
||||||
|
}).toList(),
|
||||||
|
onSelectionChanged: ((vset) {
|
||||||
|
setState(() {
|
||||||
|
_ctrVis = vset.single;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
selected: {_ctrVis},
|
||||||
|
selectedIcon: Icon(_ctrVis.icon),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
))))),
|
||||||
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
onPressed: () async {
|
||||||
|
final scaffMgr = ScaffoldMessenger.of(context);
|
||||||
|
final router = GoRouter.of(context);
|
||||||
|
final trans = AppLocalizations.of(context);
|
||||||
|
|
||||||
// ID should be at least three characters long
|
// ID should be at least three characters long
|
||||||
if (_ctrID.text.length < 3) {
|
if (_ctrID.text.length < 3) {
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: _ctrID.text.isEmpty
|
text: _ctrID.text.isEmpty
|
||||||
? 'Please specify a Room ID'
|
? trans!.errorNoRoomId
|
||||||
: 'Room ID has to be at least three characters long',
|
: trans!.errorRoomIdLength,
|
||||||
action: 'OK');
|
action: trans.ok);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// name may not be empty
|
// name may not be empty
|
||||||
if (_ctrName.text.isEmpty) {
|
if (_ctrName.text.isEmpty) {
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: 'Please specify a room name', action: 'OK');
|
text: trans!.errorNoRoomName, action: trans.ok);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
final room = Room(
|
final room = Room(
|
||||||
id: _ctrID.text,
|
id: _ctrID.text,
|
||||||
serverTag: user.server.tag,
|
serverTag: user.server.tag,
|
||||||
name: _ctrName.text,
|
name: _ctrName.text,
|
||||||
description: _ctrDescription.text,
|
description: _ctrDescription.text,
|
||||||
icon: _ctrIcon,
|
icon: _ctrIcon,
|
||||||
visibility: _ctrVis);
|
visibility: _ctrVis);
|
||||||
|
|
||||||
doNetworkRequest(scaffMgr,
|
doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
target: user.server,
|
target: user.server,
|
||||||
credentials: user,
|
credentials: user,
|
||||||
path: 'createRoom',
|
path: 'createRoom',
|
||||||
body: {
|
body: {
|
||||||
'room': room.id,
|
'room': room.id,
|
||||||
'title': room.name,
|
'title': room.name,
|
||||||
'description': room.description,
|
'description': room.description,
|
||||||
'icon': room.icon?.type,
|
'icon': room.icon?.type,
|
||||||
'visibility': room.visibility?.type
|
'visibility': room.visibility?.type
|
||||||
}),
|
}),
|
||||||
onOK: (_) async {
|
onOK: (_) async {
|
||||||
// room was created
|
// room was created
|
||||||
// save room
|
// save room
|
||||||
await room.toDisk();
|
await room.toDisk();
|
||||||
// move to home page
|
// move to home page
|
||||||
router.pushReplacementNamed('home');
|
router.pushReplacementNamed('home');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
label: const Text('Create'),
|
label: Text(AppLocalizations.of(context)!.createRoomShort),
|
||||||
icon: const Icon(Icons.add)),
|
icon: const Icon(Icons.add)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:outbag_app/backend/user.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class AboutRoomPage extends StatefulWidget {
|
class AboutRoomPage extends StatefulWidget {
|
||||||
final RoomInfo? info;
|
final RoomInfo? info;
|
||||||
|
@ -67,7 +68,7 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
segments: RoomVisibility.list().map((vis) {
|
segments: RoomVisibility.list().map((vis) {
|
||||||
return ButtonSegment<int>(
|
return ButtonSegment<int>(
|
||||||
value: vis.type,
|
value: vis.type,
|
||||||
label: Text(vis.text),
|
label: Text(vis.text(context)),
|
||||||
icon: Icon(vis.icon));
|
icon: Icon(vis.icon));
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onSelectionChanged: ((vset) {
|
onSelectionChanged: ((vset) {
|
||||||
|
@ -90,16 +91,14 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title:
|
title: Text(AppLocalizations.of(context)!.changeRoomVisibilityTitle),
|
||||||
const Text('Change room visibility'),
|
content: Text(AppLocalizations.of(context)!.changeRoomVisibilitySubtitle(vis.text(context))),
|
||||||
content: Text(
|
|
||||||
'Do you really want to change the room visibility to: ${vis.text}'),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pop();
|
context.pop();
|
||||||
},
|
},
|
||||||
child: const Text('Cancel'),
|
child: Text(AppLocalizations.of(context)!.cancel),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -128,7 +127,7 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: const Text('Ok'),
|
child: Text(AppLocalizations.of(context)!.ok),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
@ -156,9 +155,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: const Text('Edit Metadata'),
|
title: Text(AppLocalizations.of(context)!.editRoomMetadata),
|
||||||
subtitle: const Text(
|
subtitle: Text(AppLocalizations.of(context)!.editRoomMetadataSubtitle),
|
||||||
'Change the rooms name, description and icon'),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// show edit room screen
|
// show edit room screen
|
||||||
context.goNamed('edit-room', params: {
|
context.goNamed('edit-room', params: {
|
||||||
|
@ -172,8 +170,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
// open members view
|
// open members view
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: const Text('Members'),
|
title: Text(AppLocalizations.of(context)!.showRoomMembers),
|
||||||
subtitle: const Text('Show Member list'),
|
subtitle: Text(AppLocalizations.of(context)!.showRoomMembersSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// open member view screen
|
// open member view screen
|
||||||
context.goNamed('room-members', params: {
|
context.goNamed('room-members', params: {
|
||||||
|
@ -192,9 +190,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: const Text('Edit Permissions'),
|
title: Text(AppLocalizations.of(context)!.editRoomPermissions),
|
||||||
subtitle: const Text(
|
subtitle: Text(AppLocalizations.of(context)!.editRoomPermissionsSubtitle),
|
||||||
'Change the default permission-set for all members'),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// show checkbox screen
|
// show checkbox screen
|
||||||
context.goNamed('room-permissions', params: {
|
context.goNamed('room-permissions', params: {
|
||||||
|
@ -213,8 +210,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: const Text('OTA'),
|
title: Text(AppLocalizations.of(context)!.manageRoomOTA),
|
||||||
subtitle: const Text('Add and delete OTAs'),
|
subtitle: Text(AppLocalizations.of(context)!.manageRoomOTASubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// show manage ota screen
|
// show manage ota screen
|
||||||
context.goNamed('room-ota', params: {
|
context.goNamed('room-ota', params: {
|
||||||
|
@ -225,8 +222,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: const Text('Invites'),
|
title: Text(AppLocalizations.of(context)!.manageRoomInvites),
|
||||||
subtitle: const Text('Invite people to this room'),
|
subtitle: Text(AppLocalizations.of(context)!.manageRoomInvitesSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// show manage ota screen
|
// show manage ota screen
|
||||||
context.goNamed('room-invite', params: {
|
context.goNamed('room-invite', params: {
|
||||||
|
@ -246,25 +243,26 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: FilledButton.tonal(
|
child: FilledButton.tonal(
|
||||||
child: Text(((widget.info?.isOwner)!)
|
child: Text(((widget.info?.isOwner)!)
|
||||||
? 'Delete Room'
|
? AppLocalizations.of(context)!.deleteRoom
|
||||||
: 'Leave Room'),
|
: AppLocalizations.of(context)!.leaveRoom),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show confirm dialog
|
// show confirm dialog
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: Text(((widget.info?.isOwner)!)
|
title: Text(((widget.info?.isOwner)!)
|
||||||
? 'Delete Room'
|
? AppLocalizations.of(context)!.deleteRoom
|
||||||
: 'Leave Room'),
|
: AppLocalizations.of(context)!.leaveRoom),
|
||||||
content: Text(
|
content: Text(((widget.info?.isOwner)!)
|
||||||
'Do you really want to ${((widget.info?.isOwner)!) ? "delete" : "leave"} the room?'),
|
? AppLocalizations.of(context)!.deleteRoomConfirm
|
||||||
|
: AppLocalizations.of(context)!.leaveRoomConfirm),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// close popup
|
// close popup
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
},
|
},
|
||||||
child: const Text('Cancel'),
|
child: Text(AppLocalizations.of(context)!.cancel),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -302,8 +300,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Text(((widget.info?.isOwner)!)
|
child: Text(((widget.info?.isOwner)!)
|
||||||
? 'Delete'
|
? AppLocalizations.of(context)!.deleteRoomShort
|
||||||
: 'Leave'),
|
: AppLocalizations.of(context)!.leaveRoomShort),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:outbag_app/backend/permissions.dart';
|
import 'package:outbag_app/backend/permissions.dart';
|
||||||
|
@ -31,10 +31,10 @@ class _EditRoomPermissionSetPageState extends State<EditRoomPermissionSetPage> {
|
||||||
doNetworkRequest(
|
doNetworkRequest(
|
||||||
sm,
|
sm,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
path: 'getRoomInfo',
|
path: 'getRoomInfo',
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
body: {'room': widget.tag, 'server': widget.server}),
|
body: {'room': widget.tag, 'server': widget.server}),
|
||||||
onAnyErr: () {
|
onAnyErr: () {
|
||||||
// user should not be here
|
// user should not be here
|
||||||
// close screen
|
// close screen
|
||||||
|
@ -44,7 +44,7 @@ class _EditRoomPermissionSetPageState extends State<EditRoomPermissionSetPage> {
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final info = RoomInfo.fromJSON(body['data']);
|
final info = RoomInfo.fromJSON(body['data']);
|
||||||
setState(() {
|
setState(() {
|
||||||
permissions = info.permissions;
|
permissions = info.permissions;
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -62,15 +62,7 @@ class _EditRoomPermissionSetPageState extends State<EditRoomPermissionSetPage> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Default permissions'),
|
title: Text(AppLocalizations.of(context)!.roomDefaultPermissions),
|
||||||
leading: IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
// go back
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
tooltip: "Go back",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
body: ListView.builder(
|
body: ListView.builder(
|
||||||
itemCount: items.length,
|
itemCount: items.length,
|
||||||
|
@ -80,20 +72,20 @@ class _EditRoomPermissionSetPageState extends State<EditRoomPermissionSetPage> {
|
||||||
final int col = pow(2, index + 1) as int;
|
final int col = pow(2, index + 1) as int;
|
||||||
|
|
||||||
return SwitchListTile(
|
return SwitchListTile(
|
||||||
title: Text(RoomPermission.name(item)),
|
title: Text(RoomPermission.name(item, context)),
|
||||||
subtitle: Text(RoomPermission.describe(item)),
|
subtitle: Text(RoomPermission.describe(item, context)),
|
||||||
onChanged: (state) {
|
onChanged: (state) {
|
||||||
setState(() {
|
setState(() {
|
||||||
permissions += (state ? 1 : -1) * col;
|
permissions += (state ? 1 : -1) * col;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
value: permissions & col != 0);
|
value: permissions & col != 0);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
icon: const Icon(Icons.edit),
|
icon: const Icon(Icons.edit),
|
||||||
tooltip: "Update default permission set",
|
tooltip: AppLocalizations.of(context)!.updateRoomPermissionsHint,
|
||||||
label: const Text('Edit'),
|
label: Text(AppLocalizations.of(context)!.updateRoomPermissions),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final router = GoRouter.of(context);
|
final router = GoRouter.of(context);
|
||||||
final sm = ScaffoldMessenger.of(context);
|
final sm = ScaffoldMessenger.of(context);
|
||||||
|
@ -101,18 +93,18 @@ class _EditRoomPermissionSetPageState extends State<EditRoomPermissionSetPage> {
|
||||||
|
|
||||||
// update permissions
|
// update permissions
|
||||||
doNetworkRequest(sm,
|
doNetworkRequest(sm,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
path: 'setRoomRight',
|
path: 'setRoomRight',
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
body: {
|
body: {
|
||||||
'room': widget.tag,
|
'room': widget.tag,
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'rights': permissions
|
'rights': permissions
|
||||||
}),
|
}),
|
||||||
onOK: (_) {
|
onOK: (_) {
|
||||||
router.pop();
|
router.pop();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:outbag_app/backend/request.dart';
|
||||||
import 'package:outbag_app/backend/user.dart';
|
import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:outbag_app/tools/snackbar.dart';
|
import 'package:outbag_app/tools/snackbar.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class ChangePasswordDialog extends StatefulWidget {
|
class ChangePasswordDialog extends StatefulWidget {
|
||||||
|
@ -21,59 +22,57 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Change Password'),
|
title: Text(AppLocalizations.of(context)!.changeThemeTitle),
|
||||||
icon: const Icon(Icons.password),
|
icon: const Icon(Icons.password),
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrOldPassword,
|
controller: _ctrOldPassword,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.lock),
|
prefixIcon: const Icon(Icons.lock),
|
||||||
labelText: 'Old Password',
|
labelText: AppLocalizations.of(context)!.inputOldPasswordLabel,
|
||||||
hintText: 'Your current password',
|
hintText: AppLocalizations.of(context)!.inputOldPasswordHint,
|
||||||
helperText:
|
helperText:AppLocalizations.of(context)!.inputOldPasswordHelp,
|
||||||
'For safety, you have to type your current passwort',
|
border: const OutlineInputBorder(),
|
||||||
border: OutlineInputBorder(),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.all(8),
|
||||||
padding: const EdgeInsets.all(8),
|
child: TextField(
|
||||||
child: TextField(
|
controller: _ctrNewPassword,
|
||||||
controller: _ctrNewPassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
obscureText: true,
|
||||||
obscureText: true,
|
decoration: InputDecoration(
|
||||||
decoration: const InputDecoration(
|
prefixIcon: const Icon(Icons.lock),
|
||||||
prefixIcon: Icon(Icons.lock),
|
labelText: AppLocalizations.of(context)!.inputNewPasswordLabel,
|
||||||
labelText: 'New Password',
|
hintText: AppLocalizations.of(context)!.inputNewPasswordHint,
|
||||||
hintText: 'Your new password',
|
helperText:AppLocalizations.of(context)!.inputNewPasswordHelp,
|
||||||
helperText: 'Password have to be at least six characters long',
|
border: const OutlineInputBorder(),
|
||||||
border: OutlineInputBorder(),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.all(8),
|
||||||
padding: const EdgeInsets.all(8),
|
child: TextField(
|
||||||
child: TextField(
|
controller: _ctrNewPasswordRepeat,
|
||||||
controller: _ctrNewPasswordRepeat,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
obscureText: true,
|
||||||
obscureText: true,
|
decoration: InputDecoration(
|
||||||
decoration: const InputDecoration(
|
prefixIcon: const Icon(Icons.lock),
|
||||||
prefixIcon: Icon(Icons.lock),
|
labelText: AppLocalizations.of(context)!.inputNewPasswordRepeatLabel,
|
||||||
labelText: 'Repeat new Password',
|
hintText: AppLocalizations.of(context)!.inputNewPasswordRepeatHint,
|
||||||
hintText: 'Type your new password again',
|
helperText:AppLocalizations.of(context)!.inputNewPasswordRepeatHelp,
|
||||||
helperText:
|
border: const OutlineInputBorder(),
|
||||||
'Type your new password again, to make sure you know it',
|
),
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
|
||||||
)),
|
)),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
|
@ -81,20 +80,21 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
||||||
// close popup
|
// close popup
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text('Cancel'),
|
child: Text(AppLocalizations.of(context)!.cancel),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final scaffMgr = ScaffoldMessenger.of(context);
|
final scaffMgr = ScaffoldMessenger.of(context);
|
||||||
final nav = Navigator.of(context);
|
final nav = Navigator.of(context);
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
|
final trans = AppLocalizations.of(context);
|
||||||
|
|
||||||
// validate password
|
// validate password
|
||||||
if (_ctrNewPassword.text.length < 6) {
|
if (_ctrNewPassword.text.length < 6) {
|
||||||
// password has to be at least 6 characters long
|
// password has to be at least 6 characters long
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: 'Password has to be at least 6 characters longs',
|
text: trans!.errorPasswordLength,
|
||||||
action: 'Dismiss');
|
action: trans.dismiss);
|
||||||
|
|
||||||
_ctrNewPasswordRepeat.clear();
|
_ctrNewPasswordRepeat.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -102,7 +102,7 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
||||||
if (_ctrNewPassword.text != _ctrNewPasswordRepeat.text) {
|
if (_ctrNewPassword.text != _ctrNewPasswordRepeat.text) {
|
||||||
// new passwords do not match
|
// new passwords do not match
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: 'New passwords do not match', action: 'Dismiss');
|
text: trans!.errorPasswordsDoNotMatch, action: trans.dismiss);
|
||||||
|
|
||||||
_ctrNewPasswordRepeat.clear();
|
_ctrNewPasswordRepeat.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -110,7 +110,7 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
||||||
if (hashPassword(_ctrOldPassword.text) != user.password) {
|
if (hashPassword(_ctrOldPassword.text) != user.password) {
|
||||||
// current password wrong
|
// current password wrong
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: 'Old password is wrong', action: 'Dismiss');
|
text: trans!.errorOldPasswordWrong, action: trans.dismiss);
|
||||||
|
|
||||||
_ctrOldPassword.clear();
|
_ctrOldPassword.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -120,25 +120,25 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
||||||
|
|
||||||
// send request
|
// send request
|
||||||
doNetworkRequest(scaffMgr,
|
doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
path: 'changePassword',
|
path: 'changePassword',
|
||||||
target: user.server,
|
target: user.server,
|
||||||
body: {'accountKey': password},
|
body: {'accountKey': password},
|
||||||
credentials: user),
|
credentials: user),
|
||||||
onOK: (_) async {
|
onOK: (_) async {
|
||||||
// update local user struct
|
// update local user struct
|
||||||
final updatedUser = User(
|
final updatedUser = User(
|
||||||
username: user.username,
|
username: user.username,
|
||||||
password: password,
|
password: password,
|
||||||
server: user.server);
|
server: user.server);
|
||||||
await updatedUser.toDisk();
|
await updatedUser.toDisk();
|
||||||
},
|
},
|
||||||
after: () {
|
after: () {
|
||||||
// close popup
|
// close popup
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: const Text('Change password'),
|
child: Text(AppLocalizations.of(context)!.changeThemeTitle),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:outbag_app/backend/user.dart';
|
||||||
import 'package:outbag_app/screens/settings/dialogs/password.dart';
|
import 'package:outbag_app/screens/settings/dialogs/password.dart';
|
||||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class SettingsPage extends StatefulWidget {
|
class SettingsPage extends StatefulWidget {
|
||||||
const SettingsPage({super.key});
|
const SettingsPage({super.key});
|
||||||
|
@ -26,7 +27,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Settings'),
|
title: Text(AppLocalizations.of(context)!.settings),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
|
@ -44,51 +45,58 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
child: Text(user.humanReadable,
|
child: Text(user.humanReadable,
|
||||||
style: textTheme.titleLarge)),
|
style: textTheme.titleLarge)),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Room count limit:'),
|
title: Text(
|
||||||
subtitle: const Text(
|
AppLocalizations.of(context)!.limitRoomCount),
|
||||||
'How many rooms you are allowed to own'),
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
|
.limitRoomCountSubtitle),
|
||||||
trailing: Text('${meta?.maxRoomCount ?? ""}'),
|
trailing: Text('${meta?.maxRoomCount ?? ""}'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Room size limit:'),
|
title: Text(
|
||||||
subtitle: const Text(
|
AppLocalizations.of(context)!.limitRoomSize),
|
||||||
'How many items/products/categories each room may contain'),
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
|
.limitRoomSizeSubtitle),
|
||||||
trailing: Text('${meta?.maxRoomSize ?? ""}'),
|
trailing: Text('${meta?.maxRoomSize ?? ""}'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Room member limit:'),
|
title: Text(AppLocalizations.of(context)!
|
||||||
subtitle: const Text(
|
.limitRoomMemberCount),
|
||||||
'How many members each of your rooms may have'),
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
|
.limitRoomMemberCountSubtitle),
|
||||||
trailing:
|
trailing:
|
||||||
Text('${meta?.maxRoomMemberCount ?? ""}')),
|
Text('${meta?.maxRoomMemberCount ?? ""}')),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Discoverable'),
|
title: Text(AppLocalizations.of(context)!
|
||||||
subtitle: const Text(
|
.userDiscoverable),
|
||||||
'Determines if your account can be discovered by users from other servers'),
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
|
.userDiscoverableSubtitle),
|
||||||
trailing: Checkbox(
|
trailing: Checkbox(
|
||||||
tristate: true,
|
tristate: true,
|
||||||
value: meta?.discoverable,
|
value: meta?.discoverable,
|
||||||
onChanged: (_) {},
|
onChanged: (_) {
|
||||||
|
// TODO: implement changeVisibility
|
||||||
|
},
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
)))),
|
)))),
|
||||||
|
|
||||||
// change theme button
|
// change theme button
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Change Theme'),
|
title: Text(AppLocalizations.of(context)!.changeThemeTitle),
|
||||||
subtitle: const Text(
|
subtitle: Text(AppLocalizations.of(context)!.changeThemeSubtitle),
|
||||||
'You can change between a light theme, a dark theme and automatic theme selection'),
|
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Change Theme'),
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!.changeThemeTitle),
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Text('Choose your preferred theme'),
|
child: Text(AppLocalizations.of(context)!
|
||||||
|
.changeThemeSubtitle),
|
||||||
),
|
),
|
||||||
SegmentedButton<AppTheme>(
|
SegmentedButton<AppTheme>(
|
||||||
selected: {context.watch<AppTheme>()},
|
selected: {context.watch<AppTheme>()},
|
||||||
|
@ -100,7 +108,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
return ButtonSegment<AppTheme>(
|
return ButtonSegment<AppTheme>(
|
||||||
value: item,
|
value: item,
|
||||||
icon: Icon(item.icon),
|
icon: Icon(item.icon),
|
||||||
label: Text(item.name));
|
label: Text(item.name(context)));
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onSelectionChanged: (item) async {
|
onSelectionChanged: (item) async {
|
||||||
try {
|
try {
|
||||||
|
@ -111,9 +119,9 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
])),
|
])),
|
||||||
actions: [
|
actions: [
|
||||||
FilledButton(
|
FilledButton(
|
||||||
child: const Text('Close'),
|
child: Text(AppLocalizations.of(context)!.close),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
context.pop();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -123,8 +131,9 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
|
|
||||||
// change password button
|
// change password button
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Change password'),
|
title: Text(AppLocalizations.of(context)!.changePasswordTitle),
|
||||||
subtitle: const Text('Choose a new password for your account'),
|
subtitle:
|
||||||
|
Text(AppLocalizations.of(context)!.changePasswordSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -135,8 +144,8 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
|
|
||||||
// export account to json
|
// export account to json
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Export account'),
|
title: Text(AppLocalizations.of(context)!.exportAccountTitle),
|
||||||
subtitle: const Text('Export account data'),
|
subtitle: Text(AppLocalizations.of(context)!.exportAccountSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// TODO: show confirm dialog
|
// TODO: show confirm dialog
|
||||||
// NOTE: json dump the localstore
|
// NOTE: json dump the localstore
|
||||||
|
@ -148,8 +157,8 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
|
|
||||||
// delete account button
|
// delete account button
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Delete account'),
|
title: Text(AppLocalizations.of(context)!.deleteAccountTitle),
|
||||||
subtitle: const Text('Delete your account from your homeserver'),
|
subtitle: Text(AppLocalizations.of(context)!.deleteAccountSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// show confirm dialog
|
// show confirm dialog
|
||||||
// NOTE: same as logout
|
// NOTE: same as logout
|
||||||
|
@ -158,16 +167,17 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('Delete account'),
|
title: Text(
|
||||||
content: const Text(
|
AppLocalizations.of(context)!.deleteAccountTitle),
|
||||||
'Do you really want to delete your account?'),
|
content: Text(
|
||||||
|
AppLocalizations.of(context)!.deleteAccountConfirm),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// close popup
|
// close popup
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
},
|
},
|
||||||
child: const Text('Cancel'),
|
child: Text(AppLocalizations.of(context)!.cancel),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -198,7 +208,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: const Text('Delete Account'),
|
child: Text(AppLocalizations.of(context)!.yes),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
@ -210,22 +220,23 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: FilledButton.tonal(
|
child: FilledButton.tonal(
|
||||||
child: const Text('Log out'),
|
child: Text(AppLocalizations.of(context)!.logOut),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show confirm dialog
|
// show confirm dialog
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('Log out'),
|
title: Text(AppLocalizations.of(context)!.logOut),
|
||||||
content:
|
content: Text(
|
||||||
const Text('Do you really want to log out?'),
|
AppLocalizations.of(context)!.logOutConfirm),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// close popup
|
// close popup
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
},
|
},
|
||||||
child: const Text('Cancel'),
|
child:
|
||||||
|
Text(AppLocalizations.of(context)!.cancel),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -242,7 +253,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
// go back home
|
// go back home
|
||||||
router.pushReplacementNamed('home');
|
router.pushReplacementNamed('home');
|
||||||
},
|
},
|
||||||
child: const Text('Log out'),
|
child: Text(AppLocalizations.of(context)!.yes),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:outbag_app/tools/assets.dart';
|
import 'package:outbag_app/tools/assets.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
class WelcomePage extends StatefulWidget {
|
class WelcomePage extends StatefulWidget {
|
||||||
|
@ -33,17 +34,17 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
.textTheme
|
.textTheme
|
||||||
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
||||||
|
|
||||||
String fabText = "Next";
|
String fabText = AppLocalizations.of(context)!.next;
|
||||||
if (_currentPage == 0) {
|
if (_currentPage == 0) {
|
||||||
fabText = "Let's go";
|
fabText = AppLocalizations.of(context)!.letsGo;
|
||||||
} else if (_currentPage == 4 - 1) {
|
} else if (_currentPage == 4 - 1) {
|
||||||
fabText = "Sign up";
|
fabText = AppLocalizations.of(context)!.signUp;
|
||||||
}
|
}
|
||||||
String fabTooltip = "Continue Tour";
|
String fabTooltip = AppLocalizations.of(context)!.continueTour;
|
||||||
if (_currentPage == 0) {
|
if (_currentPage == 0) {
|
||||||
fabTooltip = "Take Tour";
|
fabTooltip = AppLocalizations.of(context)!.takeTour;
|
||||||
} else if (_currentPage == 4 - 1) {
|
} else if (_currentPage == 4 - 1) {
|
||||||
fabTooltip = "Create an account";
|
fabTooltip = AppLocalizations.of(context)!.createNewAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
double width = MediaQuery.of(context).size.width;
|
double width = MediaQuery.of(context).size.width;
|
||||||
|
@ -66,10 +67,13 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
width: smallest * 0.5,
|
width: smallest * 0.5,
|
||||||
height: smallest * 0.5),
|
height: smallest * 0.5),
|
||||||
Text(
|
Text(
|
||||||
'Welcome to Outbag',
|
AppLocalizations.of(context)!.welcomeTitle,
|
||||||
style: textTheme.displaySmall,
|
style: textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
Text('Shopping lists made easy', style: textTheme.bodyMedium)
|
Text(
|
||||||
|
AppLocalizations.of(context)!.welcomeSubtitle,
|
||||||
|
style: textTheme.bodyMedium
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
|
@ -81,10 +85,11 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
width: smallest * 0.5,
|
width: smallest * 0.5,
|
||||||
height: smallest * 0.5),
|
height: smallest * 0.5),
|
||||||
Text(
|
Text(
|
||||||
'Open. Decentralized',
|
AppLocalizations.of(context)!.page2Title,
|
||||||
style: textTheme.displaySmall,
|
style: textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
Text('One account, multiple servers',
|
Text(
|
||||||
|
AppLocalizations.of(context)!.page2Subtitle,
|
||||||
style: textTheme.bodyMedium)
|
style: textTheme.bodyMedium)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -97,10 +102,11 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
width: smallest * 0.5,
|
width: smallest * 0.5,
|
||||||
height: smallest * 0.5),
|
height: smallest * 0.5),
|
||||||
Text(
|
Text(
|
||||||
'Made to share',
|
AppLocalizations.of(context)!.page3Title,
|
||||||
style: textTheme.displaySmall,
|
style: textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
Text('Collaborate on your shopping list in real time',
|
Text(
|
||||||
|
AppLocalizations.of(context)!.page3Subtitle,
|
||||||
style: textTheme.bodyMedium)
|
style: textTheme.bodyMedium)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -113,10 +119,11 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
width: smallest * 0.5,
|
width: smallest * 0.5,
|
||||||
height: smallest * 0.5),
|
height: smallest * 0.5),
|
||||||
Text(
|
Text(
|
||||||
'Pocket-sized',
|
AppLocalizations.of(context)!.page4Title,
|
||||||
style: textTheme.displaySmall,
|
style: textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
Text('Always have your shopping list with you',
|
Text(
|
||||||
|
AppLocalizations.of(context)!.page4Subtitle,
|
||||||
style: textTheme.bodyMedium)
|
style: textTheme.bodyMedium)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -126,7 +133,8 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.goNamed('signin');
|
context.goNamed('signin');
|
||||||
},
|
},
|
||||||
child: const Text('I already have an account'),
|
child: Text(AppLocalizations.of(context)!.userHasAnAccount
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:outbag_app/backend/errors.dart';
|
import 'package:outbag_app/backend/errors.dart';
|
||||||
import 'package:outbag_app/backend/request.dart';
|
import 'package:outbag_app/backend/request.dart';
|
||||||
import 'package:outbag_app/tools/snackbar.dart';
|
import 'package:outbag_app/tools/snackbar.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
void doNetworkRequest(ScaffoldMessengerState? sm,
|
void doNetworkRequest(ScaffoldMessengerState? sm,
|
||||||
{required Future<Response> Function() req,
|
{required Future<Response> Function() req,
|
||||||
|
@ -11,6 +12,8 @@ void doNetworkRequest(ScaffoldMessengerState? sm,
|
||||||
Function()? onAnyErr,
|
Function()? onAnyErr,
|
||||||
Function()? after,
|
Function()? after,
|
||||||
bool Function(Map<String, dynamic>)? onServerErr}) async {
|
bool Function(Map<String, dynamic>)? onServerErr}) async {
|
||||||
|
AppLocalizations? trans = (sm!=null)?AppLocalizations.of(sm.context):null;
|
||||||
|
|
||||||
Response res;
|
Response res;
|
||||||
try {
|
try {
|
||||||
res = await req();
|
res = await req();
|
||||||
|
@ -23,7 +26,7 @@ void doNetworkRequest(ScaffoldMessengerState? sm,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showBar && sm != null) {
|
if (showBar && sm != null) {
|
||||||
showSimpleSnackbar(sm, text: 'Network Error', action: 'Dismiss');
|
showSimpleSnackbar(sm, text: trans!.errorNetwork, action: trans.dismiss);
|
||||||
}
|
}
|
||||||
if (onAnyErr != null) {
|
if (onAnyErr != null) {
|
||||||
onAnyErr();
|
onAnyErr();
|
||||||
|
@ -47,7 +50,7 @@ void doNetworkRequest(ScaffoldMessengerState? sm,
|
||||||
showBar = onServerErr(res.body);
|
showBar = onServerErr(res.body);
|
||||||
}
|
}
|
||||||
if (showBar && sm != null) {
|
if (showBar && sm != null) {
|
||||||
showSimpleSnackbar(sm, text: errorAsString(res.body), action: 'OK');
|
showSimpleSnackbar(sm, text: errorAsString(res.body, trans!), action: trans.ok);
|
||||||
}
|
}
|
||||||
if (onAnyErr != null) {
|
if (onAnyErr != null) {
|
||||||
onAnyErr();
|
onAnyErr();
|
||||||
|
@ -61,7 +64,7 @@ void doNetworkRequest(ScaffoldMessengerState? sm,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showBar && sm != null) {
|
if (showBar && sm != null) {
|
||||||
showSimpleSnackbar(sm, text: 'Unknown Error', action: 'OK');
|
showSimpleSnackbar(sm, text: trans!.errorUnknown, action: trans.ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
pubspec.lock
13
pubspec.lock
|
@ -134,6 +134,11 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_svg:
|
flutter_svg:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -184,6 +189,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.15"
|
version: "4.0.15"
|
||||||
|
intl:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.0"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -36,6 +36,9 @@ dependencies:
|
||||||
crypto: ^3.0.2
|
crypto: ^3.0.2
|
||||||
provider: ^6.0.5
|
provider: ^6.0.5
|
||||||
go_router: ^6.5.0
|
go_router: ^6.5.0
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
|
intl: any
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -54,7 +57,8 @@ dev_dependencies:
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
# The following section is specific to Flutter packages.
|
||||||
flutter:
|
flutter:
|
||||||
|
# needed for l10n localizations
|
||||||
|
generate: true
|
||||||
# The following line ensures that the Material Icons font is
|
# The following line ensures that the Material Icons font is
|
||||||
# included with your application, so that you can use the icons in
|
# included with your application, so that you can use the icons in
|
||||||
# the material Icons class.
|
# the material Icons class.
|
||||||
|
|
Loading…
Reference in a new issue