feat: New material 3 design

This commit is contained in:
Christian Pauly 2022-07-07 18:50:13 +02:00
parent 802ff0fa9d
commit 091958be0b
28 changed files with 821 additions and 1311 deletions

View File

@ -2826,5 +2826,7 @@
"user": {} "user": {}
} }
}, },
"noEmailWarning": "Please enter a valid email address. Otherwise you won't be able to reset your password. If you don't want to, tap again on the button to continue." "noEmailWarning": "Please enter a valid email address. Otherwise you won't be able to reset your password. If you don't want to, tap again on the button to continue.",
"stories": "Stories",
"users": "Users"
} }

View File

@ -17,7 +17,6 @@ import 'package:fluffychat/pages/login/login.dart';
import 'package:fluffychat/pages/new_group/new_group.dart'; import 'package:fluffychat/pages/new_group/new_group.dart';
import 'package:fluffychat/pages/new_private_chat/new_private_chat.dart'; import 'package:fluffychat/pages/new_private_chat/new_private_chat.dart';
import 'package:fluffychat/pages/new_space/new_space.dart'; import 'package:fluffychat/pages/new_space/new_space.dart';
import 'package:fluffychat/pages/search/search.dart';
import 'package:fluffychat/pages/settings/settings.dart'; import 'package:fluffychat/pages/settings/settings.dart';
import 'package:fluffychat/pages/settings_3pid/settings_3pid.dart'; import 'package:fluffychat/pages/settings_3pid/settings_3pid.dart';
import 'package:fluffychat/pages/settings_account/settings_account.dart'; import 'package:fluffychat/pages/settings_account/settings_account.dart';
@ -92,10 +91,6 @@ class AppRoutes {
widget: const Settings(), widget: const Settings(),
stackedRoutes: _settingsRoutes, stackedRoutes: _settingsRoutes,
), ),
VWidget(
path: '/search',
widget: const Search(),
),
VWidget( VWidget(
path: '/archive', path: '/archive',
widget: const Archive(), widget: const Archive(),
@ -225,14 +220,6 @@ class AppRoutes {
), ),
], ],
), ),
VWidget(
path: '/search',
widget: const TwoColumnLayout(
mainView: Search(),
sideView: EmptyPage(),
),
buildTransition: _fadeTransition,
),
VWidget( VWidget(
path: '/archive', path: '/archive',
widget: const TwoColumnLayout( widget: const TwoColumnLayout(

View File

@ -9,55 +9,9 @@ abstract class FluffyThemes {
static bool isColumnMode(BuildContext context) => static bool isColumnMode(BuildContext context) =>
MediaQuery.of(context).size.width > columnWidth * 2; MediaQuery.of(context).size.width > columnWidth * 2;
static const fallbackTextStyle = static const fallbackTextStyle = TextStyle(
TextStyle(fontFamily: 'Roboto', fontFamilyFallback: ['NotoEmoji']); fontFamily: 'Roboto',
fontFamilyFallback: ['NotoEmoji'],
static const TextStyle loginTextFieldStyle = TextStyle(color: Colors.black);
static InputDecoration loginTextFieldDecoration({
String? errorText,
String? labelText,
String? hintText,
Widget? suffixIcon,
Widget? prefixIcon,
Color? errorColor,
}) =>
InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
fillColor: Colors.white.withAlpha(200),
labelText: labelText,
hintText: hintText,
suffixIcon: suffixIcon,
prefixIcon: prefixIcon,
suffixIconColor: Colors.black,
prefixIconColor: Colors.black,
iconColor: Colors.black,
errorText: errorText,
errorMaxLines: 4,
errorStyle: TextStyle(
color: errorColor ?? Colors.redAccent.shade200,
shadows: const [
Shadow(
color: Colors.black,
offset: Offset(0, 0),
blurRadius: 10,
),
],
),
hintStyle: TextStyle(color: Colors.grey.shade700),
labelStyle: const TextStyle(
color: Colors.white,
shadows: [
Shadow(
color: Colors.black,
offset: Offset(0, 0),
blurRadius: 5,
),
],
),
contentPadding: const EdgeInsets.all(16),
); );
static var fallbackTextTheme = const TextTheme( static var fallbackTextTheme = const TextTheme(
@ -83,12 +37,12 @@ abstract class FluffyThemes {
colorSchemeSeed: AppConfig.colorSchemeSeed ?? colorSchemeSeed: AppConfig.colorSchemeSeed ??
colorScheme?.primary ?? colorScheme?.primary ??
AppConfig.chatColor, AppConfig.chatColor,
scaffoldBackgroundColor: Colors.white,
textTheme: PlatformInfos.isDesktop textTheme: PlatformInfos.isDesktop
? Typography.material2018().black.merge(fallbackTextTheme) ? Typography.material2018().black.merge(fallbackTextTheme)
: null, : null,
snackBarTheme: snackBarTheme: const SnackBarThemeData(
const SnackBarThemeData(behavior: SnackBarBehavior.floating), behavior: SnackBarBehavior.floating,
),
pageTransitionsTheme: const PageTransitionsTheme( pageTransitionsTheme: const PageTransitionsTheme(
builders: { builders: {
TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(), TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(),
@ -100,31 +54,12 @@ abstract class FluffyThemes {
}, },
), ),
dividerColor: Colors.blueGrey.shade50, dividerColor: Colors.blueGrey.shade50,
elevatedButtonTheme: ElevatedButtonThemeData( inputDecorationTheme: const InputDecorationTheme(
style: ElevatedButton.styleFrom( border: UnderlineInputBorder(borderSide: BorderSide(width: 1)),
textStyle: const TextStyle(fontSize: 16),
elevation: 6,
shadowColor: const Color(0x44000000),
minimumSize: const Size.fromHeight(48),
padding: const EdgeInsets.all(12),
),
),
cardTheme: const CardTheme(
elevation: 6,
// shadowColor: Color(0x44000000),
clipBehavior: Clip.hardEdge,
),
inputDecorationTheme: InputDecorationTheme(
border: const UnderlineInputBorder(borderSide: BorderSide(width: 1)),
filled: true, filled: true,
fillColor: Colors.blueGrey.shade50,
), ),
appBarTheme: const AppBarTheme( appBarTheme: const AppBarTheme(
elevation: 6,
shadowColor: Color(0x44000000),
systemOverlayStyle: SystemUiOverlayStyle.dark, systemOverlayStyle: SystemUiOverlayStyle.dark,
surfaceTintColor: Colors.white,
backgroundColor: Colors.white,
), ),
); );
@ -135,7 +70,6 @@ abstract class FluffyThemes {
colorSchemeSeed: AppConfig.colorSchemeSeed ?? colorSchemeSeed: AppConfig.colorSchemeSeed ??
colorScheme?.primary ?? colorScheme?.primary ??
AppConfig.chatColor, AppConfig.chatColor,
scaffoldBackgroundColor: Colors.black,
textTheme: PlatformInfos.isDesktop textTheme: PlatformInfos.isDesktop
? Typography.material2018().white.merge(fallbackTextTheme) ? Typography.material2018().white.merge(fallbackTextTheme)
: null, : null,
@ -151,20 +85,11 @@ abstract class FluffyThemes {
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
}, },
), ),
dividerColor: Colors.blueGrey.shade600, inputDecorationTheme: const InputDecorationTheme(
elevatedButtonTheme: ElevatedButtonThemeData( border: UnderlineInputBorder(borderSide: BorderSide(width: 1)),
style: ElevatedButton.styleFrom( filled: true,
primary: AppConfig.chatColor,
onPrimary: Colors.white,
minimumSize: const Size.fromHeight(48),
textStyle: const TextStyle(fontSize: 16),
padding: const EdgeInsets.all(12),
),
),
appBarTheme: const AppBarTheme(
elevation: 6,
backgroundColor: Color(0xff1D1D1D),
), ),
dividerColor: Colors.blueGrey.shade900,
); );
static Color blackWhiteColor(BuildContext context) => static Color blackWhiteColor(BuildContext context) =>

View File

@ -255,12 +255,13 @@ class ChatView extends StatelessWidget {
), ),
elevation: 6, elevation: 6,
shadowColor: Theme.of(context) shadowColor: Theme.of(context)
.secondaryHeaderColor .dividerColor
.withAlpha(100), .withAlpha(100),
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
color: Theme.of(context) color: Theme.of(context).brightness ==
.appBarTheme Brightness.light
.backgroundColor, ? Colors.white
: Colors.black,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [

View File

@ -68,7 +68,9 @@ class Message extends StatelessWidget {
final client = Matrix.of(context).client; final client = Matrix.of(context).client;
final ownMessage = event.senderId == client.userID; final ownMessage = event.senderId == client.userID;
final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft; final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft;
var color = Theme.of(context).scaffoldBackgroundColor; var color = Theme.of(context).brightness == Brightness.light
? Colors.white
: Colors.black;
final displayTime = event.type == EventTypes.RoomCreate || final displayTime = event.type == EventTypes.RoomCreate ||
nextEvent == null || nextEvent == null ||
!event.originServerTs.sameEnvironment(nextEvent!.originServerTs); !event.originServerTs.sameEnvironment(nextEvent!.originServerTs);

View File

@ -14,9 +14,9 @@ import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat_list/chat_list_view.dart'; import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
import 'package:fluffychat/pages/chat_list/spaces_bottom_bar.dart';
import 'package:fluffychat/pages/chat_list/spaces_entry.dart'; import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/famedlysdk_store.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import '../../../utils/account_bundles.dart'; import '../../../utils/account_bundles.dart';
import '../../main.dart'; import '../../main.dart';
@ -52,6 +52,97 @@ class ChatListController extends State<ChatList> with TickerProviderStateMixin {
SpacesEntry? _activeSpacesEntry; SpacesEntry? _activeSpacesEntry;
bool isSearchMode = false;
Future<QueryPublicRoomsResponse>? publicRoomsResponse;
String? searchServer;
Timer? _coolDown;
SearchUserDirectoryResponse? userSearchResult;
QueryPublicRoomsResponse? roomSearchResult;
bool isSearching = false;
static const String _serverStoreNamespace = 'im.fluffychat.search.server';
void setServer() async {
final newServer = await showTextInputDialog(
useRootNavigator: false,
title: L10n.of(context)!.changeTheHomeserver,
context: context,
okLabel: L10n.of(context)!.ok,
cancelLabel: L10n.of(context)!.cancel,
textFields: [
DialogTextField(
prefixText: 'https://',
hintText: Matrix.of(context).client.homeserver?.host,
initialText: searchServer,
keyboardType: TextInputType.url,
autocorrect: false)
]);
if (newServer == null) return;
Store().setItem(_serverStoreNamespace, newServer.single);
setState(() {
searchServer = newServer.single;
});
onSearchEnter(searchController.text);
}
final TextEditingController searchController = TextEditingController();
void _search() async {
final client = Matrix.of(context).client;
if (!isSearching) {
setState(() {
isSearching = true;
});
}
SearchUserDirectoryResponse? userSearchResult;
QueryPublicRoomsResponse? roomSearchResult;
try {
roomSearchResult = await client.queryPublicRooms(
server: searchServer,
filter: PublicRoomQueryFilter(genericSearchTerm: searchController.text),
limit: 20,
);
userSearchResult = await client.searchUserDirectory(
searchController.text,
limit: 20,
);
} catch (e, s) {
Logs().w('Searching has crashed', e, s);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
e.toLocalizedString(context),
),
),
);
}
setState(() {
isSearching = false;
this.roomSearchResult = roomSearchResult;
this.userSearchResult = userSearchResult;
});
}
void onSearchEnter(String text) {
if (text.isEmpty) {
cancelSearch();
return;
}
setState(() {
isSearchMode = true;
});
_coolDown?.cancel();
_coolDown = Timer(const Duration(milliseconds: 500), _search);
}
void cancelSearch() => setState(() {
searchController.clear();
isSearchMode = false;
roomSearchResult = userSearchResult = null;
isSearching = false;
});
SpacesEntry get activeSpacesEntry { SpacesEntry get activeSpacesEntry {
final id = _activeSpacesEntry; final id = _activeSpacesEntry;
return (id == null || !id.stillValid(context)) ? defaultSpacesEntry : id; return (id == null || !id.stillValid(context)) ? defaultSpacesEntry : id;
@ -72,6 +163,8 @@ class ChatListController extends State<ChatList> with TickerProviderStateMixin {
Stream<Client> get clientStream => _clientStream.stream; Stream<Client> get clientStream => _clientStream.stream;
void addAccountAction() => VRouter.of(context).to('/settings/account/add');
void _onScroll() { void _onScroll() {
final newScrolledToTop = scrollController.position.pixels <= 0; final newScrolledToTop = scrollController.position.pixels <= 0;
if (newScrolledToTop != scrolledToTop) { if (newScrolledToTop != scrolledToTop) {
@ -82,12 +175,7 @@ class ChatListController extends State<ChatList> with TickerProviderStateMixin {
} }
void setActiveSpacesEntry(BuildContext context, SpacesEntry? spaceId) { void setActiveSpacesEntry(BuildContext context, SpacesEntry? spaceId) {
if ((snappingSheetController.isAttached Scaffold.of(context).closeDrawer();
? snappingSheetController.currentPosition
: 0) !=
kSpacesBottomBarHeight) {
snapBackSpacesSheet();
}
setState(() => _activeSpacesEntry = spaceId); setState(() => _activeSpacesEntry = spaceId);
} }
@ -212,6 +300,10 @@ class ChatListController extends State<ChatList> with TickerProviderStateMixin {
scrollController.addListener(_onScroll); scrollController.addListener(_onScroll);
_waitForFirstSync(); _waitForFirstSync();
_hackyWebRTCFixForWeb(); _hackyWebRTCFixForWeb();
WidgetsBinding.instance.addPostFrameCallback((_) async {
searchServer = await Store().getItem(_serverStoreNamespace);
});
super.initState(); super.initState();
} }
@ -338,32 +430,6 @@ class ChatListController extends State<ChatList> with TickerProviderStateMixin {
); );
} }
void onPopupMenuSelect(action) {
switch (action) {
case PopupMenuAction.setStatus:
setStatus();
break;
case PopupMenuAction.settings:
VRouter.of(context).to('/settings');
break;
case PopupMenuAction.invite:
FluffyShare.share(
L10n.of(context)!.inviteText(Matrix.of(context).client.userID!,
'https://matrix.to/#/${Matrix.of(context).client.userID}?client=im.fluffychat'),
context);
break;
case PopupMenuAction.newGroup:
VRouter.of(context).to('/newgroup');
break;
case PopupMenuAction.newSpace:
VRouter.of(context).to('/newspace');
break;
case PopupMenuAction.archive:
VRouter.of(context).to('/archive');
break;
}
}
Future<void> _archiveSelectedRooms() async { Future<void> _archiveSelectedRooms() async {
final client = Matrix.of(context).client; final client = Matrix.of(context).client;
while (selectedRoomIds.isNotEmpty) { while (selectedRoomIds.isNotEmpty) {
@ -593,15 +659,6 @@ class ChatListController extends State<ChatList> with TickerProviderStateMixin {
Matrix.of(context).voipPlugin?.context = context; Matrix.of(context).voipPlugin?.context = context;
} }
void snapBackSpacesSheet() {
snappingSheetController.snapToPosition(
const SnappingPosition.pixels(
positionPixels: kSpacesBottomBarHeight,
snappingDuration: Duration(milliseconds: 500),
),
);
}
expandSpaces() { expandSpaces() {
snappingSheetController.snapToPosition( snappingSheetController.snapToPosition(
const SnappingPosition.factor(positionFactor: 0.5), const SnappingPosition.factor(positionFactor: 0.5),

View File

@ -8,9 +8,12 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
import 'package:fluffychat/pages/chat_list/spaces_bottom_bar.dart';
import 'package:fluffychat/pages/chat_list/spaces_entry.dart'; import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
import 'package:fluffychat/pages/chat_list/stories_header.dart'; import 'package:fluffychat/pages/chat_list/stories_header.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/connection_status_header.dart';
import 'package:fluffychat/widgets/profile_bottom_sheet.dart';
import 'package:fluffychat/widgets/public_room_bottom_sheet.dart';
import '../../utils/stream_extension.dart'; import '../../utils/stream_extension.dart';
import '../../widgets/matrix.dart'; import '../../widgets/matrix.dart';
@ -46,6 +49,8 @@ class _ChatListViewBodyState extends State<ChatListViewBody> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final reversed = !_animationReversed(); final reversed = !_animationReversed();
final roomSearchResult = widget.controller.roomSearchResult;
final userSearchResult = widget.controller.userSearchResult;
Widget child; Widget child;
if (widget.controller.waitForFirstSync && if (widget.controller.waitForFirstSync &&
Matrix.of(context).client.prevBatch != null) { Matrix.of(context).client.prevBatch != null) {
@ -86,13 +91,113 @@ class _ChatListViewBodyState extends State<ChatListViewBody> {
itemBuilder: (BuildContext context, int i) { itemBuilder: (BuildContext context, int i) {
if (displayStoriesHeader) { if (displayStoriesHeader) {
if (i == 0) { if (i == 0) {
return const StoriesHeader(); return Column(
mainAxisSize: MainAxisSize.min,
children: [
const ConnectionStatusHeader(),
if (roomSearchResult != null) ...[
_SearchTitle(title: L10n.of(context)!.publicRooms),
AnimatedContainer(
height: roomSearchResult.chunk.isEmpty ? 0 : 106,
duration: const Duration(milliseconds: 250),
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: roomSearchResult.chunk.length,
itemBuilder: (context, i) => _SearchItem(
title: roomSearchResult.chunk[i].name ??
roomSearchResult
.chunk[i].canonicalAlias?.localpart ??
L10n.of(context)!.group,
avatar: roomSearchResult.chunk[i].avatarUrl,
onPressed: () => showModalBottomSheet(
context: context,
builder: (c) => PublicRoomBottomSheet(
roomAlias:
roomSearchResult.chunk[i].canonicalAlias ??
roomSearchResult.chunk[i].roomId,
outerContext: context,
chunk: roomSearchResult.chunk[i],
),
),
),
),
),
],
if (userSearchResult != null) ...[
_SearchTitle(title: L10n.of(context)!.users),
AnimatedContainer(
height: userSearchResult.results.isEmpty ? 0 : 106,
duration: const Duration(milliseconds: 250),
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: userSearchResult.results.length,
itemBuilder: (context, i) => _SearchItem(
title: userSearchResult.results[i].displayName ??
userSearchResult.results[i].userId.localpart ??
L10n.of(context)!.unknownDevice,
avatar: userSearchResult.results[i].avatarUrl,
onPressed: () => showModalBottomSheet(
context: context,
builder: (c) => ProfileBottomSheet(
userId: userSearchResult.results[i].userId,
outerContext: context,
),
),
),
),
),
],
if (widget.controller.isSearchMode)
_SearchTitle(title: L10n.of(context)!.stories),
StoriesHeader(
filter: widget.controller.searchController.text,
),
AnimatedContainer(
height: !widget.controller.isSearchMode &&
widget.controller.showChatBackupBanner
? 54
: 0,
duration: const Duration(milliseconds: 300),
clipBehavior: Clip.hardEdge,
curve: Curves.bounceInOut,
decoration: const BoxDecoration(),
child: Material(
color: Theme.of(context).colorScheme.surface,
child: ListTile(
leading: Image.asset(
'assets/backup.png',
fit: BoxFit.contain,
width: 44,
),
title: Text(
L10n.of(context)!.setupChatBackupNow,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
),
),
trailing: const Icon(Icons.chevron_right_outlined),
onTap: widget.controller.firstRunBootstrapAction,
),
),
),
if (widget.controller.isSearchMode)
_SearchTitle(title: L10n.of(context)!.chats),
],
);
} }
i--; i--;
} }
if (i >= rooms.length) { if (i >= rooms.length) {
return const ListTile(); return const ListTile();
} }
if (!rooms[i].displayname.toLowerCase().contains(
widget.controller.searchController.text.toLowerCase())) {
return Container();
}
return ChatListItem( return ChatListItem(
rooms[i], rooms[i],
selected: widget.controller.selectedRoomIds.contains(rooms[i].id), selected: widget.controller.selectedRoomIds.contains(rooms[i].id),
@ -176,13 +281,7 @@ class _ChatListViewBodyState extends State<ChatListViewBody> {
return SharedAxisTransition( return SharedAxisTransition(
animation: primaryAnimation, animation: primaryAnimation,
secondaryAnimation: secondaryAnimation, secondaryAnimation: secondaryAnimation,
transitionType: (widget.controller.snappingSheetController.isAttached transitionType: SharedAxisTransitionType.vertical,
? widget
.controller.snappingSheetController.currentPosition
: 0) ==
kSpacesBottomBarHeight
? SharedAxisTransitionType.horizontal
: SharedAxisTransitionType.vertical,
fillColor: Theme.of(context).scaffoldBackgroundColor, fillColor: Theme.of(context).scaffoldBackgroundColor,
child: child, child: child,
); );
@ -221,3 +320,77 @@ class _ChatListViewBodyState extends State<ChatListViewBody> {
return reversed; return reversed;
} }
} }
class _SearchTitle extends StatelessWidget {
final String title;
const _SearchTitle({required this.title, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Container(
decoration: BoxDecoration(
border: Border.symmetric(
horizontal: BorderSide(
color: Theme.of(context).dividerColor,
width: 1,
)),
color: Theme.of(context).colorScheme.surface,
),
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
child: Text(title,
textAlign: TextAlign.left,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 12,
fontWeight: FontWeight.bold,
)),
),
),
);
}
class _SearchItem extends StatelessWidget {
final String title;
final Uri? avatar;
final void Function() onPressed;
const _SearchItem({
required this.title,
this.avatar,
required this.onPressed,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) => InkWell(
onTap: onPressed,
child: SizedBox(
width: 84,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 8),
Avatar(
mxContent: avatar,
name: title,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
title,
maxLines: 2,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 12,
),
),
),
],
),
),
);
}

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/spaces_drawer.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/widgets/matrix.dart';
class ChatListDrawer extends StatelessWidget {
final ChatListController controller;
const ChatListDrawer(this.controller, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Drawer(
child: SafeArea(
child: Column(
children: [
Expanded(
child: SpacesDrawer(
controller: controller,
),
),
const Divider(),
ListTile(
leading: Icon(
Icons.group_add_outlined,
color: Theme.of(context).colorScheme.onBackground,
),
title: Text(L10n.of(context)!.createNewGroup),
onTap: () {
Scaffold.of(context).closeDrawer();
VRouter.of(context).to('/newgroup');
},
),
ListTile(
leading: Icon(
Icons.adaptive.share_outlined,
color: Theme.of(context).colorScheme.onBackground,
),
title: Text(L10n.of(context)!.inviteContact),
onTap: () {
Scaffold.of(context).closeDrawer();
FluffyShare.share(
L10n.of(context)!.inviteText(
Matrix.of(context).client.userID!,
'https://matrix.to/#/${Matrix.of(context).client.userID}?client=im.fluffychat'),
context);
},
),
ListTile(
leading: Icon(
Icons.settings_outlined,
color: Theme.of(context).colorScheme.onBackground,
),
title: Text(L10n.of(context)!.settings),
onTap: () {
Scaffold.of(context).closeDrawer();
VRouter.of(context).to('/settings');
},
),
],
),
),
);
}

View File

@ -1,15 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:animations/animations.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart'; import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
import '../../widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
final ChatListController controller; final ChatListController controller;
@ -21,15 +17,9 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
final selectMode = controller.selectMode; final selectMode = controller.selectMode;
return AppBar( return AppBar(
elevation: controller.scrolledToTop ? 0 : null, titleSpacing: 8,
actionsIconTheme: IconThemeData( automaticallyImplyLeading: false,
color: controller.selectedRoomIds.isEmpty leading: selectMode == SelectMode.normal
? null
: Theme.of(context).colorScheme.primary,
),
leading: Matrix.of(context).isMultiAccount
? ClientChooserButton(controller)
: selectMode == SelectMode.normal
? null ? null
: IconButton( : IconButton(
tooltip: L10n.of(context)!.cancel, tooltip: L10n.of(context)!.cancel,
@ -37,7 +27,82 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
onPressed: controller.cancelAction, onPressed: controller.cancelAction,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
centerTitle: false, title: selectMode == SelectMode.share
? Text(
L10n.of(context)!.share,
key: const ValueKey(SelectMode.share),
)
: selectMode == SelectMode.select
? Text(
controller.selectedRoomIds.length.toString(),
key: const ValueKey(SelectMode.select),
)
: TextField(
controller: controller.searchController,
textInputAction: TextInputAction.search,
onChanged: controller.onSearchEnter,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(90),
borderSide: BorderSide.none,
),
hintText: L10n.of(context)!.search,
prefixIcon: controller.isSearchMode
? IconButton(
tooltip: L10n.of(context)!.cancel,
icon: const Icon(Icons.close_outlined),
onPressed: controller.cancelSearch,
color: Theme.of(context).colorScheme.primary,
)
: IconButton(
onPressed: Scaffold.of(context).openDrawer,
icon: Icon(
Icons.menu,
color: Theme.of(context).colorScheme.onBackground,
),
),
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: controller.isSearchMode
? [
if (controller.isSearching)
const CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
TextButton(
onPressed: controller.setServer,
style: TextButton.styleFrom(
textStyle: const TextStyle(fontSize: 12),
),
child: Text(
controller.searchServer ??
Matrix.of(context)
.client
.homeserver!
.host,
maxLines: 2,
),
),
]
: [
IconButton(
icon: Icon(
Icons.camera_alt_outlined,
color: Theme.of(context)
.colorScheme
.onBackground,
),
tooltip: L10n.of(context)!.addToStory,
onPressed: () =>
VRouter.of(context).to('/stories/create'),
),
ClientChooserButton(controller),
const SizedBox(width: 12),
],
),
),
),
actions: selectMode == SelectMode.share actions: selectMode == SelectMode.share
? null ? null
: selectMode == SelectMode.select : selectMode == SelectMode.select
@ -75,138 +140,7 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
onPressed: controller.archiveAction, onPressed: controller.archiveAction,
), ),
] ]
: [ : null,
KeyBoardShortcuts(
keysToPress: {
LogicalKeyboardKey.controlLeft,
LogicalKeyboardKey.keyF
},
onKeysPressed: () => VRouter.of(context).to('/search'),
helpLabel: L10n.of(context)!.search,
child: IconButton(
icon: const Icon(Icons.search_outlined),
tooltip: L10n.of(context)!.search,
onPressed: () => VRouter.of(context).to('/search'),
),
),
if (selectMode == SelectMode.normal)
IconButton(
icon: const Icon(Icons.camera_alt_outlined),
tooltip: L10n.of(context)!.addToStory,
onPressed: () =>
VRouter.of(context).to('/stories/create'),
),
PopupMenuButton<PopupMenuAction>(
onSelected: controller.onPopupMenuSelect,
itemBuilder: (_) => [
PopupMenuItem(
value: PopupMenuAction.setStatus,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.edit_outlined),
const SizedBox(width: 12),
Text(L10n.of(context)!.setStatus),
],
),
),
PopupMenuItem(
value: PopupMenuAction.newGroup,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.group_add_outlined),
const SizedBox(width: 12),
Text(L10n.of(context)!.createNewGroup),
],
),
),
PopupMenuItem(
value: PopupMenuAction.newSpace,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.group_work_outlined),
const SizedBox(width: 12),
Text(L10n.of(context)!.createNewSpace),
],
),
),
PopupMenuItem(
value: PopupMenuAction.invite,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.adaptive.share_outlined),
const SizedBox(width: 12),
Text(L10n.of(context)!.inviteContact),
],
),
),
PopupMenuItem(
value: PopupMenuAction.archive,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.archive_outlined),
const SizedBox(width: 12),
Text(L10n.of(context)!.archive),
],
),
),
PopupMenuItem(
value: PopupMenuAction.settings,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.settings_outlined),
const SizedBox(width: 12),
Text(L10n.of(context)!.settings),
],
),
),
],
),
],
title: PageTransitionSwitcher(
reverse: false,
transitionBuilder: (
Widget child,
Animation<double> primaryAnimation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.scaled,
fillColor: Colors.transparent,
child: child,
);
},
layoutBuilder: (children) => Stack(
alignment: AlignmentDirectional.centerStart,
children: children,
),
child: selectMode == SelectMode.share
? Text(
L10n.of(context)!.share,
key: const ValueKey(SelectMode.share),
)
: selectMode == SelectMode.select
? Text(
controller.selectedRoomIds.length.toString(),
key: const ValueKey(SelectMode.select),
)
: (() {
final name = controller.activeSpaceId == null
? AppConfig.applicationName
: Matrix.of(context)
.client
.getRoomById(controller.activeSpaceId!)!
.displayname;
return Text(name, key: ValueKey(name));
})(),
),
); );
} }

