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.
254 lines
13 KiB
Dart
254 lines
13 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 NewRoomPage extends StatefulWidget {
|
|
const NewRoomPage({super.key});
|
|
|
|
@override
|
|
State<StatefulWidget> createState() => _NewRoomPageState();
|
|
}
|
|
|
|
class _NewRoomPageState extends State {
|
|
final TextEditingController _ctrID = TextEditingController();
|
|
final TextEditingController _ctrName = TextEditingController();
|
|
final TextEditingController _ctrDescription = TextEditingController();
|
|
RoomVisibility _ctrVis = RoomVisibility.private;
|
|
RoomIcon _ctrIcon = RoomIcon.other;
|
|
|
|
bool showSpinner = false;
|
|
|
|
@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)!.newRoom),
|
|
),
|
|
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,
|
|
),
|
|
// do not display tooltip for now
|
|
// as it is hard to translate
|
|
// and the tooltip prevented the click event,
|
|
// when clicked on the tooltip bar
|
|
// tooltip:icon.text,
|
|
onPressed: () {
|
|
setState(() {
|
|
_ctrIcon =
|
|
icon;
|
|
});
|
|
Navigator.of(
|
|
context)
|
|
.pop();
|
|
}));
|
|
}).toList())),
|
|
));
|
|
},
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
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(),
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
AppLocalizations.of(context)!
|
|
.roomVisibilityTitle,
|
|
style: textTheme.labelLarge),
|
|
Text(
|
|
AppLocalizations.of(context)!
|
|
.roomVisibilitySubtitle,
|
|
style: textTheme.bodySmall),
|
|
SegmentedButton<RoomVisibility>(
|
|
showSelectedIcon: true,
|
|
multiSelectionEnabled: false,
|
|
emptySelectionAllowed: false,
|
|
segments: RoomVisibility.list().map((vis) {
|
|
return ButtonSegment<RoomVisibility>(
|
|
value: vis,
|
|
label: Text(vis.text(context)),
|
|
icon: Icon(vis.icon));
|
|
}).toList(),
|
|
onSelectionChanged: ((vset) {
|
|
setState(() {
|
|
_ctrVis = vset.single;
|
|
});
|
|
}),
|
|
selected: {_ctrVis},
|
|
selectedIcon: Icon(_ctrVis.icon),
|
|
),
|
|
],
|
|
))))),
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
onPressed: () async {
|
|
final scaffMgr = ScaffoldMessenger.of(context);
|
|
final router = GoRouter.of(context);
|
|
final trans = AppLocalizations.of(context);
|
|
|
|
// ID should be at least three characters long
|
|
if (_ctrID.text.length < 3) {
|
|
showSimpleSnackbar(scaffMgr,
|
|
text: _ctrID.text.isEmpty
|
|
? trans!.errorNoRoomId
|
|
: trans!.errorRoomIdLength,
|
|
action: trans.ok);
|
|
|
|
return;
|
|
}
|
|
|
|
// name may not be empty
|
|
if (_ctrName.text.isEmpty) {
|
|
showSimpleSnackbar(scaffMgr,
|
|
text: trans!.errorNoRoomName, action: trans.ok);
|
|
|
|
return;
|
|
}
|
|
|
|
final user = context.read<User>();
|
|
final room = Room(
|
|
id: _ctrID.text,
|
|
serverTag: user.server.tag,
|
|
name: _ctrName.text,
|
|
description: _ctrDescription.text,
|
|
icon: _ctrIcon,
|
|
visibility: _ctrVis);
|
|
|
|
doNetworkRequest(scaffMgr,
|
|
req: () => postWithCreadentials(
|
|
target: user.server,
|
|
credentials: user,
|
|
path: 'createRoom',
|
|
body: {
|
|
'room': room.id,
|
|
'title': room.name,
|
|
'description': room.description,
|
|
'icon': room.icon?.type,
|
|
'visibility': room.visibility?.type
|
|
}),
|
|
onOK: (_) async {
|
|
// room was created
|
|
// save room
|
|
await room.toDisk();
|
|
// move to home page
|
|
router.pushReplacementNamed('home');
|
|
});
|
|
},
|
|
label: Text(AppLocalizations.of(context)!.createRoomShort),
|
|
icon: const Icon(Icons.add)),
|
|
);
|
|
}
|
|
}
|