Formatted files using dart format
This commit is contained in:
parent
1975d66419
commit
b320d51fa1
28 changed files with 2466 additions and 2422 deletions
|
@ -107,19 +107,19 @@ class RoomPermission {
|
||||||
|
|
||||||
switch (permission) {
|
switch (permission) {
|
||||||
case 1:
|
case 1:
|
||||||
return trans!.roomPermissionAddItems;
|
return trans!.roomPermissionAddItems;
|
||||||
case 2:
|
case 2:
|
||||||
return trans!.roomPermissionRemoveItems;
|
return trans!.roomPermissionRemoveItems;
|
||||||
case 4:
|
case 4:
|
||||||
return trans!.roomPermissionEditContent;
|
return trans!.roomPermissionEditContent;
|
||||||
case 8:
|
case 8:
|
||||||
return trans!.roomPermissionChangeMeta;
|
return trans!.roomPermissionChangeMeta;
|
||||||
case 16:
|
case 16:
|
||||||
return trans!.roomPermissionManageOTA;
|
return trans!.roomPermissionManageOTA;
|
||||||
case 32:
|
case 32:
|
||||||
return trans!.roomPermissionManageAdmins;
|
return trans!.roomPermissionManageAdmins;
|
||||||
case 64:
|
case 64:
|
||||||
return trans!.roomPermissionManageMembers;
|
return trans!.roomPermissionManageMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
return trans!.roomPermissionUnknown;
|
return trans!.roomPermissionUnknown;
|
||||||
|
@ -130,19 +130,19 @@ class RoomPermission {
|
||||||
|
|
||||||
switch (permission) {
|
switch (permission) {
|
||||||
case 1:
|
case 1:
|
||||||
return trans!.roomPermissionAddItemsSubtitle;
|
return trans!.roomPermissionAddItemsSubtitle;
|
||||||
case 2:
|
case 2:
|
||||||
return trans!.roomPermissionRemoveItemsSubtitle;
|
return trans!.roomPermissionRemoveItemsSubtitle;
|
||||||
case 4:
|
case 4:
|
||||||
return trans!.roomPermissionEditContentSubtitle;
|
return trans!.roomPermissionEditContentSubtitle;
|
||||||
case 8:
|
case 8:
|
||||||
return trans!.roomPermissionChangeMetaSubtitle;
|
return trans!.roomPermissionChangeMetaSubtitle;
|
||||||
case 16:
|
case 16:
|
||||||
return trans!.roomPermissionManageOTASubtitle;
|
return trans!.roomPermissionManageOTASubtitle;
|
||||||
case 32:
|
case 32:
|
||||||
return trans!.roomPermissionManageAdminsSubtitle;
|
return trans!.roomPermissionManageAdminsSubtitle;
|
||||||
case 64:
|
case 64:
|
||||||
return trans!.roomPermissionManageMembersSubtitle;
|
return trans!.roomPermissionManageMembersSubtitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
return trans!.roomPermissionUnknownSubtitle;
|
return trans!.roomPermissionUnknownSubtitle;
|
||||||
|
|
|
@ -13,19 +13,19 @@ class Response {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Response> usePostApi(
|
Future<Response> usePostApi(
|
||||||
{required OutbagServer target,
|
{required OutbagServer target,
|
||||||
String path = '',
|
String path = '',
|
||||||
required Map<String, String> headers,
|
required Map<String, String> headers,
|
||||||
required Map<String, dynamic> body}) async {
|
required Map<String, dynamic> body}) async {
|
||||||
final resp = await http.post(Uri.parse('${target.base}api/$path'),
|
final resp = await http.post(Uri.parse('${target.base}api/$path'),
|
||||||
headers: headers, body: jsonEncode({'data': body}));
|
headers: headers, body: jsonEncode({'data': body}));
|
||||||
final json = jsonDecode(resp.body);
|
final json = jsonDecode(resp.body);
|
||||||
return Response(
|
return Response(
|
||||||
body: json, res: resp.statusCode == 200 ? Result.ok : Result.err);
|
body: json, res: resp.statusCode == 200 ? Result.ok : Result.err);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Response> postWithCreadentials(
|
Future<Response> postWithCreadentials(
|
||||||
{required OutbagServer target,
|
{required OutbagServer target,
|
||||||
String path = '',
|
String path = '',
|
||||||
required Map<String, dynamic> body,
|
required Map<String, dynamic> body,
|
||||||
required User credentials}) async {
|
required User credentials}) async {
|
||||||
|
@ -33,14 +33,14 @@ Future<Response> postWithCreadentials(
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
'Connection': 'keep-alive',
|
'Connection': 'keep-alive',
|
||||||
'Authorization':
|
'Authorization':
|
||||||
'Digest name=${credentials.username} server=${target.tag} accountKey=${credentials.password}'
|
'Digest name=${credentials.username} server=${target.tag} accountKey=${credentials.password}'
|
||||||
};
|
};
|
||||||
return await usePostApi(
|
return await usePostApi(
|
||||||
target: target, path: path, headers: headers, body: body);
|
target: target, path: path, headers: headers, body: body);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Response> postWithToken(
|
Future<Response> postWithToken(
|
||||||
{required OutbagServer target,
|
{required OutbagServer target,
|
||||||
String path = '',
|
String path = '',
|
||||||
required Map<String, dynamic> body,
|
required Map<String, dynamic> body,
|
||||||
required String token}) async {
|
required String token}) async {
|
||||||
|
@ -49,16 +49,16 @@ Future<Response> postWithToken(
|
||||||
'Authorization': 'Bearer $token'
|
'Authorization': 'Bearer $token'
|
||||||
};
|
};
|
||||||
return await usePostApi(
|
return await usePostApi(
|
||||||
target: target, path: path, headers: headers, body: body);
|
target: target, path: path, headers: headers, body: body);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Response> postUnauthorized(
|
Future<Response> postUnauthorized(
|
||||||
{required OutbagServer target,
|
{required OutbagServer target,
|
||||||
String path = '',
|
String path = '',
|
||||||
required Map<String, dynamic> body}) async {
|
required Map<String, dynamic> body}) async {
|
||||||
Map<String, String> headers = {
|
Map<String, String> headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
};
|
};
|
||||||
return await usePostApi(
|
return await usePostApi(
|
||||||
target: target, path: path, headers: headers, body: body);
|
target: target, path: path, headers: headers, body: body);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,11 +61,10 @@ class OutbagServer {
|
||||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
return OutbagServer(
|
return OutbagServer(
|
||||||
path: prefs.getString('server-path')!,
|
path: prefs.getString('server-path')!,
|
||||||
port: prefs.getInt('server-port')!,
|
port: prefs.getInt('server-port')!,
|
||||||
tag: prefs.getString('server-tag')!,
|
tag: prefs.getString('server-tag')!,
|
||||||
host: prefs.getString('server-host')!
|
host: prefs.getString('server-host')!);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> removeDisk() async {
|
static Future<void> removeDisk() async {
|
||||||
|
|
|
@ -322,11 +322,17 @@ class RoomMember {
|
||||||
final bool isInvitePending;
|
final bool isInvitePending;
|
||||||
|
|
||||||
const RoomMember(
|
const RoomMember(
|
||||||
{required this.id, required this.serverTag, required this.isAdmin, this.isInvitePending=false});
|
{required this.id,
|
||||||
|
required this.serverTag,
|
||||||
|
required this.isAdmin,
|
||||||
|
this.isInvitePending = false});
|
||||||
|
|
||||||
factory RoomMember.fromJSON(dynamic json) {
|
factory RoomMember.fromJSON(dynamic json) {
|
||||||
return RoomMember(
|
return RoomMember(
|
||||||
id: json['name'], serverTag: json['server'], isAdmin: json['admin'], isInvitePending: json['confirmed']);
|
id: json['name'],
|
||||||
|
serverTag: json['server'],
|
||||||
|
isAdmin: json['admin'],
|
||||||
|
isInvitePending: json['confirmed']);
|
||||||
}
|
}
|
||||||
|
|
||||||
String get humanReadableName {
|
String get humanReadableName {
|
||||||
|
|
|
@ -9,7 +9,8 @@ class CategoryChip extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ActionChip(
|
return ActionChip(
|
||||||
avatar: Icon(Icons.square_rounded, color: category?.color ?? RoomCategory.other(context).color),
|
avatar: Icon(Icons.square_rounded,
|
||||||
|
color: category?.color ?? RoomCategory.other(context).color),
|
||||||
label: Text(category?.name ?? RoomCategory.other(context).name),
|
label: Text(category?.name ?? RoomCategory.other(context).name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ class CategoryPicker extends StatelessWidget {
|
||||||
final String? label;
|
final String? label;
|
||||||
|
|
||||||
const CategoryPicker(
|
const CategoryPicker(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.categories,
|
required this.categories,
|
||||||
this.selected,
|
this.selected,
|
||||||
this.onSelect,
|
this.onSelect,
|
||||||
|
@ -23,36 +23,34 @@ class CategoryPicker extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: DropdownButtonFormField<int?>(
|
child: DropdownButtonFormField<int?>(
|
||||||
hint: hint==null?null:Text(hint!),
|
hint: hint == null ? null : Text(hint!),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: label==null?null:Text(label!),
|
label: label == null ? null : Text(label!),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
prefixIcon: const Icon(Icons.category)
|
prefixIcon: const Icon(Icons.category)),
|
||||||
),
|
value: selected,
|
||||||
value: selected,
|
items: [...categories, RoomCategory.other(context)]
|
||||||
items: [
|
.map((category) => DropdownMenuItem<int?>(
|
||||||
...categories,
|
value: category.id,
|
||||||
RoomCategory.other(context)
|
child: Row(
|
||||||
].map((category)=>DropdownMenuItem<int?>(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
value: category.id,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
child: Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
Text(category.name),
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
Icon(Icons.square_rounded,
|
||||||
children: [
|
color: category.color, size: 32)
|
||||||
Text(category.name),
|
]),
|
||||||
Icon(Icons.square_rounded,
|
))
|
||||||
color:category.color,
|
.toList(),
|
||||||
size: 32)
|
onChanged: enabled
|
||||||
]
|
? (cid) {
|
||||||
),
|
if (onSelect != null) {
|
||||||
)).toList(),
|
onSelect!(cid);
|
||||||
onChanged: enabled?(cid) {
|
}
|
||||||
if (onSelect != null) {
|
}
|
||||||
onSelect!(cid);
|
: null,
|
||||||
}
|
));
|
||||||
}:null,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,10 @@ class LabeledDivider extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(children: [
|
||||||
children: [
|
const Expanded(child: Divider()),
|
||||||
const Expanded(child: Divider()),
|
Padding(padding: const EdgeInsets.all(8), child: Text(label)),
|
||||||
Padding(
|
const Expanded(child: Divider()),
|
||||||
padding: const EdgeInsets.all(8),
|
]);
|
||||||
child: Text(label)),
|
|
||||||
const Expanded(child: Divider()),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ class ProductPicker extends StatelessWidget {
|
||||||
final String? help;
|
final String? help;
|
||||||
|
|
||||||
const ProductPicker(
|
const ProductPicker(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.products,
|
required this.products,
|
||||||
this.selected,
|
this.selected,
|
||||||
this.onSelect,
|
this.onSelect,
|
||||||
|
@ -26,32 +26,32 @@ class ProductPicker extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: DropdownButtonFormField<int?>(
|
child: DropdownButtonFormField<int?>(
|
||||||
hint: hint == null ? null : Text(hint!),
|
hint: hint == null ? null : Text(hint!),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: label == null ? null : Text(label!),
|
label: label == null ? null : Text(label!),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
prefixIcon: const Icon(Icons.inventory_2),
|
prefixIcon: const Icon(Icons.inventory_2),
|
||||||
helperText: help),
|
helperText: help),
|
||||||
value: selected,
|
value: selected,
|
||||||
items: [
|
items: [
|
||||||
// "no product" entry
|
// "no product" entry
|
||||||
DropdownMenuItem<int?>(
|
DropdownMenuItem<int?>(
|
||||||
value: null,
|
value: null,
|
||||||
child: Text(AppLocalizations.of(context)!.productNameNone),
|
child: Text(AppLocalizations.of(context)!.productNameNone),
|
||||||
),
|
),
|
||||||
// other products
|
// other products
|
||||||
...products.map((product) => DropdownMenuItem<int?>(
|
...products.map((product) => DropdownMenuItem<int?>(
|
||||||
value: product.id, child: Text(product.name)))
|
value: product.id, child: Text(product.name)))
|
||||||
],
|
],
|
||||||
onChanged: enabled
|
onChanged: enabled
|
||||||
? (pid) {
|
? (pid) {
|
||||||
if (onSelect != null) {
|
if (onSelect != null) {
|
||||||
onSelect!(pid);
|
onSelect!(pid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,12 +39,7 @@ class RoomIconPicker extends StatelessWidget {
|
||||||
if (onSelect != null) {
|
if (onSelect != null) {
|
||||||
onSelect!(icon);
|
onSelect!(icon);
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
)
|
}).toList())));
|
||||||
);
|
|
||||||
}).toList()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
439
lib/main.dart
439
lib/main.dart
|
@ -25,15 +25,16 @@ import 'package:outbag_app/screens/settings/main.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
runApp(const OutbagApp());
|
runApp(const OutbagApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
final GlobalKey<NavigatorState> _rootNavigatorKey =
|
final GlobalKey<NavigatorState> _rootNavigatorKey =
|
||||||
GlobalKey<NavigatorState>(debugLabel: 'root');
|
GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||||
final GlobalKey<NavigatorState> _userShellNavigatorKey =
|
final GlobalKey<NavigatorState> _userShellNavigatorKey =
|
||||||
GlobalKey<NavigatorState>(debugLabel: 'user');
|
GlobalKey<NavigatorState>(debugLabel: 'user');
|
||||||
|
|
||||||
class OutbagApp extends StatefulWidget {
|
class OutbagApp extends StatefulWidget {
|
||||||
const OutbagApp({super.key});
|
const OutbagApp({super.key});
|
||||||
|
@ -55,7 +56,7 @@ class _OutbagAppState extends State {
|
||||||
try {
|
try {
|
||||||
final theme = await AppTheme.fromDisk();
|
final theme = await AppTheme.fromDisk();
|
||||||
setState(() {
|
setState(() {
|
||||||
this.theme = theme;
|
this.theme = theme;
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
@ -65,14 +66,14 @@ class _OutbagAppState extends State {
|
||||||
try {
|
try {
|
||||||
final user = await User.fromDisk();
|
final user = await User.fromDisk();
|
||||||
setState(() {
|
setState(() {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
});
|
});
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// user unavailable
|
// user unavailable
|
||||||
// invalid credentials
|
// invalid credentials
|
||||||
// log out
|
// log out
|
||||||
setState(() {
|
setState(() {
|
||||||
user = null;
|
user = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,8 +83,8 @@ class _OutbagAppState extends State {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
loadTheme();
|
loadTheme();
|
||||||
loadUser();
|
loadUser();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,31 +94,31 @@ class _OutbagAppState extends State {
|
||||||
// with existing details
|
// with existing details
|
||||||
// NOTE: also functions as a way to verify ther data
|
// NOTE: also functions as a way to verify ther data
|
||||||
await doNetworkRequest(null,
|
await doNetworkRequest(null,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getMyAccount',
|
path: 'getMyAccount',
|
||||||
credentials: user,
|
credentials: user,
|
||||||
body: {}),
|
body: {}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final i = AccountMeta.fromJSON(body['data']);
|
final i = AccountMeta.fromJSON(body['data']);
|
||||||
info = i;
|
info = i;
|
||||||
},
|
},
|
||||||
onServerErr: (_) {
|
onServerErr: (_) {
|
||||||
info = null;
|
info = null;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
this.user = null;
|
this.user = null;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onNetworkErr: () {
|
||||||
|
info = null;
|
||||||
|
// user is currently offline
|
||||||
|
// approve login,
|
||||||
|
// until user goes back offline
|
||||||
|
// NOTE TODO: check user data once online
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
},
|
|
||||||
onNetworkErr: () {
|
|
||||||
info = null;
|
|
||||||
// user is currently offline
|
|
||||||
// approve login,
|
|
||||||
// until user goes back offline
|
|
||||||
// NOTE TODO: check user data once online
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -125,195 +126,213 @@ class _OutbagAppState extends State {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
Provider<AppTheme>.value(value: theme),
|
Provider<AppTheme>.value(value: theme),
|
||||||
],
|
|
||||||
child: MaterialApp.router(
|
|
||||||
title: "Outbag",
|
|
||||||
localizationsDelegates: const [
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
AppLocalizations.delegate
|
|
||||||
],
|
],
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
child: MaterialApp.router(
|
||||||
themeMode: theme.mode,
|
title: "Outbag",
|
||||||
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
|
localizationsDelegates: const [
|
||||||
darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
|
GlobalMaterialLocalizations.delegate,
|
||||||
routerConfig: GoRouter(
|
GlobalWidgetsLocalizations.delegate,
|
||||||
navigatorKey: _rootNavigatorKey,
|
GlobalCupertinoLocalizations.delegate,
|
||||||
initialLocation: '/',
|
AppLocalizations.delegate
|
||||||
redirect: (context, state) async {
|
],
|
||||||
if (user == null) {
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
// prelogin
|
themeMode: theme.mode,
|
||||||
if (!state.subloc.startsWith('/welcome')) {
|
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
|
||||||
// prevent unauthorized user from accessing home
|
darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
|
||||||
return '/welcome';
|
routerConfig: GoRouter(
|
||||||
}
|
navigatorKey: _rootNavigatorKey,
|
||||||
} else {
|
initialLocation: '/',
|
||||||
// post login
|
redirect: (context, state) async {
|
||||||
if (state.subloc.startsWith('/welcome')) {
|
if (user == null) {
|
||||||
// prevent authorized user from accessing /welcome
|
// prelogin
|
||||||
return '/';
|
if (!state.subloc.startsWith('/welcome')) {
|
||||||
}
|
// prevent unauthorized user from accessing home
|
||||||
}
|
return '/welcome';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// post login
|
||||||
|
if (state.subloc.startsWith('/welcome')) {
|
||||||
|
// prevent authorized user from accessing /welcome
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
routes: <RouteBase>[
|
|
||||||
// unauthorized routes
|
|
||||||
GoRoute(
|
|
||||||
name: 'welcome',
|
|
||||||
path: '/welcome',
|
|
||||||
builder: (context, state) => const WelcomePage(),
|
|
||||||
routes: <RouteBase>[
|
routes: <RouteBase>[
|
||||||
|
// unauthorized routes
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'signin',
|
name: 'welcome',
|
||||||
path: 'signin',
|
path: '/welcome',
|
||||||
builder: (context, state) =>
|
builder: (context, state) => const WelcomePage(),
|
||||||
AuthPage(mode: Mode.signin, refresh: loadUser),
|
routes: <RouteBase>[
|
||||||
),
|
GoRoute(
|
||||||
GoRoute(
|
name: 'signin',
|
||||||
name: 'signup',
|
path: 'signin',
|
||||||
path: 'signup',
|
builder: (context, state) =>
|
||||||
builder: (context, state) =>
|
AuthPage(mode: Mode.signin, refresh: loadUser),
|
||||||
AuthPage(mode: Mode.signup, refresh: loadUser),
|
),
|
||||||
),
|
GoRoute(
|
||||||
GoRoute(
|
name: 'signup',
|
||||||
name: 'signup-ota',
|
path: 'signup',
|
||||||
path: 'signup-ota',
|
builder: (context, state) =>
|
||||||
builder: (context, state) =>
|
AuthPage(mode: Mode.signup, refresh: loadUser),
|
||||||
AuthPage(mode: Mode.signupOTA, refresh: loadUser),
|
),
|
||||||
),
|
GoRoute(
|
||||||
]),
|
name: 'signup-ota',
|
||||||
|
path: 'signup-ota',
|
||||||
// authorized routes
|
builder: (context, state) =>
|
||||||
ShellRoute(
|
AuthPage(mode: Mode.signupOTA, refresh: loadUser),
|
||||||
navigatorKey: _userShellNavigatorKey,
|
),
|
||||||
builder: (context, state, child) => Provider.value(
|
|
||||||
value: user!,
|
|
||||||
child: FutureProvider(
|
|
||||||
initialData: null,
|
|
||||||
child: child,
|
|
||||||
create: (context)=>fetchInfo(context.read<User>()),
|
|
||||||
)),
|
|
||||||
routes: <RouteBase>[
|
|
||||||
GoRoute(
|
|
||||||
path: '/',
|
|
||||||
name: 'home',
|
|
||||||
builder: (context, state) => const HomePage(),
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
name: 'settings',
|
|
||||||
path: 'settings',
|
|
||||||
builder: (context, state) =>
|
|
||||||
SettingsPage(
|
|
||||||
refreshTheme: loadTheme,
|
|
||||||
refreshUser: loadUser
|
|
||||||
)),
|
|
||||||
GoRoute(
|
|
||||||
path: 'join-room',
|
|
||||||
name: 'add-room',
|
|
||||||
builder: (context, state) =>
|
|
||||||
const JoinRoomPage(),
|
|
||||||
routes: <RouteBase>[
|
|
||||||
GoRoute(
|
|
||||||
path: 'new',
|
|
||||||
name: 'new-room',
|
|
||||||
builder: (context, state) => NewRoomPage()),
|
|
||||||
]),
|
]),
|
||||||
GoRoute(
|
|
||||||
name: 'room',
|
|
||||||
path: 'r/:server/:id',
|
|
||||||
builder: (context, state) => RoomPage(
|
|
||||||
state.params['server'] ?? '',
|
|
||||||
state.params['id'] ?? ''),
|
|
||||||
routes: <RouteBase>[
|
|
||||||
GoRoute(
|
|
||||||
name: 'edit-room',
|
|
||||||
path: 'edit',
|
|
||||||
builder: (context, state) => NewRoomPage(
|
|
||||||
server: state.params['server'] ?? '',
|
|
||||||
tag: state.params['id'] ?? '')),
|
|
||||||
GoRoute(
|
|
||||||
name: 'room-members',
|
|
||||||
path: 'members',
|
|
||||||
builder: (context, state) =>
|
|
||||||
ManageRoomMembersPage(
|
|
||||||
state.params['server'] ?? '',
|
|
||||||
state.params['id'] ?? '')),
|
|
||||||
GoRoute(
|
|
||||||
name: 'room-permissions',
|
|
||||||
path: 'roles',
|
|
||||||
builder: (context, state) =>
|
|
||||||
EditRoomPermissionSetPage(
|
|
||||||
state.params['server'] ?? '',
|
|
||||||
state.params['id'] ?? '')),
|
|
||||||
|
|
||||||
GoRoute(
|
// authorized routes
|
||||||
name: 'new-category',
|
ShellRoute(
|
||||||
path: 'new-category',
|
navigatorKey: _userShellNavigatorKey,
|
||||||
builder: (context, state)=>EditCategoryPage(
|
builder: (context, state, child) => Provider.value(
|
||||||
state.params['server'] ?? '',
|
value: user!,
|
||||||
state.params['id'] ?? '')),
|
child: FutureProvider(
|
||||||
GoRoute(
|
initialData: null,
|
||||||
name: 'edit-category',
|
child: child,
|
||||||
path: 'edit-category/:category',
|
create: (context) => fetchInfo(context.read<User>()),
|
||||||
builder: (context, state)=>EditCategoryPage(
|
)),
|
||||||
state.params['server'] ?? '',
|
routes: <RouteBase>[
|
||||||
state.params['id'] ?? '',
|
GoRoute(
|
||||||
id: int.tryParse(state.params['category'] ?? ''))),
|
path: '/',
|
||||||
|
name: 'home',
|
||||||
GoRoute(
|
builder: (context, state) => const HomePage(),
|
||||||
name: 'new-product',
|
|
||||||
path: 'new-product',
|
|
||||||
builder: (context, state)=>EditProductPage(
|
|
||||||
server: state.params['server'] ?? '',
|
|
||||||
room: state.params['id'] ?? '',)),
|
|
||||||
GoRoute(
|
|
||||||
name: 'view-product',
|
|
||||||
path: 'p/:product',
|
|
||||||
builder: (context, state)=>ViewProductPage(
|
|
||||||
server: state.params['server'] ?? '',
|
|
||||||
room: state.params['id'] ?? '',
|
|
||||||
product: int.tryParse(state.params['product'] ?? '') ?? 0),
|
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'edit-product',
|
name: 'settings',
|
||||||
path: 'edit',
|
path: 'settings',
|
||||||
builder: (context, state)=>EditProductPage(
|
builder: (context, state) => SettingsPage(
|
||||||
server: state.params['server'] ?? '',
|
refreshTheme: loadTheme,
|
||||||
room: state.params['id'] ?? '',
|
refreshUser: loadUser)),
|
||||||
product: int.tryParse(state.params['product'] ?? ''))),
|
GoRoute(
|
||||||
]
|
path: 'join-room',
|
||||||
),
|
name: 'add-room',
|
||||||
|
builder: (context, state) =>
|
||||||
|
const JoinRoomPage(),
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
path: 'new',
|
||||||
|
name: 'new-room',
|
||||||
|
builder: (context, state) =>
|
||||||
|
NewRoomPage()),
|
||||||
|
]),
|
||||||
|
GoRoute(
|
||||||
|
name: 'room',
|
||||||
|
path: 'r/:server/:id',
|
||||||
|
builder: (context, state) => RoomPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? ''),
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
name: 'edit-room',
|
||||||
|
path: 'edit',
|
||||||
|
builder: (context, state) => NewRoomPage(
|
||||||
|
server: state.params['server'] ?? '',
|
||||||
|
tag: state.params['id'] ?? '')),
|
||||||
|
GoRoute(
|
||||||
|
name: 'room-members',
|
||||||
|
path: 'members',
|
||||||
|
builder: (context, state) =>
|
||||||
|
ManageRoomMembersPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '')),
|
||||||
|
GoRoute(
|
||||||
|
name: 'room-permissions',
|
||||||
|
path: 'roles',
|
||||||
|
builder: (context, state) =>
|
||||||
|
EditRoomPermissionSetPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '')),
|
||||||
|
GoRoute(
|
||||||
|
name: 'new-category',
|
||||||
|
path: 'new-category',
|
||||||
|
builder: (context, state) =>
|
||||||
|
EditCategoryPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '')),
|
||||||
|
GoRoute(
|
||||||
|
name: 'edit-category',
|
||||||
|
path: 'edit-category/:category',
|
||||||
|
builder: (context, state) =>
|
||||||
|
EditCategoryPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '',
|
||||||
|
id: int.tryParse(
|
||||||
|
state.params['category'] ??
|
||||||
|
''))),
|
||||||
|
GoRoute(
|
||||||
|
name: 'new-product',
|
||||||
|
path: 'new-product',
|
||||||
|
builder: (context, state) =>
|
||||||
|
EditProductPage(
|
||||||
|
server:
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
room: state.params['id'] ?? '',
|
||||||
|
)),
|
||||||
|
GoRoute(
|
||||||
|
name: 'view-product',
|
||||||
|
path: 'p/:product',
|
||||||
|
builder: (context, state) =>
|
||||||
|
ViewProductPage(
|
||||||
|
server:
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
room: state.params['id'] ?? '',
|
||||||
|
product: int.tryParse(
|
||||||
|
state.params['product'] ??
|
||||||
|
'') ??
|
||||||
|
0),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
name: 'edit-product',
|
||||||
|
path: 'edit',
|
||||||
|
builder: (context, state) =>
|
||||||
|
EditProductPage(
|
||||||
|
server: state
|
||||||
|
.params['server'] ??
|
||||||
|
'',
|
||||||
|
room: state.params['id'] ??
|
||||||
|
'',
|
||||||
|
product: int.tryParse(
|
||||||
|
state.params[
|
||||||
|
'product'] ??
|
||||||
|
''))),
|
||||||
|
]),
|
||||||
|
GoRoute(
|
||||||
|
name: 'new-item',
|
||||||
|
path: 'new-item',
|
||||||
|
builder: (context, state) => EditItemPage(
|
||||||
|
server:
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
room: state.params['id'] ?? '',
|
||||||
|
)),
|
||||||
|
GoRoute(
|
||||||
|
name: 'edit-item',
|
||||||
|
path: 'i/:item',
|
||||||
|
builder: (context, state) => EditItemPage(
|
||||||
|
server: state.params['server'] ?? '',
|
||||||
|
room: state.params['id'] ?? '',
|
||||||
|
item: int.tryParse(
|
||||||
|
state.params['item'] ?? '') ??
|
||||||
|
0),
|
||||||
|
)
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
|
||||||
GoRoute(
|
// routes that can be accessed
|
||||||
name: 'new-item',
|
// with and without an account
|
||||||
path: 'new-item',
|
// i.e the about screen
|
||||||
builder: (context, state)=>EditItemPage(
|
GoRoute(
|
||||||
server: state.params['server'] ?? '',
|
path: '/about',
|
||||||
room: state.params['id'] ?? '',)),
|
name: 'about',
|
||||||
GoRoute(
|
builder: (context, state) => const Text('About'))
|
||||||
name: 'edit-item',
|
]),
|
||||||
path: 'i/:item',
|
));
|
||||||
builder: (context, state)=>EditItemPage(
|
|
||||||
server: state.params['server'] ?? '',
|
|
||||||
room: state.params['id'] ?? '',
|
|
||||||
item: int.tryParse(state.params['item'] ?? '') ?? 0),
|
|
||||||
)
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
|
|
||||||
// routes that can be accessed
|
|
||||||
// with and without an account
|
|
||||||
// i.e the about screen
|
|
||||||
GoRoute(
|
|
||||||
path: '/about',
|
|
||||||
name: 'about',
|
|
||||||
builder: (context, state) => const Text('About'))
|
|
||||||
]),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,12 @@ class _HomePageState extends State<HomePage> {
|
||||||
|
|
||||||
// wait for background room changes
|
// wait for background room changes
|
||||||
Room.listen((_) async {
|
Room.listen((_) async {
|
||||||
try {
|
try {
|
||||||
final newRooms = await Room.listRooms();
|
final newRooms = await Room.listRooms();
|
||||||
setState(() {
|
setState(() {
|
||||||
rooms = newRooms;
|
rooms = newRooms;
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => fetchList());
|
WidgetsBinding.instance.addPostFrameCallback((_) => fetchList());
|
||||||
|
@ -43,17 +43,17 @@ class _HomePageState extends State<HomePage> {
|
||||||
try {
|
try {
|
||||||
final newRooms = await Room.listRooms();
|
final newRooms = await Room.listRooms();
|
||||||
setState(() {
|
setState(() {
|
||||||
rooms = newRooms;
|
rooms = newRooms;
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
doNetworkRequest(
|
doNetworkRequest(
|
||||||
sm,
|
sm,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
path: 'listRooms', credentials: user, target: user.server, body: {}),
|
path: 'listRooms', credentials: user, target: user.server, body: {}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final List<Room> list = body['data'].map<Room>((json) {
|
final List<Room> list = body['data'].map<Room>((json) {
|
||||||
return Room.fromJSON(json);
|
return Room.fromJSON(json);
|
||||||
}).toList();
|
}).toList();
|
||||||
for (Room r in list) {
|
for (Room r in list) {
|
||||||
await r.toDisk();
|
await r.toDisk();
|
||||||
|
@ -91,33 +91,34 @@ class _HomePageState extends State<HomePage> {
|
||||||
},
|
},
|
||||||
menuChildren: [
|
menuChildren: [
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(Icons.settings),
|
leadingIcon: const Icon(Icons.settings),
|
||||||
child: Text(AppLocalizations.of(context)!.settings),
|
child: Text(AppLocalizations.of(context)!.settings),
|
||||||
onPressed: () {
|
|
||||||
// show settings screen
|
|
||||||
context.goNamed('settings');
|
|
||||||
}),
|
|
||||||
...(context.watch<AccountMeta?>() != null &&
|
|
||||||
(context.watch<AccountMeta?>()?.permissions)! &
|
|
||||||
ServerPermission.allManagement !=
|
|
||||||
0)
|
|
||||||
? [
|
|
||||||
MenuItemButton(
|
|
||||||
leadingIcon: const Icon(Icons.dns),
|
|
||||||
child: Text(AppLocalizations.of(context)!.serverDashboard),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show settings screen
|
// show settings screen
|
||||||
context.goNamed('dash');
|
context.goNamed('settings');
|
||||||
}),
|
}),
|
||||||
]
|
...(context.watch<AccountMeta?>() != null &&
|
||||||
: [],
|
(context.watch<AccountMeta?>()?.permissions)! &
|
||||||
|
ServerPermission.allManagement !=
|
||||||
|
0)
|
||||||
|
? [
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(Icons.dns),
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.serverDashboard),
|
||||||
|
onPressed: () {
|
||||||
|
// show settings screen
|
||||||
|
context.goNamed('dash');
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(Icons.info_rounded),
|
leadingIcon: const Icon(Icons.info_rounded),
|
||||||
child: Text(AppLocalizations.of(context)!.about),
|
child: Text(AppLocalizations.of(context)!.about),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show about screen
|
// show about screen
|
||||||
context.goNamed('about');
|
context.goNamed('about');
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -127,31 +128,31 @@ class _HomePageState extends State<HomePage> {
|
||||||
itemBuilder: (ctx, i) {
|
itemBuilder: (ctx, i) {
|
||||||
final room = rooms[i];
|
final room = rooms[i];
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.all(8.0),
|
margin: const EdgeInsets.all(8.0),
|
||||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||||
semanticContainer: true,
|
semanticContainer: true,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// open room
|
// open room
|
||||||
context.goNamed('room',
|
context.goNamed('room',
|
||||||
params: {'server': room.serverTag, 'id': room.id});
|
params: {'server': room.serverTag, 'id': room.id});
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
// open bottom sheet
|
// open bottom sheet
|
||||||
// NOTE: feature yet to be confirmed
|
// NOTE: feature yet to be confirmed
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
|
padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(room.name),
|
title: Text(room.name),
|
||||||
visualDensity: const VisualDensity(vertical: 3),
|
visualDensity: const VisualDensity(vertical: 3),
|
||||||
subtitle: Text(room.description),
|
subtitle: Text(room.description),
|
||||||
leading: AspectRatio(
|
leading: AspectRatio(
|
||||||
aspectRatio: 1 / 1,
|
aspectRatio: 1 / 1,
|
||||||
child: SvgPicture.asset("${room.icon?.img}"),
|
child: SvgPicture.asset("${room.icon?.img}"),
|
||||||
),
|
),
|
||||||
hoverColor: Colors.transparent,
|
hoverColor: Colors.transparent,
|
||||||
))));
|
))));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
|
1
lib/screens/room/about/invite.dart
Normal file
1
lib/screens/room/about/invite.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -30,10 +30,10 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
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
|
||||||
|
@ -43,7 +43,7 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final info = RoomInfo.fromJSON(body['data']);
|
final info = RoomInfo.fromJSON(body['data']);
|
||||||
setState(() {
|
setState(() {
|
||||||
this.info = info;
|
this.info = info;
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -56,26 +56,26 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
|
|
||||||
doNetworkRequest(sm,
|
doNetworkRequest(sm,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getRoomMembers',
|
path: 'getRoomMembers',
|
||||||
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
|
||||||
router.pushReplacementNamed('home');
|
router.pushReplacementNamed('home');
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
onOK: (body) {
|
onOK: (body) {
|
||||||
final List<RoomMember> list = body['data'].map<RoomMember>((json) {
|
final List<RoomMember> list = body['data'].map<RoomMember>((json) {
|
||||||
return RoomMember.fromJSON(json);
|
return RoomMember.fromJSON(json);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
this.list = list;
|
this.list = list;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -83,21 +83,21 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
fetchUserInfo();
|
fetchUserInfo();
|
||||||
fetchMembers();
|
fetchMembers();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title:
|
title:
|
||||||
Text(AppLocalizations.of(context)!.roomMembersTitle(list.length)),
|
Text(AppLocalizations.of(context)!.roomMembersTitle(list.length)),
|
||||||
//actions: [
|
//actions: [
|
||||||
// // NOTE: Maybe add a search icon
|
// // NOTE: Maybe add a search icon
|
||||||
// // and general search functionality here
|
// // and general search functionality here
|
||||||
|
@ -109,8 +109,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
|
|
||||||
String role = AppLocalizations.of(context)!.roleMember;
|
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 = AppLocalizations.of(context)!.roleOwner;
|
role = AppLocalizations.of(context)!.roleOwner;
|
||||||
} else if (item.isAdmin) {
|
} else if (item.isAdmin) {
|
||||||
role = AppLocalizations.of(context)!.roleAdmin;
|
role = AppLocalizations.of(context)!.roleAdmin;
|
||||||
|
@ -119,17 +119,17 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
bool enable = true;
|
bool enable = true;
|
||||||
// perform permission check
|
// perform permission check
|
||||||
if (info == null ||
|
if (info == null ||
|
||||||
!((info?.isAdmin)! ||
|
!((info?.isAdmin)! ||
|
||||||
(info?.isOwner)! ||
|
(info?.isOwner)! ||
|
||||||
((info?.permissions)! & oB("1100000") != 0))) {
|
((info?.permissions)! & oB("1100000") != 0))) {
|
||||||
// NOTE: do not show error message
|
// NOTE: do not show error message
|
||||||
// user should assume,
|
// user should assume,
|
||||||
// that it wasn't even possible
|
// that it wasn't even possible
|
||||||
// to click on ListTile
|
// to click on ListTile
|
||||||
enable = false;
|
enable = false;
|
||||||
} else if (info != null &&
|
} else if (info != null &&
|
||||||
item.id == info?.owner &&
|
item.id == info?.owner &&
|
||||||
widget.server == item.serverTag) {
|
widget.server == item.serverTag) {
|
||||||
// cannot kick admin
|
// cannot kick admin
|
||||||
enable = false;
|
enable = false;
|
||||||
}
|
}
|
||||||
|
@ -139,236 +139,236 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
||||||
subtitle: Text(role),
|
subtitle: Text(role),
|
||||||
leading: const Icon(Icons.person),
|
leading: const Icon(Icons.person),
|
||||||
onTap: !enable
|
onTap: !enable
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => BottomSheet(
|
builder: (context) => BottomSheet(
|
||||||
onClosing: () {},
|
onClosing: () {},
|
||||||
builder: (context) => Column(
|
builder: (context) => Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Text(item.humanReadableName,
|
child: Text(item.humanReadableName,
|
||||||
style: textTheme.displaySmall)),
|
style: textTheme.displaySmall)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
...((info?.isAdmin)! ||
|
...((info?.isAdmin)! ||
|
||||||
(info?.isOwner)! ||
|
(info?.isOwner)! ||
|
||||||
((info?.permissions)! &
|
((info?.permissions)! &
|
||||||
RoomPermission
|
RoomPermission
|
||||||
.changeAdmin !=
|
.changeAdmin !=
|
||||||
0))
|
0))
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.supervisor_account),
|
Icons.supervisor_account),
|
||||||
title: Text(item.isAdmin
|
title: Text(item.isAdmin
|
||||||
? AppLocalizations.of(
|
? AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
.removeAdminTitle
|
.removeAdminTitle
|
||||||
: AppLocalizations.of(
|
: AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
.makeAdminTitle),
|
.makeAdminTitle),
|
||||||
subtitle: Text(item.isAdmin
|
subtitle: Text(item.isAdmin
|
||||||
? AppLocalizations.of(
|
? AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
.removeAdminSubtitle
|
.removeAdminSubtitle
|
||||||
: AppLocalizations.of(
|
: AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
.makeAdminSubtitle),
|
.makeAdminSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// make user admin
|
// make user admin
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder:
|
builder:
|
||||||
(ctx) =>
|
(ctx) =>
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons
|
Icons
|
||||||
.supervisor_account),
|
.supervisor_account),
|
||||||
title: Text(item.isAdmin
|
title: Text(item.isAdmin
|
||||||
? AppLocalizations.of(
|
? AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
.removeAdminTitle
|
.removeAdminTitle
|
||||||
: AppLocalizations.of(
|
: AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
.makeAdminTitle),
|
.makeAdminTitle),
|
||||||
content: Text(item
|
content: Text(item
|
||||||
.isAdmin
|
.isAdmin
|
||||||
? AppLocalizations.of(
|
? AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
.removeAdminConfirm(item
|
.removeAdminConfirm(item
|
||||||
.humanReadableName)
|
.humanReadableName)
|
||||||
: AppLocalizations.of(
|
: AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
.makeAdminConfirm(
|
.makeAdminConfirm(
|
||||||
item.humanReadableName)),
|
item.humanReadableName)),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
() {
|
() {
|
||||||
// close popup
|
// close popup
|
||||||
// NOTE: cancel only closes the dialog
|
// NOTE: cancel only closes the dialog
|
||||||
// whilst OK closes both
|
// whilst OK closes both
|
||||||
Navigator.of(ctx)
|
Navigator.of(ctx)
|
||||||
.pop();
|
.pop();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context)!
|
AppLocalizations.of(context)!
|
||||||
.cancel),
|
.cancel),
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
() async {
|
() async {
|
||||||
// send request
|
// send request
|
||||||
final scaffMgr =
|
final scaffMgr =
|
||||||
ScaffoldMessenger.of(context);
|
ScaffoldMessenger.of(context);
|
||||||
final nav =
|
final nav =
|
||||||
Navigator.of(ctx);
|
Navigator.of(ctx);
|
||||||
final nav2 =
|
final nav2 =
|
||||||
Navigator.of(context);
|
Navigator.of(context);
|
||||||
final user =
|
final user =
|
||||||
context.read<User>();
|
context.read<User>();
|
||||||
|
|
||||||
doNetworkRequest(
|
doNetworkRequest(
|
||||||
scaffMgr,
|
scaffMgr,
|
||||||
req: () =>
|
req: () =>
|
||||||
postWithCreadentials(path: 'setAdminStatus', credentials: user, target: user.server, body: {
|
postWithCreadentials(path: 'setAdminStatus', credentials: user, target: user.server, body: {
|
||||||
'room': widget.tag,
|
'room': widget.tag,
|
||||||
'roomServer': widget.server,
|
'roomServer': widget.server,
|
||||||
'server': item.serverTag,
|
'server': item.serverTag,
|
||||||
'name': item.id,
|
'name': item.id,
|
||||||
'admin': !item.isAdmin
|
'admin': !item.isAdmin
|
||||||
}),
|
}),
|
||||||
onOK: (_) {
|
onOK: (_) {
|
||||||
fetchMembers();
|
fetchMembers();
|
||||||
},
|
},
|
||||||
after: () {
|
after: () {
|
||||||
// close popup
|
// close popup
|
||||||
nav.pop();
|
nav.pop();
|
||||||
// close bottom sheet
|
// close bottom sheet
|
||||||
nav2.pop();
|
nav2.pop();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context)!
|
AppLocalizations.of(context)!
|
||||||
.ok),
|
.ok),
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
...((info?.isAdmin)! ||
|
||||||
|
(info?.isOwner)! ||
|
||||||
|
((info?.permissions)! &
|
||||||
|
RoomPermission
|
||||||
|
.manageMembers !=
|
||||||
|
0))
|
||||||
|
? [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(
|
||||||
|
Icons.person_remove),
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.kickUserTitle),
|
||||||
|
subtitle: Text(
|
||||||
|
AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.kickUserSubtitle),
|
||||||
|
onTap: () {
|
||||||
|
// remove user from room
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder:
|
||||||
|
(ctx) =>
|
||||||
|
AlertDialog(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons
|
||||||
|
.person_remove),
|
||||||
|
title: Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.kickUserTitle),
|
||||||
|
content: Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.kichUserConfirm(
|
||||||
|
item.humanReadableName)),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed:
|
||||||
|
() {
|
||||||
|
// close popup
|
||||||
|
// NOTE: cancel only closes the dialog
|
||||||
|
// whilst OK closes both
|
||||||
|
|
||||||
|
Navigator.of(ctx)
|
||||||
|
.pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.cancel),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed:
|
||||||
|
() async {
|
||||||
|
// send request
|
||||||
|
final scaffMgr =
|
||||||
|
ScaffoldMessenger.of(ctx);
|
||||||
|
final nav =
|
||||||
|
Navigator.of(ctx);
|
||||||
|
final nav2 =
|
||||||
|
Navigator.of(context);
|
||||||
|
final user =
|
||||||
|
context.read<User>();
|
||||||
|
|
||||||
|
doNetworkRequest(
|
||||||
|
scaffMgr,
|
||||||
|
req: () =>
|
||||||
|
postWithCreadentials(path: 'kickMember', credentials: user, target: user.server, body: {
|
||||||
|
'room': widget.tag,
|
||||||
|
'roomServer': widget.server,
|
||||||
|
'name': item.id,
|
||||||
|
'server': item.serverTag
|
||||||
|
}),
|
||||||
|
onOK: (_) {
|
||||||
|
fetchMembers();
|
||||||
|
},
|
||||||
|
after: () {
|
||||||
|
// close popup
|
||||||
|
nav.pop();
|
||||||
|
// close bottom sheet
|
||||||
|
nav2.pop();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.ok),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
: [],
|
||||||
],
|
],
|
||||||
));
|
),
|
||||||
},
|
),
|
||||||
)
|
FilledButton(
|
||||||
]
|
child: Text(
|
||||||
: [],
|
AppLocalizations.of(context)!.close),
|
||||||
...((info?.isAdmin)! ||
|
onPressed: () {
|
||||||
(info?.isOwner)! ||
|
Navigator.of(context).pop();
|
||||||
((info?.permissions)! &
|
},
|
||||||
RoomPermission
|
)
|
||||||
.manageMembers !=
|
],
|
||||||
0))
|
),
|
||||||
? [
|
));
|
||||||
ListTile(
|
},
|
||||||
leading: const Icon(
|
|
||||||
Icons.person_remove),
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.kickUserTitle),
|
|
||||||
subtitle: Text(
|
|
||||||
AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.kickUserSubtitle),
|
|
||||||
onTap: () {
|
|
||||||
// remove user from room
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder:
|
|
||||||
(ctx) =>
|
|
||||||
AlertDialog(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons
|
|
||||||
.person_remove),
|
|
||||||
title: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.kickUserTitle),
|
|
||||||
content: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.kichUserConfirm(
|
|
||||||
item.humanReadableName)),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed:
|
|
||||||
() {
|
|
||||||
// close popup
|
|
||||||
// NOTE: cancel only closes the dialog
|
|
||||||
// whilst OK closes both
|
|
||||||
|
|
||||||
Navigator.of(ctx)
|
|
||||||
.pop();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.cancel),
|
|
||||||
),
|
|
||||||
FilledButton(
|
|
||||||
onPressed:
|
|
||||||
() async {
|
|
||||||
// send request
|
|
||||||
final scaffMgr =
|
|
||||||
ScaffoldMessenger.of(ctx);
|
|
||||||
final nav =
|
|
||||||
Navigator.of(ctx);
|
|
||||||
final nav2 =
|
|
||||||
Navigator.of(context);
|
|
||||||
final user =
|
|
||||||
context.read<User>();
|
|
||||||
|
|
||||||
doNetworkRequest(
|
|
||||||
scaffMgr,
|
|
||||||
req: () =>
|
|
||||||
postWithCreadentials(path: 'kickMember', credentials: user, target: user.server, body: {
|
|
||||||
'room': widget.tag,
|
|
||||||
'roomServer': widget.server,
|
|
||||||
'name': item.id,
|
|
||||||
'server': item.serverTag
|
|
||||||
}),
|
|
||||||
onOK: (_) {
|
|
||||||
fetchMembers();
|
|
||||||
},
|
|
||||||
after: () {
|
|
||||||
// close popup
|
|
||||||
nav.pop();
|
|
||||||
// close bottom sheet
|
|
||||||
nav2.pop();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.ok),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FilledButton(
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.close),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
|
|
1
lib/screens/room/about/ota.dart
Normal file
1
lib/screens/room/about/ota.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
@ -72,18 +72,18 @@ class _EditRoomPermissionSetPageState extends State<EditRoomPermissionSetPage> {
|
||||||
final int col = pow(2, index) as int;
|
final int col = pow(2, index) as int;
|
||||||
|
|
||||||
return SwitchListTile(
|
return SwitchListTile(
|
||||||
title: Text(RoomPermission.name(item, context)),
|
title: Text(RoomPermission.name(item, context)),
|
||||||
subtitle: Text(RoomPermission.describe(item, context)),
|
subtitle: Text(RoomPermission.describe(item, context)),
|
||||||
onChanged: (state) {
|
onChanged: (state) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (state) {
|
if (state) {
|
||||||
permissions |= col;
|
permissions |= col;
|
||||||
} else {
|
} else {
|
||||||
permissions &= ~col;
|
permissions &= ~col;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
value: permissions & col != 0);
|
value: permissions & col != 0);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
@ -97,18 +97,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();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,11 +31,11 @@ class _EditCategoryPageState extends State<EditCategoryPage> {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (widget.id == null) {
|
if (widget.id == null) {
|
||||||
// trying to create a new category
|
// trying to create a new category
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetchCategory();
|
fetchCategory();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,210 +45,210 @@ class _EditCategoryPageState extends State<EditCategoryPage> {
|
||||||
// TODO: load cached rooms
|
// TODO: load cached rooms
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getCategory',
|
path: 'getCategory',
|
||||||
body: {
|
body: {
|
||||||
'room': widget.tag,
|
'room': widget.tag,
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'listCatID': widget.id
|
'listCatID': widget.id
|
||||||
}),
|
}),
|
||||||
onOK: (json) {
|
onOK: (json) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ctrName.text = json['data']['title'];
|
_ctrName.text = json['data']['title'];
|
||||||
_ctrColor = colorFromString(json['data']['color']);
|
_ctrColor = colorFromString(json['data']['color']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@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(AppLocalizations.of(context)!.loading,
|
Text(AppLocalizations.of(context)!.loading,
|
||||||
style: textTheme.titleLarge),
|
style: textTheme.titleLarge),
|
||||||
])))
|
])))
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text((widget.id == null)
|
title: Text((widget.id == null)
|
||||||
? AppLocalizations.of(context)!.newCategory
|
? AppLocalizations.of(context)!.newCategory
|
||||||
: AppLocalizations.of(context)!.editCategory),
|
: AppLocalizations.of(context)!.editCategory),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 400),
|
constraints: const BoxConstraints(maxWidth: 400),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.square_rounded,
|
icon: Icon(Icons.square_rounded,
|
||||||
size: 48.0, color: _ctrColor),
|
size: 48.0, color: _ctrColor),
|
||||||
tooltip: AppLocalizations.of(context)!
|
tooltip: AppLocalizations.of(context)!
|
||||||
.changeCategoryColor,
|
.changeCategoryColor,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context)!
|
AppLocalizations.of(context)!
|
||||||
.chooseCategoryColor),
|
.chooseCategoryColor),
|
||||||
actions: const [],
|
actions: const [],
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: smallest * 0.3 * 3,
|
width: smallest * 0.3 * 3,
|
||||||
height: smallest * 0.3 * 3,
|
height: smallest * 0.3 * 3,
|
||||||
child: GridView.count(
|
child: GridView.count(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
children: RoomCategory
|
children: RoomCategory
|
||||||
.listColors()
|
.listColors()
|
||||||
.map((color) {
|
.map((color) {
|
||||||
return GridTile(
|
return GridTile(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons
|
Icons
|
||||||
.square_rounded,
|
.square_rounded,
|
||||||
color:
|
color:
|
||||||
color,
|
color,
|
||||||
size: 48.0),
|
size: 48.0),
|
||||||
// do not display tooltip for now
|
// do not display tooltip for now
|
||||||
// as it is hard to translate
|
// as it is hard to translate
|
||||||
// and the tooltip prevented the click event,
|
// and the tooltip prevented the click event,
|
||||||
// when clicked on the tooltip bar
|
// when clicked on the tooltip bar
|
||||||
// tooltip:icon.text,
|
// tooltip:icon.text,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ctrColor =
|
_ctrColor =
|
||||||
color;
|
color;
|
||||||
});
|
});
|
||||||
Navigator.of(
|
Navigator.of(
|
||||||
ctx)
|
ctx)
|
||||||
.pop();
|
.pop();
|
||||||
}));
|
}));
|
||||||
}).toList())),
|
}).toList())),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrName,
|
controller: _ctrName,
|
||||||
keyboardType: TextInputType.name,
|
keyboardType: TextInputType.name,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.badge),
|
prefixIcon: const Icon(Icons.badge),
|
||||||
labelText: AppLocalizations.of(context)!
|
labelText: AppLocalizations.of(context)!
|
||||||
.inputCategoryNameLabel,
|
.inputCategoryNameLabel,
|
||||||
hintText: AppLocalizations.of(context)!
|
hintText: AppLocalizations.of(context)!
|
||||||
.inputCategoryNameHint,
|
.inputCategoryNameHint,
|
||||||
helperText: AppLocalizations.of(context)!
|
helperText: AppLocalizations.of(context)!
|
||||||
.inputCategoryNameHelp,
|
.inputCategoryNameHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
))))),
|
))))),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final scaffMgr = ScaffoldMessenger.of(context);
|
final scaffMgr = ScaffoldMessenger.of(context);
|
||||||
final router = GoRouter.of(context);
|
final router = GoRouter.of(context);
|
||||||
final trans = AppLocalizations.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) {
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: trans!.errorNoRoomName, action: trans.ok);
|
text: trans!.errorNoRoomName, action: trans.ok);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
showSpinner = true;
|
showSpinner = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
final color = colorIdFromColor(_ctrColor);
|
final color = colorIdFromColor(_ctrColor);
|
||||||
|
|
||||||
if (widget.id == null) {
|
if (widget.id == null) {
|
||||||
await doNetworkRequest(scaffMgr,
|
await doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
target: user.server,
|
target: user.server,
|
||||||
credentials: user,
|
credentials: user,
|
||||||
path: 'addCategory',
|
path: 'addCategory',
|
||||||
body: {
|
body: {
|
||||||
'room': widget.tag,
|
'room': widget.tag,
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'title': _ctrName.text,
|
'title': _ctrName.text,
|
||||||
'color': color
|
'color': color
|
||||||
}),
|
}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final id = body['data']['catID'];
|
final id = body['data']['catID'];
|
||||||
|
|
||||||
final cat = RoomCategory(
|
final cat = RoomCategory(
|
||||||
id: id, name: _ctrName.text, color: _ctrColor);
|
id: id, name: _ctrName.text, color: _ctrColor);
|
||||||
// TODO: cache category
|
// TODO: cache category
|
||||||
|
|
||||||
// go back
|
// go back
|
||||||
router.pop();
|
router.pop();
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
after: () {
|
after: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
showSpinner = false;
|
showSpinner = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await doNetworkRequest(scaffMgr,
|
await doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
target: user.server,
|
target: user.server,
|
||||||
credentials: user,
|
credentials: user,
|
||||||
path: 'changeCategory',
|
path: 'changeCategory',
|
||||||
body: {
|
body: {
|
||||||
'room': widget.tag,
|
'room': widget.tag,
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'title': _ctrName.text,
|
'title': _ctrName.text,
|
||||||
'listCatID': widget.id,
|
'listCatID': widget.id,
|
||||||
'color': color
|
'color': color
|
||||||
}),
|
}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final cat = RoomCategory(
|
final cat = RoomCategory(
|
||||||
id: widget.id!,
|
id: widget.id!,
|
||||||
name: _ctrName.text,
|
name: _ctrName.text,
|
||||||
color: _ctrColor);
|
color: _ctrColor);
|
||||||
// TODO: cache category
|
// TODO: cache category
|
||||||
|
|
||||||
// go back
|
// go back
|
||||||
router.pop();
|
router.pop();
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
after: () {
|
after: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
showSpinner = false;
|
showSpinner = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label: Text((widget.id == null)
|
label: Text((widget.id == null)
|
||||||
? AppLocalizations.of(context)!.newCategoryShort
|
? AppLocalizations.of(context)!.newCategoryShort
|
||||||
: AppLocalizations.of(context)!.editCategoryShort),
|
: AppLocalizations.of(context)!.editCategoryShort),
|
||||||
icon: Icon((widget.id == null) ? Icons.add : Icons.edit)),
|
icon: Icon((widget.id == null) ? Icons.add : Icons.edit)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ class EditItemPage extends StatefulWidget {
|
||||||
final int? item;
|
final int? item;
|
||||||
|
|
||||||
const EditItemPage(
|
const EditItemPage(
|
||||||
{super.key, required this.room, required this.server, this.item});
|
{super.key, required this.room, required this.server, this.item});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _EditItemPageState();
|
State<StatefulWidget> createState() => _EditItemPageState();
|
||||||
|
@ -43,20 +43,20 @@ class _EditItemPageState extends State<EditItemPage> {
|
||||||
// TODO: load cached categories first
|
// TODO: load cached categories first
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getCategories',
|
path: 'getCategories',
|
||||||
body: {'room': widget.room, 'server': widget.server}),
|
body: {'room': widget.room, 'server': widget.server}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = body['data']
|
final resp = body['data']
|
||||||
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
categories = resp;
|
categories = resp;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fetchProducts() {
|
void fetchProducts() {
|
||||||
|
@ -65,20 +65,20 @@ class _EditItemPageState extends State<EditItemPage> {
|
||||||
// TODO: load cached products first
|
// TODO: load cached products first
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getProducts',
|
path: 'getProducts',
|
||||||
body: {'room': widget.room, 'server': widget.server}),
|
body: {'room': widget.room, 'server': widget.server}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = body['data']
|
final resp = body['data']
|
||||||
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
products = resp;
|
products = resp;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fetchItem() {
|
void fetchItem() {
|
||||||
|
@ -87,21 +87,21 @@ class _EditItemPageState extends State<EditItemPage> {
|
||||||
// TODO: load cached item first
|
// TODO: load cached item first
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getItem',
|
path: 'getItem',
|
||||||
body: {
|
body: {
|
||||||
'room': widget.room,
|
'room': widget.room,
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'listItemID': widget.item
|
'listItemID': widget.item
|
||||||
}),
|
}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = RoomItem.fromJSON(body['data']);
|
final resp = RoomItem.fromJSON(body['data']);
|
||||||
setState(() {
|
setState(() {
|
||||||
item = resp;
|
item = resp;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -109,12 +109,12 @@ class _EditItemPageState extends State<EditItemPage> {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
fetchCategories();
|
fetchCategories();
|
||||||
fetchProducts();
|
fetchProducts();
|
||||||
|
|
||||||
if (widget.item != null) {
|
if (widget.item != null) {
|
||||||
fetchItem();
|
fetchItem();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,156 +123,156 @@ class _EditItemPageState extends State<EditItemPage> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text((widget.item == null)
|
title: Text((widget.item == null)
|
||||||
? AppLocalizations.of(context)!.createItem
|
? AppLocalizations.of(context)!.createItem
|
||||||
: AppLocalizations.of(context)!.editItem),
|
: AppLocalizations.of(context)!.editItem),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 400),
|
constraints: const BoxConstraints(maxWidth: 400),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrName,
|
controller: _ctrName,
|
||||||
keyboardType: TextInputType.name,
|
keyboardType: TextInputType.name,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.badge),
|
prefixIcon: const Icon(Icons.badge),
|
||||||
labelText: AppLocalizations.of(context)!
|
labelText: AppLocalizations.of(context)!
|
||||||
.inputItemNameLabel,
|
.inputItemNameLabel,
|
||||||
hintText: AppLocalizations.of(context)!
|
hintText: AppLocalizations.of(context)!
|
||||||
.inputItemNameHint,
|
.inputItemNameHint,
|
||||||
helperText: AppLocalizations.of(context)!
|
helperText: AppLocalizations.of(context)!
|
||||||
.inputItemNameHelp,
|
.inputItemNameHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ProductPicker(
|
ProductPicker(
|
||||||
label: AppLocalizations.of(context)!
|
label: AppLocalizations.of(context)!
|
||||||
.selectLinkedProductLabel,
|
.selectLinkedProductLabel,
|
||||||
hint: AppLocalizations.of(context)!
|
hint: AppLocalizations.of(context)!
|
||||||
.selectLinkedProductHint,
|
.selectLinkedProductHint,
|
||||||
products: products,
|
products: products,
|
||||||
selected: _ctrLink,
|
selected: _ctrLink,
|
||||||
onSelect: (pid) {
|
onSelect: (pid) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ctrLink = pid;
|
_ctrLink = pid;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrDescription,
|
controller: _ctrDescription,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: AppLocalizations.of(context)!
|
labelText: AppLocalizations.of(context)!
|
||||||
.inputItemDescriptionLabel,
|
.inputItemDescriptionLabel,
|
||||||
hintText: AppLocalizations.of(context)!
|
hintText: AppLocalizations.of(context)!
|
||||||
.inputItemDescriptionHint,
|
.inputItemDescriptionHint,
|
||||||
helperText: AppLocalizations.of(context)!
|
helperText: AppLocalizations.of(context)!
|
||||||
.inputItemDescriptionHelp,
|
.inputItemDescriptionHelp,
|
||||||
prefixIcon: const Icon(Icons.dns),
|
prefixIcon: const Icon(Icons.dns),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DynamicValueUnitInput(
|
DynamicValueUnitInput(
|
||||||
initialUnit: _ctrUnit,
|
initialUnit: _ctrUnit,
|
||||||
initialValue: _ctrValue,
|
initialValue: _ctrValue,
|
||||||
onUnitChange: (unit) {
|
onUnitChange: (unit) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ctrUnit = unit;
|
_ctrUnit = unit;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onValueChange: (value) {
|
onValueChange: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ctrValue = value;
|
_ctrValue = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
CategoryPicker(
|
CategoryPicker(
|
||||||
label: AppLocalizations.of(context)!
|
label: AppLocalizations.of(context)!
|
||||||
.selectCategoryLabel,
|
.selectCategoryLabel,
|
||||||
hint: AppLocalizations.of(context)!
|
hint: AppLocalizations.of(context)!
|
||||||
.selectCategoryHint,
|
.selectCategoryHint,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
selected: _ctrCategory,
|
selected: _ctrCategory,
|
||||||
onSelect: (cid) {
|
onSelect: (cid) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ctrCategory = cid;
|
_ctrCategory = cid;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
))))),
|
))))),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final scaffMgr = ScaffoldMessenger.of(context);
|
final scaffMgr = ScaffoldMessenger.of(context);
|
||||||
final trans = AppLocalizations.of(context);
|
final trans = AppLocalizations.of(context);
|
||||||
final nav = Navigator.of(context);
|
final nav = Navigator.of(context);
|
||||||
|
|
||||||
if (_ctrName.text.isEmpty) {
|
if (_ctrName.text.isEmpty) {
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: trans!.errorProductNameShouldNotBeEmpty,
|
text: trans!.errorProductNameShouldNotBeEmpty,
|
||||||
action: trans.ok);
|
action: trans.ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
|
|
||||||
if (widget.item == null) {
|
if (widget.item == null) {
|
||||||
doNetworkRequest(scaffMgr,
|
doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'addItem',
|
path: 'addItem',
|
||||||
body: {
|
body: {
|
||||||
'room': widget.room,
|
'room': widget.room,
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'state': 0,
|
'state': 0,
|
||||||
'title': _ctrName.text,
|
'title': _ctrName.text,
|
||||||
'description': _ctrDescription.text,
|
'description': _ctrDescription.text,
|
||||||
'listCatID': _ctrCategory,
|
'listCatID': _ctrCategory,
|
||||||
'unit': _ctrUnit,
|
'unit': _ctrUnit,
|
||||||
'value': _ctrValue,
|
'value': _ctrValue,
|
||||||
'listProdID': _ctrLink
|
'listProdID': _ctrLink
|
||||||
}),
|
}),
|
||||||
onOK: (_) async {
|
onOK: (_) async {
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
doNetworkRequest(scaffMgr,
|
doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'changeItem',
|
path: 'changeItem',
|
||||||
body: {
|
body: {
|
||||||
'listItemID': widget.item,
|
'listItemID': widget.item,
|
||||||
'room': widget.room,
|
'room': widget.room,
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'title': _ctrName.text,
|
'title': _ctrName.text,
|
||||||
'description': _ctrDescription.text,
|
'description': _ctrDescription.text,
|
||||||
'listCatID': _ctrCategory,
|
'listCatID': _ctrCategory,
|
||||||
'defUnit': _ctrUnit,
|
'defUnit': _ctrUnit,
|
||||||
'defValue': _ctrValue,
|
'defValue': _ctrValue,
|
||||||
'listProdID': _ctrLink
|
'listProdID': _ctrLink
|
||||||
}),
|
}),
|
||||||
onOK: (_) async {
|
onOK: (_) async {
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label: Text(widget.item != null
|
label: Text(widget.item != null
|
||||||
? AppLocalizations.of(context)!.editItemShort
|
? AppLocalizations.of(context)!.editItemShort
|
||||||
: AppLocalizations.of(context)!.createItemShort),
|
: AppLocalizations.of(context)!.createItemShort),
|
||||||
icon: Icon(widget.item != null ? Icons.edit : Icons.add)),
|
icon: Icon(widget.item != null ? Icons.edit : Icons.add)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,60 +24,60 @@ class _JoinRoomPageState extends State {
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
|
|
||||||
doNetworkRequest(null,
|
doNetworkRequest(null,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
path: 'listPublicRooms',
|
path: 'listPublicRooms',
|
||||||
credentials: user,
|
|
||||||
target: user.server,
|
|
||||||
body: {}),
|
|
||||||
onOK: (body) async {
|
|
||||||
// parse rooms
|
|
||||||
final list = body['data'];
|
|
||||||
|
|
||||||
// try to fetch a list of rooms the user is a member of
|
|
||||||
// use an empty blacklist when request is not successful
|
|
||||||
final List<Room> blacklist = [];
|
|
||||||
await doNetworkRequest(sm,
|
|
||||||
req: () => postWithCreadentials(
|
|
||||||
path: 'listRooms',
|
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
body: {}),
|
body: {}),
|
||||||
onOK: (body) {
|
onOK: (body) async {
|
||||||
final List<Room> list = body['data'].map<Room>((json) {
|
// parse rooms
|
||||||
return Room.fromJSON(json);
|
final list = body['data'];
|
||||||
}).toList();
|
|
||||||
for (Room r in list) {
|
|
||||||
blacklist.add(r);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// process the list of public rooms
|
// try to fetch a list of rooms the user is a member of
|
||||||
final List<Room> builder = [];
|
// use an empty blacklist when request is not successful
|
||||||
processor:
|
final List<Room> blacklist = [];
|
||||||
for (dynamic raw in list) {
|
await doNetworkRequest(sm,
|
||||||
try {
|
req: () => postWithCreadentials(
|
||||||
final room = Room.fromJSON(raw);
|
path: 'listRooms',
|
||||||
|
credentials: user,
|
||||||
|
target: user.server,
|
||||||
|
body: {}),
|
||||||
|
onOK: (body) {
|
||||||
|
final List<Room> list = body['data'].map<Room>((json) {
|
||||||
|
return Room.fromJSON(json);
|
||||||
|
}).toList();
|
||||||
|
for (Room r in list) {
|
||||||
|
blacklist.add(r);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// figure out if room is on blacklist
|
// process the list of public rooms
|
||||||
// only add room to list,
|
final List<Room> builder = [];
|
||||||
// if not on blacklist
|
processor:
|
||||||
for (Room r in blacklist) {
|
for (dynamic raw in list) {
|
||||||
if (r == room) {
|
try {
|
||||||
// server on white list
|
final room = Room.fromJSON(raw);
|
||||||
// move to next iteration on outer for loop
|
|
||||||
continue processor;
|
// figure out if room is on blacklist
|
||||||
|
// only add room to list,
|
||||||
|
// if not on blacklist
|
||||||
|
for (Room r in blacklist) {
|
||||||
|
if (r == room) {
|
||||||
|
// server on white list
|
||||||
|
// move to next iteration on outer for loop
|
||||||
|
continue processor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
builder.add(room);
|
builder.add(room);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// ignore room
|
// ignore room
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
setState(() {
|
||||||
setState(() {
|
|
||||||
rooms = builder;
|
rooms = builder;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -90,8 +90,8 @@ class _JoinRoomPageState 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;
|
||||||
|
@ -118,182 +118,189 @@ class _JoinRoomPageState extends State {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuAnchor(
|
MenuAnchor(
|
||||||
builder: (ctx, controller, child) {
|
builder: (ctx, controller, child) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (controller.isOpen) {
|
if (controller.isOpen) {
|
||||||
controller.close();
|
controller.close();
|
||||||
} else {
|
} else {
|
||||||
controller.open();
|
controller.open();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
menuChildren: [
|
menuChildren: [
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(Icons.drafts),
|
leadingIcon: const Icon(Icons.drafts),
|
||||||
child: Text(AppLocalizations.of(context)!.joinRoomInvite),
|
child: Text(AppLocalizations.of(context)!.joinRoomInvite),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show settings screen
|
// show settings screen
|
||||||
context.goNamed('join-room-ota');
|
context.goNamed('join-room-ota');
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: rooms.isEmpty
|
body: rooms.isEmpty
|
||||||
? Center(
|
? Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(AppLocalizations.of(context)!.noNewRoomsFound, style: textTheme.titleLarge),
|
Text(AppLocalizations.of(context)!.noNewRoomsFound,
|
||||||
],
|
style: textTheme.titleLarge),
|
||||||
))
|
],
|
||||||
: ListView.builder(
|
))
|
||||||
itemCount: rooms.length,
|
: ListView.builder(
|
||||||
itemBuilder: (ctx, i) {
|
itemCount: rooms.length,
|
||||||
final room = rooms[i];
|
itemBuilder: (ctx, i) {
|
||||||
return Card(
|
final room = rooms[i];
|
||||||
margin: const EdgeInsets.all(8.0),
|
return Card(
|
||||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
margin: const EdgeInsets.all(8.0),
|
||||||
semanticContainer: true,
|
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||||
child: InkWell(
|
semanticContainer: true,
|
||||||
onTap: () {
|
child: InkWell(
|
||||||
// show modalBottomSheet
|
onTap: () {
|
||||||
// with room information
|
// show modalBottomSheet
|
||||||
// and join button
|
// with room information
|
||||||
showModalBottomSheet(
|
// and join button
|
||||||
context: ctx,
|
showModalBottomSheet(
|
||||||
builder: (ctx) {
|
context: ctx,
|
||||||
return BottomSheet(
|
builder: (ctx) {
|
||||||
onClosing: () {},
|
return BottomSheet(
|
||||||
builder: (ctx) {
|
onClosing: () {},
|
||||||
return Column(
|
builder: (ctx) {
|
||||||
crossAxisAlignment:
|
return Column(
|
||||||
CrossAxisAlignment.center,
|
crossAxisAlignment:
|
||||||
mainAxisAlignment:
|
CrossAxisAlignment.center,
|
||||||
MainAxisAlignment.center,
|
mainAxisAlignment:
|
||||||
children: [
|
MainAxisAlignment.center,
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.all(14),
|
Padding(
|
||||||
child: Column(children: [
|
padding: const EdgeInsets.all(14),
|
||||||
// room icon
|
child: Column(children: [
|
||||||
SvgPicture.asset(
|
// room icon
|
||||||
(room.icon?.img)!,
|
SvgPicture.asset(
|
||||||
width: smallest * 0.2,
|
(room.icon?.img)!,
|
||||||
height: smallest * 0.2,
|
width: smallest * 0.2,
|
||||||
),
|
height: smallest * 0.2,
|
||||||
// room name
|
),
|
||||||
Text(
|
// room name
|
||||||
room.name,
|
Text(
|
||||||
style: textTheme.displayMedium,
|
room.name,
|
||||||
),
|
style: textTheme.displayMedium,
|
||||||
Text(
|
),
|
||||||
'${room.id}@${room.serverTag}',
|
Text(
|
||||||
style: textTheme.labelSmall,
|
'${room.id}@${room.serverTag}',
|
||||||
),
|
style: textTheme.labelSmall,
|
||||||
// description
|
),
|
||||||
Text(room.description,
|
// description
|
||||||
style: textTheme.bodyLarge),
|
Text(room.description,
|
||||||
// visibility
|
style: textTheme.bodyLarge),
|
||||||
Row(
|
// visibility
|
||||||
mainAxisAlignment:
|
Row(
|
||||||
MainAxisAlignment.center,
|
mainAxisAlignment:
|
||||||
children: [
|
MainAxisAlignment.center,
|
||||||
Icon(room.visibility?.icon),
|
children: [
|
||||||
Text((room
|
Icon(room.visibility?.icon),
|
||||||
.visibility?.text(context))!),
|
Text((room.visibility
|
||||||
]),
|
?.text(context))!),
|
||||||
])),
|
]),
|
||||||
// action buttons
|
])),
|
||||||
Row(
|
// action buttons
|
||||||
mainAxisAlignment:
|
Row(
|
||||||
MainAxisAlignment.center,
|
mainAxisAlignment:
|
||||||
children: [
|
MainAxisAlignment.center,
|
||||||
// cancel button
|
children: [
|
||||||
Padding(
|
// cancel button
|
||||||
padding:
|
Padding(
|
||||||
const EdgeInsets.all(14),
|
padding:
|
||||||
child: ElevatedButton.icon(
|
const EdgeInsets.all(14),
|
||||||
icon:
|
child: ElevatedButton.icon(
|
||||||
const Icon(Icons.close),
|
icon:
|
||||||
label: Text(AppLocalizations.of(context)!.cancel),
|
const Icon(Icons.close),
|
||||||
onPressed: () {
|
label: Text(
|
||||||
// close sheet
|
AppLocalizations.of(
|
||||||
Navigator.pop(context);
|
context)!
|
||||||
},
|
.cancel),
|
||||||
)),
|
onPressed: () {
|
||||||
// join room button
|
// close sheet
|
||||||
Padding(
|
Navigator.pop(context);
|
||||||
padding:
|
},
|
||||||
const EdgeInsets.all(14),
|
)),
|
||||||
child: FilledButton.icon(
|
// join room button
|
||||||
icon:
|
Padding(
|
||||||
const Icon(Icons.check),
|
padding:
|
||||||
label: Text(AppLocalizations.of(context)!.joinRoom),
|
const EdgeInsets.all(14),
|
||||||
onPressed: () async {
|
child: FilledButton.icon(
|
||||||
final scaffMgr =
|
icon:
|
||||||
ScaffoldMessenger.of(
|
const Icon(Icons.check),
|
||||||
context);
|
label: Text(
|
||||||
|
AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.joinRoom),
|
||||||
|
onPressed: () async {
|
||||||
|
final scaffMgr =
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context);
|
||||||
|
|
||||||
final nav =
|
final nav =
|
||||||
Navigator.of(context);
|
Navigator.of(context);
|
||||||
final user =
|
final user =
|
||||||
context.read<User>();
|
context.read<User>();
|
||||||
final router =
|
final router =
|
||||||
GoRouter.of(context);
|
GoRouter.of(context);
|
||||||
|
|
||||||
doNetworkRequest(scaffMgr,
|
doNetworkRequest(scaffMgr,
|
||||||
req: () =>
|
req: () =>
|
||||||
postWithCreadentials(
|
postWithCreadentials(
|
||||||
credentials:
|
credentials:
|
||||||
user,
|
user,
|
||||||
target: user
|
target: user
|
||||||
.server,
|
.server,
|
||||||
path:
|
path:
|
||||||
'joinPublicRoom',
|
'joinPublicRoom',
|
||||||
body: {
|
body: {
|
||||||
'room':
|
'room':
|
||||||
room.id,
|
room.id,
|
||||||
'server': room
|
'server': room
|
||||||
.serverTag
|
.serverTag
|
||||||
}),
|
}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
await room.toDisk();
|
await room.toDisk();
|
||||||
nav.pop();
|
nav.pop();
|
||||||
router
|
router
|
||||||
.pushReplacementNamed(
|
.pushReplacementNamed(
|
||||||
'room',
|
'room',
|
||||||
params: {
|
params: {
|
||||||
'server': room
|
'server': room
|
||||||
.serverTag,
|
.serverTag,
|
||||||
'id': room.id
|
'id': room.id
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(room.name),
|
||||||
|
visualDensity: const VisualDensity(vertical: 3),
|
||||||
|
subtitle: Text(room.description),
|
||||||
|
leading: AspectRatio(
|
||||||
|
aspectRatio: 1 / 1,
|
||||||
|
child: SvgPicture.asset("${room.icon?.img}"),
|
||||||
|
),
|
||||||
|
hoverColor: Colors.transparent,
|
||||||
|
))));
|
||||||
},
|
},
|
||||||
child: Container(
|
),
|
||||||
padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
|
|
||||||
child: ListTile(
|
|
||||||
title: Text(room.name),
|
|
||||||
visualDensity: const VisualDensity(vertical: 3),
|
|
||||||
subtitle: Text(room.description),
|
|
||||||
leading: AspectRatio(
|
|
||||||
aspectRatio: 1 / 1,
|
|
||||||
child: SvgPicture.asset("${room.icon?.img}"),
|
|
||||||
),
|
|
||||||
hoverColor: Colors.transparent,
|
|
||||||
))));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
label: Text(AppLocalizations.of(context)!.newRoom),
|
label: Text(AppLocalizations.of(context)!.newRoom),
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
|
|
|
@ -34,72 +34,69 @@ class _RoomPageState extends State<RoomPage> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final diskRoom =
|
final diskRoom =
|
||||||
await Room.fromDisk(serverTag: widget.server, id: widget.tag);
|
await Room.fromDisk(serverTag: widget.server, id: widget.tag);
|
||||||
setState(() {
|
setState(() {
|
||||||
room = diskRoom;
|
room = diskRoom;
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
doNetworkRequest(sm,
|
doNetworkRequest(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}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final info = RoomInfo.fromJSON(body['data']);
|
final info = RoomInfo.fromJSON(body['data']);
|
||||||
final room = Room.fromJSON(body['data']);
|
final room = Room.fromJSON(body['data']);
|
||||||
|
|
||||||
room.toDisk();
|
room.toDisk();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
this.info = info;
|
this.info = info;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onNetworkErr: () {
|
||||||
|
// user offline
|
||||||
|
if (room == null) {
|
||||||
|
// no room data available
|
||||||
|
// NOTE: close room?
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onServerErr: (json) {
|
||||||
|
// user no longer in room
|
||||||
|
// TODO: close room
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
},
|
|
||||||
onNetworkErr: () {
|
|
||||||
// user offline
|
|
||||||
if (room == null) {
|
|
||||||
// no room data available
|
|
||||||
// NOTE: close room?
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
onServerErr: (json) {
|
|
||||||
// user no longer in room
|
|
||||||
// TODO: close room
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
room = Room(
|
room = Room(id: widget.tag, serverTag: widget.server);
|
||||||
id: widget.tag,
|
|
||||||
serverTag: widget.server
|
|
||||||
);
|
|
||||||
|
|
||||||
_ctr.addListener(() {
|
_ctr.addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
page = _ctr.page?.toInt() ?? _ctr.initialPage;
|
page = _ctr.page?.toInt() ?? _ctr.initialPage;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Room.listen((_) async {
|
Room.listen((_) async {
|
||||||
// rooms changed on disk
|
// rooms changed on disk
|
||||||
// probably this one,
|
// probably this one,
|
||||||
// because it is currently open
|
// because it is currently open
|
||||||
// NOTE: might be a different room
|
// NOTE: might be a different room
|
||||||
// (if a background listener is implemented at some point,
|
// (if a background listener is implemented at some point,
|
||||||
// checking if this room changed might improve performance)
|
// checking if this room changed might improve performance)
|
||||||
try {
|
try {
|
||||||
final r = await Room.fromDisk(serverTag: widget.server, id: widget.tag);
|
final r = await Room.fromDisk(serverTag: widget.server, id: widget.tag);
|
||||||
setState(() {
|
setState(() {
|
||||||
room = r;
|
room = r;
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => fetchInfo());
|
WidgetsBinding.instance.addPostFrameCallback((_) => fetchInfo());
|
||||||
|
@ -123,27 +120,27 @@ class _RoomPageState extends State<RoomPage> {
|
||||||
bottomNavigationBar: NavigationBar(
|
bottomNavigationBar: NavigationBar(
|
||||||
onDestinationSelected: (int index) {
|
onDestinationSelected: (int index) {
|
||||||
_ctr.animateToPage(index,
|
_ctr.animateToPage(index,
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
duration: const Duration(milliseconds: 300));
|
duration: const Duration(milliseconds: 300));
|
||||||
},
|
},
|
||||||
selectedIndex: page,
|
selectedIndex: page,
|
||||||
destinations: [
|
destinations: [
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: const Icon(Icons.list),
|
icon: const Icon(Icons.list),
|
||||||
label: AppLocalizations.of(context)!.roomListTitle,
|
label: AppLocalizations.of(context)!.roomListTitle,
|
||||||
tooltip: AppLocalizations.of(context)!.roomListSubtitle),
|
tooltip: AppLocalizations.of(context)!.roomListSubtitle),
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: const Icon(Icons.inventory_2),
|
icon: const Icon(Icons.inventory_2),
|
||||||
label: AppLocalizations.of(context)!.roomProductsTitle,
|
label: AppLocalizations.of(context)!.roomProductsTitle,
|
||||||
tooltip: AppLocalizations.of(context)!.roomProductsSubtitle),
|
tooltip: AppLocalizations.of(context)!.roomProductsSubtitle),
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: const Icon(Icons.category),
|
icon: const Icon(Icons.category),
|
||||||
label: AppLocalizations.of(context)!.roomCategoriesTitle,
|
label: AppLocalizations.of(context)!.roomCategoriesTitle,
|
||||||
tooltip: AppLocalizations.of(context)!.roomCategoriesSubtitle),
|
tooltip: AppLocalizations.of(context)!.roomCategoriesSubtitle),
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: const Icon(Icons.info_rounded),
|
icon: const Icon(Icons.info_rounded),
|
||||||
label: AppLocalizations.of(context)!.roomAboutTitle,
|
label: AppLocalizations.of(context)!.roomAboutTitle,
|
||||||
tooltip: AppLocalizations.of(context)!.roomAboutSubtitle),
|
tooltip: AppLocalizations.of(context)!.roomAboutSubtitle),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,7 +38,7 @@ class _NewRoomPageState extends State<NewRoomPage> {
|
||||||
_ctrIcon = room.icon!;
|
_ctrIcon = room.icon!;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
this.room = room;
|
this.room = room;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,17 +50,17 @@ class _NewRoomPageState extends State<NewRoomPage> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final diskRoom =
|
final diskRoom =
|
||||||
await Room.fromDisk(serverTag: widget.server!, id: widget.tag!);
|
await Room.fromDisk(serverTag: widget.server!, id: widget.tag!);
|
||||||
initFromRoom(diskRoom);
|
initFromRoom(diskRoom);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
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}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final room = Room.fromJSON(body['data']);
|
final room = Room.fromJSON(body['data']);
|
||||||
room.toDisk();
|
room.toDisk();
|
||||||
|
@ -70,9 +70,9 @@ class _NewRoomPageState extends State<NewRoomPage> {
|
||||||
// no room data available
|
// no room data available
|
||||||
// use data from disk
|
// use data from disk
|
||||||
(() async {
|
(() async {
|
||||||
// no room data available
|
// no room data available
|
||||||
// close screen
|
// close screen
|
||||||
router.pushReplacementNamed('home');
|
router.pushReplacementNamed('home');
|
||||||
})();
|
})();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -89,9 +89,9 @@ class _NewRoomPageState extends State<NewRoomPage> {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (isEditPage()) {
|
if (isEditPage()) {
|
||||||
fetchInfo();
|
fetchInfo();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,235 +102,233 @@ class _NewRoomPageState extends State<NewRoomPage> {
|
||||||
@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(AppLocalizations.of(context)!.loading,
|
Text(AppLocalizations.of(context)!.loading,
|
||||||
style: textTheme.titleLarge),
|
style: textTheme.titleLarge),
|
||||||
])))
|
])))
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isEditPage()
|
title: Text(isEditPage()
|
||||||
? AppLocalizations.of(context)!.editRoomMetadata
|
? AppLocalizations.of(context)!.editRoomMetadata
|
||||||
: AppLocalizations.of(context)!.newRoom),
|
: AppLocalizations.of(context)!.newRoom),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 400),
|
constraints: const BoxConstraints(maxWidth: 400),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
_ctrIcon.img,
|
_ctrIcon.img,
|
||||||
width: smallest * 0.3,
|
width: smallest * 0.3,
|
||||||
height: smallest * 0.3,
|
height: smallest * 0.3,
|
||||||
),
|
),
|
||||||
tooltip: AppLocalizations.of(context)!
|
tooltip: AppLocalizations.of(context)!
|
||||||
.changeRoomIcon,
|
.changeRoomIcon,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) =>
|
builder: (ctx) =>
|
||||||
RoomIconPicker(onSelect: (icon) {
|
RoomIconPicker(onSelect: (icon) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ctrIcon = icon;
|
_ctrIcon = icon;
|
||||||
});
|
});
|
||||||
context.pop();
|
context.pop();
|
||||||
}));
|
}));
|
||||||
}),
|
}),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
enabled: !isEditPage(),
|
enabled: !isEditPage(),
|
||||||
controller: _ctrID,
|
controller: _ctrID,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.fact_check),
|
prefixIcon: const Icon(Icons.fact_check),
|
||||||
labelText: AppLocalizations.of(context)!
|
labelText: AppLocalizations.of(context)!
|
||||||
.inputRoomIdLabel,
|
.inputRoomIdLabel,
|
||||||
hintText: AppLocalizations.of(context)!
|
hintText: AppLocalizations.of(context)!
|
||||||
.inputRoomIdHint,
|
.inputRoomIdHint,
|
||||||
helperText: AppLocalizations.of(context)!
|
helperText: AppLocalizations.of(context)!
|
||||||
.inputRoomIdHelp,
|
.inputRoomIdHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrName,
|
controller: _ctrName,
|
||||||
keyboardType: TextInputType.name,
|
keyboardType: TextInputType.name,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.badge),
|
prefixIcon: const Icon(Icons.badge),
|
||||||
labelText: AppLocalizations.of(context)!
|
labelText: AppLocalizations.of(context)!
|
||||||
.inputRoomNameLabel,
|
.inputRoomNameLabel,
|
||||||
hintText: AppLocalizations.of(context)!
|
hintText: AppLocalizations.of(context)!
|
||||||
.inputRoomNameHint,
|
.inputRoomNameHint,
|
||||||
helperText: AppLocalizations.of(context)!
|
helperText: AppLocalizations.of(context)!
|
||||||
.inputRoomNameHelp,
|
.inputRoomNameHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _ctrDescription,
|
controller: _ctrDescription,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: AppLocalizations.of(context)!
|
labelText: AppLocalizations.of(context)!
|
||||||
.inputRoomDescriptionLabel,
|
.inputRoomDescriptionLabel,
|
||||||
hintText: AppLocalizations.of(context)!
|
hintText: AppLocalizations.of(context)!
|
||||||
.inputRoomDescriptionHint,
|
.inputRoomDescriptionHint,
|
||||||
helperText: AppLocalizations.of(context)!
|
helperText: AppLocalizations.of(context)!
|
||||||
.inputRoomDescriptionHelp,
|
.inputRoomDescriptionHelp,
|
||||||
prefixIcon: const Icon(Icons.dns),
|
prefixIcon: const Icon(Icons.dns),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...(!isEditPage())
|
...(!isEditPage())
|
||||||
? [
|
? [
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context)!
|
AppLocalizations.of(context)!
|
||||||
.roomVisibilityTitle,
|
.roomVisibilityTitle,
|
||||||
style: textTheme.labelLarge),
|
style: textTheme.labelLarge),
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context)!
|
AppLocalizations.of(context)!
|
||||||
.roomVisibilitySubtitle,
|
.roomVisibilitySubtitle,
|
||||||
style: textTheme.bodySmall),
|
style: textTheme.bodySmall),
|
||||||
SegmentedButton<RoomVisibility>(
|
SegmentedButton<RoomVisibility>(
|
||||||
showSelectedIcon: true,
|
showSelectedIcon: true,
|
||||||
multiSelectionEnabled: false,
|
multiSelectionEnabled: false,
|
||||||
emptySelectionAllowed: false,
|
emptySelectionAllowed: false,
|
||||||
segments:
|
segments:
|
||||||
RoomVisibility.list().map((vis) {
|
RoomVisibility.list().map((vis) {
|
||||||
return ButtonSegment<
|
return ButtonSegment<
|
||||||
RoomVisibility>(
|
RoomVisibility>(
|
||||||
value: vis,
|
value: vis,
|
||||||
label: Text(vis.text(context)),
|
label: Text(vis.text(context)),
|
||||||
icon: Icon(vis.icon));
|
icon: Icon(vis.icon));
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onSelectionChanged: ((vset) {
|
onSelectionChanged: ((vset) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ctrVis = vset.single;
|
_ctrVis = vset.single;
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
selected: {_ctrVis},
|
selected: {_ctrVis},
|
||||||
selectedIcon: Icon(_ctrVis.icon),
|
selectedIcon: Icon(_ctrVis.icon),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
],
|
],
|
||||||
))))),
|
))))),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final scaffMgr = ScaffoldMessenger.of(context);
|
final scaffMgr = ScaffoldMessenger.of(context);
|
||||||
final router = GoRouter.of(context);
|
final router = GoRouter.of(context);
|
||||||
final trans = AppLocalizations.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) {
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: trans!.errorNoRoomName, action: trans.ok);
|
text: trans!.errorNoRoomName, action: trans.ok);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
|
|
||||||
if (isEditPage()) {
|
if (isEditPage()) {
|
||||||
final nav = Navigator.of(context);
|
final nav = Navigator.of(context);
|
||||||
Room clone = room!;
|
Room clone = room!;
|
||||||
clone.name = _ctrName.text;
|
clone.name = _ctrName.text;
|
||||||
clone.description = _ctrDescription.text;
|
clone.description = _ctrDescription.text;
|
||||||
clone.icon = _ctrIcon;
|
clone.icon = _ctrIcon;
|
||||||
|
|
||||||
doNetworkRequest(scaffMgr,
|
doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
target: user.server,
|
target: user.server,
|
||||||
credentials: user,
|
credentials: user,
|
||||||
path: 'changeRoomMeta',
|
path: 'changeRoomMeta',
|
||||||
body: {
|
body: {
|
||||||
'room': clone.id,
|
'room': clone.id,
|
||||||
'server': clone.serverTag,
|
'server': clone.serverTag,
|
||||||
'title': clone.name,
|
'title': clone.name,
|
||||||
'description': clone.description,
|
'description': clone.description,
|
||||||
'icon': clone.icon?.type,
|
'icon': clone.icon?.type,
|
||||||
}),
|
}),
|
||||||
onOK: (_) async {
|
onOK: (_) async {
|
||||||
// room was created
|
// room was created
|
||||||
// save room
|
// save room
|
||||||
await clone.toDisk();
|
await clone.toDisk();
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// new room specific tests & request
|
||||||
|
|
||||||
} else {
|
// ID should be at least three characters long
|
||||||
// new room specific tests & request
|
if (_ctrID.text.length < 3) {
|
||||||
|
showSimpleSnackbar(scaffMgr,
|
||||||
|
text: _ctrID.text.isEmpty
|
||||||
|
? trans!.errorNoRoomId
|
||||||
|
: trans!.errorRoomIdLength,
|
||||||
|
action: trans.ok);
|
||||||
|
|
||||||
// ID should be at least three characters long
|
return;
|
||||||
if (_ctrID.text.length < 3) {
|
}
|
||||||
showSimpleSnackbar(scaffMgr,
|
|
||||||
text: _ctrID.text.isEmpty
|
|
||||||
? trans!.errorNoRoomId
|
|
||||||
: trans!.errorRoomIdLength,
|
|
||||||
action: trans.ok);
|
|
||||||
|
|
||||||
return;
|
final room = Room(
|
||||||
}
|
id: _ctrID.text,
|
||||||
|
serverTag: user.server.tag,
|
||||||
|
name: _ctrName.text,
|
||||||
|
description: _ctrDescription.text,
|
||||||
|
icon: _ctrIcon,
|
||||||
|
visibility: _ctrVis);
|
||||||
|
|
||||||
final room = Room(
|
doNetworkRequest(scaffMgr,
|
||||||
id: _ctrID.text,
|
req: () => postWithCreadentials(
|
||||||
serverTag: user.server.tag,
|
target: user.server,
|
||||||
name: _ctrName.text,
|
credentials: user,
|
||||||
description: _ctrDescription.text,
|
path: 'createRoom',
|
||||||
icon: _ctrIcon,
|
body: {
|
||||||
visibility: _ctrVis);
|
'room': room.id,
|
||||||
|
'title': room.name,
|
||||||
doNetworkRequest(scaffMgr,
|
'description': room.description,
|
||||||
req: () => postWithCreadentials(
|
'icon': room.icon?.type,
|
||||||
target: user.server,
|
'visibility': room.visibility?.type
|
||||||
credentials: user,
|
}),
|
||||||
path: 'createRoom',
|
onOK: (_) async {
|
||||||
body: {
|
// room was created
|
||||||
'room': room.id,
|
// save room
|
||||||
'title': room.name,
|
await room.toDisk();
|
||||||
'description': room.description,
|
// move to home page
|
||||||
'icon': room.icon?.type,
|
router.pushReplacementNamed('home');
|
||||||
'visibility': room.visibility?.type
|
});
|
||||||
}),
|
}
|
||||||
onOK: (_) async {
|
},
|
||||||
// room was created
|
label: Text(isEditPage()
|
||||||
// save room
|
? AppLocalizations.of(context)!.editRoomMetadataShort
|
||||||
await room.toDisk();
|
: AppLocalizations.of(context)!.createRoomShort),
|
||||||
// move to home page
|
icon: Icon(isEditPage() ? Icons.edit : Icons.add)),
|
||||||
router.pushReplacementNamed('home');
|
);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
label: Text(isEditPage()
|
|
||||||
? AppLocalizations.of(context)!.editRoomMetadataShort
|
|
||||||
: AppLocalizations.of(context)!.createRoomShort),
|
|
||||||
icon: Icon(isEditPage() ? Icons.edit : Icons.add)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,19 +24,19 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
@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(width, height);
|
double smallest = min(width, height);
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
// room meta display
|
// room meta display
|
||||||
...(widget.room != null)
|
...(widget.room != null)
|
||||||
? [
|
? [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -60,27 +60,27 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: SegmentedButton<int>(
|
child: SegmentedButton<int>(
|
||||||
showSelectedIcon: true,
|
showSelectedIcon: true,
|
||||||
multiSelectionEnabled: false,
|
multiSelectionEnabled: false,
|
||||||
emptySelectionAllowed: false,
|
emptySelectionAllowed: false,
|
||||||
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(context)),
|
label: Text(vis.text(context)),
|
||||||
icon: Icon(vis.icon));
|
icon: Icon(vis.icon));
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onSelectionChanged: ((vset) {
|
onSelectionChanged: ((vset) {
|
||||||
// check permission
|
// check permission
|
||||||
// only show confirm dialog when user
|
// only show confirm dialog when user
|
||||||
// is admin, owner or has CHANGE_ADMIN permission
|
// is admin, owner or has CHANGE_ADMIN permission
|
||||||
if (widget.info == null ||
|
if (widget.info == null ||
|
||||||
(!(widget.info?.isAdmin ?? false) &&
|
(!(widget.info?.isAdmin ?? false) &&
|
||||||
!(widget.info?.isOwner ?? false) &&
|
!(widget.info?.isOwner ?? false) &&
|
||||||
((widget.info?.permissions)! &
|
((widget.info?.permissions)! &
|
||||||
RoomPermission.ota ==
|
RoomPermission.ota ==
|
||||||
0))) {
|
0))) {
|
||||||
// action not permitted
|
// action not permitted
|
||||||
// NOTE: no error dialog should be shown
|
// NOTE: no error dialog should be shown
|
||||||
// because the action is supposed to be hidden
|
// because the action is supposed to be hidden
|
||||||
|
@ -89,226 +89,247 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
|
|
||||||
final vis = RoomVisibility(vset.first);
|
final vis = RoomVisibility(vset.first);
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: Text(AppLocalizations.of(context)!.changeRoomVisibilityTitle),
|
title: Text(AppLocalizations.of(context)!
|
||||||
content: Text(AppLocalizations.of(context)!.changeRoomVisibilitySubtitle(vis.text(context))),
|
.changeRoomVisibilityTitle),
|
||||||
actions: [
|
content: Text(
|
||||||
TextButton(
|
AppLocalizations.of(context)!
|
||||||
onPressed: () {
|
.changeRoomVisibilitySubtitle(
|
||||||
context.pop();
|
vis.text(context))),
|
||||||
},
|
actions: [
|
||||||
child: Text(AppLocalizations.of(context)!.cancel),
|
TextButton(
|
||||||
),
|
onPressed: () {
|
||||||
FilledButton(
|
context.pop();
|
||||||
onPressed: () async {
|
|
||||||
final scaffMgr =
|
|
||||||
ScaffoldMessenger.of(context);
|
|
||||||
final nav = Navigator.of(context);
|
|
||||||
final user = context.read<User>();
|
|
||||||
|
|
||||||
doNetworkRequest(scaffMgr,
|
|
||||||
req: () => postWithCreadentials(
|
|
||||||
path: 'setVisibility',
|
|
||||||
target: user.server,
|
|
||||||
body: {
|
|
||||||
'room': widget.room?.id,
|
|
||||||
'server': (widget
|
|
||||||
.room?.serverTag)!,
|
|
||||||
'visibility': vset.first
|
|
||||||
},
|
},
|
||||||
credentials: user),
|
child: Text(
|
||||||
onOK: (_) {
|
AppLocalizations.of(context)!
|
||||||
Room r = widget.room!;
|
.cancel),
|
||||||
r.visibility = vis;
|
),
|
||||||
r.toDisk();
|
FilledButton(
|
||||||
},
|
onPressed: () async {
|
||||||
after: () {
|
final scaffMgr =
|
||||||
nav.pop();
|
ScaffoldMessenger.of(context);
|
||||||
});
|
final nav = Navigator.of(context);
|
||||||
},
|
final user = context.read<User>();
|
||||||
child: Text(AppLocalizations.of(context)!.ok),
|
|
||||||
)
|
doNetworkRequest(scaffMgr,
|
||||||
],
|
req: () => postWithCreadentials(
|
||||||
));
|
path: 'setVisibility',
|
||||||
}),
|
target: user.server,
|
||||||
selected: {(widget.room?.visibility?.type)!},
|
body: {
|
||||||
selectedIcon: Icon((widget.room?.visibility?.icon)!),
|
'room': widget.room?.id,
|
||||||
)),
|
'server': (widget
|
||||||
|
.room?.serverTag)!,
|
||||||
|
'visibility': vset.first
|
||||||
|
},
|
||||||
|
credentials: user),
|
||||||
|
onOK: (_) {
|
||||||
|
Room r = widget.room!;
|
||||||
|
r.visibility = vis;
|
||||||
|
r.toDisk();
|
||||||
|
},
|
||||||
|
after: () {
|
||||||
|
nav.pop();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.ok),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}),
|
||||||
|
selected: {(widget.room?.visibility?.type)!},
|
||||||
|
selectedIcon: Icon((widget.room?.visibility?.icon)!),
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// edit room meta button
|
// edit room meta button
|
||||||
...(widget.info != null &&
|
...(widget.info != null &&
|
||||||
((widget.info?.isAdmin ?? false) ||
|
((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
((widget.info?.permissions)! &
|
((widget.info?.permissions)! &
|
||||||
RoomPermission.changeMeta !=
|
RoomPermission.changeMeta !=
|
||||||
0)))
|
0)))
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: Text(AppLocalizations.of(context)!.editRoomMetadata),
|
title: Text(
|
||||||
subtitle: Text(AppLocalizations.of(context)!.editRoomMetadataSubtitle),
|
AppLocalizations.of(context)!.editRoomMetadata),
|
||||||
onTap: () {
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
// show edit room screen
|
.editRoomMetadataSubtitle),
|
||||||
context.goNamed('edit-room', params: {
|
onTap: () {
|
||||||
|
// show edit room screen
|
||||||
|
context.goNamed('edit-room', params: {
|
||||||
'server': (widget.room?.serverTag)!,
|
'server': (widget.room?.serverTag)!,
|
||||||
'id': (widget.room?.id)!
|
'id': (widget.room?.id)!
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
// open members view
|
// open members view
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: Text(AppLocalizations.of(context)!.showRoomMembers),
|
title: Text(AppLocalizations.of(context)!.showRoomMembers),
|
||||||
subtitle: Text(AppLocalizations.of(context)!.showRoomMembersSubtitle),
|
subtitle:
|
||||||
onTap: () {
|
Text(AppLocalizations.of(context)!.showRoomMembersSubtitle),
|
||||||
// open member view screen
|
onTap: () {
|
||||||
context.goNamed('room-members', params: {
|
// open member view screen
|
||||||
'server': (widget.room?.serverTag)!,
|
context.goNamed('room-members', params: {
|
||||||
'id': (widget.room?.id)!
|
'server': (widget.room?.serverTag)!,
|
||||||
});
|
'id': (widget.room?.id)!
|
||||||
},
|
});
|
||||||
),
|
},
|
||||||
// edit default member permission
|
),
|
||||||
...(widget.info != null &&
|
// edit default member permission
|
||||||
((widget.info?.isAdmin ?? false) ||
|
...(widget.info != null &&
|
||||||
(widget.info?.isOwner ?? false) ||
|
((widget.info?.isAdmin ?? false) ||
|
||||||
((widget.info?.permissions)! &
|
(widget.info?.isOwner ?? false) ||
|
||||||
RoomPermission.changeAdmin !=
|
((widget.info?.permissions)! &
|
||||||
0)))
|
RoomPermission.changeAdmin !=
|
||||||
|
0)))
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: Text(AppLocalizations.of(context)!.editRoomPermissions),
|
title: Text(
|
||||||
subtitle: Text(AppLocalizations.of(context)!.editRoomPermissionsSubtitle),
|
AppLocalizations.of(context)!.editRoomPermissions),
|
||||||
onTap: () {
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
// show checkbox screen
|
.editRoomPermissionsSubtitle),
|
||||||
context.goNamed('room-permissions', params: {
|
onTap: () {
|
||||||
|
// show checkbox screen
|
||||||
|
context.goNamed('room-permissions', params: {
|
||||||
'server': (widget.room?.serverTag)!,
|
'server': (widget.room?.serverTag)!,
|
||||||
'id': (widget.room?.id)!
|
'id': (widget.room?.id)!
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
...(widget.info != null &&
|
...(widget.info != null &&
|
||||||
((widget.info?.isAdmin ?? false) ||
|
((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
((widget.info?.permissions)! & RoomPermission.ota !=
|
((widget.info?.permissions)! & RoomPermission.ota !=
|
||||||
0)))
|
0)))
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: Text(AppLocalizations.of(context)!.manageRoomOTA),
|
title:
|
||||||
subtitle: Text(AppLocalizations.of(context)!.manageRoomOTASubtitle),
|
Text(AppLocalizations.of(context)!.manageRoomOTA),
|
||||||
onTap: () {
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
// show manage ota screen
|
.manageRoomOTASubtitle),
|
||||||
context.goNamed('room-ota', params: {
|
onTap: () {
|
||||||
|
// show manage ota screen
|
||||||
|
context.goNamed('room-ota', params: {
|
||||||
'server': (widget.room?.serverTag)!,
|
'server': (widget.room?.serverTag)!,
|
||||||
'id': (widget.room?.id)!
|
'id': (widget.room?.id)!
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
title: Text(AppLocalizations.of(context)!.manageRoomInvites),
|
title: Text(
|
||||||
subtitle: Text(AppLocalizations.of(context)!.manageRoomInvitesSubtitle),
|
AppLocalizations.of(context)!.manageRoomInvites),
|
||||||
onTap: () {
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
// show manage ota screen
|
.manageRoomInvitesSubtitle),
|
||||||
context.goNamed('room-invite', params: {
|
onTap: () {
|
||||||
|
// show manage ota screen
|
||||||
|
context.goNamed('room-invite', params: {
|
||||||
'server': (widget.room?.serverTag)!,
|
'server': (widget.room?.serverTag)!,
|
||||||
'id': (widget.room?.id)!
|
'id': (widget.room?.id)!
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
|
|
||||||
...(widget.info != null)
|
...(widget.info != null)
|
||||||
? [
|
? [
|
||||||
Padding(
|
Padding(
|
||||||
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)!)
|
||||||
? AppLocalizations.of(context)!.deleteRoom
|
? AppLocalizations.of(context)!.deleteRoom
|
||||||
: AppLocalizations.of(context)!.leaveRoom),
|
: 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)!)
|
||||||
? AppLocalizations.of(context)!.deleteRoom
|
? AppLocalizations.of(context)!.deleteRoom
|
||||||
: AppLocalizations.of(context)!.leaveRoom),
|
: AppLocalizations.of(context)!.leaveRoom),
|
||||||
content: Text(((widget.info?.isOwner)!)
|
content: Text(((widget.info?.isOwner)!)
|
||||||
? AppLocalizations.of(context)!.deleteRoomConfirm
|
? AppLocalizations.of(context)!
|
||||||
: AppLocalizations.of(context)!.leaveRoomConfirm),
|
.deleteRoomConfirm
|
||||||
actions: [
|
: AppLocalizations.of(context)!
|
||||||
TextButton(
|
.leaveRoomConfirm),
|
||||||
onPressed: () {
|
actions: [
|
||||||
// close popup
|
TextButton(
|
||||||
Navigator.of(ctx).pop();
|
onPressed: () {
|
||||||
},
|
// close popup
|
||||||
child: Text(AppLocalizations.of(context)!.cancel),
|
Navigator.of(ctx).pop();
|
||||||
),
|
},
|
||||||
FilledButton(
|
child: Text(
|
||||||
onPressed: () async {
|
AppLocalizations.of(context)!.cancel),
|
||||||
// send request
|
),
|
||||||
final scaffMgr =
|
FilledButton(
|
||||||
ScaffoldMessenger.of(ctx);
|
onPressed: () async {
|
||||||
final nav = Navigator.of(ctx);
|
// send request
|
||||||
final router = GoRouter.of(context);
|
final scaffMgr =
|
||||||
final user = context.read<User>();
|
ScaffoldMessenger.of(ctx);
|
||||||
|
final nav = Navigator.of(ctx);
|
||||||
|
final router = GoRouter.of(context);
|
||||||
|
final user = context.read<User>();
|
||||||
|
|
||||||
doNetworkRequest(scaffMgr,
|
doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
path: ((widget.info?.isOwner)!)
|
path: ((widget.info?.isOwner)!)
|
||||||
? 'deleteRoom'
|
? 'deleteRoom'
|
||||||
: 'leaveRoom',
|
: 'leaveRoom',
|
||||||
target: user.server,
|
target: user.server,
|
||||||
body: {
|
body: {
|
||||||
'room': widget.room?.id,
|
'room': widget.room?.id,
|
||||||
'server':
|
'server':
|
||||||
(widget.room?.serverTag)!,
|
(widget.room?.serverTag)!,
|
||||||
},
|
},
|
||||||
credentials: user),
|
credentials: user),
|
||||||
onOK: (_) async {
|
onOK: (_) async {
|
||||||
// try delete room from disk
|
// try delete room from disk
|
||||||
try {
|
try {
|
||||||
await widget.room?.removeDisk();
|
await widget.room?.removeDisk();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
// go back home
|
// go back home
|
||||||
router.pushReplacementNamed('home');
|
router.pushReplacementNamed('home');
|
||||||
},
|
},
|
||||||
after: () {
|
after: () {
|
||||||
// close popup
|
// close popup
|
||||||
nav.pop();
|
nav.pop();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Text(((widget.info?.isOwner)!)
|
child: Text(((widget.info?.isOwner)!)
|
||||||
? AppLocalizations.of(context)!.deleteRoomShort
|
? AppLocalizations.of(context)!
|
||||||
: AppLocalizations.of(context)!.leaveRoomShort),
|
.deleteRoomShort
|
||||||
)
|
: AppLocalizations.of(context)!
|
||||||
],
|
.leaveRoomShort),
|
||||||
));
|
)
|
||||||
},
|
],
|
||||||
))
|
));
|
||||||
|
},
|
||||||
|
))
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
])));
|
])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
fetchCategories();
|
fetchCategories();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,29 +36,29 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
|
||||||
// TODO: load cached rooms
|
// TODO: load cached rooms
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getCategories',
|
path: 'getCategories',
|
||||||
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
||||||
onOK: (json) {
|
onOK: (json) {
|
||||||
final resp = json['data']
|
final resp = json['data']
|
||||||
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
list = resp;
|
list = resp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: ReorderableListView.builder(
|
body: ReorderableListView.builder(
|
||||||
|
@ -70,13 +70,13 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
|
||||||
key: Key('cat-${item.id}'),
|
key: Key('cat-${item.id}'),
|
||||||
leading: Icon(Icons.square_rounded, color: item.color),
|
leading: Icon(Icons.square_rounded, color: item.color),
|
||||||
trailing: ((widget.info?.isAdmin ?? false) ||
|
trailing: ((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
((widget.info?.permissions)! &
|
((widget.info?.permissions)! &
|
||||||
RoomPermission.editRoomContent !=
|
RoomPermission.editRoomContent !=
|
||||||
0))
|
0))
|
||||||
? ReorderableDragStartListener(
|
? ReorderableDragStartListener(
|
||||||
index: index, child: const Icon(Icons.drag_handle))
|
index: index, child: const Icon(Icons.drag_handle))
|
||||||
: null,
|
: null,
|
||||||
title: Text(item.name),
|
title: Text(item.name),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// TODO show edit category popup
|
// TODO show edit category popup
|
||||||
|
@ -86,8 +86,8 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
|
||||||
if (!((widget.info?.isAdmin ?? false) ||
|
if (!((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
((widget.info?.permissions)! &
|
((widget.info?.permissions)! &
|
||||||
RoomPermission.editRoomContent !=
|
RoomPermission.editRoomContent !=
|
||||||
0))) {
|
0))) {
|
||||||
// user is not allowed to edit or delete categories
|
// user is not allowed to edit or delete categories
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -96,104 +96,104 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => BottomSheet(
|
builder: (context) => BottomSheet(
|
||||||
builder: (context) => Column(children: [
|
builder: (context) => Column(children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.square_rounded,
|
Icon(Icons.square_rounded,
|
||||||
size: 48.0, color: item.color),
|
size: 48.0, color: item.color),
|
||||||
Text(item.name, style: textTheme.titleLarge)
|
Text(item.name, style: textTheme.titleLarge)
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
// edit category
|
// edit category
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.edit),
|
leading: const Icon(Icons.edit),
|
||||||
title: Text(AppLocalizations.of(context)!.editCategory),
|
title: Text(AppLocalizations.of(context)!.editCategory),
|
||||||
subtitle:
|
subtitle:
|
||||||
Text(AppLocalizations.of(context)!.editCategoryLong),
|
Text(AppLocalizations.of(context)!.editCategoryLong),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// close the modal bottom sheet
|
// close the modal bottom sheet
|
||||||
// so the user returns to the list,
|
// so the user returns to the list,
|
||||||
// when leaving the category editor
|
// when leaving the category editor
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
// launch category editor
|
// launch category editor
|
||||||
context.pushNamed('edit-category', params: {
|
context.pushNamed('edit-category', params: {
|
||||||
'server': widget.room!.serverTag,
|
'server': widget.room!.serverTag,
|
||||||
'id': widget.room!.id,
|
'id': widget.room!.id,
|
||||||
'category': item.id.toString()
|
'category': item.id.toString()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// delete category
|
// delete category
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.delete),
|
leading: const Icon(Icons.delete),
|
||||||
title: Text(AppLocalizations.of(context)!.deleteCategory),
|
title: Text(AppLocalizations.of(context)!.deleteCategory),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
AppLocalizations.of(context)!.deleteCategoryLong),
|
AppLocalizations.of(context)!.deleteCategoryLong),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// show popup
|
// show popup
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
title: Text(AppLocalizations.of(context)!
|
title: Text(AppLocalizations.of(context)!
|
||||||
.deleteCategory),
|
.deleteCategory),
|
||||||
content: Text(AppLocalizations.of(context)!
|
content: Text(AppLocalizations.of(context)!
|
||||||
.deleteCategoryConfirm(item.name)),
|
.deleteCategoryConfirm(item.name)),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// close popup
|
|
||||||
Navigator.of(ctx).pop();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.cancel),
|
|
||||||
),
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () async {
|
|
||||||
// send request
|
|
||||||
final scaffMgr =
|
|
||||||
ScaffoldMessenger.of(ctx);
|
|
||||||
// popup context
|
|
||||||
final navInner = Navigator.of(ctx);
|
|
||||||
// bottomsheet context
|
|
||||||
final nav = Navigator.of(context);
|
|
||||||
final user = context.read<User>();
|
|
||||||
|
|
||||||
doNetworkRequest(scaffMgr,
|
|
||||||
req: () => postWithCreadentials(
|
|
||||||
path: 'deleteCategory',
|
|
||||||
target: user.server,
|
|
||||||
body: {
|
|
||||||
'room': widget.room?.id,
|
|
||||||
'server':
|
|
||||||
widget.room?.serverTag,
|
|
||||||
'listCatID': item.id
|
|
||||||
},
|
|
||||||
credentials: user),
|
|
||||||
onOK: (_) async {
|
|
||||||
// TODO: remove cached category
|
|
||||||
fetchCategories();
|
|
||||||
},
|
|
||||||
after: () {
|
|
||||||
// close popup
|
// close popup
|
||||||
navInner.pop();
|
Navigator.of(ctx).pop();
|
||||||
// close modal bottom sheet
|
},
|
||||||
nav.pop();
|
child: Text(
|
||||||
});
|
AppLocalizations.of(context)!.cancel),
|
||||||
},
|
),
|
||||||
child: Text(AppLocalizations.of(context)!
|
FilledButton(
|
||||||
.deleteCategory),
|
onPressed: () async {
|
||||||
)
|
// send request
|
||||||
],
|
final scaffMgr =
|
||||||
));
|
ScaffoldMessenger.of(ctx);
|
||||||
},
|
// popup context
|
||||||
),
|
final navInner = Navigator.of(ctx);
|
||||||
|
// bottomsheet context
|
||||||
|
final nav = Navigator.of(context);
|
||||||
|
final user = context.read<User>();
|
||||||
|
|
||||||
|
doNetworkRequest(scaffMgr,
|
||||||
|
req: () => postWithCreadentials(
|
||||||
|
path: 'deleteCategory',
|
||||||
|
target: user.server,
|
||||||
|
body: {
|
||||||
|
'room': widget.room?.id,
|
||||||
|
'server':
|
||||||
|
widget.room?.serverTag,
|
||||||
|
'listCatID': item.id
|
||||||
|
},
|
||||||
|
credentials: user),
|
||||||
|
onOK: (_) async {
|
||||||
|
// TODO: remove cached category
|
||||||
|
fetchCategories();
|
||||||
|
},
|
||||||
|
after: () {
|
||||||
|
// close popup
|
||||||
|
navInner.pop();
|
||||||
|
// close modal bottom sheet
|
||||||
|
nav.pop();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations.of(context)!
|
||||||
|
.deleteCategory),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
onClosing: () {},
|
onClosing: () {},
|
||||||
),
|
),
|
||||||
|
@ -206,50 +206,52 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
|
||||||
if (!((widget.info?.isAdmin ?? false) ||
|
if (!((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
((widget.info?.permissions)! & RoomPermission.editRoomContent !=
|
((widget.info?.permissions)! & RoomPermission.editRoomContent !=
|
||||||
0))) {
|
0))) {
|
||||||
// user is not allowed to edit or delete categories
|
// user is not allowed to edit or delete categories
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
if (oldIndex < newIndex) {
|
if (oldIndex < newIndex) {
|
||||||
newIndex -= 1;
|
newIndex -= 1;
|
||||||
}
|
}
|
||||||
final item = list.removeAt(oldIndex);
|
final item = list.removeAt(oldIndex);
|
||||||
list.insert(newIndex, item);
|
list.insert(newIndex, item);
|
||||||
|
|
||||||
// network request
|
// network request
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'changeCategoriesOrder',
|
path: 'changeCategoriesOrder',
|
||||||
body: {
|
body: {
|
||||||
'room': widget.room?.id,
|
'room': widget.room?.id,
|
||||||
'server': widget.room?.serverTag,
|
'server': widget.room?.serverTag,
|
||||||
'listCatIDs': list.map((item) => item.id).toList()
|
'listCatIDs': list.map((item) => item.id).toList()
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: (widget.info != null && ((widget.info?.isAdmin ?? false) ||
|
floatingActionButton: (widget.info != null &&
|
||||||
(widget.info?.isOwner ?? false) ||
|
((widget.info?.isAdmin ?? false) ||
|
||||||
((widget.info?.permissions)! & RoomPermission.editRoomContent !=
|
(widget.info?.isOwner ?? false) ||
|
||||||
0)))
|
((widget.info?.permissions)! &
|
||||||
? FloatingActionButton.extended(
|
RoomPermission.editRoomContent !=
|
||||||
icon: const Icon(Icons.add),
|
0)))
|
||||||
label: Text(AppLocalizations.of(context)!.newCategoryShort),
|
? FloatingActionButton.extended(
|
||||||
tooltip: AppLocalizations.of(context)!.newCategoryLong,
|
icon: const Icon(Icons.add),
|
||||||
onPressed: () {
|
label: Text(AppLocalizations.of(context)!.newCategoryShort),
|
||||||
// show new category popup
|
tooltip: AppLocalizations.of(context)!.newCategoryLong,
|
||||||
context.pushNamed('new-category', params: {
|
onPressed: () {
|
||||||
'server': widget.room!.serverTag,
|
// show new category popup
|
||||||
'id': widget.room!.id,
|
context.pushNamed('new-category', params: {
|
||||||
});
|
'server': widget.room!.serverTag,
|
||||||
},
|
'id': widget.room!.id,
|
||||||
)
|
});
|
||||||
: null,
|
},
|
||||||
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,74 +36,74 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
// TODO: load cached items first
|
// TODO: load cached items first
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getItems',
|
path: 'getItems',
|
||||||
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = body['data']
|
final resp = body['data']
|
||||||
.map<RoomItem>((raw) => RoomItem.fromJSON(raw))
|
.map<RoomItem>((raw) => RoomItem.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
final List<RoomItem> l = [];
|
final List<RoomItem> l = [];
|
||||||
final List<RoomItem> c = [];
|
final List<RoomItem> c = [];
|
||||||
|
|
||||||
for (RoomItem item in resp) {
|
for (RoomItem item in resp) {
|
||||||
if (item.state == 0) {
|
if (item.state == 0) {
|
||||||
l.add(item);
|
l.add(item);
|
||||||
} else {
|
} else {
|
||||||
c.add(item);
|
c.add(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: cache items
|
// TODO: cache items
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
list = l;
|
list = l;
|
||||||
cart = c;
|
cart = c;
|
||||||
|
|
||||||
sortAll();
|
sortAll();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void sortAll() {
|
void sortAll() {
|
||||||
for (List<RoomItem> input in [list, cart]) {
|
for (List<RoomItem> input in [list, cart]) {
|
||||||
setState(() {
|
setState(() {
|
||||||
input.sort((a, b) {
|
input.sort((a, b) {
|
||||||
if (a.category == b.category) {
|
if (a.category == b.category) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (a.category == null) {
|
if (a.category == null) {
|
||||||
// b should be below
|
// b should be below
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (b.category == null) {
|
if (b.category == null) {
|
||||||
// a should be below
|
// a should be below
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
final weightA = weights[a.category];
|
final weightA = weights[a.category];
|
||||||
final weightB = weights[b.category];
|
final weightB = weights[b.category];
|
||||||
// both could be null now,
|
// both could be null now,
|
||||||
// so we have to check agein
|
// so we have to check agein
|
||||||
if (weightA == weightB) {
|
if (weightA == weightB) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (weightA == null) {
|
if (weightA == null) {
|
||||||
// b should be below
|
// b should be below
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (weightB == null) {
|
if (weightB == null) {
|
||||||
// a should be below
|
// a should be below
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return weightA.compareTo(weightB);
|
return weightA.compareTo(weightB);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,31 +114,31 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
// TODO: load cached categories first
|
// TODO: load cached categories first
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getCategories',
|
path: 'getCategories',
|
||||||
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = body['data']
|
final resp = body['data']
|
||||||
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
Map<int, int> map = {};
|
Map<int, int> map = {};
|
||||||
Map<int?, RoomCategory> cat = {};
|
Map<int?, RoomCategory> cat = {};
|
||||||
for (int i = 0; i < resp.length; i++) {
|
for (int i = 0; i < resp.length; i++) {
|
||||||
map[resp[i].id] = i;
|
map[resp[i].id] = i;
|
||||||
cat[resp[i].id] = resp[i];
|
cat[resp[i].id] = resp[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
weights = map;
|
weights = map;
|
||||||
categories = cat;
|
categories = cat;
|
||||||
sortAll();
|
sortAll();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void fetchProducts() {
|
void fetchProducts() {
|
||||||
|
@ -147,22 +147,22 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
// TODO: load cached products first
|
// TODO: load cached products first
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getProducts',
|
path: 'getProducts',
|
||||||
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = body['data']
|
final resp = body['data']
|
||||||
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
products = resp;
|
products = resp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -170,41 +170,41 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
fetchItems();
|
fetchItems();
|
||||||
fetchCategories();
|
fetchCategories();
|
||||||
fetchProducts();
|
fetchProducts();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeItemState(RoomItem item) {
|
void changeItemState(RoomItem item) {
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'changeItemState',
|
path: 'changeItemState',
|
||||||
body: {
|
body: {
|
||||||
'room': widget.room?.id,
|
'room': widget.room?.id,
|
||||||
'server': widget.room?.serverTag,
|
'server': widget.room?.serverTag,
|
||||||
'listItemID': item.id,
|
'listItemID': item.id,
|
||||||
'state': item.state
|
'state': item.state
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: ListView(children: [
|
body: ListView(children: [
|
||||||
LabeledDivider(AppLocalizations.of(context)!.shoppingList),
|
LabeledDivider(AppLocalizations.of(context)!.shoppingList),
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = list[index];
|
final item = list[index];
|
||||||
final cat =
|
final cat =
|
||||||
categories[item.category] ?? RoomCategory.other(context);
|
categories[item.category] ?? RoomCategory.other(context);
|
||||||
return ShoppingListItem(
|
return ShoppingListItem(
|
||||||
name: item.name,
|
name: item.name,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
category: cat,
|
category: cat,
|
||||||
|
@ -215,9 +215,9 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
item.state = 1;
|
item.state = 1;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
list.removeAt(index);
|
list.removeAt(index);
|
||||||
cart.add(item);
|
cart.add(item);
|
||||||
sortAll();
|
sortAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
// network request
|
// network request
|
||||||
|
@ -235,28 +235,28 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
// - delete item (if allowed)
|
// - delete item (if allowed)
|
||||||
// - move to/from shopping cart?
|
// - move to/from shopping cart?
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ShoppingListItemInfo(
|
builder: (context) => ShoppingListItemInfo(
|
||||||
products: products,
|
products: products,
|
||||||
category: cat,
|
category: cat,
|
||||||
info: widget.info,
|
info: widget.info,
|
||||||
item: item,
|
item: item,
|
||||||
room: widget.room!.id,
|
room: widget.room!.id,
|
||||||
server: widget.room!.serverTag));
|
server: widget.room!.serverTag));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDivider(AppLocalizations.of(context)!.shoppingCart),
|
LabeledDivider(AppLocalizations.of(context)!.shoppingCart),
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
itemCount: cart.length,
|
itemCount: cart.length,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = cart[index];
|
final item = cart[index];
|
||||||
final cat =
|
final cat =
|
||||||
categories[item.category] ?? RoomCategory.other(context);
|
categories[item.category] ?? RoomCategory.other(context);
|
||||||
|
|
||||||
return ShoppingListItem(
|
return ShoppingListItem(
|
||||||
name: item.name,
|
name: item.name,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
category: cat,
|
category: cat,
|
||||||
|
@ -266,9 +266,9 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
// move back to list
|
// move back to list
|
||||||
item.state = 0;
|
item.state = 0;
|
||||||
setState(() {
|
setState(() {
|
||||||
cart.removeAt(index);
|
cart.removeAt(index);
|
||||||
list.add(item);
|
list.add(item);
|
||||||
sortAll();
|
sortAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
// network request
|
// network request
|
||||||
|
@ -286,37 +286,37 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
// - delete item (if allowed)
|
// - delete item (if allowed)
|
||||||
// - move to/from shopping cart?
|
// - move to/from shopping cart?
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ShoppingListItemInfo(
|
builder: (context) => ShoppingListItemInfo(
|
||||||
products: products,
|
products: products,
|
||||||
category: cat,
|
category: cat,
|
||||||
item: item,
|
item: item,
|
||||||
info: widget.info,
|
info: widget.info,
|
||||||
room: widget.room!.id,
|
room: widget.room!.id,
|
||||||
server: widget.room!.serverTag));
|
server: widget.room!.serverTag));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
floatingActionButton: (widget.info != null &&
|
floatingActionButton: (widget.info != null &&
|
||||||
((widget.info?.isAdmin ?? false) ||
|
((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
((widget.info?.permissions)! &
|
((widget.info?.permissions)! &
|
||||||
RoomPermission.addShoppingListItems !=
|
RoomPermission.addShoppingListItems !=
|
||||||
0)))
|
0)))
|
||||||
? FloatingActionButton.extended(
|
? FloatingActionButton.extended(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
label: Text(AppLocalizations.of(context)!.newItemShort),
|
label: Text(AppLocalizations.of(context)!.newItemShort),
|
||||||
tooltip: AppLocalizations.of(context)!.newItemLong,
|
tooltip: AppLocalizations.of(context)!.newItemLong,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show new category popup
|
// show new category popup
|
||||||
context.pushNamed('new-item', params: {
|
context.pushNamed('new-item', params: {
|
||||||
'server': widget.room!.serverTag,
|
'server': widget.room!.serverTag,
|
||||||
'id': widget.room!.id,
|
'id': widget.room!.id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,14 +331,14 @@ class ShoppingListItem extends StatelessWidget {
|
||||||
final Function()? onTap;
|
final Function()? onTap;
|
||||||
|
|
||||||
const ShoppingListItem(
|
const ShoppingListItem(
|
||||||
{required this.name,
|
{required this.name,
|
||||||
required this.category,
|
required this.category,
|
||||||
required this.inCart,
|
required this.inCart,
|
||||||
required this.description,
|
required this.description,
|
||||||
required key,
|
required key,
|
||||||
this.onDismiss,
|
this.onDismiss,
|
||||||
this.onTap})
|
this.onTap})
|
||||||
: _key = key;
|
: _key = key;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -360,21 +360,21 @@ class ShoppingListItem extends StatelessWidget {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
background:
|
background:
|
||||||
Icon(!inCart ? Icons.shopping_cart : Icons.remove_shopping_cart),
|
Icon(!inCart ? Icons.shopping_cart : Icons.remove_shopping_cart),
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
opacity: inCart ? 0.5 : 1.0,
|
opacity: inCart ? 0.5 : 1.0,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(name),
|
title: Text(name),
|
||||||
subtitle: Text(description),
|
subtitle: Text(description),
|
||||||
trailing: CategoryChip(
|
trailing: CategoryChip(
|
||||||
category: category,
|
category: category,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (onTap != null) {
|
if (onTap != null) {
|
||||||
onTap!();
|
onTap!();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,7 +388,7 @@ class ShoppingListItemInfo extends StatelessWidget {
|
||||||
final List<RoomProduct> products;
|
final List<RoomProduct> products;
|
||||||
|
|
||||||
const ShoppingListItemInfo(
|
const ShoppingListItemInfo(
|
||||||
{super.key,
|
{super.key,
|
||||||
this.info,
|
this.info,
|
||||||
required this.item,
|
required this.item,
|
||||||
required this.server,
|
required this.server,
|
||||||
|
@ -404,88 +404,88 @@ class ShoppingListItemInfo extends StatelessWidget {
|
||||||
builder: (context) => Column(
|
builder: (context) => Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
Text(item.name, style: textTheme.headlineLarge),
|
Text(item.name, style: textTheme.headlineLarge),
|
||||||
Text(item.description, style: textTheme.titleMedium),
|
Text(item.description, style: textTheme.titleMedium),
|
||||||
CategoryChip(
|
CategoryChip(
|
||||||
category: category,
|
category: category,
|
||||||
),
|
),
|
||||||
Text(Unit.fromId(item.unit).display(context, item.value))
|
Text(Unit.fromId(item.unit).display(context, item.value))
|
||||||
]))),
|
]))),
|
||||||
...(item.link != null)
|
...(item.link != null)
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!
|
title: Text(AppLocalizations.of(context)!
|
||||||
.itemShowLinkedProductTitle),
|
.itemShowLinkedProductTitle),
|
||||||
subtitle: Text(AppLocalizations.of(context)!
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
.itemShowLinkedProductSubtitle),
|
.itemShowLinkedProductSubtitle),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// launch "view-product" page for specific product
|
// launch "view-product" page for specific product
|
||||||
context.pushNamed('view-product', params: {
|
context.pushNamed('view-product', params: {
|
||||||
'server': server,
|
'server': server,
|
||||||
'id': room,
|
'id': room,
|
||||||
'product': item.link.toString()
|
'product': item.link.toString()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
...(info != null &&
|
...(info != null &&
|
||||||
((info?.isAdmin ?? false) ||
|
((info?.isAdmin ?? false) ||
|
||||||
(info?.isOwner ?? false) ||
|
(info?.isOwner ?? false) ||
|
||||||
((info?.permissions)! &
|
((info?.permissions)! &
|
||||||
RoomPermission.addShoppingListItems !=
|
RoomPermission.addShoppingListItems !=
|
||||||
0)))
|
0)))
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.editItem),
|
title: Text(AppLocalizations.of(context)!.editItem),
|
||||||
subtitle: Text(AppLocalizations.of(context)!.editItemLong),
|
subtitle: Text(AppLocalizations.of(context)!.editItemLong),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('edit-product', params: {
|
context.pushNamed('edit-product', params: {
|
||||||
'server': server,
|
'server': server,
|
||||||
'id': room,
|
'id': room,
|
||||||
'item': item.id.toString()
|
'item': item.id.toString()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title:
|
title:
|
||||||
Text(AppLocalizations.of(context)!.deleteItemTitle),
|
Text(AppLocalizations.of(context)!.deleteItemTitle),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
AppLocalizations.of(context)!.deleteItemSubtitle),
|
AppLocalizations.of(context)!.deleteItemSubtitle),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// TODO: show confirm dialog
|
// TODO: show confirm dialog
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(item.state == 0
|
title: Text(item.state == 0
|
||||||
? AppLocalizations.of(context)!.moveItemToCartTitle
|
? AppLocalizations.of(context)!.moveItemToCartTitle
|
||||||
: AppLocalizations.of(context)!.moveItemToCartSubtitle),
|
: AppLocalizations.of(context)!.moveItemToCartSubtitle),
|
||||||
subtitle: Text(item.state == 0
|
subtitle: Text(item.state == 0
|
||||||
? AppLocalizations.of(context)!.removeItemFromCartTitle
|
? AppLocalizations.of(context)!.removeItemFromCartTitle
|
||||||
: AppLocalizations.of(context)!.removeItemFromCartSubtitle),
|
: AppLocalizations.of(context)!.removeItemFromCartSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// flip state
|
// flip state
|
||||||
item.state = (item.state - 1).abs();
|
item.state = (item.state - 1).abs();
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'changeItemState',
|
path: 'changeItemState',
|
||||||
body: {
|
body: {
|
||||||
'room': room,
|
'room': room,
|
||||||
'server': server,
|
'server': server,
|
||||||
'listItemID': item.id,
|
'listItemID': item.id,
|
||||||
'state': item.state
|
'state': item.state
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,24 +25,24 @@ class _RoomProductsPageState extends State<RoomProductsPage> {
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getProducts',
|
path: 'getProducts',
|
||||||
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
body: {'room': widget.room?.id, 'server': widget.room?.serverTag}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = body['data']
|
final resp = body['data']
|
||||||
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// TODO: cache products
|
// TODO: cache products
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
products = resp;
|
products = resp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -73,31 +73,33 @@ class _RoomProductsPageState extends State<RoomProductsPage> {
|
||||||
// where as reading the shopping item description,
|
// where as reading the shopping item description,
|
||||||
// might be a good idea
|
// might be a good idea
|
||||||
context.pushNamed('view-product', params: {
|
context.pushNamed('view-product', params: {
|
||||||
'server': widget.room!.serverTag,
|
'server': widget.room!.serverTag,
|
||||||
'id': widget.room!.id,
|
'id': widget.room!.id,
|
||||||
'product': item.id.toString()
|
'product': item.id.toString()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: (widget.info != null && ((widget.info?.isAdmin ?? false) ||
|
floatingActionButton: (widget.info != null &&
|
||||||
(widget.info?.isOwner ?? false) ||
|
((widget.info?.isAdmin ?? false) ||
|
||||||
((widget.info?.permissions)! & RoomPermission.editRoomContent !=
|
(widget.info?.isOwner ?? false) ||
|
||||||
0)))
|
((widget.info?.permissions)! &
|
||||||
? FloatingActionButton.extended(
|
RoomPermission.editRoomContent !=
|
||||||
icon: const Icon(Icons.add),
|
0)))
|
||||||
label: Text(AppLocalizations.of(context)!.newProductShort),
|
? FloatingActionButton.extended(
|
||||||
tooltip: AppLocalizations.of(context)!.newProductLong,
|
icon: const Icon(Icons.add),
|
||||||
onPressed: () {
|
label: Text(AppLocalizations.of(context)!.newProductShort),
|
||||||
// show new category popup
|
tooltip: AppLocalizations.of(context)!.newProductLong,
|
||||||
context.pushNamed('new-product', params: {
|
onPressed: () {
|
||||||
'server': widget.room!.serverTag,
|
// show new category popup
|
||||||
'id': widget.room!.id,
|
context.pushNamed('new-product', params: {
|
||||||
});
|
'server': widget.room!.serverTag,
|
||||||
},
|
'id': widget.room!.id,
|
||||||
)
|
});
|
||||||
: null,
|
},
|
||||||
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ class ViewProductPage extends StatefulWidget {
|
||||||
final String server;
|
final String server;
|
||||||
final String room;
|
final String room;
|
||||||
const ViewProductPage(
|
const ViewProductPage(
|
||||||
{required this.server,
|
{required this.server,
|
||||||
required this.room,
|
required this.room,
|
||||||
required this.product,
|
required this.product,
|
||||||
super.key});
|
super.key});
|
||||||
|
@ -39,14 +39,14 @@ class _ViewProductPageState extends State<ViewProductPage> {
|
||||||
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.room, 'server': widget.server}),
|
body: {'room': widget.room, 'server': widget.server}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final info = RoomInfo.fromJSON(body['data']);
|
final info = RoomInfo.fromJSON(body['data']);
|
||||||
setState(() {
|
setState(() {
|
||||||
this.info = info;
|
this.info = info;
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -59,25 +59,25 @@ class _ViewProductPageState extends State<ViewProductPage> {
|
||||||
// TODO: load cached categories first
|
// TODO: load cached categories first
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getCategories',
|
path: 'getCategories',
|
||||||
body: {'room': widget.room, 'server': widget.server}),
|
body: {'room': widget.room, 'server': widget.server}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = body['data']
|
final resp = body['data']
|
||||||
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
Map<int?, RoomCategory> map = {};
|
Map<int?, RoomCategory> map = {};
|
||||||
|
|
||||||
for (RoomCategory cat in resp) {
|
for (RoomCategory cat in resp) {
|
||||||
map[cat.id] = cat;
|
map[cat.id] = cat;
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
categories = map;
|
categories = map;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fetchProducts() {
|
void fetchProducts() {
|
||||||
|
@ -86,29 +86,29 @@ class _ViewProductPageState extends State<ViewProductPage> {
|
||||||
// TODO: load cached products first
|
// TODO: load cached products first
|
||||||
|
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getProducts',
|
path: 'getProducts',
|
||||||
body: {'room': widget.room, 'server': widget.server}),
|
body: {'room': widget.room, 'server': widget.server}),
|
||||||
onOK: (body) async {
|
onOK: (body) async {
|
||||||
final resp = body['data']
|
final resp = body['data']
|
||||||
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
for (RoomProduct prod in resp) {
|
for (RoomProduct prod in resp) {
|
||||||
// load product info
|
// load product info
|
||||||
// for current product
|
// for current product
|
||||||
if (prod.id == widget.product) {
|
if (prod.id == widget.product) {
|
||||||
setState(() {
|
setState(() {
|
||||||
product = prod;
|
product = prod;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
setState(() {
|
||||||
setState(() {
|
|
||||||
products = resp;
|
products = resp;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -116,9 +116,9 @@ class _ViewProductPageState extends State<ViewProductPage> {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
fetchCategories();
|
fetchCategories();
|
||||||
fetchProducts();
|
fetchProducts();
|
||||||
fetchInfo();
|
fetchInfo();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,10 +131,10 @@ class _ViewProductPageState extends State<ViewProductPage> {
|
||||||
title: Text(product?.name ?? ''),
|
title: Text(product?.name ?? ''),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
// display product into
|
// display product into
|
||||||
Center(
|
Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
@ -142,68 +142,71 @@ class _ViewProductPageState extends State<ViewProductPage> {
|
||||||
children: [
|
children: [
|
||||||
Text(product?.name ?? '', style: textTheme.headlineLarge),
|
Text(product?.name ?? '', style: textTheme.headlineLarge),
|
||||||
Text(product?.description ?? '',
|
Text(product?.description ?? '',
|
||||||
style: textTheme.titleMedium),
|
style: textTheme.titleMedium),
|
||||||
Text(product?.ean ?? ''),
|
Text(product?.ean ?? ''),
|
||||||
CategoryChip(category: categories[product?.category]),
|
CategoryChip(category: categories[product?.category]),
|
||||||
Text(product!=null?Unit.fromId(product!.defaultUnit).display(context, product!.defaultValue):'')
|
Text(product != null
|
||||||
|
? Unit.fromId(product!.defaultUnit)
|
||||||
|
.display(context, product!.defaultValue)
|
||||||
|
: '')
|
||||||
],
|
],
|
||||||
))),
|
))),
|
||||||
|
|
||||||
// show actions (if allowed / available
|
// show actions (if allowed / available
|
||||||
// edit product button
|
// edit product button
|
||||||
...(info != null &&
|
...(info != null &&
|
||||||
(info!.isAdmin ||
|
(info!.isAdmin ||
|
||||||
info!.isOwner ||
|
info!.isOwner ||
|
||||||
(info!.permissions & RoomPermission.editRoomContent != 0)))
|
(info!.permissions & RoomPermission.editRoomContent != 0)))
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.editProductTitle),
|
title: Text(AppLocalizations.of(context)!.editProductTitle),
|
||||||
subtitle:
|
subtitle:
|
||||||
Text(AppLocalizations.of(context)!.editProductSubtitle),
|
Text(AppLocalizations.of(context)!.editProductSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('edit-product', params: {
|
context.pushNamed('edit-product', params: {
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'id': widget.room,
|
'id': widget.room,
|
||||||
'product': widget.product.toString()
|
'product': widget.product.toString()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
// show parent?
|
// show parent?
|
||||||
...(product?.parent != null)
|
...(product?.parent != null)
|
||||||
? [
|
? [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context)!.viewParentProductTitle),
|
AppLocalizations.of(context)!.viewParentProductTitle),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
AppLocalizations.of(context)!.viewParentProductSubtitle),
|
AppLocalizations.of(context)!.viewParentProductSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('view-product', params: {
|
context.pushNamed('view-product', params: {
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'id': widget.room,
|
'id': widget.room,
|
||||||
'product': product!.parent.toString()
|
'product': product!.parent.toString()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
// show/manage children
|
// show/manage children
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.viewProductChildrenTitle),
|
title: Text(AppLocalizations.of(context)!.viewProductChildrenTitle),
|
||||||
subtitle:
|
subtitle:
|
||||||
Text(AppLocalizations.of(context)!.viewProductChildrenSubtitle),
|
Text(AppLocalizations.of(context)!.viewProductChildrenSubtitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('view-product-children', params: {
|
context.pushNamed('view-product-children', params: {
|
||||||
'server': widget.server,
|
'server': widget.server,
|
||||||
'id': widget.room,
|
'id': widget.room,
|
||||||
'product': widget.product.toString()
|
'product': widget.product.toString()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
),
|
),
|
||||||
])),
|
])),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
1
lib/screens/server/dashboard.dart
Normal file
1
lib/screens/server/dashboard.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -25,54 +25,57 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
||||||
title: Text(AppLocalizations.of(context)!.changeThemeTitle),
|
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: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.lock),
|
prefixIcon: const Icon(Icons.lock),
|
||||||
labelText: AppLocalizations.of(context)!.inputOldPasswordLabel,
|
labelText: AppLocalizations.of(context)!.inputOldPasswordLabel,
|
||||||
hintText: AppLocalizations.of(context)!.inputOldPasswordHint,
|
hintText: AppLocalizations.of(context)!.inputOldPasswordHint,
|
||||||
helperText:AppLocalizations.of(context)!.inputOldPasswordHelp,
|
helperText: AppLocalizations.of(context)!.inputOldPasswordHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
),
|
||||||
padding: const EdgeInsets.all(8),
|
Padding(
|
||||||
child: TextField(
|
padding: const EdgeInsets.all(8),
|
||||||
controller: _ctrNewPassword,
|
child: TextField(
|
||||||
keyboardType: TextInputType.visiblePassword,
|
controller: _ctrNewPassword,
|
||||||
obscureText: true,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
decoration: InputDecoration(
|
obscureText: true,
|
||||||
prefixIcon: const Icon(Icons.lock),
|
decoration: InputDecoration(
|
||||||
labelText: AppLocalizations.of(context)!.inputNewPasswordLabel,
|
prefixIcon: const Icon(Icons.lock),
|
||||||
hintText: AppLocalizations.of(context)!.inputNewPasswordHint,
|
labelText: AppLocalizations.of(context)!.inputNewPasswordLabel,
|
||||||
helperText:AppLocalizations.of(context)!.inputNewPasswordHelp,
|
hintText: AppLocalizations.of(context)!.inputNewPasswordHint,
|
||||||
border: const OutlineInputBorder(),
|
helperText: AppLocalizations.of(context)!.inputNewPasswordHelp,
|
||||||
),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
),
|
||||||
padding: const EdgeInsets.all(8),
|
Padding(
|
||||||
child: TextField(
|
padding: const EdgeInsets.all(8),
|
||||||
controller: _ctrNewPasswordRepeat,
|
child: TextField(
|
||||||
keyboardType: TextInputType.visiblePassword,
|
controller: _ctrNewPasswordRepeat,
|
||||||
obscureText: true,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
decoration: InputDecoration(
|
obscureText: true,
|
||||||
prefixIcon: const Icon(Icons.lock),
|
decoration: InputDecoration(
|
||||||
labelText: AppLocalizations.of(context)!.inputNewPasswordRepeatLabel,
|
prefixIcon: const Icon(Icons.lock),
|
||||||
hintText: AppLocalizations.of(context)!.inputNewPasswordRepeatHint,
|
labelText:
|
||||||
helperText:AppLocalizations.of(context)!.inputNewPasswordRepeatHelp,
|
AppLocalizations.of(context)!.inputNewPasswordRepeatLabel,
|
||||||
border: const OutlineInputBorder(),
|
hintText:
|
||||||
),
|
AppLocalizations.of(context)!.inputNewPasswordRepeatHint,
|
||||||
|
helperText:
|
||||||
|
AppLocalizations.of(context)!.inputNewPasswordRepeatHelp,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
],
|
||||||
)),
|
)),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
|
@ -93,8 +96,7 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
||||||
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: trans!.errorPasswordLength,
|
text: trans!.errorPasswordLength, action: trans.dismiss);
|
||||||
action: trans.dismiss);
|
|
||||||
|
|
||||||
_ctrNewPasswordRepeat.clear();
|
_ctrNewPasswordRepeat.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -102,7 +104,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: trans!.errorPasswordsDoNotMatch, action: trans.dismiss);
|
text: trans!.errorPasswordsDoNotMatch, action: trans.dismiss);
|
||||||
|
|
||||||
_ctrNewPasswordRepeat.clear();
|
_ctrNewPasswordRepeat.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -110,7 +112,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: trans!.errorOldPasswordWrong, action: trans.dismiss);
|
text: trans!.errorOldPasswordWrong, action: trans.dismiss);
|
||||||
|
|
||||||
_ctrOldPassword.clear();
|
_ctrOldPassword.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -120,23 +122,23 @@ 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: Text(AppLocalizations.of(context)!.changeThemeTitle),
|
child: Text(AppLocalizations.of(context)!.changeThemeTitle),
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,17 +22,17 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
|
|
||||||
_currentPage = controller.initialPage;
|
_currentPage = controller.initialPage;
|
||||||
controller.addListener(() {
|
controller.addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentPage = controller.page?.toInt() ?? controller.initialPage;
|
_currentPage = controller.page?.toInt() ?? controller.initialPage;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
||||||
|
|
||||||
String fabText = AppLocalizations.of(context)!.next;
|
String fabText = AppLocalizations.of(context)!.next;
|
||||||
if (_currentPage == 0) {
|
if (_currentPage == 0) {
|
||||||
|
@ -55,86 +55,80 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PageView(
|
child: PageView(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Column(
|
Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SvgPicture.asset(asset("undraw/undraw_shopping_app.svg"),
|
SvgPicture.asset(asset("undraw/undraw_shopping_app.svg"),
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
width: smallest * 0.5,
|
width: smallest * 0.5,
|
||||||
height: smallest * 0.5),
|
height: smallest * 0.5),
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context)!.welcomeTitle,
|
AppLocalizations.of(context)!.welcomeTitle,
|
||||||
style: textTheme.displaySmall,
|
style: textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
Text(
|
Text(AppLocalizations.of(context)!.welcomeSubtitle,
|
||||||
AppLocalizations.of(context)!.welcomeSubtitle,
|
|
||||||
style: textTheme.bodyMedium
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
SvgPicture.asset(asset("undraw/undraw_mobile_login.svg"),
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
width: smallest * 0.5,
|
|
||||||
height: smallest * 0.5),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.page2Title,
|
|
||||||
style: textTheme.displaySmall,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.page2Subtitle,
|
|
||||||
style: textTheme.bodyMedium)
|
style: textTheme.bodyMedium)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SvgPicture.asset(asset("undraw/undraw_online_connection.svg"),
|
SvgPicture.asset(asset("undraw/undraw_mobile_login.svg"),
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
width: smallest * 0.5,
|
width: smallest * 0.5,
|
||||||
height: smallest * 0.5),
|
height: smallest * 0.5),
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context)!.page3Title,
|
AppLocalizations.of(context)!.page2Title,
|
||||||
style: textTheme.displaySmall,
|
style: textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
Text(
|
Text(AppLocalizations.of(context)!.page2Subtitle,
|
||||||
AppLocalizations.of(context)!.page3Subtitle,
|
|
||||||
style: textTheme.bodyMedium)
|
style: textTheme.bodyMedium)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SvgPicture.asset(asset("undraw/undraw_online_groceries.svg"),
|
SvgPicture.asset(asset("undraw/undraw_online_connection.svg"),
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
width: smallest * 0.5,
|
width: smallest * 0.5,
|
||||||
height: smallest * 0.5),
|
height: smallest * 0.5),
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context)!.page4Title,
|
AppLocalizations.of(context)!.page3Title,
|
||||||
style: textTheme.displaySmall,
|
style: textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
Text(
|
Text(AppLocalizations.of(context)!.page3Subtitle,
|
||||||
AppLocalizations.of(context)!.page4Subtitle,
|
|
||||||
style: textTheme.bodyMedium)
|
style: textTheme.bodyMedium)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
SvgPicture.asset(asset("undraw/undraw_online_groceries.svg"),
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
width: smallest * 0.5,
|
||||||
|
height: smallest * 0.5),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.page4Title,
|
||||||
|
style: textTheme.displaySmall,
|
||||||
|
),
|
||||||
|
Text(AppLocalizations.of(context)!.page4Subtitle,
|
||||||
|
style: textTheme.bodyMedium)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
)),
|
)),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.goNamed('signin');
|
context.goNamed('signin');
|
||||||
},
|
},
|
||||||
child: Text(AppLocalizations.of(context)!.userHasAnAccount
|
child: Text(AppLocalizations.of(context)!.userHasAnAccount),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -149,8 +143,8 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
} else {
|
} else {
|
||||||
// move to next page
|
// move to next page
|
||||||
controller.nextPage(
|
controller.nextPage(
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
duration: const Duration(milliseconds: 300));
|
duration: const Duration(milliseconds: 300));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue