Basic large screen support

This commit is contained in:
2024-05-02 00:49:38 +08:00
parent fceb3edbc6
commit b39c8c770e
41 changed files with 716 additions and 607 deletions

View File

@ -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},
);
},
);

View File

@ -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();
}
}

View File

@ -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!);

View File

@ -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(

View File

@ -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
View 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),
),
],
),
);
}
}

View File

@ -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>(

View File

@ -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(),

View File

@ -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,

View File

@ -85,7 +85,7 @@ class _NotificationButtonState extends State<NotificationButton> {
child: IconButton(
icon: const Icon(Icons.notifications),
onPressed: () {
router.pushNamed("notification");
router.pushNamed('notification');
},
),
);

View File

@ -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)}',
),
],
),

View File

@ -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();

View File

@ -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;

View File

@ -30,7 +30,7 @@ class _AttachmentItemState extends State<AttachmentItem> {
late final _videoPlayer = Player(
configuration: PlayerConfiguration(
title: "Attachment #${getTag()}",
title: 'Attachment #${getTag()}',
logLevel: MPVLogLevel.error,
),
);

View File

@ -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: () {

View File

@ -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'),
),
),
);