View File

@ -4,12 +4,10 @@ import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart'; import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
import 'package:snapping_sheet/snapping_sheet.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/spaces_bottom_bar.dart'; import 'package:fluffychat/pages/chat_list/chat_list_drawer.dart';
import 'package:fluffychat/widgets/connection_status_header.dart';
import '../../widgets/matrix.dart'; import '../../widgets/matrix.dart';
import 'chat_list_body.dart'; import 'chat_list_body.dart';
import 'chat_list_header.dart'; import 'chat_list_header.dart';
@ -25,8 +23,6 @@ class ChatListView extends StatelessWidget {
stream: Matrix.of(context).onShareContentChanged.stream, stream: Matrix.of(context).onShareContentChanged.stream,
builder: (_, __) { builder: (_, __) {
final selectMode = controller.selectMode; final selectMode = controller.selectMode;
final showSpaces = controller.spacesEntries.length > 1 &&
controller.selectedRoomIds.isEmpty;
return VWidgetGuard( return VWidgetGuard(
onSystemPop: (redirector) async { onSystemPop: (redirector) async {
final selMode = controller.selectMode; final selMode = controller.selectMode;
@ -35,69 +31,14 @@ class ChatListView extends StatelessWidget {
}, },
child: Scaffold( child: Scaffold(
appBar: ChatListHeader(controller: controller), appBar: ChatListHeader(controller: controller),
body: LayoutBuilder( body: ChatListViewBody(controller),
builder: (context, size) { drawer: ChatListDrawer(controller),
controller.snappingSheetContainerSize = size;
return SnappingSheet(
key: ValueKey(Matrix.of(context).client.userID.toString() +
showSpaces.toString()),
controller: controller.snappingSheetController,
child: Column(
children: [
AnimatedContainer(
height: controller.showChatBackupBanner ? 54 : 0,
duration: const Duration(milliseconds: 300),
clipBehavior: Clip.hardEdge,
curve: Curves.bounceInOut,
decoration: const BoxDecoration(),
child: Material(
color: Theme.of(context).colorScheme.surface,
child: ListTile(
leading: Image.asset(
'assets/backup.png',
fit: BoxFit.contain,
width: 44,
),
title: Text(L10n.of(context)!.setupChatBackupNow),
trailing: const Icon(Icons.chevron_right_outlined),
onTap: controller.firstRunBootstrapAction,
),
),
),
Expanded(child: ChatListViewBody(controller)),
],
),
initialSnappingPosition: showSpaces
? const SnappingPosition.pixels(
positionPixels: kSpacesBottomBarHeight)
: const SnappingPosition.factor(positionFactor: 0.0),
snappingPositions: showSpaces
? const [
SnappingPosition.pixels(
positionPixels: kSpacesBottomBarHeight),
SnappingPosition.factor(positionFactor: 0.5),
SnappingPosition.factor(positionFactor: 0.9),
]
: [const SnappingPosition.factor(positionFactor: 0.0)],
sheetBelow: showSpaces
? SnappingSheetContent(
childScrollController:
controller.snappingSheetScrollContentController,
draggable: true,
child: SpacesBottomBar(controller),
)
: null,
);
},
),
floatingActionButton: selectMode == SelectMode.normal floatingActionButton: selectMode == SelectMode.normal
? Padding( ? KeyBoardShortcuts(
padding: showSpaces
? const EdgeInsets.only(bottom: 64.0)
: const EdgeInsets.all(0),
child: KeyBoardShortcuts(
child: FloatingActionButton.extended( child: FloatingActionButton.extended(
isExtended: controller.scrolledToTop, isExtended: controller.scrolledToTop,
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
onPressed: () => onPressed: () =>
VRouter.of(context).to('/newprivatechat'), VRouter.of(context).to('/newprivatechat'),
icon: const Icon(CupertinoIcons.chat_bubble), icon: const Icon(CupertinoIcons.chat_bubble),
@ -110,12 +51,8 @@ class ChatListView extends StatelessWidget {
onKeysPressed: () => onKeysPressed: () =>
VRouter.of(context).to('/newprivatechat'), VRouter.of(context).to('/newprivatechat'),
helpLabel: L10n.of(context)!.newChat, helpLabel: L10n.of(context)!.newChat,
),
) )
: null, : null,
bottomNavigationBar: const SafeArea(
child: ConnectionStatusHeader(),
),
), ),
); );
}, },

View File

@ -79,6 +79,16 @@ class ClientChooserButton extends StatelessWidget {
) )
.toList(), .toList(),
], ],
PopupMenuItem(
value: AddAccountAction.addAccount,
child: Row(
children: [
const Icon(Icons.person_add_outlined),
const SizedBox(width: 18),
Text(L10n.of(context)!.addAccount),
],
),
),
]; ];
} }
@ -124,7 +134,8 @@ class ClientChooserButton extends StatelessWidget {
), ),
PopupMenuButton<Object>( PopupMenuButton<Object>(
child: Material( child: Material(
borderRadius: BorderRadius.zero, color: Colors.transparent,
borderRadius: BorderRadius.circular(99),
child: Avatar( child: Avatar(
mxContent: snapshot.data?.avatarUrl, mxContent: snapshot.data?.avatarUrl,
name: snapshot.data?.displayName ?? name: snapshot.data?.displayName ??
@ -158,6 +169,8 @@ class ClientChooserButton extends StatelessWidget {
controller.setActiveClient(object); controller.setActiveClient(object);
} else if (object is String) { } else if (object is String) {
controller.setActiveBundle(object); controller.setActiveBundle(object);
} else if (object == AddAccountAction.addAccount) {
controller.addAccountAction();
} }
} }
@ -222,3 +235,5 @@ class ClientChooserButton extends StatelessWidget {
_handleKeyboardShortcut(matrix, lastIndex! - 1); _handleKeyboardShortcut(matrix, lastIndex! - 1);
} }
} }
enum AddAccountAction { addAccount }

View File

@ -1,164 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:salomon_bottom_bar/salomon_bottom_bar.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/spaces_drawer.dart';
import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
const kSpacesBottomBarHeight = 56.0;
final GlobalKey _globalKey = GlobalKey();
class SpacesBottomBar extends StatelessWidget {
final ChatListController controller;
const SpacesBottomBar(this.controller, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Material(
color: Theme.of(context).navigationBarTheme.backgroundColor,
elevation: 6,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(AppConfig.borderRadius)),
clipBehavior: Clip.hardEdge,
child: SafeArea(
child: StreamBuilder<Object>(
stream: Matrix.of(context).client.onSync.stream.where((sync) =>
(sync.rooms?.join?.values.any((r) =>
r.state?.any((s) => s.type.startsWith('m.space')) ??
false) ??
false) ||
(sync.rooms?.leave?.isNotEmpty ?? false)),
builder: (context, snapshot) {
return SingleChildScrollView(
controller: controller.snappingSheetScrollContentController,
child: AnimatedBuilder(
child: _SpacesBottomNavigation(
key: _globalKey, controller: controller),
builder: (context, child) {
if (controller.snappingSheetContainerSize == null) {
return child!;
}
final rawPosition =
controller.snappingSheetController.isAttached
? controller.snappingSheetController.currentPosition
: 0;
final position = rawPosition /
controller.snappingSheetContainerSize!.maxHeight;
if (rawPosition <= kSpacesBottomBarHeight) {
return child!;
} else if (position >= 0.5) {
return SpacesDrawer(controller: controller);
} else {
final normalized = (rawPosition - kSpacesBottomBarHeight) /
(controller.snappingSheetContainerSize!.maxHeight -
kSpacesBottomBarHeight) *
2;
var boxHeight = (1 - normalized) * kSpacesBottomBarHeight;
if (boxHeight < 0) boxHeight = 0;
return Column(
children: [
SizedBox(
height: boxHeight,
child: ClipRect(
clipBehavior: Clip.hardEdge,
child: Opacity(
opacity: 1 - normalized, child: child!)),
),
Opacity(
opacity: normalized,
child: SpacesDrawer(controller: controller),
),
],
);
}
},
animation: controller.snappingSheetController,
),
);
},
),
),
);
}
}
class _SpacesBottomNavigation extends StatelessWidget {
final ChatListController controller;
const _SpacesBottomNavigation({Key? key, required this.controller})
: super(key: key);
@override
Widget build(BuildContext context) {
final currentIndex = controller.spacesEntries.indexWhere((space) =>
controller.activeSpacesEntry.runtimeType == space.runtimeType &&
(controller.activeSpaceId == space.getSpace(context)?.id)) +
1;
return Container(
height: 56,
alignment: Alignment.center,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SalomonBottomBar(
itemPadding: const EdgeInsets.all(8),
currentIndex: currentIndex,
onTap: (i) => i == 0
? controller.expandSpaces()
: controller.setActiveSpacesEntry(
context,
controller.spacesEntries[i - 1],
),
selectedItemColor: Theme.of(context).colorScheme.primary,
items: [
SalomonBottomBarItem(
icon: const Icon(Icons.keyboard_arrow_up),
title: Text(L10n.of(context)!.showSpaces),
),
...controller.spacesEntries
.map((space) => _buildSpacesEntryUI(context, space))
.toList(),
],
),
),
);
}
SalomonBottomBarItem _buildSpacesEntryUI(
BuildContext context, SpacesEntry entry) {
final space = entry.getSpace(context);
if (space != null) {
return SalomonBottomBarItem(
icon: InkWell(
borderRadius: BorderRadius.circular(28),
onTap: () => controller.setActiveSpacesEntry(
context,
entry,
),
onLongPress: () => controller.editSpace(context, space.id),
child: Avatar(
mxContent: space.avatar,
name: space.displayname,
size: 24,
fontSize: 12,
),
),
title: Text(entry.getName(context)),
);
}
return SalomonBottomBarItem(
icon: entry.getIcon(false),
activeIcon: entry.getIcon(true),
title: Text(entry.getName(context)),
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat_list/spaces_entry.dart'; import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
@ -22,29 +23,66 @@ class SpacesDrawer extends StatelessWidget {
// TODO(TheOeWithTheBraid): wait for space hierarchy https://gitlab.com/famedly/company/frontend/libraries/matrix_api_lite/-/merge_requests/58 // TODO(TheOeWithTheBraid): wait for space hierarchy https://gitlab.com/famedly/company/frontend/libraries/matrix_api_lite/-/merge_requests/58
return WillPopScope( return ListView.builder(
onWillPop: () async { itemCount: spaceHierarchy.length + 2,
controller.snapBackSpacesSheet(); itemBuilder: (context, i) {
return false; if (i == spaceHierarchy.length) {
return ListTile(
leading: CircleAvatar(
radius: Avatar.defaultSize / 2,
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
foregroundColor:
Theme.of(context).colorScheme.onSecondaryContainer,
child: const Icon(
Icons.archive_outlined,
),
),
title: Text(L10n.of(context)!.archive),
onTap: () {
Scaffold.of(context).closeDrawer();
VRouter.of(context).to('/archive');
}, },
child: Column( );
children: List.generate(spaceHierarchy.length, (index) { }
final space = spaceHierarchy.keys.toList()[index]; if (i == spaceHierarchy.length + 1) {
return ListTile(
leading: CircleAvatar(
child: const Icon(Icons.group_work_outlined),
radius: Avatar.defaultSize / 2,
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
foregroundColor:
Theme.of(context).colorScheme.onSecondaryContainer,
),
title: Text(L10n.of(context)!.createNewSpace),
onTap: () {
Scaffold.of(context).closeDrawer();
VRouter.of(context).to('/newspace');
},
);
}
final space = spaceHierarchy.keys.toList()[i];
final room = space.getSpace(context); final room = space.getSpace(context);
final active = currentIndex == index; final active = currentIndex == i;
return ListTile( return ListTile(
selected: active, selected: active,
leading: index == 0 leading: room == null
? const Icon(Icons.keyboard_arrow_down) ? CircleAvatar(
: room == null child: space.getIcon(active),
? space.getIcon(active) radius: Avatar.defaultSize / 2,
backgroundColor:
Theme.of(context).colorScheme.secondaryContainer,
foregroundColor:
Theme.of(context).colorScheme.onSecondaryContainer,
)
: Avatar( : Avatar(
mxContent: room.avatar, mxContent: room.avatar,
name: space.getName(context), name: space.getName(context),
size: 24,
fontSize: 12,
), ),
title: Text(space.getName(context)), title: Text(
space.getName(context),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: room?.topic.isEmpty ?? true subtitle: room?.topic.isEmpty ?? true
? null ? null
: Tooltip( : Tooltip(
@ -60,15 +98,18 @@ class SpacesDrawer extends StatelessWidget {
space, space,
), ),
trailing: room != null trailing: room != null
? IconButton( ? SizedBox(
icon: const Icon(Icons.edit), width: 32,
child: IconButton(
splashRadius: 24,
icon: const Icon(Icons.edit_outlined),
tooltip: L10n.of(context)!.edit, tooltip: L10n.of(context)!.edit,
onPressed: () => controller.editSpace(context, room.id), onPressed: () => controller.editSpace(context, room.id),
),
) )
: null, : null,
); );
}), },
),
); );
} }
} }

View File

@ -18,7 +18,8 @@ enum ContextualRoomAction {
} }
class StoriesHeader extends StatelessWidget { class StoriesHeader extends StatelessWidget {
const StoriesHeader({Key? key}) : super(key: key); final String filter;
const StoriesHeader({required this.filter, Key? key}) : super(key: key);
void _addToStoryAction(BuildContext context) => void _addToStoryAction(BuildContext context) =>
VRouter.of(context).to('/stories/create'); VRouter.of(context).to('/stories/create');
@ -105,7 +106,10 @@ class StoriesHeader extends StatelessWidget {
onTap: () => _addToStoryAction(context), onTap: () => _addToStoryAction(context),
); );
} }
if (client.storiesRooms.isEmpty) { if (client.storiesRooms.isEmpty ||
!client.storiesRooms.any((room) => room.displayname
.toLowerCase()
.contains(filter.toLowerCase()))) {
return Container(); return Container();
} }
final ownStoryRoom = client.storiesRooms final ownStoryRoom = client.storiesRooms
@ -130,6 +134,11 @@ class StoriesHeader extends StatelessWidget {
userId?.localpart ?? userId?.localpart ??
'Unknown'; 'Unknown';
final avatarUrl = snapshot.data?.avatarUrl; final avatarUrl = snapshot.data?.avatarUrl;
if (!displayname
.toLowerCase()
.contains(filter.toLowerCase())) {
return Container();
}
return _StoryButton( return _StoryButton(
profile: Profile( profile: Profile(
displayName: displayname, displayName: displayname,
@ -139,7 +148,7 @@ class StoriesHeader extends StatelessWidget {
hasPosts: room.hasPosts || room == ownStoryRoom, hasPosts: room.hasPosts || room == ownStoryRoom,
showEditFab: userId == client.userID, showEditFab: userId == client.userID,
unread: room.membership == Membership.invite || unread: room.membership == Membership.invite ||
room.hasNewMessages, (room.hasNewMessages && room.hasPosts),
onPressed: () => _goToStoryAction(context, room.id), onPressed: () => _goToStoryAction(context, room.id),
onLongPressed: () => onLongPressed: () =>
_contextualActions(context, room), _contextualActions(context, room),

View File

@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/connect/connect_page.dart'; import 'package:fluffychat/pages/connect/connect_page.dart';
import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
@ -90,14 +89,14 @@ class ConnectPageView extends StatelessWidget {
child: TextField( child: TextField(
controller: controller.usernameController, controller: controller.usernameController,
onSubmitted: (_) => controller.signUp(), onSubmitted: (_) => controller.signUp(),
style: FluffyThemes.loginTextFieldStyle, decoration: InputDecoration(
decoration: FluffyThemes.loginTextFieldDecoration( prefixIcon: const Icon(Icons.account_box_outlined),
prefixIcon: const Icon(
Icons.account_box_outlined,
color: Colors.black,
),
hintText: L10n.of(context)!.chooseAUsername, hintText: L10n.of(context)!.chooseAUsername,
errorText: controller.signupError, errorText: controller.signupError,
fillColor: Theme.of(context)
.colorScheme
.background
.withOpacity(0.75),
), ),
), ),
), ),
@ -106,12 +105,7 @@ class ConnectPageView extends StatelessWidget {
child: Hero( child: Hero(
tag: 'loginButton', tag: 'loginButton',
child: ElevatedButton( child: ElevatedButton(
onPressed: controller.loading ? null : controller.signUp, onPressed: controller.loading ? () {} : controller.signUp,
style: ElevatedButton.styleFrom(
primary: Colors.white.withAlpha(200),
onPrimary: Colors.black,
shadowColor: Colors.white,
),
child: controller.loading child: controller.loading
? const LinearProgressIndicator() ? const LinearProgressIndicator()
: Text(L10n.of(context)!.signUp), : Text(L10n.of(context)!.signUp),
@ -148,11 +142,6 @@ class ConnectPageView extends StatelessWidget {
child: ElevatedButton( child: ElevatedButton(
onPressed: () => controller onPressed: () => controller
.ssoLoginAction(identityProviders.single.id!), .ssoLoginAction(identityProviders.single.id!),
style: ElevatedButton.styleFrom(
primary: Colors.white.withAlpha(200),
onPrimary: Colors.black,
shadowColor: Colors.white,
),
child: Text(identityProviders.single.name ?? child: Text(identityProviders.single.name ??
identityProviders.single.brand ?? identityProviders.single.brand ??
L10n.of(context)!.loginWithOneClick), L10n.of(context)!.loginWithOneClick),
@ -176,11 +165,6 @@ class ConnectPageView extends StatelessWidget {
tag: 'signinButton', tag: 'signinButton',
child: ElevatedButton( child: ElevatedButton(
onPressed: controller.loading ? () {} : controller.login, onPressed: controller.loading ? () {} : controller.login,
style: ElevatedButton.styleFrom(
primary: Colors.white.withAlpha(200),
onPrimary: Colors.black,
shadowColor: Colors.white,
),
child: Text(L10n.of(context)!.login), child: Text(L10n.of(context)!.login),
), ),
), ),

View File

@ -22,17 +22,17 @@ class SsoButton extends StatelessWidget {
onTap: onPressed, onTap: onPressed,
borderRadius: BorderRadius.circular(7), borderRadius: BorderRadius.circular(7),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Material( Material(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(7), borderRadius: BorderRadius.circular(8),
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
child: Padding( child: Padding(
padding: const EdgeInsets.all(2.0), padding: const EdgeInsets.all(4.0),
child: identityProvider.icon == null child: identityProvider.icon == null
? const Icon(Icons.web_outlined) ? const Icon(Icons.web_outlined)
: CachedNetworkImage( : CachedNetworkImage(

View File

@ -37,8 +37,13 @@ class DevicesSettingsView extends StatelessWidget {
return const Center( return const Center(
child: CircularProgressIndicator.adaptive(strokeWidth: 2)); child: CircularProgressIndicator.adaptive(strokeWidth: 2));
} }
return ListView.builder(
itemCount: controller.notThisDevice.length + 1,
itemBuilder: (BuildContext context, int i) {
if (i == 0) {
return Column( return Column(
children: <Widget>[ mainAxisSize: MainAxisSize.min,
children: [
if (controller.thisDevice != null) if (controller.thisDevice != null)
UserDeviceListItem( UserDeviceListItem(
controller.thisDevice!, controller.thisDevice!,
@ -62,35 +67,23 @@ class DevicesSettingsView extends StatelessWidget {
: const Icon(Icons.delete_outline), : const Icon(Icons.delete_outline),
onTap: controller.loadingDeletingDevices onTap: controller.loadingDeletingDevices
? null ? null
: () => controller : () => controller.removeDevicesAction(
.removeDevicesAction(controller.notThisDevice), controller.notThisDevice),
), ),
const Divider(height: 1), const Divider(height: 1),
Expanded( ],
child: controller.notThisDevice.isEmpty );
? Center( }
child: Icon( i--;
Icons.devices_other, return UserDeviceListItem(
size: 60,
color: Theme.of(context).secondaryHeaderColor,
),
)
: ListView.separated(
separatorBuilder: (BuildContext context, int i) =>
const Divider(height: 1),
itemCount: controller.notThisDevice.length,
itemBuilder: (BuildContext context, int i) =>
UserDeviceListItem(
controller.notThisDevice[i], controller.notThisDevice[i],
rename: controller.renameDeviceAction, rename: controller.renameDeviceAction,
remove: (d) => controller.removeDevicesAction([d]), remove: (d) => controller.removeDevicesAction([d]),
verify: controller.verifyDeviceAction, verify: controller.verifyDeviceAction,
block: controller.blockDeviceAction, block: controller.blockDeviceAction,
unblock: controller.unblockDeviceAction, unblock: controller.unblockDeviceAction,
), );
), },
),
],
); );
}, },
), ),

View File

@ -108,12 +108,13 @@ class UserDeviceListItem extends StatelessWidget {
), ),
title: Row( title: Row(
children: <Widget>[ children: <Widget>[
Text( Expanded(
child: Text(
userDevice.displayname, userDevice.displayname,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
const Spacer(), ),
if (keys != null) if (keys != null)
Text( Text(
keys.blocked keys.blocked

View File

@ -5,7 +5,6 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
import 'homeserver_picker.dart'; import 'homeserver_picker.dart';
@ -22,16 +21,15 @@ class HomeserverPickerView extends StatelessWidget {
appBar: VRouter.of(context).path == '/home' appBar: VRouter.of(context).path == '/home'
? null ? null
: AppBar(title: Text(L10n.of(context)!.addAccount)), : AppBar(title: Text(L10n.of(context)!.addAccount)),
body: Column( body: SafeArea(
child: Column(
children: [ children: [
Expanded( Expanded(
child: ListView( child: ListView(
children: [ children: [
AnimatedContainer( Container(
duration: const Duration(milliseconds: 300),
constraints: BoxConstraints(
maxHeight: controller.displayServerList ? 0 : 256),
alignment: Alignment.center, alignment: Alignment.center,
height: 256,
child: Image.asset('assets/info-logo.png'), child: Image.asset('assets/info-logo.png'),
), ),
Padding( Padding(
@ -40,15 +38,15 @@ class HomeserverPickerView extends StatelessWidget {
focusNode: controller.homeserverFocusNode, focusNode: controller.homeserverFocusNode,
controller: controller.homeserverController, controller: controller.homeserverController,
onChanged: controller.onChanged, onChanged: controller.onChanged,
style: FluffyThemes.loginTextFieldStyle, decoration: InputDecoration(
decoration: FluffyThemes.loginTextFieldDecoration( prefixText: '${L10n.of(context)!.homeserver}: ',
labelText: L10n.of(context)!.homeserver,
hintText: L10n.of(context)!.enterYourHomeserver, hintText: L10n.of(context)!.enterYourHomeserver,
suffixIcon: const Icon( suffixIcon: const Icon(Icons.search),
Icons.search,
color: Colors.black,
),
errorText: controller.error, errorText: controller.error,
fillColor: Theme.of(context)
.colorScheme
.background
.withOpacity(0.75),
), ),
readOnly: !AppConfig.allowOtherHomeservers, readOnly: !AppConfig.allowOtherHomeservers,
onSubmitted: (_) => controller.checkHomeserverAction(), onSubmitted: (_) => controller.checkHomeserverAction(),
@ -127,19 +125,15 @@ class HomeserverPickerView extends StatelessWidget {
], ],
), ),
), ),
Padding( Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
width: double.infinity,
child: Hero( child: Hero(
tag: 'loginButton', tag: 'loginButton',
child: ElevatedButton( child: ElevatedButton(
onPressed: controller.isLoading onPressed: controller.isLoading
? () {} ? null
: controller.checkHomeserverAction, : controller.checkHomeserverAction,
style: ElevatedButton.styleFrom(
primary: Colors.white.withAlpha(200),
onPrimary: Colors.black,
shadowColor: Colors.white,
),
child: controller.isLoading child: controller.isLoading
? const LinearProgressIndicator() ? const LinearProgressIndicator()
: Text(L10n.of(context)!.connect), : Text(L10n.of(context)!.connect),
@ -148,6 +142,7 @@ class HomeserverPickerView extends StatelessWidget {
), ),
], ],
), ),
),
); );
} }
} }

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'login.dart'; import 'login.dart';
@ -44,16 +43,16 @@ class LoginView extends StatelessWidget {
controller: controller.usernameController, controller: controller.usernameController,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
style: FluffyThemes.loginTextFieldStyle,
autofillHints: autofillHints:
controller.loading ? null : [AutofillHints.username], controller.loading ? null : [AutofillHints.username],
decoration: FluffyThemes.loginTextFieldDecoration( decoration: InputDecoration(
prefixIcon: const Icon( prefixIcon: const Icon(Icons.account_box_outlined),
Icons.account_box_outlined,
color: Colors.black,
),
errorText: controller.usernameError, errorText: controller.usernameError,
hintText: L10n.of(context)!.emailOrUsername, hintText: L10n.of(context)!.emailOrUsername,
fillColor: Theme.of(context)
.colorScheme
.background
.withOpacity(0.75),
), ),
), ),
), ),
@ -68,12 +67,8 @@ class LoginView extends StatelessWidget {
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
obscureText: !controller.showPassword, obscureText: !controller.showPassword,
onSubmitted: controller.login, onSubmitted: controller.login,
style: FluffyThemes.loginTextFieldStyle, decoration: InputDecoration(
decoration: FluffyThemes.loginTextFieldDecoration( prefixIcon: const Icon(Icons.lock_outlined),
prefixIcon: const Icon(
Icons.lock_outlined,
color: Colors.black,
),
errorText: controller.passwordError, errorText: controller.passwordError,
suffixIcon: IconButton( suffixIcon: IconButton(
tooltip: L10n.of(context)!.showPassword, tooltip: L10n.of(context)!.showPassword,
@ -86,6 +81,10 @@ class LoginView extends StatelessWidget {
onPressed: controller.toggleShowPassword, onPressed: controller.toggleShowPassword,
), ),
hintText: L10n.of(context)!.password, hintText: L10n.of(context)!.password,
fillColor: Theme.of(context)
.colorScheme
.background
.withOpacity(0.75),
), ),
), ),
), ),
@ -97,11 +96,6 @@ class LoginView extends StatelessWidget {
onPressed: controller.loading onPressed: controller.loading
? null ? null
: () => controller.login(context), : () => controller.login(context),
style: ElevatedButton.styleFrom(
primary: Colors.white.withAlpha(200),
onPrimary: Colors.black,
shadowColor: Colors.white,
),
child: controller.loading child: controller.loading
? const LinearProgressIndicator() ? const LinearProgressIndicator()
: Text(L10n.of(context)!.login), : Text(L10n.of(context)!.login),
@ -126,11 +120,7 @@ class LoginView extends StatelessWidget {
child: ElevatedButton( child: ElevatedButton(
onPressed: onPressed:
controller.loading ? () {} : controller.passwordForgotten, controller.loading ? () {} : controller.passwordForgotten,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(onPrimary: Colors.red),
primary: Colors.white.withAlpha(156),
onPrimary: Colors.red,
shadowColor: Colors.white,
),
child: Text(L10n.of(context)!.passwordForgotten), child: Text(L10n.of(context)!.passwordForgotten),
), ),
), ),

View File

@ -1,125 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/utils/famedlysdk_store.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/public_room_bottom_sheet.dart';
import 'search_view.dart';
class Search extends StatefulWidget {
const Search({Key? key}) : super(key: key);
@override
SearchController createState() => SearchController();
}
class SearchController extends State<Search> {
final TextEditingController controller = TextEditingController();
Future<QueryPublicRoomsResponse>? publicRoomsResponse;
String? lastServer;
Timer? _coolDown;
String? genericSearchTerm;
void search(String query) async {
setState(() {});
_coolDown?.cancel();
_coolDown = Timer(
const Duration(milliseconds: 500),
() => setState(() {
genericSearchTerm = query;
publicRoomsResponse = null;
searchUser(context, controller.text);
}),
);
}
void joinGroupAction(PublicRoomsChunk room) {
showModalBottomSheet(
context: context,
builder: (c) => PublicRoomBottomSheet(
roomAlias: room.canonicalAlias ?? room.roomId,
outerContext: context,
chunk: room,
),
);
}
String? server;
static const String _serverStoreNamespace = 'im.fluffychat.search.server';
void setServer() async {
final newServer = await showTextInputDialog(
useRootNavigator: false,
title: L10n.of(context)!.changeTheHomeserver,
context: context,
okLabel: L10n.of(context)!.ok,
cancelLabel: L10n.of(context)!.cancel,
textFields: [
DialogTextField(
prefixText: 'https://',
hintText: Matrix.of(context).client.homeserver?.host,
initialText: server,
keyboardType: TextInputType.url,
autocorrect: false)
]);
if (newServer == null) return;
Store().setItem(_serverStoreNamespace, newServer.single);
setState(() {
server = newServer.single;
});
}
String? currentSearchTerm;
List<Profile> foundProfiles = [];
static const searchUserDirectoryLimit = 10;
void searchUser(BuildContext context, String text) async {
if (text.isEmpty) {
setState(() {
foundProfiles = [];
});
}
currentSearchTerm = text;
if (currentSearchTerm?.isEmpty ?? true) return;
final matrix = Matrix.of(context);
SearchUserDirectoryResponse? response;
try {
response = await matrix.client.searchUserDirectory(
text,
limit: searchUserDirectoryLimit,
);
} catch (_) {}
foundProfiles = List<Profile>.from(response?.results ?? []);
if (foundProfiles.isEmpty && text.isValidMatrixId && text.sigil == '@') {
foundProfiles.add(Profile.fromJson({
'displayname': text.localpart,
'user_id': text,
}));
}
setState(() {});
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
controller.text = VRouter.of(context).queryParameters['query'] ?? '';
final server = await Store().getItem(_serverStoreNamespace);
if (server?.isNotEmpty ?? false) {
this.server = server;
}
search(controller.text);
});
}
@override
Widget build(BuildContext context) => SearchView(this);
}

View File

@ -1,299 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
import 'package:fluffychat/utils/string_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/contacts_list.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../utils/localized_exception_extension.dart';
import '../../utils/platform_infos.dart';
import 'search.dart';
class SearchView extends StatelessWidget {
final SearchController controller;
const SearchView(this.controller, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final server = controller.genericSearchTerm?.isValidMatrixId ?? false
? controller.genericSearchTerm!.domain
: controller.server;
if (controller.lastServer != server) {
controller.lastServer = server;
controller.publicRoomsResponse = null;
}
controller.publicRoomsResponse ??= Matrix.of(context)
.client
.queryPublicRooms(
server: server,
filter: PublicRoomQueryFilter(
genericSearchTerm: controller.genericSearchTerm,
),
)
.catchError((error) {
if (!(controller.genericSearchTerm?.isValidMatrixId ?? false)) {
throw error;
}
return QueryPublicRoomsResponse.fromJson({
'chunk': [],
});
}).then((QueryPublicRoomsResponse res) {
final genericSearchTerm = controller.genericSearchTerm;
if (genericSearchTerm != null &&
!res.chunk.any(
(room) => room.canonicalAlias == controller.genericSearchTerm)) {
// we have to tack on the original alias
res.chunk.add(
PublicRoomsChunk(
name: genericSearchTerm,
numJoinedMembers: 0,
roomId: '!unknown',
worldReadable: true,
guestCanJoin: true,
),
);
}
return res;
});
final rooms = List<Room>.from(Matrix.of(context).client.rooms);
rooms.removeWhere(
(room) =>
room.lastEvent == null ||
!room.displayname.toLowerCase().removeDiacritics().contains(
controller.controller.text.toLowerCase().removeDiacritics()),
);
const tabCount = 3;
return DefaultTabController(
length: tabCount,
initialIndex: controller.controller.text.startsWith('#') ? 0 : 1,
child: Scaffold(
appBar: AppBar(
leading: const BackButton(),
titleSpacing: 0,
title: TextField(
autofocus: true,
controller: controller.controller,
decoration: InputDecoration(
suffix: const Icon(Icons.search_outlined),
hintText: L10n.of(context)!.search,
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
),
onChanged: controller.search,
),
bottom: TabBar(
indicatorColor: Theme.of(context).colorScheme.secondary,
labelColor: Theme.of(context).colorScheme.secondary,
unselectedLabelColor: Theme.of(context).textTheme.bodyText1!.color,
labelStyle: const TextStyle(fontSize: 16),
labelPadding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 0,
),
tabs: [
Tab(child: Text(L10n.of(context)!.discover, maxLines: 1)),
Tab(child: Text(L10n.of(context)!.chats, maxLines: 1)),
Tab(child: Text(L10n.of(context)!.people, maxLines: 1)),
],
),
),
body: TabBarView(
children: [
ListView(
keyboardDismissBehavior: PlatformInfos.isIOS
? ScrollViewKeyboardDismissBehavior.onDrag
: ScrollViewKeyboardDismissBehavior.manual,
children: [
const SizedBox(height: 12),
ListTile(
leading: CircleAvatar(
foregroundColor: Theme.of(context).colorScheme.secondary,
backgroundColor: Theme.of(context).secondaryHeaderColor,
child: const Icon(Icons.edit_outlined),
),
title: Text(L10n.of(context)!.changeTheServer),
onTap: controller.setServer,
),
FutureBuilder<QueryPublicRoomsResponse>(
future: controller.publicRoomsResponse,
builder: (BuildContext context,
AsyncSnapshot<QueryPublicRoomsResponse> snapshot) {
if (snapshot.hasError) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 32),
const Icon(
Icons.error_outlined,
size: 80,
color: Colors.grey,
),
Center(
child: Text(
snapshot.error!.toLocalizedString(context),
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.grey,
fontSize: 16,
),
),
),
],
);
}
if (snapshot.connectionState != ConnectionState.done) {
return const Center(
child: CircularProgressIndicator.adaptive(
strokeWidth: 2));
}
final publicRoomsResponse = snapshot.data!;
if (publicRoomsResponse.chunk.isEmpty) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 32),
const Icon(
Icons.search_outlined,
size: 80,
color: Colors.grey,
),
Center(
child: Text(
L10n.of(context)!.noPublicRoomsFound,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.grey,
fontSize: 16,
),
),
),
],
);
}
return GridView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(12),
physics: const NeverScrollableScrollPhysics(),
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: publicRoomsResponse.chunk.length,
itemBuilder: (BuildContext context, int i) => Material(
elevation: 2,
borderRadius: BorderRadius.circular(16),
child: InkWell(
onTap: () => controller.joinGroupAction(
publicRoomsResponse.chunk[i],
),
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Avatar(
mxContent:
publicRoomsResponse.chunk[i].avatarUrl,
name: publicRoomsResponse.chunk[i].name,
),
Text(
publicRoomsResponse.chunk[i].name!,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
maxLines: 1,
textAlign: TextAlign.center,
),
Text(
L10n.of(context)!.countParticipants(
publicRoomsResponse
.chunk[i].numJoinedMembers),
style: const TextStyle(fontSize: 10.5),
maxLines: 1,
textAlign: TextAlign.center,
),
Text(
publicRoomsResponse.chunk[i].topic ??
L10n.of(context)!.noDescription,
maxLines: 4,
textAlign: TextAlign.center,
),
],
),
),
),
),
);
}),
],
),
ListView.builder(
keyboardDismissBehavior: PlatformInfos.isIOS
? ScrollViewKeyboardDismissBehavior.onDrag
: ScrollViewKeyboardDismissBehavior.manual,
itemCount: rooms.length,
itemBuilder: (_, i) => ChatListItem(rooms[i]),
),
controller.foundProfiles.isNotEmpty
? ListView.builder(
keyboardDismissBehavior: PlatformInfos.isIOS
? ScrollViewKeyboardDismissBehavior.onDrag
: ScrollViewKeyboardDismissBehavior.manual,
itemCount: controller.foundProfiles.length,
itemBuilder: (BuildContext context, int i) {
final foundProfile = controller.foundProfiles[i];
return ListTile(
onTap: () async {
final roomID = await showFutureLoadingDialog(
context: context,
future: () async {
final client = Matrix.of(context).client;
final roomId = await client
.startDirectChat(foundProfile.userId);
return roomId;
},
);
if (roomID.error == null) {
VRouter.of(context)
.toSegments(['rooms', roomID.result!]);
}
},
leading: Avatar(
mxContent: foundProfile.avatarUrl,
name: foundProfile.displayName ?? foundProfile.userId,
//size: 24,
),
title: Text(
foundProfile.displayName ??
foundProfile.userId.localpart!,
style: const TextStyle(),
maxLines: 1,
),
subtitle: Text(
foundProfile.userId,
maxLines: 1,
style: const TextStyle(
fontSize: 12,
),
),
);
},
)
: ContactsList(searchController: controller.controller),
],
),
),
);
}
}

View File

@ -37,7 +37,7 @@ class SettingsView extends StatelessWidget {
), ),
], ],
body: ListTileTheme( body: ListTileTheme(
iconColor: Theme.of(context).textTheme.bodyText1!.color, iconColor: Theme.of(context).colorScheme.onBackground,
child: ListView( child: ListView(
children: <Widget>[ children: <Widget>[
ListTile( ListTile(

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
import 'signup.dart'; import 'signup.dart';
@ -38,11 +37,8 @@ class SignupPageView extends StatelessWidget {
controller: controller.passwordController, controller: controller.passwordController,
obscureText: !controller.showPassword, obscureText: !controller.showPassword,
validator: controller.password1TextFieldValidator, validator: controller.password1TextFieldValidator,
decoration: FluffyThemes.loginTextFieldDecoration( decoration: InputDecoration(
prefixIcon: const Icon( prefixIcon: const Icon(Icons.vpn_key_outlined),
Icons.vpn_key_outlined,
color: Colors.black,
),
suffixIcon: IconButton( suffixIcon: IconButton(
tooltip: L10n.of(context)!.showPassword, tooltip: L10n.of(context)!.showPassword,
icon: Icon( icon: Icon(
@ -53,7 +49,12 @@ class SignupPageView extends StatelessWidget {
), ),
onPressed: controller.toggleShowPassword, onPressed: controller.toggleShowPassword,
), ),
errorStyle: const TextStyle(color: Colors.orange),
hintText: L10n.of(context)!.chooseAStrongPassword, hintText: L10n.of(context)!.chooseAStrongPassword,
fillColor: Theme.of(context)
.colorScheme
.background
.withOpacity(0.75),
), ),
), ),
), ),
@ -68,12 +69,14 @@ class SignupPageView extends StatelessWidget {
controller: controller.password2Controller, controller: controller.password2Controller,
obscureText: !controller.showPassword, obscureText: !controller.showPassword,
validator: controller.password2TextFieldValidator, validator: controller.password2TextFieldValidator,
decoration: FluffyThemes.loginTextFieldDecoration( decoration: InputDecoration(
prefixIcon: const Icon( prefixIcon: const Icon(Icons.repeat_outlined),
Icons.repeat_outlined,
color: Colors.black,
),
hintText: L10n.of(context)!.repeatPassword, hintText: L10n.of(context)!.repeatPassword,
errorStyle: const TextStyle(color: Colors.orange),
fillColor: Theme.of(context)
.colorScheme
.background
.withOpacity(0.75),
), ),
), ),
), ),
@ -87,16 +90,19 @@ class SignupPageView extends StatelessWidget {
autofillHints: autofillHints:
controller.loading ? null : [AutofillHints.username], controller.loading ? null : [AutofillHints.username],
validator: controller.emailTextFieldValidator, validator: controller.emailTextFieldValidator,
decoration: FluffyThemes.loginTextFieldDecoration( decoration: InputDecoration(
prefixIcon: const Icon( prefixIcon: const Icon(Icons.mail_outlined),
Icons.mail_outlined,
color: Colors.black,
),
hintText: L10n.of(context)!.enterAnEmailAddress, hintText: L10n.of(context)!.enterAnEmailAddress,
errorText: controller.error, errorText: controller.error,
errorColor: controller.emailController.text.isEmpty fillColor: Theme.of(context)
.colorScheme
.background
.withOpacity(0.75),
errorStyle: TextStyle(
color: controller.emailController.text.isEmpty
? Colors.orangeAccent ? Colors.orangeAccent
: null, : Colors.orange,
),
), ),
), ),
), ),
@ -106,11 +112,6 @@ class SignupPageView extends StatelessWidget {
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: ElevatedButton( child: ElevatedButton(
onPressed: controller.loading ? () {} : controller.signup, onPressed: controller.loading ? () {} : controller.signup,
style: ElevatedButton.styleFrom(
primary: Colors.white.withAlpha(200),
onPrimary: Colors.black,
shadowColor: Colors.white,
),
child: controller.loading child: controller.loading
? const LinearProgressIndicator() ? const LinearProgressIndicator()
: Text(L10n.of(context)!.signUp), : Text(L10n.of(context)!.signUp),

View File

@ -12,10 +12,6 @@ PODS:
- FlutterMacOS - FlutterMacOS
- emoji_picker_flutter (0.0.1): - emoji_picker_flutter (0.0.1):
- FlutterMacOS - FlutterMacOS
- file_selector_macos (0.0.1):
- FlutterMacOS
- flutter_app_badger (1.3.0):
- FlutterMacOS
- flutter_local_notifications (0.0.1): - flutter_local_notifications (0.0.1):
- FlutterMacOS - FlutterMacOS
- flutter_secure_storage_macos (3.3.1): - flutter_secure_storage_macos (3.3.1):
@ -24,7 +20,7 @@ PODS:
- FlutterMacOS - FlutterMacOS
- flutter_webrtc (0.7.1): - flutter_webrtc (0.7.1):
- FlutterMacOS - FlutterMacOS
- WebRTC-SDK (= 97.4692.02) - WebRTC-SDK (= 97.4692.05)
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- FMDB (2.7.5): - FMDB (2.7.5):
- FMDB/standard (= 2.7.5) - FMDB/standard (= 2.7.5)
@ -33,13 +29,15 @@ PODS:
- FlutterMacOS - FlutterMacOS
- just_audio (0.0.1): - just_audio (0.0.1):
- FlutterMacOS - FlutterMacOS
- package_info (0.0.1):
- FlutterMacOS
- package_info_plus_macos (0.0.1): - package_info_plus_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- path_provider_macos (0.0.1): - path_provider_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- ReachabilitySwift (5.0.0) - ReachabilitySwift (5.0.0)
- record_macos (1.0.0):
- FlutterMacOS
- share_plus_macos (0.0.1):
- FlutterMacOS
- shared_preferences_macos (0.0.1): - shared_preferences_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- sqflite (0.0.2): - sqflite (0.0.2):
@ -51,7 +49,7 @@ PODS:
- FlutterMacOS - FlutterMacOS
- wakelock_macos (0.0.1): - wakelock_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- WebRTC-SDK (97.4692.02) - WebRTC-SDK (97.4692.05)
DEPENDENCIES: DEPENDENCIES:
- audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`)
@ -60,8 +58,6 @@ DEPENDENCIES:
- desktop_lifecycle (from `Flutter/ephemeral/.symlinks/plugins/desktop_lifecycle/macos`) - desktop_lifecycle (from `Flutter/ephemeral/.symlinks/plugins/desktop_lifecycle/macos`)
- device_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos`) - device_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos`)
- emoji_picker_flutter (from `Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos`) - emoji_picker_flutter (from `Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos`)
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
- flutter_app_badger (from `Flutter/ephemeral/.symlinks/plugins/flutter_app_badger/macos`)
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
- flutter_web_auth (from `Flutter/ephemeral/.symlinks/plugins/flutter_web_auth/macos`) - flutter_web_auth (from `Flutter/ephemeral/.symlinks/plugins/flutter_web_auth/macos`)
@ -69,9 +65,10 @@ DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`) - FlutterMacOS (from `Flutter/ephemeral`)
- geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos`) - geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos`)
- just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/macos`) - just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/macos`)
- package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`)
- package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`) - package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`)
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
@ -97,10 +94,6 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos
emoji_picker_flutter: emoji_picker_flutter:
:path: Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos :path: Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos
file_selector_macos:
:path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
flutter_app_badger:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_app_badger/macos
flutter_local_notifications: flutter_local_notifications:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos :path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos
flutter_secure_storage_macos: flutter_secure_storage_macos:
@ -115,12 +108,14 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos :path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos
just_audio: just_audio:
:path: Flutter/ephemeral/.symlinks/plugins/just_audio/macos :path: Flutter/ephemeral/.symlinks/plugins/just_audio/macos
package_info:
:path: Flutter/ephemeral/.symlinks/plugins/package_info/macos
package_info_plus_macos: package_info_plus_macos:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos
path_provider_macos: path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
record_macos:
:path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos
share_plus_macos:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
shared_preferences_macos: shared_preferences_macos:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
sqflite: sqflite:
@ -139,26 +134,25 @@ SPEC CHECKSUMS:
desktop_lifecycle: a600c10e12fe033c7be9078f2e929b8241f2c1e3 desktop_lifecycle: a600c10e12fe033c7be9078f2e929b8241f2c1e3
device_info_plus_macos: 1ad388a1ef433505c4038e7dd9605aadd1e2e9c7 device_info_plus_macos: 1ad388a1ef433505c4038e7dd9605aadd1e2e9c7
emoji_picker_flutter: 533634326b1c5de9a181ba14b9758e6dfe967a20 emoji_picker_flutter: 533634326b1c5de9a181ba14b9758e6dfe967a20
file_selector_macos: ff6dc948d4ddd34e8602a1f60b7d0b4cc6051a47
flutter_app_badger: 55a64b179f8438e89d574320c77b306e327a1730
flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4 flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa
flutter_web_auth: ae2c29ca9b98c00b4e0e8c0919bb4a05d44b76df flutter_web_auth: ae2c29ca9b98c00b4e0e8c0919bb4a05d44b76df
flutter_webrtc: 238124d0a7ba1c43543791f31a92a672370497c2 flutter_webrtc: 37c4efd66d9d306878c1323d5ac5a1d10c748b3a
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
geolocator_apple: 821be05bbdb1b49500e029ebcbf2d6acf2dfb966 geolocator_apple: 821be05bbdb1b49500e029ebcbf2d6acf2dfb966
just_audio: 9b67ca7b97c61cfc9784ea23cd8cc55eb226d489 just_audio: 9b67ca7b97c61cfc9784ea23cd8cc55eb226d489
package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2
package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c
path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
record_macos: dcf4f2bb654970437e012521cb4ea1fca4f78bb9
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727 shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3 url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
video_compress: c896234f100791b5fef7f049afa38f6d2ef7b42f video_compress: c896234f100791b5fef7f049afa38f6d2ef7b42f
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
WebRTC-SDK: dda4e50186f9eed672dc6bcf4faafb30c6ce48e3 WebRTC-SDK: a6ee40bda0e3f7dba057907c3897374005c5715b
PODFILE CHECKSUM: 9b8d08a513b178c33212d1b54cc9e3cba756d95b PODFILE CHECKSUM: 9b8d08a513b178c33212d1b54cc9e3cba756d95b

View File

@ -268,17 +268,16 @@
"${BUILT_PRODUCTS_DIR}/desktop_lifecycle/desktop_lifecycle.framework", "${BUILT_PRODUCTS_DIR}/desktop_lifecycle/desktop_lifecycle.framework",
"${BUILT_PRODUCTS_DIR}/device_info_plus_macos/device_info_plus_macos.framework", "${BUILT_PRODUCTS_DIR}/device_info_plus_macos/device_info_plus_macos.framework",
"${BUILT_PRODUCTS_DIR}/emoji_picker_flutter/emoji_picker_flutter.framework", "${BUILT_PRODUCTS_DIR}/emoji_picker_flutter/emoji_picker_flutter.framework",
"${BUILT_PRODUCTS_DIR}/file_selector_macos/file_selector_macos.framework",
"${BUILT_PRODUCTS_DIR}/flutter_app_badger/flutter_app_badger.framework",
"${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework", "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework",
"${BUILT_PRODUCTS_DIR}/flutter_secure_storage_macos/flutter_secure_storage_macos.framework", "${BUILT_PRODUCTS_DIR}/flutter_secure_storage_macos/flutter_secure_storage_macos.framework",
"${BUILT_PRODUCTS_DIR}/flutter_web_auth/flutter_web_auth.framework", "${BUILT_PRODUCTS_DIR}/flutter_web_auth/flutter_web_auth.framework",
"${BUILT_PRODUCTS_DIR}/flutter_webrtc/flutter_webrtc.framework", "${BUILT_PRODUCTS_DIR}/flutter_webrtc/flutter_webrtc.framework",
"${BUILT_PRODUCTS_DIR}/geolocator_apple/geolocator_apple.framework", "${BUILT_PRODUCTS_DIR}/geolocator_apple/geolocator_apple.framework",
"${BUILT_PRODUCTS_DIR}/just_audio/just_audio.framework", "${BUILT_PRODUCTS_DIR}/just_audio/just_audio.framework",
"${BUILT_PRODUCTS_DIR}/package_info/package_info.framework",
"${BUILT_PRODUCTS_DIR}/package_info_plus_macos/package_info_plus_macos.framework", "${BUILT_PRODUCTS_DIR}/package_info_plus_macos/package_info_plus_macos.framework",
"${BUILT_PRODUCTS_DIR}/path_provider_macos/path_provider_macos.framework", "${BUILT_PRODUCTS_DIR}/path_provider_macos/path_provider_macos.framework",
"${BUILT_PRODUCTS_DIR}/record_macos/record_macos.framework",
"${BUILT_PRODUCTS_DIR}/share_plus_macos/share_plus_macos.framework",
"${BUILT_PRODUCTS_DIR}/shared_preferences_macos/shared_preferences_macos.framework", "${BUILT_PRODUCTS_DIR}/shared_preferences_macos/shared_preferences_macos.framework",
"${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework",
"${BUILT_PRODUCTS_DIR}/url_launcher_macos/url_launcher_macos.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_macos/url_launcher_macos.framework",
@ -296,17 +295,16 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/desktop_lifecycle.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/desktop_lifecycle.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus_macos.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus_macos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/emoji_picker_flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/emoji_picker_flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_selector_macos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_app_badger.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage_macos.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage_macos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_web_auth.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_web_auth.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_webrtc.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_webrtc.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/geolocator_apple.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/geolocator_apple.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/just_audio.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/just_audio.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus_macos.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus_macos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_macos.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_macos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/record_macos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus_macos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_macos.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_macos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_macos.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_macos.framework",

View File

@ -1423,13 +1423,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.27.3" version: "0.27.3"
salomon_bottom_bar:
dependency: "direct main"
description:
name: salomon_bottom_bar
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.0"
scroll_to_index: scroll_to_index:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -71,7 +71,6 @@ dependencies:
qr_flutter: ^4.0.0 qr_flutter: ^4.0.0
receive_sharing_intent: ^1.4.5 receive_sharing_intent: ^1.4.5
record: ^4.1.1 record: ^4.1.1
salomon_bottom_bar: ^3.2.0
scroll_to_index: ^3.0.1 scroll_to_index: ^3.0.1
sentry: ^6.3.0 sentry: ^6.3.0
share_plus: ^4.0.9 share_plus: ^4.0.9