8fffafde47
Translations are provided in *.arb* format. Some keys have descriptions (indicated by leading @-symbol). Descriptions should not be copied into the translation itself. Currently only English is supported (app_en.arb), but German is planned. Apparently weblate merged .arb support at some time, so it would be nice to enable community translations at some point.
299 lines
10 KiB
Dart
299 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:outbag_app/backend/request.dart';
|
|
import 'package:outbag_app/backend/room.dart';
|
|
import 'package:outbag_app/backend/user.dart';
|
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
|
import 'package:outbag_app/tools/snackbar.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
import 'dart:math';
|
|
|
|
class EditRoomPage extends StatefulWidget {
|
|
final String server;
|
|
final String tag;
|
|
|
|
const EditRoomPage(this.server, this.tag, {super.key});
|
|
|
|
@override
|
|
State<StatefulWidget> createState() => _EditRoomPageState();
|
|
}
|
|
|
|
class _EditRoomPageState extends State<EditRoomPage> {
|
|
final TextEditingController _ctrName = TextEditingController();
|
|
final TextEditingController _ctrID = TextEditingController();
|
|
final TextEditingController _ctrDescription = TextEditingController();
|
|
RoomIcon _ctrIcon = RoomIcon.other;
|
|
Room? room;
|
|
|
|
// show spinner by default
|
|
// until data has been fetched
|
|
bool showSpinner = true;
|
|
|
|
void initFromRoom(Room room) {
|
|
_ctrID.text = room.id;
|
|
_ctrName.text = room.name;
|
|
_ctrDescription.text = room.description;
|
|
_ctrIcon = room.icon!;
|
|
|
|
setState(() {
|
|
this.room = room;
|
|
});
|
|
}
|
|
|
|
// fetch room information
|
|
void fetchInfo() {
|
|
final sm = ScaffoldMessenger.of(context);
|
|
final router = GoRouter.of(context);
|
|
final user = context.read<User>();
|
|
|
|
doNetworkRequest(
|
|
sm,
|
|
req: () => postWithCreadentials(
|
|
path: 'getRoomInfo',
|
|
credentials: user,
|
|
target: user.server,
|
|
body: {'room': widget.tag, 'server': widget.server}),
|
|
onOK: (body) async {
|
|
final room = Room.fromJSON(body['data']);
|
|
room.toDisk();
|
|
},
|
|
onNetworkErr: () {
|
|
// no room data available
|
|
// use data from disk
|
|
(() async {
|
|
try {
|
|
final diskRoom =
|
|
await Room.fromDisk(serverTag: widget.server, id: widget.tag);
|
|
initFromRoom(diskRoom);
|
|
} catch (_) {
|
|
// no room data available
|
|
// close screen
|
|
router.pushReplacementNamed('home');
|
|
}
|
|
})();
|
|
return true;
|
|
},
|
|
onServerErr: (json) {
|
|
// user not allowed to be here
|
|
// close screen
|
|
router.pushReplacementNamed('home');
|
|
return true;
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
Room.listen((_) async {
|
|
// rooms changed on disk
|
|
// probably this one,
|
|
// because it is currently open
|
|
// NOTE: might be a different room
|
|
// (if a background listener is implemented at some point,
|
|
// checking if this room changed might improve performance)
|
|
try {
|
|
final room =
|
|
await Room.fromDisk(serverTag: widget.server, id: widget.tag);
|
|
|
|
initFromRoom(room);
|
|
|
|
setState(() {
|
|
showSpinner = false;
|
|
});
|
|
} catch (_) {}
|
|
});
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) => fetchInfo());
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final textTheme = Theme.of(context)
|
|
.textTheme
|
|
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
|
|
|
double width = MediaQuery.of(context).size.width;
|
|
double height = MediaQuery.of(context).size.height;
|
|
double smallest = min(min(width, height), 400);
|
|
|
|
return showSpinner
|
|
? Scaffold(
|
|
body: Center(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const CircularProgressIndicator(),
|
|
Text(AppLocalizations.of(context)!.loading, style: textTheme.titleLarge),
|
|
])))
|
|
: Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(AppLocalizations.of(context)!.editRoomMetadataShort),
|
|
),
|
|
body: SingleChildScrollView(
|
|
child: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(14),
|
|
child: ConstrainedBox(
|
|
constraints: const BoxConstraints(maxWidth: 400),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
IconButton(
|
|
icon: SvgPicture.asset(
|
|
_ctrIcon.img,
|
|
width: smallest * 0.3,
|
|
height: smallest * 0.3,
|
|
),
|
|
tooltip: AppLocalizations.of(context)!.changeRoomIcon,
|
|
onPressed: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (ctx) => AlertDialog(
|
|
title: Text(
|
|
AppLocalizations.of(context)!.chooseRoomIcon),
|
|
actions: const [],
|
|
content: SizedBox(
|
|
width: smallest * 0.3 * 3,
|
|
height: smallest * 0.3 * 3,
|
|
child: GridView.count(
|
|
crossAxisCount: 3,
|
|
children: RoomIcon.list()
|
|
.map((icon) {
|
|
return GridTile(
|
|
child: IconButton(
|
|
icon: SvgPicture
|
|
.asset(
|
|
icon.img,
|
|
width:
|
|
smallest *
|
|
0.3,
|
|
height:
|
|
smallest *
|
|
0.3,
|
|
),
|
|
tooltip:
|
|
icon.text,
|
|
onPressed: () {
|
|
setState(() {
|
|
_ctrIcon =
|
|
icon;
|
|
});
|
|
context.pop();
|
|
}));
|
|
}).toList())),
|
|
));
|
|
},
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
enabled: false,
|
|
controller: _ctrID,
|
|
keyboardType: TextInputType.emailAddress,
|
|
decoration: InputDecoration(
|
|
prefixIcon: const Icon(Icons.fact_check),
|
|
labelText: AppLocalizations.of(context)!
|
|
.inputRoomIdLabel,
|
|
hintText: AppLocalizations.of(context)!
|
|
.inputRoomIdHint,
|
|
helperText: AppLocalizations.of(context)!
|
|
.inputRoomIdHelp,
|
|
border: const OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
controller: _ctrName,
|
|
keyboardType: TextInputType.name,
|
|
decoration: InputDecoration(
|
|
prefixIcon: const Icon(Icons.badge),
|
|
labelText: AppLocalizations.of(context)!
|
|
.inputRoomNameLabel,
|
|
hintText: AppLocalizations.of(context)!
|
|
.inputRoomNameHint,
|
|
helperText: AppLocalizations.of(context)!
|
|
.inputRoomNameHelp,
|
|
border: const OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
controller: _ctrDescription,
|
|
keyboardType: TextInputType.text,
|
|
decoration: InputDecoration(
|
|
labelText: AppLocalizations.of(context)!
|
|
.inputRoomDescriptionLabel,
|
|
hintText: AppLocalizations.of(context)!
|
|
.inputRoomDescriptionHint,
|
|
helperText: AppLocalizations.of(context)!
|
|
.inputRoomDescriptionHelp,
|
|
prefixIcon: const Icon(Icons.dns),
|
|
border: const OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
))))),
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
onPressed: () async {
|
|
final scaffMgr = ScaffoldMessenger.of(context);
|
|
final nav = Navigator.of(context);
|
|
final trans = AppLocalizations.of(context);
|
|
|
|
// name may not be empty
|
|
if (_ctrName.text.isEmpty) {
|
|
showSimpleSnackbar(scaffMgr,
|
|
text: trans!.errorNoRoomName, action: trans.ok);
|
|
|
|
return;
|
|
}
|
|
|
|
User user;
|
|
try {
|
|
user = await User.fromDisk();
|
|
} catch (_) {
|
|
// user data invalid
|
|
// shouldn't happen
|
|
return;
|
|
}
|
|
|
|
Room clone = room!;
|
|
clone.name = _ctrName.text;
|
|
clone.description = _ctrDescription.text;
|
|
clone.icon = _ctrIcon;
|
|
|
|
doNetworkRequest(scaffMgr,
|
|
req: ()=>postWithCreadentials(
|
|
target: user.server,
|
|
credentials: user,
|
|
path: 'changeRoomMeta',
|
|
body: {
|
|
'room': clone.id,
|
|
'server': clone.serverTag,
|
|
'title': clone.name,
|
|
'description': clone.description,
|
|
'icon': clone.icon?.type,
|
|
}),
|
|
onOK: (_) async {
|
|
// room was created
|
|
// save room
|
|
await clone.toDisk();
|
|
nav.pop();
|
|
}
|
|
);
|
|
},
|
|
label: Text(AppLocalizations.of(context)!.update),
|
|
icon: const Icon(Icons.edit)),
|
|
);
|
|
}
|
|
}
|