mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-23 20:49:26 +01:00
feat: New material 3 design
This commit is contained in:
parent
802ff0fa9d
commit
091958be0b
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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) =>
|
||||||
|
@ -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: [
|
||||||
|
@ -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);
|
||||||
|
@ -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),
|
||||||
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
67
lib/pages/chat_list/chat_list_drawer.dart
Normal file
67
lib/pages/chat_list/chat_list_drawer.dart
Normal 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');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
@ -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));
|
|
||||||
})(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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 }
|
||||||
|
@ -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)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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(
|
||||||
|
@ -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,
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -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),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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(
|
||||||
|
@ -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),
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user