mirror of
https://github.com/krille-chan/fluffychat
synced 2024-07-13 19:34:04 +00:00
refactor: Move room headers into appbar bottom field
This commit is contained in:
parent
f42509f710
commit
e81a366a5b
61
lib/pages/chat/chat_app_bar_list_tile.dart
Normal file
61
lib/pages/chat/chat_app_bar_list_tile.dart
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||||
|
|
||||||
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
|
import 'package:fluffychat/utils/url_launcher.dart';
|
||||||
|
|
||||||
|
class ChatAppBarListTile extends StatelessWidget {
|
||||||
|
final Widget? leading;
|
||||||
|
final String title;
|
||||||
|
final Widget? trailing;
|
||||||
|
final void Function()? onTap;
|
||||||
|
|
||||||
|
const ChatAppBarListTile({
|
||||||
|
super.key,
|
||||||
|
this.leading,
|
||||||
|
required this.title,
|
||||||
|
this.trailing,
|
||||||
|
this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final leading = this.leading;
|
||||||
|
final trailing = this.trailing;
|
||||||
|
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (leading != null) leading,
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
|
child: Linkify(
|
||||||
|
text: title,
|
||||||
|
options: const LinkifyOptions(humanize: false),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
fontSize: fontSize,
|
||||||
|
),
|
||||||
|
linkStyle: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
fontSize: fontSize,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
decorationColor:
|
||||||
|
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (trailing != null) trailing,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,13 +8,13 @@ import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/themes.dart';
|
import 'package:fluffychat/config/themes.dart';
|
||||||
import 'package:fluffychat/pages/chat/chat.dart';
|
import 'package:fluffychat/pages/chat/chat.dart';
|
||||||
|
import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart';
|
||||||
import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
|
import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
|
||||||
import 'package:fluffychat/pages/chat/chat_event_list.dart';
|
import 'package:fluffychat/pages/chat/chat_event_list.dart';
|
||||||
import 'package:fluffychat/pages/chat/encryption_button.dart';
|
import 'package:fluffychat/pages/chat/encryption_button.dart';
|
||||||
import 'package:fluffychat/pages/chat/pinned_events.dart';
|
import 'package:fluffychat/pages/chat/pinned_events.dart';
|
||||||
import 'package:fluffychat/pages/chat/reactions_picker.dart';
|
import 'package:fluffychat/pages/chat/reactions_picker.dart';
|
||||||
import 'package:fluffychat/pages/chat/reply_display.dart';
|
import 'package:fluffychat/pages/chat/reply_display.dart';
|
||||||
import 'package:fluffychat/pages/chat/tombstone_display.dart';
|
|
||||||
import 'package:fluffychat/utils/account_config.dart';
|
import 'package:fluffychat/utils/account_config.dart';
|
||||||
import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
|
import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
|
||||||
import 'package:fluffychat/widgets/connection_status_header.dart';
|
import 'package:fluffychat/widgets/connection_status_header.dart';
|
||||||
|
@ -155,6 +155,18 @@ class ChatView extends StatelessWidget {
|
||||||
builder: (context, snapshot) => FutureBuilder(
|
builder: (context, snapshot) => FutureBuilder(
|
||||||
future: controller.loadTimelineFuture,
|
future: controller.loadTimelineFuture,
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
|
var appbarBottomHeight = 0.0;
|
||||||
|
if (controller.room.pinnedEventIds.isNotEmpty) {
|
||||||
|
appbarBottomHeight += 42;
|
||||||
|
}
|
||||||
|
if (scrollUpBannerEventId != null) {
|
||||||
|
appbarBottomHeight += 42;
|
||||||
|
}
|
||||||
|
final tombstoneEvent =
|
||||||
|
controller.room.getState(EventTypes.RoomTombstone);
|
||||||
|
if (tombstoneEvent != null) {
|
||||||
|
appbarBottomHeight += 42;
|
||||||
|
}
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
actionsIconTheme: IconThemeData(
|
actionsIconTheme: IconThemeData(
|
||||||
|
@ -177,6 +189,50 @@ class ChatView extends StatelessWidget {
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: ChatAppBarTitle(controller),
|
title: ChatAppBarTitle(controller),
|
||||||
actions: _appBarActions(context),
|
actions: _appBarActions(context),
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: Size.fromHeight(appbarBottomHeight),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
PinnedEvents(controller),
|
||||||
|
if (tombstoneEvent != null)
|
||||||
|
ChatAppBarListTile(
|
||||||
|
title: tombstoneEvent.parsedTombstoneContent.body,
|
||||||
|
leading: const Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Icon(Icons.upgrade_outlined),
|
||||||
|
),
|
||||||
|
trailing: TextButton(
|
||||||
|
onPressed: controller.goToNewRoomAction,
|
||||||
|
child: Text(L10n.of(context)!.goToTheNewRoom),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (scrollUpBannerEventId != null)
|
||||||
|
ChatAppBarListTile(
|
||||||
|
leading: IconButton(
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
tooltip: L10n.of(context)!.close,
|
||||||
|
onPressed: () {
|
||||||
|
controller.discardScrollUpBannerEventId();
|
||||||
|
controller.setReadMarker();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: L10n.of(context)!.jumpToLastReadMessage,
|
||||||
|
trailing: TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
controller.scrollToEventId(
|
||||||
|
scrollUpBannerEventId,
|
||||||
|
);
|
||||||
|
controller.discardScrollUpBannerEventId();
|
||||||
|
},
|
||||||
|
child: Text(L10n.of(context)!.jump),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: controller.showScrollDownButton &&
|
floatingActionButton: controller.showScrollDownButton &&
|
||||||
controller.selectedEvents.isEmpty
|
controller.selectedEvents.isEmpty
|
||||||
|
@ -211,45 +267,6 @@ class ChatView extends StatelessWidget {
|
||||||
SafeArea(
|
SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
TombstoneDisplay(controller),
|
|
||||||
if (scrollUpBannerEventId != null)
|
|
||||||
Material(
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.surfaceVariant,
|
|
||||||
shape: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
width: 1,
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: ListTile(
|
|
||||||
leading: IconButton(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onSurfaceVariant,
|
|
||||||
icon: const Icon(Icons.close),
|
|
||||||
tooltip: L10n.of(context)!.close,
|
|
||||||
onPressed: () {
|
|
||||||
controller.discardScrollUpBannerEventId();
|
|
||||||
controller.setReadMarker();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
L10n.of(context)!.jumpToLastReadMessage,
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.only(left: 8),
|
|
||||||
trailing: TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
controller.scrollToEventId(
|
|
||||||
scrollUpBannerEventId,
|
|
||||||
);
|
|
||||||
controller.discardScrollUpBannerEventId();
|
|
||||||
},
|
|
||||||
child: Text(L10n.of(context)!.jump),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PinnedEvents(controller),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: controller.clearSingleSelectedEvent,
|
onTap: controller.clearSingleSelectedEvent,
|
||||||
|
|
|
@ -4,14 +4,12 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
|
||||||
import 'package:fluffychat/pages/chat/chat.dart';
|
import 'package:fluffychat/pages/chat/chat.dart';
|
||||||
|
import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart';
|
||||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||||
import 'package:fluffychat/utils/url_launcher.dart';
|
|
||||||
|
|
||||||
class PinnedEvents extends StatelessWidget {
|
class PinnedEvents extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
|
@ -65,80 +63,32 @@ class PinnedEvents extends StatelessWidget {
|
||||||
future: controller.room.getEventById(pinnedEventIds.last),
|
future: controller.room.getEventById(pinnedEventIds.last),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final event = snapshot.data;
|
final event = snapshot.data;
|
||||||
|
return FutureBuilder<String>(
|
||||||
if (event == null) {
|
future: event?.calcLocalizedBody(
|
||||||
return const SizedBox.shrink();
|
MatrixLocals(L10n.of(context)!),
|
||||||
}
|
withSenderNamePrefix: true,
|
||||||
|
hideReply: true,
|
||||||
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
|
|
||||||
return Material(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
|
||||||
shape: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
width: 1,
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: InkWell(
|
builder: (context, snapshot) => ChatAppBarListTile(
|
||||||
onTap: () => _displayPinnedEventsDialog(context),
|
title: snapshot.data ??
|
||||||
child: Row(
|
event?.calcLocalizedBodyFallback(
|
||||||
children: [
|
MatrixLocals(L10n.of(context)!),
|
||||||
IconButton(
|
withSenderNamePrefix: true,
|
||||||
splashRadius: 20,
|
hideReply: true,
|
||||||
iconSize: 20,
|
) ??
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
L10n.of(context)!.loadingPleaseWait,
|
||||||
icon: const Icon(Icons.push_pin),
|
leading: IconButton(
|
||||||
tooltip: L10n.of(context)!.unpin,
|
splashRadius: 20,
|
||||||
onPressed:
|
iconSize: 20,
|
||||||
controller.room.canSendEvent(EventTypes.RoomPinnedEvents)
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
? () => controller.unpinEvent(event.eventId)
|
icon: const Icon(Icons.push_pin),
|
||||||
: null,
|
tooltip: L10n.of(context)!.unpin,
|
||||||
),
|
onPressed:
|
||||||
Expanded(
|
controller.room.canSendEvent(EventTypes.RoomPinnedEvents)
|
||||||
child: Padding(
|
? () => controller.unpinEvent(event!.eventId)
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
: null,
|
||||||
child: FutureBuilder<String>(
|
|
||||||
future: event.calcLocalizedBody(
|
|
||||||
MatrixLocals(L10n.of(context)!),
|
|
||||||
withSenderNamePrefix: true,
|
|
||||||
hideReply: true,
|
|
||||||
),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
return Linkify(
|
|
||||||
text: snapshot.data ??
|
|
||||||
event.calcLocalizedBodyFallback(
|
|
||||||
MatrixLocals(L10n.of(context)!),
|
|
||||||
withSenderNamePrefix: true,
|
|
||||||
hideReply: true,
|
|
||||||
),
|
|
||||||
options: const LinkifyOptions(humanize: false),
|
|
||||||
maxLines: 2,
|
|
||||||
style: TextStyle(
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
fontSize: fontSize,
|
|
||||||
decoration: event.redacted
|
|
||||||
? TextDecoration.lineThrough
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
linkStyle: TextStyle(
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
fontSize: fontSize,
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
decorationColor:
|
|
||||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
onOpen: (url) =>
|
|
||||||
UrlLauncher(context, url.url).launchUrl(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
onTap: () => _displayPinnedEventsDialog(context),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|
||||||
import 'package:matrix/matrix.dart';
|
|
||||||
|
|
||||||
import 'chat.dart';
|
|
||||||
|
|
||||||
class TombstoneDisplay extends StatelessWidget {
|
|
||||||
final ChatController controller;
|
|
||||||
const TombstoneDisplay(this.controller, {super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (controller.room.getState(EventTypes.RoomTombstone) == null) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
return SizedBox(
|
|
||||||
height: 72,
|
|
||||||
child: Material(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
|
||||||
elevation: 1,
|
|
||||||
child: ListTile(
|
|
||||||
leading: CircleAvatar(
|
|
||||||
foregroundColor: Theme.of(context).colorScheme.onSecondary,
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
|
||||||
child: const Icon(Icons.upgrade_outlined),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
controller.room
|
|
||||||
.getState(EventTypes.RoomTombstone)!
|
|
||||||
.parsedTombstoneContent
|
|
||||||
.body,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subtitle: Text(L10n.of(context)!.goToTheNewRoom),
|
|
||||||
onTap: controller.goToNewRoomAction,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue