✨ Basic large screen support
This commit is contained in:
@ -14,7 +14,7 @@ class CallOverlay extends StatelessWidget {
|
||||
|
||||
final chat = context.watch<ChatProvider>();
|
||||
|
||||
if (chat.isShown || chat.call == null) {
|
||||
if (chat.isCallShown || chat.currentCall == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
@ -54,8 +54,8 @@ class CallOverlay extends StatelessWidget {
|
||||
onTap: () {
|
||||
router.pushNamed(
|
||||
'chat.channel.call',
|
||||
extra: chat.call!.info,
|
||||
pathParameters: {'channel': chat.call!.channel.alias},
|
||||
extra: chat.currentCall!.info,
|
||||
pathParameters: {'channel': chat.currentCall!.channel.alias},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -73,9 +73,9 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
||||
if (await context.showDisconnectDialog() != true) return;
|
||||
|
||||
final chat = context.read<ChatProvider>();
|
||||
if (chat.call != null) {
|
||||
chat.call!.deactivate();
|
||||
chat.call!.dispose();
|
||||
if (chat.currentCall != null) {
|
||||
chat.currentCall!.deactivate();
|
||||
chat.currentCall!.dispose();
|
||||
router.pop();
|
||||
}
|
||||
}
|
||||
|
@ -55,23 +55,19 @@ class _ChatMaintainerState extends State<ChatMaintainer> {
|
||||
switch (result.method) {
|
||||
case 'messages.new':
|
||||
final payload = Message.fromJson(result.payload!);
|
||||
if (payload.channelId == widget.channel.id)
|
||||
widget.onInsertMessage(payload);
|
||||
if (payload.channelId == widget.channel.id) widget.onInsertMessage(payload);
|
||||
break;
|
||||
case 'messages.update':
|
||||
final payload = Message.fromJson(result.payload!);
|
||||
if (payload.channelId == widget.channel.id)
|
||||
widget.onUpdateMessage(payload);
|
||||
if (payload.channelId == widget.channel.id) widget.onUpdateMessage(payload);
|
||||
break;
|
||||
case 'messages.burnt':
|
||||
final payload = Message.fromJson(result.payload!);
|
||||
if (payload.channelId == widget.channel.id)
|
||||
widget.onDeleteMessage(payload);
|
||||
if (payload.channelId == widget.channel.id) widget.onDeleteMessage(payload);
|
||||
break;
|
||||
case 'calls.new':
|
||||
final payload = Call.fromJson(result.payload!);
|
||||
if (payload.channelId == widget.channel.id)
|
||||
widget.onCallStarted(payload);
|
||||
if (payload.channelId == widget.channel.id) widget.onCallStarted(payload);
|
||||
break;
|
||||
case 'calls.end':
|
||||
final payload = Call.fromJson(result.payload!);
|
||||
|
@ -56,7 +56,7 @@ class _ChatMessageEditorState extends State<ChatMessageEditor> {
|
||||
? getRequestUri('messaging', '/api/channels/${widget.channel}/messages')
|
||||
: getRequestUri('messaging', '/api/channels/${widget.channel}/messages/${widget.editing!.id}');
|
||||
|
||||
final req = Request(widget.editing == null ? "POST" : "PUT", uri);
|
||||
final req = Request(widget.editing == null ? 'POST' : 'PUT', uri);
|
||||
req.headers['Content-Type'] = 'application/json';
|
||||
req.body = jsonEncode(<String, dynamic>{
|
||||
'content': _textController.value.text,
|
||||
@ -163,7 +163,6 @@ class _ChatMessageEditorState extends State<ChatMessageEditor> {
|
||||
focusNode: _focusNode,
|
||||
controller: _textController,
|
||||
maxLines: null,
|
||||
autofocus: true,
|
||||
autocorrect: true,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: InputDecoration.collapsed(
|
||||
|
@ -22,7 +22,11 @@ class LayoutWrapper extends StatelessWidget {
|
||||
final content = child ?? Container();
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(title), actions: appBarActions),
|
||||
appBar: AppBar(
|
||||
title: Text(title),
|
||||
actions: appBarActions,
|
||||
centerTitle: false,
|
||||
),
|
||||
floatingActionButton: floatingActionButton,
|
||||
drawer: const SolianNavigationDrawer(),
|
||||
body: noSafeArea ? content : SafeArea(child: content),
|
||||
|
23
lib/widgets/empty.dart
Normal file
23
lib/widgets/empty.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class SelectionEmptyWidget extends StatelessWidget {
|
||||
const SelectionEmptyWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.asset('assets/logo.png', width: 64, height: 64),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.appName,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -9,8 +9,8 @@ extension SolianCommonExtensions on BuildContext {
|
||||
return message
|
||||
.split(' ')
|
||||
.map((element) =>
|
||||
"${element[0].toUpperCase()}${element.substring(1).toLowerCase()}")
|
||||
.join(" ");
|
||||
'${element[0].toUpperCase()}${element.substring(1).toLowerCase()}')
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
return showDialog<void>(
|
||||
|
@ -5,6 +5,7 @@ import 'package:solian/widgets/navigation_drawer.dart';
|
||||
|
||||
class IndentWrapper extends LayoutWrapper {
|
||||
final bool hideDrawer;
|
||||
final bool fixedAppBarColor;
|
||||
|
||||
const IndentWrapper({
|
||||
super.key,
|
||||
@ -13,6 +14,7 @@ class IndentWrapper extends LayoutWrapper {
|
||||
super.floatingActionButton,
|
||||
super.appBarActions,
|
||||
this.hideDrawer = false,
|
||||
this.fixedAppBarColor = false,
|
||||
super.noSafeArea = false,
|
||||
}) : super();
|
||||
|
||||
@ -30,6 +32,8 @@ class IndentWrapper extends LayoutWrapper {
|
||||
: null,
|
||||
title: Text(title),
|
||||
actions: appBarActions,
|
||||
centerTitle: false,
|
||||
elevation: fixedAppBarColor ? 4 : null,
|
||||
),
|
||||
floatingActionButton: floatingActionButton,
|
||||
drawer: const SolianNavigationDrawer(),
|
||||
|
@ -39,21 +39,21 @@ class _SolianNavigationDrawerState extends State<SolianNavigationDrawer> {
|
||||
icon: const Icon(Icons.explore),
|
||||
label: Text(AppLocalizations.of(context)!.explore),
|
||||
),
|
||||
"explore",
|
||||
'explore',
|
||||
),
|
||||
(
|
||||
NavigationDrawerDestination(
|
||||
icon: const Icon(Icons.send),
|
||||
label: Text(AppLocalizations.of(context)!.chat),
|
||||
),
|
||||
"chat",
|
||||
'chat',
|
||||
),
|
||||
(
|
||||
NavigationDrawerDestination(
|
||||
icon: const Icon(Icons.account_circle),
|
||||
label: Text(AppLocalizations.of(context)!.account),
|
||||
),
|
||||
"account",
|
||||
'account',
|
||||
),
|
||||
];
|
||||
|
||||
@ -69,7 +69,7 @@ class _SolianNavigationDrawerState extends State<SolianNavigationDrawer> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Image.asset("assets/logo.png", width: 26, height: 26),
|
||||
Image.asset('assets/logo.png', width: 26, height: 26),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.appName,
|
||||
|
@ -85,7 +85,7 @@ class _NotificationButtonState extends State<NotificationButton> {
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.notifications),
|
||||
onPressed: () {
|
||||
router.pushNamed("notification");
|
||||
router.pushNamed('notification');
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -112,7 +112,7 @@ class _AttachmentEditorState extends State<AttachmentEditor> {
|
||||
var res = await auth.client!.send(req);
|
||||
if (res.statusCode == 200) {
|
||||
var result = Attachment.fromJson(
|
||||
jsonDecode(utf8.decode(await res.stream.toBytes()))["info"],
|
||||
jsonDecode(utf8.decode(await res.stream.toBytes()))['info'],
|
||||
);
|
||||
setState(() => _attachments.add(result));
|
||||
widget.onUpdate(_attachments);
|
||||
@ -252,7 +252,7 @@ class _AttachmentEditorState extends State<AttachmentEditor> {
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
Text(
|
||||
"${getFileType(element)} · ${formatBytes(element.filesize)}",
|
||||
'${getFileType(element)} · ${formatBytes(element.filesize)}',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -121,7 +121,7 @@ class CommentListHeader extends StatelessWidget {
|
||||
return TextButton(
|
||||
onPressed: () async {
|
||||
final did = await router.pushNamed(
|
||||
"posts.comments.editor",
|
||||
'posts.comments.editor',
|
||||
extra: CommentPostArguments(related: related),
|
||||
);
|
||||
if (did == true) paging.refresh();
|
||||
|
@ -55,7 +55,7 @@ class ArticleContent extends StatelessWidget {
|
||||
},
|
||||
imageBuilder: (url, _, __) {
|
||||
Uri uri;
|
||||
if (url.toString().startsWith("/api/attachments")) {
|
||||
if (url.toString().startsWith('/api/attachments')) {
|
||||
uri = getRequestUri('interactive', url.toString());
|
||||
} else {
|
||||
uri = url;
|
||||
|
@ -30,7 +30,7 @@ class _AttachmentItemState extends State<AttachmentItem> {
|
||||
|
||||
late final _videoPlayer = Player(
|
||||
configuration: PlayerConfiguration(
|
||||
title: "Attachment #${getTag()}",
|
||||
title: 'Attachment #${getTag()}',
|
||||
logLevel: MPVLogLevel.error,
|
||||
),
|
||||
);
|
||||
|
@ -13,8 +13,8 @@ import 'package:timeago/timeago.dart' as timeago;
|
||||
|
||||
class PostItem extends StatefulWidget {
|
||||
final Post item;
|
||||
final bool? brief;
|
||||
final bool? ripple;
|
||||
final bool brief;
|
||||
final bool ripple;
|
||||
final Function? onUpdate;
|
||||
final Function? onDelete;
|
||||
final Function? onTap;
|
||||
@ -22,8 +22,8 @@ class PostItem extends StatefulWidget {
|
||||
const PostItem({
|
||||
super.key,
|
||||
required this.item,
|
||||
this.brief,
|
||||
this.ripple,
|
||||
this.brief = true,
|
||||
this.ripple = true,
|
||||
this.onUpdate,
|
||||
this.onDelete,
|
||||
this.onTap,
|
||||
@ -79,9 +79,9 @@ class _PostItemState extends State<PostItem> {
|
||||
Widget renderContent() {
|
||||
switch (widget.item.modelType) {
|
||||
case 'article':
|
||||
return ArticleContent(item: widget.item, brief: widget.brief ?? true);
|
||||
return ArticleContent(item: widget.item, brief: widget.brief);
|
||||
default:
|
||||
return MomentContent(item: widget.item, brief: widget.brief ?? true);
|
||||
return MomentContent(item: widget.item, brief: widget.brief);
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ class _PostItemState extends State<PostItem> {
|
||||
|
||||
Widget content;
|
||||
|
||||
if (widget.brief ?? true) {
|
||||
if (widget.brief) {
|
||||
content = Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
child: Column(
|
||||
@ -199,7 +199,7 @@ class _PostItemState extends State<PostItem> {
|
||||
content = Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12, top: 16),
|
||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 16),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -230,17 +230,17 @@ class _PostItemState extends State<PostItem> {
|
||||
child: Divider(thickness: 0.3),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||
child: renderContent(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: renderAttachments(),
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
|
||||
child: renderReactions(),
|
||||
),
|
||||
),
|
||||
@ -248,9 +248,7 @@ class _PostItemState extends State<PostItem> {
|
||||
);
|
||||
}
|
||||
|
||||
final ripple = widget.ripple ?? true;
|
||||
|
||||
if (ripple) {
|
||||
if (widget.ripple) {
|
||||
return InkWell(
|
||||
child: content,
|
||||
onTap: () {
|
||||
|
@ -134,8 +134,8 @@ class _ReactionActionPopupState extends State<ReactionActionPopup> {
|
||||
child: ListTile(
|
||||
title: Text(info.value.icon),
|
||||
subtitle: Text(
|
||||
":${info.key}:",
|
||||
style: const TextStyle(fontFamily: "monospace"),
|
||||
':${info.key}:',
|
||||
style: const TextStyle(fontFamily: 'monospace'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
Reference in New Issue
Block a user