fluffychat merge
This commit is contained in:
commit
17652f9f34
631 changed files with 10790 additions and 15332 deletions
|
|
@ -120,12 +120,10 @@ abstract class AppConfig {
|
|||
static const double readingAssistanceInputBarHeight = 175.0;
|
||||
static String errorSubscriptionId = "pangea_subscription_error";
|
||||
|
||||
static TextStyle messageTextStyle(
|
||||
Event? event,
|
||||
Color textColor,
|
||||
) {
|
||||
static TextStyle messageTextStyle(Event? event, Color textColor) {
|
||||
final fontSize = messageFontSize * AppSettings.fontSizeFactor.value;
|
||||
final bigEmotes = event != null &&
|
||||
final bigEmotes =
|
||||
event != null &&
|
||||
event.onlyEmotes &&
|
||||
event.numberEmotes > 0 &&
|
||||
event.numberEmotes <= 3;
|
||||
|
|
@ -133,10 +131,12 @@ abstract class AppConfig {
|
|||
return TextStyle(
|
||||
color: textColor,
|
||||
fontSize: bigEmotes ? fontSize * 5 : fontSize,
|
||||
decoration:
|
||||
(event?.redacted ?? false) ? TextDecoration.lineThrough : null,
|
||||
decoration: (event?.redacted ?? false)
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
height: 1.3,
|
||||
);
|
||||
}
|
||||
|
||||
// Pangea#
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,26 +75,22 @@ abstract class AppRoutes {
|
|||
static FutureOr<String?> loggedInRedirect(
|
||||
BuildContext context,
|
||||
GoRouterState state,
|
||||
) {
|
||||
// #Pangea
|
||||
// Matrix.of(context).widget.clients.any((client) => client.isLogged())
|
||||
// ? '/rooms'
|
||||
// : null;
|
||||
return PAuthGaurd.homeRedirect(context, state);
|
||||
// Pangea#
|
||||
}
|
||||
// ) => Matrix.of(context).widget.clients.any((client) => client.isLogged())
|
||||
// ? '/rooms'
|
||||
// : null;
|
||||
) => PAuthGaurd.homeRedirect(context, state);
|
||||
// Pangea#
|
||||
|
||||
static FutureOr<String?> loggedOutRedirect(
|
||||
BuildContext context,
|
||||
GoRouterState state,
|
||||
) {
|
||||
// #Pangea
|
||||
// Matrix.of(context).widget.clients.any((client) => client.isLogged())
|
||||
// ) => Matrix.of(context).widget.clients.any((client) => client.isLogged())
|
||||
// ? null
|
||||
// : '/home';
|
||||
return PAuthGaurd.roomsRedirect(context, state);
|
||||
// Pangea#
|
||||
}
|
||||
) => PAuthGaurd.roomsRedirect(context, state);
|
||||
// Pangea#
|
||||
|
||||
AppRoutes();
|
||||
|
||||
|
|
@ -103,8 +99,8 @@ abstract class AppRoutes {
|
|||
path: '/',
|
||||
redirect: (context, state) =>
|
||||
Matrix.of(context).widget.clients.any((client) => client.isLogged())
|
||||
? '/rooms'
|
||||
: '/home',
|
||||
? '/rooms'
|
||||
: '/home',
|
||||
),
|
||||
GoRoute(
|
||||
path: '/home',
|
||||
|
|
@ -145,28 +141,20 @@ abstract class AppRoutes {
|
|||
// #Pangea
|
||||
GoRoute(
|
||||
path: 'language',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const LanguageSelectionPage(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const LanguageSelectionPage()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'signup',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const SignupPage(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const SignupPage()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'email',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const SignupPage(
|
||||
withEmail: true,
|
||||
),
|
||||
const SignupPage(withEmail: true),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -178,28 +166,19 @@ abstract class AppRoutes {
|
|||
),
|
||||
GoRoute(
|
||||
path: '/logs',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const LogViewer(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const LogViewer()),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/configs',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const ConfigViewer(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const ConfigViewer()),
|
||||
),
|
||||
// #Pangea
|
||||
GoRoute(
|
||||
path: '/registration',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const LanguageSelectionPage(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const LanguageSelectionPage()),
|
||||
redirect: PAuthGaurd.onboardingRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
|
|
@ -212,14 +191,13 @@ abstract class AppRoutes {
|
|||
),
|
||||
GoRoute(
|
||||
path: 'notifications',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const EnableNotifications(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const EnableNotifications()),
|
||||
redirect: (context, state) async {
|
||||
final redirect =
|
||||
await PAuthGaurd.onboardingRedirect(context, state);
|
||||
final redirect = await PAuthGaurd.onboardingRedirect(
|
||||
context,
|
||||
state,
|
||||
);
|
||||
if (redirect != null) return redirect;
|
||||
final enabled = await Matrix.of(context).notificationsEnabled;
|
||||
if (enabled) return "/registration/course";
|
||||
|
|
@ -228,11 +206,8 @@ abstract class AppRoutes {
|
|||
),
|
||||
GoRoute(
|
||||
path: 'course',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const SpaceCodeOnboarding(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const SpaceCodeOnboarding()),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -253,9 +228,7 @@ abstract class AppRoutes {
|
|||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
BootstrapDialog(
|
||||
wipe: state.uri.queryParameters['wipe'] == 'true',
|
||||
),
|
||||
BootstrapDialog(wipe: state.uri.queryParameters['wipe'] == 'true'),
|
||||
),
|
||||
),
|
||||
ShellRoute(
|
||||
|
|
@ -278,10 +251,7 @@ abstract class AppRoutes {
|
|||
// sideView: child,
|
||||
// )
|
||||
// : child,
|
||||
TwoColumnLayout(
|
||||
state: state,
|
||||
sideView: child,
|
||||
),
|
||||
TwoColumnLayout(state: state, sideView: child),
|
||||
// Pangea#
|
||||
),
|
||||
routes: [
|
||||
|
|
@ -310,11 +280,8 @@ abstract class AppRoutes {
|
|||
routes: [
|
||||
GoRoute(
|
||||
path: 'archive',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const Archive(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const Archive()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: ':roomid',
|
||||
|
|
@ -333,23 +300,14 @@ abstract class AppRoutes {
|
|||
),
|
||||
GoRoute(
|
||||
path: 'newprivatechat',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const NewPrivateChat(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const NewPrivateChat()),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'newgroup',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
// #Pangea
|
||||
// const NewGroup(),
|
||||
NewGroup(spaceId: state.uri.queryParameters['space']),
|
||||
// Pangea#
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const NewGroup()),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
|
|
@ -364,11 +322,8 @@ abstract class AppRoutes {
|
|||
// #Pangea
|
||||
GoRoute(
|
||||
path: 'course',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const FindCoursePage(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const FindCoursePage()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'private',
|
||||
|
|
@ -638,11 +593,8 @@ abstract class AppRoutes {
|
|||
),
|
||||
GoRoute(
|
||||
path: 'style',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const SettingsStyle(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const SettingsStyle()),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
|
|
@ -656,20 +608,15 @@ abstract class AppRoutes {
|
|||
),
|
||||
GoRoute(
|
||||
path: 'chat',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const SettingsChat(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const SettingsChat()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'emotes',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
EmotesSettings(
|
||||
roomId: state.pathParameters['roomid'],
|
||||
),
|
||||
EmotesSettings(roomId: state.pathParameters['roomid']),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -758,9 +705,7 @@ abstract class AppRoutes {
|
|||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const SettingsLearning(
|
||||
isDialog: false,
|
||||
),
|
||||
const SettingsLearning(isDialog: false),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
|
|
@ -780,11 +725,8 @@ abstract class AppRoutes {
|
|||
// #Pangea
|
||||
GoRoute(
|
||||
path: 'spaces',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const EmptyPage(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const EmptyPage()),
|
||||
redirect: (context, state) {
|
||||
if (state.pathParameters['spaceid'] == null) {
|
||||
return "/rooms";
|
||||
|
|
@ -806,15 +748,12 @@ abstract class AppRoutes {
|
|||
routes: [
|
||||
GoRoute(
|
||||
path: 'details',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const EmptyPage(),
|
||||
),
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const EmptyPage()),
|
||||
redirect: (context, state) {
|
||||
String subroute =
|
||||
state.fullPath?.split(":spaceid/details").last ??
|
||||
"";
|
||||
"";
|
||||
|
||||
if (state.uri.queryParameters.isNotEmpty) {
|
||||
final queryString = state.uri.queryParameters.entries
|
||||
|
|
@ -911,10 +850,10 @@ abstract class AppRoutes {
|
|||
roomId: state.pathParameters['roomid']!,
|
||||
initialFilter:
|
||||
state.uri.queryParameters['filter'] != null
|
||||
? InvitationFilter.fromString(
|
||||
state.uri.queryParameters['filter']!,
|
||||
)
|
||||
: null,
|
||||
? InvitationFilter.fromString(
|
||||
state.uri.queryParameters['filter']!,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
|
|
@ -966,9 +905,7 @@ abstract class AppRoutes {
|
|||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ChatSearchPage(
|
||||
roomId: state.pathParameters['roomid']!,
|
||||
),
|
||||
ChatSearchPage(roomId: state.pathParameters['roomid']!),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
|
|
@ -1009,9 +946,7 @@ abstract class AppRoutes {
|
|||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ChatDetails(
|
||||
roomId: state.pathParameters['roomid']!,
|
||||
),
|
||||
ChatDetails(roomId: state.pathParameters['roomid']!),
|
||||
),
|
||||
// #Pangea
|
||||
routes: roomDetailsRoutes('roomid'),
|
||||
|
|
@ -1063,9 +998,7 @@ abstract class AppRoutes {
|
|||
// pageBuilder: (context, state) => defaultPageBuilder(
|
||||
// context,
|
||||
// state,
|
||||
// EmotesSettings(
|
||||
// roomId: state.pathParameters['roomid'],
|
||||
// ),
|
||||
// EmotesSettings(roomId: state.pathParameters['roomid']),
|
||||
// ),
|
||||
// redirect: loggedOutRedirect,
|
||||
// ),
|
||||
|
|
@ -1085,142 +1018,126 @@ abstract class AppRoutes {
|
|||
BuildContext context,
|
||||
GoRouterState state,
|
||||
Widget child,
|
||||
) =>
|
||||
NoTransitionPage(
|
||||
key: state.pageKey,
|
||||
restorationId: state.pageKey.value,
|
||||
child: child,
|
||||
);
|
||||
) => NoTransitionPage(
|
||||
key: state.pageKey,
|
||||
restorationId: state.pageKey.value,
|
||||
child: child,
|
||||
);
|
||||
|
||||
static Page defaultPageBuilder(
|
||||
BuildContext context,
|
||||
GoRouterState state,
|
||||
Widget child,
|
||||
) =>
|
||||
// #Pangea
|
||||
noTransitionPageBuilder(context, state, child);
|
||||
// FluffyThemes.isColumnMode(context)
|
||||
// ? noTransitionPageBuilder(context, state, child)
|
||||
// : MaterialPage(
|
||||
// key: state.pageKey,
|
||||
// restorationId: state.pageKey.value,
|
||||
// child: child,
|
||||
// );
|
||||
// #Pangea
|
||||
// ) => FluffyThemes.isColumnMode(context)
|
||||
// ? noTransitionPageBuilder(context, state, child)
|
||||
// : MaterialPage(
|
||||
// key: state.pageKey,
|
||||
// restorationId: state.pageKey.value,
|
||||
// child: child,
|
||||
// );
|
||||
) => noTransitionPageBuilder(context, state, child);
|
||||
// Pangea#
|
||||
|
||||
// #Pangea
|
||||
static List<RouteBase> get newRoomRoutes => [
|
||||
GoRoute(
|
||||
path: 'newgroup',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const NewGroup(),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'newspace',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const NewGroup(createGroupType: CreateGroupType.space),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
];
|
||||
GoRoute(
|
||||
path: 'newgroup',
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const NewGroup()),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'newspace',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const NewGroup(createGroupType: CreateGroupType.space),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
];
|
||||
|
||||
static List<RouteBase> roomDetailsRoutes(String roomKey) => [
|
||||
GoRoute(
|
||||
path: '/edit',
|
||||
redirect: loggedOutRedirect,
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
EditCourse(roomId: state.pathParameters[roomKey]!),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/edit',
|
||||
redirect: loggedOutRedirect,
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
EditCourse(roomId: state.pathParameters[roomKey]!),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/analytics',
|
||||
redirect: loggedOutRedirect,
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
SpaceAnalytics(roomId: state.pathParameters[roomKey]!),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'access',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ChatAccessSettings(roomId: state.pathParameters[roomKey]!),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'members',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ChatMembersPage(
|
||||
roomId: state.pathParameters[roomKey]!,
|
||||
filter: state.uri.queryParameters['filter'],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/analytics',
|
||||
redirect: loggedOutRedirect,
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
SpaceAnalytics(
|
||||
roomId: state.pathParameters[roomKey]!,
|
||||
),
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'permissions',
|
||||
pageBuilder: (context, state) =>
|
||||
defaultPageBuilder(context, state, const ChatPermissionsSettings()),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'invite',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
PangeaInvitationSelection(
|
||||
roomId: state.pathParameters[roomKey]!,
|
||||
initialFilter: state.uri.queryParameters['filter'] != null
|
||||
? InvitationFilter.fromString(
|
||||
state.uri.queryParameters['filter']!,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'access',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ChatAccessSettings(
|
||||
roomId: state.pathParameters[roomKey]!,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'members',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ChatMembersPage(
|
||||
roomId: state.pathParameters[roomKey]!,
|
||||
filter: state.uri.queryParameters['filter'],
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'permissions',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const ChatPermissionsSettings(),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'invite',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
PangeaInvitationSelection(
|
||||
roomId: state.pathParameters[roomKey]!,
|
||||
initialFilter: state.uri.queryParameters['filter'] != null
|
||||
? InvitationFilter.fromString(
|
||||
state.uri.queryParameters['filter']!,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'emotes',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
EmotesSettings(
|
||||
roomId: state.pathParameters[roomKey]!,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'emotes/:state_key',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
EmotesSettings(
|
||||
roomId: state.pathParameters[roomKey]!,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
];
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'emotes',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
EmotesSettings(roomId: state.pathParameters[roomKey]!),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'emotes/:state_key',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
EmotesSettings(roomId: state.pathParameters[roomKey]!),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
];
|
||||
// Pangea#
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,10 +54,7 @@ enum AppSettings<T> {
|
|||
'chat.fluffy.no_encryption_warning_shown',
|
||||
false,
|
||||
),
|
||||
displayChatDetailsColumn(
|
||||
'chat.fluffy.display_chat_details_column',
|
||||
false,
|
||||
),
|
||||
displayChatDetailsColumn('chat.fluffy.display_chat_details_column', false),
|
||||
// AppConfig-mirrored settings
|
||||
// #Pangea
|
||||
// applicationName<String>('chat.fluffy.application_name', 'FluffyChat'),
|
||||
|
|
@ -65,14 +62,9 @@ enum AppSettings<T> {
|
|||
applicationName<String>('chat.fluffy.application_name', 'Pangea Chat'),
|
||||
// Pangea#
|
||||
// colorSchemeSeed stored as ARGB int
|
||||
colorSchemeSeedInt<int>(
|
||||
'chat.fluffy.color_scheme_seed',
|
||||
// #Pangea
|
||||
// 0xFF5625BA,
|
||||
0xFF8560E0,
|
||||
// Pangea#
|
||||
),
|
||||
// #Pangea
|
||||
// colorSchemeSeedInt<int>('chat.fluffy.color_scheme_seed', 0xFF5625BA),
|
||||
colorSchemeSeedInt<int>('chat.fluffy.color_scheme_seed', 0xFF8560E0),
|
||||
volume<double>('pangea.volume', 1.0),
|
||||
// Pangea#
|
||||
emojiSuggestionLocale<String>('emoji_suggestion_locale', ''),
|
||||
|
|
@ -92,10 +84,9 @@ enum AppSettings<T> {
|
|||
final store = AppSettings._store = await SharedPreferences.getInstance();
|
||||
|
||||
// Migrate wrong datatype for fontSizeFactor
|
||||
final fontSizeFactorString =
|
||||
Result(() => store.getString(AppSettings.fontSizeFactor.key))
|
||||
.asValue
|
||||
?.value;
|
||||
final fontSizeFactorString = Result(
|
||||
() => store.getString(AppSettings.fontSizeFactor.key),
|
||||
).asValue?.value;
|
||||
if (fontSizeFactorString != null) {
|
||||
Logs().i('Migrate wrong datatype for fontSizeFactor!');
|
||||
await store.remove(AppSettings.fontSizeFactor.key);
|
||||
|
|
@ -110,8 +101,9 @@ enum AppSettings<T> {
|
|||
}
|
||||
if (kIsWeb && loadWebConfigFile) {
|
||||
try {
|
||||
final configJsonString =
|
||||
utf8.decode((await http.get(Uri.parse('config.json'))).bodyBytes);
|
||||
final configJsonString = utf8.decode(
|
||||
(await http.get(Uri.parse('config.json'))).bodyBytes,
|
||||
);
|
||||
final configJson =
|
||||
json.decode(configJsonString) as Map<String, Object?>;
|
||||
for (final setting in AppSettings.values) {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@ abstract class FluffyThemes {
|
|||
static bool isThreeColumnMode(BuildContext context) =>
|
||||
MediaQuery.sizeOf(context).width > FluffyThemes.columnWidth * 3.5;
|
||||
|
||||
static LinearGradient backgroundGradient(
|
||||
BuildContext context,
|
||||
int alpha,
|
||||
) {
|
||||
static LinearGradient backgroundGradient(BuildContext context, int alpha) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
|
|
@ -95,16 +92,18 @@ abstract class FluffyThemes {
|
|||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
toolbarHeight: isColumnMode ? 72 : 56,
|
||||
shadowColor:
|
||||
isColumnMode ? colorScheme.surfaceContainer.withAlpha(128) : null,
|
||||
shadowColor: isColumnMode
|
||||
? colorScheme.surfaceContainer.withAlpha(128)
|
||||
: null,
|
||||
// #Pangea
|
||||
// surfaceTintColor: isColumnMode ? colorScheme.surface : null,
|
||||
// backgroundColor: isColumnMode ? colorScheme.surface : null,
|
||||
surfaceTintColor: colorScheme.surface,
|
||||
backgroundColor: colorScheme.surface,
|
||||
// Pangea#
|
||||
actionsPadding:
|
||||
isColumnMode ? const EdgeInsets.symmetric(horizontal: 16.0) : null,
|
||||
actionsPadding: isColumnMode
|
||||
? const EdgeInsets.symmetric(horizontal: 16.0)
|
||||
: null,
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: brightness.reversed,
|
||||
|
|
@ -115,10 +114,7 @@ abstract class FluffyThemes {
|
|||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
side: BorderSide(width: 1, color: colorScheme.primary),
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(color: colorScheme.primary),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
|
|
@ -201,8 +197,8 @@ extension BubbleColorTheme on ThemeData {
|
|||
: colorScheme.onPrimaryContainer;
|
||||
|
||||
Color get secondaryBubbleColor => HSLColor.fromColor(
|
||||
brightness == Brightness.light
|
||||
? colorScheme.tertiary
|
||||
: colorScheme.tertiaryContainer,
|
||||
).withSaturation(0.5).toColor();
|
||||
brightness == Brightness.light
|
||||
? colorScheme.tertiary
|
||||
: colorScheme.tertiaryContainer,
|
||||
).withSaturation(0.5).toColor();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,8 +114,9 @@ Future<void> startGui(List<Client> clients, SharedPreferences store) async {
|
|||
String? pin;
|
||||
if (PlatformInfos.isMobile) {
|
||||
try {
|
||||
pin =
|
||||
await const FlutterSecureStorage().read(key: 'chat.fluffy.app_lock');
|
||||
pin = await const FlutterSecureStorage().read(
|
||||
key: 'chat.fluffy.app_lock',
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logs().d('Unable to read PIN from Secure storage', e, s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,9 @@ class ArchiveView extends StatelessWidget {
|
|||
itemBuilder: (BuildContext context, int i) => ChatListItem(
|
||||
controller.archive[i],
|
||||
onForget: () => controller.forgetRoomAction(i),
|
||||
onTap: () => context
|
||||
.go('/rooms/archive/${controller.archive[i].id}'),
|
||||
onTap: () => context.go(
|
||||
'/rooms/archive/${controller.archive[i].id}',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,7 @@ import '../key_verification/key_verification_dialog.dart';
|
|||
class BootstrapDialog extends StatefulWidget {
|
||||
final bool wipe;
|
||||
|
||||
const BootstrapDialog({
|
||||
super.key,
|
||||
this.wipe = false,
|
||||
});
|
||||
const BootstrapDialog({super.key, this.wipe = false});
|
||||
|
||||
@override
|
||||
BootstrapDialogState createState() => BootstrapDialogState();
|
||||
|
|
@ -148,7 +145,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
builder: (context, snapshot) {
|
||||
final status = snapshot.data;
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
CircularProgressIndicator.adaptive(value: status?.progress),
|
||||
if (status != null) Text(status.calcLocalizedString(context)),
|
||||
|
|
@ -177,8 +174,9 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
),
|
||||
body: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: FluffyThemes.columnWidth * 1.5),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 1.5,
|
||||
),
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
children: [
|
||||
|
|
@ -193,10 +191,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
),
|
||||
subtitle: Text(L10n.of(context).chatBackupDescription),
|
||||
),
|
||||
const Divider(
|
||||
height: 32,
|
||||
thickness: 1,
|
||||
),
|
||||
const Divider(height: 32, thickness: 1),
|
||||
TextField(
|
||||
minLines: 2,
|
||||
maxLines: 4,
|
||||
|
|
@ -220,8 +215,9 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
});
|
||||
},
|
||||
title: Text(_getSecureStorageLocalizedName()),
|
||||
subtitle:
|
||||
Text(L10n.of(context).storeInSecureStorageDescription),
|
||||
subtitle: Text(
|
||||
L10n.of(context).storeInSecureStorageDescription,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
CheckboxListTile.adaptive(
|
||||
|
|
@ -241,16 +237,16 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
label: Text(L10n.of(context).next),
|
||||
onPressed:
|
||||
(_recoveryKeyCopied || _storeInSecureStorage == true)
|
||||
? () {
|
||||
if (_storeInSecureStorage == true) {
|
||||
const FlutterSecureStorage().write(
|
||||
key: _secureStorageKey,
|
||||
value: key,
|
||||
);
|
||||
}
|
||||
setState(() => _recoveryKeyStored = true);
|
||||
}
|
||||
: null,
|
||||
? () {
|
||||
if (_storeInSecureStorage == true) {
|
||||
const FlutterSecureStorage().write(
|
||||
key: _secureStorageKey,
|
||||
value: key,
|
||||
);
|
||||
}
|
||||
setState(() => _recoveryKeyStored = true);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -303,8 +299,9 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
padding: const EdgeInsets.all(16.0),
|
||||
children: [
|
||||
ListTile(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
),
|
||||
trailing: Icon(
|
||||
Icons.info_outlined,
|
||||
color: theme.colorScheme.primary,
|
||||
|
|
@ -370,7 +367,9 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
);
|
||||
try {
|
||||
await bootstrap
|
||||
.client.encryption!.crossSigning
|
||||
.client
|
||||
.encryption!
|
||||
.crossSigning
|
||||
.selfSign(recoveryKey: key);
|
||||
Logs().d('Successful selfsigned');
|
||||
} catch (e, s) {
|
||||
|
|
@ -383,13 +382,14 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
}
|
||||
} on InvalidPassphraseException catch (e) {
|
||||
setState(
|
||||
() => _recoveryKeyInputError =
|
||||
e.toLocalizedString(context),
|
||||
() => _recoveryKeyInputError = e
|
||||
.toLocalizedString(context),
|
||||
);
|
||||
} on FormatException catch (_) {
|
||||
setState(
|
||||
() => _recoveryKeyInputError =
|
||||
L10n.of(context).wrongRecoveryKey,
|
||||
() => _recoveryKeyInputError = L10n.of(
|
||||
context,
|
||||
).wrongRecoveryKey,
|
||||
);
|
||||
} catch (e, s) {
|
||||
ErrorReporter(
|
||||
|
|
@ -397,8 +397,8 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
'Unable to open SSSS with recovery key',
|
||||
).onErrorCallback(e, s);
|
||||
setState(
|
||||
() => _recoveryKeyInputError =
|
||||
e.toLocalizedString(context),
|
||||
() => _recoveryKeyInputError = e
|
||||
.toLocalizedString(context),
|
||||
);
|
||||
} finally {
|
||||
setState(
|
||||
|
|
@ -428,8 +428,9 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
final consent = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).verifyOtherDevice,
|
||||
message: L10n.of(context)
|
||||
.verifyOtherDeviceDescription,
|
||||
message: L10n.of(
|
||||
context,
|
||||
).verifyOtherDeviceDescription,
|
||||
okLabel: L10n.of(context).ok,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
);
|
||||
|
|
@ -452,17 +453,18 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
|
||||
final waitForSecret = Completer();
|
||||
final secretsSub = client
|
||||
.encryption!.ssss.onSecretStored.stream
|
||||
.listen((
|
||||
event,
|
||||
) async {
|
||||
if (await client.encryption!.keyManager
|
||||
.isCached() &&
|
||||
await client.encryption!.crossSigning
|
||||
.isCached()) {
|
||||
waitForSecret.complete();
|
||||
}
|
||||
});
|
||||
.encryption!
|
||||
.ssss
|
||||
.onSecretStored
|
||||
.stream
|
||||
.listen((event) async {
|
||||
if (await client.encryption!.keyManager
|
||||
.isCached() &&
|
||||
await client.encryption!.crossSigning
|
||||
.isCached()) {
|
||||
waitForSecret.complete();
|
||||
}
|
||||
});
|
||||
|
||||
final result = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
|
|
@ -542,7 +544,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
case BootstrapState.done:
|
||||
titleText = L10n.of(context).everythingReady;
|
||||
body = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.check_circle_rounded,
|
||||
|
|
@ -576,13 +578,9 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
body,
|
||||
const SizedBox(height: 8),
|
||||
...buttons,
|
||||
],
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .stretch,
|
||||
children: [body, const SizedBox(height: 8), ...buttons],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -19,20 +19,21 @@ class AddWidgetTileView extends StatelessWidget {
|
|||
CupertinoSegmentedControl(
|
||||
groupValue: controller.widgetType,
|
||||
padding: const EdgeInsets.all(8),
|
||||
children: {
|
||||
'm.etherpad': Text(L10n.of(context).widgetEtherpad),
|
||||
'm.jitsi': Text(L10n.of(context).widgetJitsi),
|
||||
'm.video': Text(L10n.of(context).widgetVideo),
|
||||
'm.custom': Text(L10n.of(context).widgetCustom),
|
||||
}.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: value,
|
||||
children:
|
||||
{
|
||||
'm.etherpad': Text(L10n.of(context).widgetEtherpad),
|
||||
'm.jitsi': Text(L10n.of(context).widgetJitsi),
|
||||
'm.video': Text(L10n.of(context).widgetVideo),
|
||||
'm.custom': Text(L10n.of(context).widgetCustom),
|
||||
}.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
onValueChanged: controller.setWidgetType,
|
||||
),
|
||||
Padding(
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -31,7 +31,7 @@ class ChatAppBarListTile extends StatelessWidget {
|
|||
onTap: onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
if (leading != null) leading,
|
||||
?leading,
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
|
|
@ -56,7 +56,7 @@ class ChatAppBarListTile extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (trailing != null) trailing,
|
||||
?trailing,
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -36,14 +36,10 @@ class ChatAppBarTitle extends StatelessWidget {
|
|||
onTap: controller.isArchived
|
||||
? null
|
||||
: () => FluffyThemes.isThreeColumnMode(context)
|
||||
? controller.toggleDisplayChatDetailsColumn()
|
||||
// #Pangea
|
||||
// : context.go('/rooms/${room.id}/details'),
|
||||
: NavigationUtil.goToSpaceRoute(
|
||||
room.id,
|
||||
['details'],
|
||||
context,
|
||||
),
|
||||
? controller.toggleDisplayChatDetailsColumn()
|
||||
// #Pangea
|
||||
// : context.go('/rooms/${room.id}/details'),
|
||||
: NavigationUtil.goToSpaceRoute(room.id, ['details'], context),
|
||||
// Pangea#
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
@ -63,22 +59,22 @@ class ChatAppBarTitle extends StatelessWidget {
|
|||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Text(
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: room.client.onSyncStatus.stream,
|
||||
builder: (context, snapshot) {
|
||||
final status = room.client.onSyncStatus.value ??
|
||||
final status =
|
||||
room.client.onSyncStatus.value ??
|
||||
const SyncStatusUpdate(SyncStatus.waitingForResponse);
|
||||
final hide = FluffyThemes.isColumnMode(context) ||
|
||||
final hide =
|
||||
FluffyThemes.isColumnMode(context) ||
|
||||
(room.client.onSync.value != null &&
|
||||
status.status != SyncStatus.error &&
|
||||
room.client.prevBatch != null);
|
||||
|
|
@ -103,8 +99,9 @@ class ChatAppBarTitle extends StatelessWidget {
|
|||
if (lastActiveTimestamp != null) {
|
||||
return Text(
|
||||
L10n.of(context).lastActiveAgo(
|
||||
lastActiveTimestamp
|
||||
.localizedTimeShort(context),
|
||||
lastActiveTimestamp.localizedTimeShort(
|
||||
context,
|
||||
),
|
||||
),
|
||||
style: style,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -54,20 +54,22 @@ class ChatEmojiPicker extends StatelessWidget {
|
|||
// #Pangea
|
||||
// enabled: false,
|
||||
showBackspaceButton: false,
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainer,
|
||||
buttonColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainer,
|
||||
buttonIconColor:
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainer,
|
||||
buttonColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainer,
|
||||
buttonIconColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurface,
|
||||
// Pangea#
|
||||
),
|
||||
categoryViewConfig: CategoryViewConfig(
|
||||
backspaceColor: theme.colorScheme.primary,
|
||||
iconColor:
|
||||
theme.colorScheme.primary.withAlpha(128),
|
||||
iconColor: theme.colorScheme.primary.withAlpha(
|
||||
128,
|
||||
),
|
||||
iconColorSelected: theme.colorScheme.primary,
|
||||
indicatorColor: theme.colorScheme.primary,
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@ import 'package:fluffychat/utils/platform_infos.dart';
|
|||
class ChatEventList extends StatelessWidget {
|
||||
final ChatController controller;
|
||||
|
||||
const ChatEventList({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
const ChatEventList({super.key, required this.controller});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -31,10 +28,7 @@ class ChatEventList extends StatelessWidget {
|
|||
}
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final colors = [
|
||||
theme.secondaryBubbleColor,
|
||||
theme.bubbleColor,
|
||||
];
|
||||
final colors = [theme.secondaryBubbleColor, theme.bubbleColor];
|
||||
|
||||
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
|
||||
|
||||
|
|
@ -96,7 +90,7 @@ class ChatEventList extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
SeenByRow(controller),
|
||||
TypingIndicators(controller),
|
||||
|
|
@ -118,8 +112,9 @@ class ChatEventList extends StatelessWidget {
|
|||
);
|
||||
if (visibleIndex > timeline.events.length - 50) {
|
||||
// #Pangea
|
||||
// WidgetsBinding.instance
|
||||
// .addPostFrameCallback(controller.requestHistory);
|
||||
// WidgetsBinding.instance.addPostFrameCallback(
|
||||
// controller.requestHistory,
|
||||
// );
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => controller.requestHistory(),
|
||||
);
|
||||
|
|
@ -156,7 +151,8 @@ class ChatEventList extends StatelessWidget {
|
|||
|
||||
// The message at this index:
|
||||
final event = events[i];
|
||||
final animateIn = animateInEventIndex != null &&
|
||||
final animateIn =
|
||||
animateInEventIndex != null &&
|
||||
timeline.events.length > animateInEventIndex &&
|
||||
event == timeline.events[animateInEventIndex];
|
||||
|
||||
|
|
@ -164,10 +160,12 @@ class ChatEventList extends StatelessWidget {
|
|||
final previousEvent = i > 0 ? events[i - 1] : null;
|
||||
|
||||
// Collapsed state event
|
||||
final canExpand = event.isCollapsedState &&
|
||||
final canExpand =
|
||||
event.isCollapsedState &&
|
||||
nextEvent?.isCollapsedState == true &&
|
||||
previousEvent?.isCollapsedState != true;
|
||||
final isCollapsed = event.isCollapsedState &&
|
||||
final isCollapsed =
|
||||
event.isCollapsedState &&
|
||||
previousEvent?.isCollapsedState == true &&
|
||||
!controller.expandedEventIds.contains(event.eventId);
|
||||
|
||||
|
|
@ -202,11 +200,12 @@ class ChatEventList extends StatelessWidget {
|
|||
isButton: event.eventId == controller.buttonEventID,
|
||||
canRefresh: event.eventId == controller.refreshEventID,
|
||||
// Pangea#
|
||||
selected: controller.selectedEvents
|
||||
.any((e) => e.eventId == event.eventId),
|
||||
selected: controller.selectedEvents.any(
|
||||
(e) => e.eventId == event.eventId,
|
||||
),
|
||||
singleSelected:
|
||||
controller.selectedEvents.singleOrNull?.eventId ==
|
||||
event.eventId,
|
||||
event.eventId,
|
||||
onEdit: () => controller.editSelectedEventAction(),
|
||||
timeline: timeline,
|
||||
displayReadMarker:
|
||||
|
|
@ -222,10 +221,9 @@ class ChatEventList extends StatelessWidget {
|
|||
: null,
|
||||
onExpand: canExpand
|
||||
? () => controller.expandEventsFrom(
|
||||
event,
|
||||
!controller.expandedEventIds
|
||||
.contains(event.eventId),
|
||||
)
|
||||
event,
|
||||
!controller.expandedEventIds.contains(event.eventId),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
// import 'package:flutter/material.dart';
|
||||
|
||||
// import 'package:animations/animations.dart';
|
||||
// import 'package:emoji_picker_flutter/locales/default_emoji_set_locale.dart';
|
||||
// import 'package:matrix/matrix.dart';
|
||||
|
||||
// import 'package:fluffychat/config/setting_keys.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/pages/chat/recording_input_row.dart';
|
||||
|
|
@ -8,9 +12,6 @@
|
|||
// import 'package:fluffychat/utils/platform_infos.dart';
|
||||
// import 'package:fluffychat/widgets/avatar.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:matrix/matrix.dart';
|
||||
|
||||
// import '../../config/themes.dart';
|
||||
// import 'chat.dart';
|
||||
// import 'input_bar.dart';
|
||||
|
|
@ -52,12 +53,13 @@
|
|||
// );
|
||||
// }
|
||||
// return Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: .end,
|
||||
// mainAxisAlignment: .spaceBetween,
|
||||
// children: controller.selectMode
|
||||
// ? <Widget>[
|
||||
// if (controller.selectedEvents
|
||||
// .every((event) => event.status == EventStatus.error))
|
||||
// if (controller.selectedEvents.every(
|
||||
// (event) => event.status == EventStatus.error,
|
||||
// ))
|
||||
// SizedBox(
|
||||
// height: height,
|
||||
// child: TextButton(
|
||||
|
|
@ -89,36 +91,36 @@
|
|||
// ),
|
||||
// controller.selectedEvents.length == 1
|
||||
// ? controller.selectedEvents.first
|
||||
// .getDisplayEvent(controller.timeline!)
|
||||
// .status
|
||||
// .isSent
|
||||
// ? SizedBox(
|
||||
// height: height,
|
||||
// child: TextButton(
|
||||
// style: selectedTextButtonStyle,
|
||||
// onPressed: controller.replyAction,
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// Text(L10n.of(context).reply),
|
||||
// const Icon(Icons.keyboard_arrow_right),
|
||||
// ],
|
||||
// .getDisplayEvent(controller.timeline!)
|
||||
// .status
|
||||
// .isSent
|
||||
// ? SizedBox(
|
||||
// height: height,
|
||||
// child: TextButton(
|
||||
// style: selectedTextButtonStyle,
|
||||
// onPressed: controller.replyAction,
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// Text(L10n.of(context).reply),
|
||||
// const Icon(Icons.keyboard_arrow_right),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// : SizedBox(
|
||||
// height: height,
|
||||
// child: TextButton(
|
||||
// style: selectedTextButtonStyle,
|
||||
// onPressed: controller.sendAgainAction,
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// Text(L10n.of(context).tryToSendAgain),
|
||||
// const SizedBox(width: 4),
|
||||
// const Icon(Icons.send_outlined, size: 16),
|
||||
// ],
|
||||
// )
|
||||
// : SizedBox(
|
||||
// height: height,
|
||||
// child: TextButton(
|
||||
// style: selectedTextButtonStyle,
|
||||
// onPressed: controller.sendAgainAction,
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// Text(L10n.of(context).tryToSendAgain),
|
||||
// const SizedBox(width: 4),
|
||||
// const Icon(Icons.send_outlined, size: 16),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// )
|
||||
// : const SizedBox.shrink(),
|
||||
// ]
|
||||
// : <Widget>[
|
||||
|
|
@ -126,8 +128,9 @@
|
|||
// AnimatedContainer(
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// width:
|
||||
// controller.sendController.text.isNotEmpty ? 0 : height,
|
||||
// width: controller.sendController.text.isNotEmpty
|
||||
// ? 0
|
||||
// : height,
|
||||
// height: height,
|
||||
// alignment: Alignment.center,
|
||||
// decoration: const BoxDecoration(),
|
||||
|
|
@ -189,8 +192,9 @@
|
|||
// theme.colorScheme.onPrimaryContainer,
|
||||
// foregroundColor:
|
||||
// theme.colorScheme.primaryContainer,
|
||||
// child:
|
||||
// const Icon(Icons.video_camera_back_outlined),
|
||||
// child: const Icon(
|
||||
// Icons.video_camera_back_outlined,
|
||||
// ),
|
||||
// ),
|
||||
// title: Text(L10n.of(context).sendVideo),
|
||||
// contentPadding: const EdgeInsets.all(0),
|
||||
|
|
@ -269,19 +273,20 @@
|
|||
// tooltip: L10n.of(context).emojis,
|
||||
// color: theme.colorScheme.onPrimaryContainer,
|
||||
// icon: PageTransitionSwitcher(
|
||||
// transitionBuilder: (
|
||||
// Widget child,
|
||||
// Animation<double> primaryAnimation,
|
||||
// Animation<double> secondaryAnimation,
|
||||
// ) {
|
||||
// return SharedAxisTransition(
|
||||
// animation: primaryAnimation,
|
||||
// secondaryAnimation: secondaryAnimation,
|
||||
// transitionType: SharedAxisTransitionType.scaled,
|
||||
// fillColor: Colors.transparent,
|
||||
// child: child,
|
||||
// );
|
||||
// },
|
||||
// transitionBuilder:
|
||||
// (
|
||||
// Widget child,
|
||||
// Animation<double> primaryAnimation,
|
||||
// Animation<double> secondaryAnimation,
|
||||
// ) {
|
||||
// return SharedAxisTransition(
|
||||
// animation: primaryAnimation,
|
||||
// secondaryAnimation: secondaryAnimation,
|
||||
// transitionType: SharedAxisTransitionType.scaled,
|
||||
// fillColor: Colors.transparent,
|
||||
// child: child,
|
||||
// );
|
||||
// },
|
||||
// child: Icon(
|
||||
// controller.showEmojiPicker
|
||||
// ? Icons.keyboard
|
||||
|
|
@ -312,9 +317,9 @@
|
|||
// keyboardType: TextInputType.multiline,
|
||||
// textInputAction:
|
||||
// AppSettings.sendOnEnter.value == true &&
|
||||
// PlatformInfos.isMobile
|
||||
// ? TextInputAction.send
|
||||
// : null,
|
||||
// PlatformInfos.isMobile
|
||||
// ? TextInputAction.send
|
||||
// : null,
|
||||
// onSubmitted: controller.onInputBarSubmitted,
|
||||
// onSubmitImage: controller.sendImageFromClipBoard,
|
||||
// focusNode: controller.inputFocus,
|
||||
|
|
@ -334,14 +339,18 @@
|
|||
// filled: false,
|
||||
// ),
|
||||
// onChanged: controller.onInputBarChanged,
|
||||
// suggestionEmojis: getDefaultEmojiLocale(
|
||||
// AppSettings.emojiSuggestionLocale.value.isNotEmpty
|
||||
// ? Locale(AppSettings.emojiSuggestionLocale.value)
|
||||
// : Localizations.localeOf(context),
|
||||
// ).fold(
|
||||
// [],
|
||||
// (emojis, category) => emojis..addAll(category.emoji),
|
||||
// ),
|
||||
// suggestionEmojis:
|
||||
// getDefaultEmojiLocale(
|
||||
// AppSettings.emojiSuggestionLocale.value.isNotEmpty
|
||||
// ? Locale(
|
||||
// AppSettings.emojiSuggestionLocale.value,
|
||||
// )
|
||||
// : Localizations.localeOf(context),
|
||||
// ).fold(
|
||||
// [],
|
||||
// (emojis, category) =>
|
||||
// emojis..addAll(category.emoji),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
|
|
@ -349,19 +358,21 @@
|
|||
// height: height,
|
||||
// width: height,
|
||||
// alignment: Alignment.center,
|
||||
// child: PlatformInfos.platformCanRecord &&
|
||||
// child:
|
||||
// PlatformInfos.platformCanRecord &&
|
||||
// controller.sendController.text.isEmpty
|
||||
// ? IconButton(
|
||||
// tooltip: L10n.of(context).voiceMessage,
|
||||
// onPressed: () =>
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(
|
||||
// content: Text(
|
||||
// L10n.of(context)
|
||||
// .longPressToRecordVoiceMessage,
|
||||
// SnackBar(
|
||||
// content: Text(
|
||||
// L10n.of(
|
||||
// context,
|
||||
// ).longPressToRecordVoiceMessage,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// onLongPress: () => recordingViewModel
|
||||
// .startRecording(controller.room),
|
||||
// style: IconButton.styleFrom(
|
||||
|
|
@ -393,9 +404,9 @@
|
|||
// const _ChatAccountPicker(this.controller);
|
||||
|
||||
// void _popupMenuButtonSelected(String mxid, BuildContext context) {
|
||||
// final client = Matrix.of(context)
|
||||
// .currentBundle!
|
||||
// .firstWhere((cl) => cl!.userID == mxid, orElse: () => null);
|
||||
// final client = Matrix.of(
|
||||
// context,
|
||||
// ).currentBundle!.firstWhere((cl) => cl!.userID == mxid, orElse: () => null);
|
||||
// if (client == null) {
|
||||
// Logs().w('Attempted to switch to a non-existing client $mxid');
|
||||
// return;
|
||||
|
|
@ -422,7 +433,8 @@
|
|||
// builder: (context, snapshot) => ListTile(
|
||||
// leading: Avatar(
|
||||
// mxContent: snapshot.data?.avatarUrl,
|
||||
// name: snapshot.data?.displayName ??
|
||||
// name:
|
||||
// snapshot.data?.displayName ??
|
||||
// client.userID!.localpart,
|
||||
// size: 20,
|
||||
// ),
|
||||
|
|
@ -435,7 +447,8 @@
|
|||
// .toList(),
|
||||
// child: Avatar(
|
||||
// mxContent: snapshot.data?.avatarUrl,
|
||||
// name: snapshot.data?.displayName ??
|
||||
// name:
|
||||
// snapshot.data?.displayName ??
|
||||
// Matrix.of(context).client.userID!.localpart,
|
||||
// size: 20,
|
||||
// ),
|
||||
|
|
|
|||
|
|
@ -60,8 +60,9 @@ class ChatView extends StatelessWidget {
|
|||
// IconButton(
|
||||
// icon: const Icon(Icons.message_outlined),
|
||||
// tooltip: L10n.of(context).replyInThread,
|
||||
// onPressed: () => controller
|
||||
// .enterThread(controller.selectedEvents.single.eventId),
|
||||
// onPressed: () => controller.enterThread(
|
||||
// controller.selectedEvents.single.eventId,
|
||||
// ),
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.copy_outlined),
|
||||
|
|
@ -94,7 +95,7 @@ class ChatView extends StatelessWidget {
|
|||
// onTap: controller.pinEvent,
|
||||
// value: null,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// const Icon(Icons.push_pin_outlined),
|
||||
// const SizedBox(width: 12),
|
||||
|
|
@ -107,7 +108,7 @@ class ChatView extends StatelessWidget {
|
|||
// onTap: () => controller.saveSelectedEvent(context),
|
||||
// value: null,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// const Icon(Icons.download_outlined),
|
||||
// const SizedBox(width: 12),
|
||||
|
|
@ -118,7 +119,7 @@ class ChatView extends StatelessWidget {
|
|||
// PopupMenuItem(
|
||||
// value: _EventContextAction.info,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// const Icon(Icons.info_outlined),
|
||||
// const SizedBox(width: 12),
|
||||
|
|
@ -130,12 +131,9 @@ class ChatView extends StatelessWidget {
|
|||
// PopupMenuItem(
|
||||
// value: _EventContextAction.report,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// const Icon(
|
||||
// Icons.shield_outlined,
|
||||
// color: Colors.red,
|
||||
// ),
|
||||
// const Icon(Icons.shield_outlined, color: Colors.red),
|
||||
// const SizedBox(width: 12),
|
||||
// Text(L10n.of(context).reportMessage),
|
||||
// ],
|
||||
|
|
@ -166,10 +164,7 @@ class ChatView extends StatelessWidget {
|
|||
if (controller.room.showActivityChatUI) {
|
||||
return [
|
||||
ActivityMenuButton(controller: controller),
|
||||
ActivitySessionPopupMenu(
|
||||
controller.room,
|
||||
onLeave: controller.onLeave,
|
||||
),
|
||||
ActivitySessionPopupMenu(controller.room, onLeave: controller.onLeave),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -178,11 +173,9 @@ class ChatView extends StatelessWidget {
|
|||
icon: const Icon(Icons.search_outlined),
|
||||
tooltip: L10n.of(context).search,
|
||||
onPressed: () {
|
||||
NavigationUtil.goToSpaceRoute(
|
||||
controller.room.id,
|
||||
['search'],
|
||||
context,
|
||||
);
|
||||
NavigationUtil.goToSpaceRoute(controller.room.id, [
|
||||
'search',
|
||||
], context);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
|
|
@ -190,17 +183,11 @@ class ChatView extends StatelessWidget {
|
|||
tooltip: L10n.of(context).chatDetails,
|
||||
onPressed: () {
|
||||
if (GoRouterState.of(context).uri.path.endsWith('/details')) {
|
||||
NavigationUtil.goToSpaceRoute(
|
||||
controller.room.id,
|
||||
[],
|
||||
context,
|
||||
);
|
||||
NavigationUtil.goToSpaceRoute(controller.room.id, [], context);
|
||||
} else {
|
||||
NavigationUtil.goToSpaceRoute(
|
||||
controller.room.id,
|
||||
['details'],
|
||||
context,
|
||||
);
|
||||
NavigationUtil.goToSpaceRoute(controller.room.id, [
|
||||
'details',
|
||||
], context);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
@ -224,7 +211,8 @@ class ChatView extends StatelessWidget {
|
|||
final accountConfig = Matrix.of(context).client.applicationAccountConfig;
|
||||
|
||||
return PopScope(
|
||||
canPop: controller.selectedEvents.isEmpty &&
|
||||
canPop:
|
||||
controller.selectedEvents.isEmpty &&
|
||||
!controller.showEmojiPicker &&
|
||||
controller.activeThreadId == null,
|
||||
onPopInvokedWithResult: (pop, _) async {
|
||||
|
|
@ -276,8 +264,8 @@ class ChatView extends StatelessWidget {
|
|||
// ),
|
||||
// backgroundColor: controller.selectedEvents.isEmpty
|
||||
// ? controller.activeThreadId != null
|
||||
// ? theme.colorScheme.secondaryContainer
|
||||
// : null
|
||||
// ? theme.colorScheme.secondaryContainer
|
||||
// : null
|
||||
// : theme.colorScheme.tertiaryContainer,
|
||||
// Pangea#
|
||||
automaticallyImplyLeading: false,
|
||||
|
|
@ -289,55 +277,50 @@ class ChatView extends StatelessWidget {
|
|||
color: theme.colorScheme.onTertiaryContainer,
|
||||
)
|
||||
: activeThreadId != null
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: controller.closeThread,
|
||||
tooltip: L10n.of(context).backToMainChat,
|
||||
color: theme.colorScheme.onSecondaryContainer,
|
||||
)
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: controller.closeThread,
|
||||
tooltip: L10n.of(context).backToMainChat,
|
||||
color: theme.colorScheme.onSecondaryContainer,
|
||||
)
|
||||
// #Pangea
|
||||
: controller.widget.backButton != null
|
||||
? controller.widget.backButton!
|
||||
// : FluffyThemes.isColumnMode(context)
|
||||
// ? null
|
||||
// Pangea#
|
||||
: StreamBuilder<Object>(
|
||||
stream: Matrix.of(context).client.onSync.stream.where(
|
||||
(syncUpdate) => syncUpdate.hasRoomUpdate,
|
||||
),
|
||||
// #Pangea
|
||||
: controller.widget.backButton != null
|
||||
? controller.widget.backButton!
|
||||
// : FluffyThemes.isColumnMode(context)
|
||||
// ? null
|
||||
// Pangea#
|
||||
: StreamBuilder<Object>(
|
||||
stream: Matrix.of(context)
|
||||
.client
|
||||
.onSync
|
||||
.stream
|
||||
.where(
|
||||
(syncUpdate) => syncUpdate.hasRoomUpdate,
|
||||
),
|
||||
// #Pangea
|
||||
// builder: (context, _) => UnreadRoomsBadge(
|
||||
// filter: (r) => r.id != controller.roomId,
|
||||
// badgePosition:
|
||||
// BadgePosition.topEnd(end: 8, top: 4),
|
||||
// child: const Center(child: BackButton()),
|
||||
// ),
|
||||
builder: (context, _) => Center(
|
||||
child: SizedBox(
|
||||
height: kToolbarHeight,
|
||||
child: UnreadRoomsBadge(
|
||||
filter: (r) => r.id != controller.roomId,
|
||||
badgePosition: BadgePosition.topEnd(
|
||||
end: 8,
|
||||
top: 9,
|
||||
),
|
||||
child: const Center(child: BackButton()),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
// builder: (context, _) => UnreadRoomsBadge(
|
||||
// filter: (r) => r.id != controller.roomId,
|
||||
// badgePosition: BadgePosition.topEnd(end: 8, top: 4),
|
||||
// child: const Center(child: BackButton()),
|
||||
// ),
|
||||
builder: (context, _) => Center(
|
||||
child: SizedBox(
|
||||
height: kToolbarHeight,
|
||||
child: UnreadRoomsBadge(
|
||||
filter: (r) => r.id != controller.roomId,
|
||||
badgePosition: BadgePosition.topEnd(
|
||||
end: 8,
|
||||
top: 9,
|
||||
),
|
||||
child: const Center(child: BackButton()),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
),
|
||||
titleSpacing: FluffyThemes.isColumnMode(context) ? 24 : 0,
|
||||
title: ChatAppBarTitle(controller),
|
||||
actions: _appBarActions(context),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: Size.fromHeight(appbarBottomHeight),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
PinnedEvents(controller),
|
||||
if (activeThreadId != null)
|
||||
|
|
@ -374,9 +357,7 @@ class ChatView extends StatelessWidget {
|
|||
title: L10n.of(context).jumpToLastReadMessage,
|
||||
trailing: TextButton(
|
||||
onPressed: () {
|
||||
controller.scrollToEventId(
|
||||
scrollUpBannerEventId,
|
||||
);
|
||||
controller.scrollToEventId(scrollUpBannerEventId);
|
||||
controller.discardScrollUpBannerEventId();
|
||||
},
|
||||
child: Text(L10n.of(context).jump),
|
||||
|
|
@ -389,7 +370,8 @@ class ChatView extends StatelessWidget {
|
|||
// #Pangea
|
||||
// floatingActionButtonLocation:
|
||||
// FloatingActionButtonLocation.miniCenterFloat,
|
||||
// floatingActionButton: controller.showScrollDownButton &&
|
||||
// floatingActionButton:
|
||||
// controller.showScrollDownButton &&
|
||||
// controller.selectedEvents.isEmpty
|
||||
// ? Padding(
|
||||
// padding: const EdgeInsets.only(bottom: 56.0),
|
||||
|
|
@ -429,7 +411,8 @@ class ChatView extends StatelessWidget {
|
|||
sigmaX: accountConfig.wallpaperBlur ?? 0.0,
|
||||
sigmaY: accountConfig.wallpaperBlur ?? 0.0,
|
||||
),
|
||||
child: controller.room.activityPlan!.imageURL!
|
||||
child:
|
||||
controller.room.activityPlan!.imageURL!
|
||||
.toString()
|
||||
.startsWith('mxc')
|
||||
? MxcImage(
|
||||
|
|
@ -438,7 +421,9 @@ class ChatView extends StatelessWidget {
|
|||
height: MediaQuery.sizeOf(context).height,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
cacheKey: controller
|
||||
.room.activityPlan!.imageURL
|
||||
.room
|
||||
.activityPlan!
|
||||
.imageURL
|
||||
.toString(),
|
||||
isThumbnail: false,
|
||||
)
|
||||
|
|
@ -448,8 +433,8 @@ class ChatView extends StatelessWidget {
|
|||
fit: BoxFit.cover,
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
headers: controller
|
||||
.room.activityPlan!.imageURL
|
||||
headers:
|
||||
controller.room.activityPlan!.imageURL
|
||||
.toString()
|
||||
.contains(Environment.cmsApi)
|
||||
? {
|
||||
|
|
@ -512,10 +497,7 @@ class ChatView extends StatelessWidget {
|
|||
),
|
||||
// #Pangea
|
||||
// if (controller.showScrollDownButton)
|
||||
// Divider(
|
||||
// height: 1,
|
||||
// color: theme.dividerColor,
|
||||
// ),
|
||||
// Divider(height: 1, color: theme.dividerColor),
|
||||
ListenableBuilder(
|
||||
listenable: controller.scrollController,
|
||||
builder: (context, _) {
|
||||
|
|
@ -561,14 +543,11 @@ class ChatView extends StatelessWidget {
|
|||
// ),
|
||||
// child: controller.room.isAbandonedDMRoom == true
|
||||
// ? Row(
|
||||
// mainAxisAlignment:
|
||||
// MainAxisAlignment.spaceEvenly,
|
||||
// mainAxisAlignment: .spaceEvenly,
|
||||
// children: [
|
||||
// TextButton.icon(
|
||||
// style: TextButton.styleFrom(
|
||||
// padding: const EdgeInsets.all(
|
||||
// 16,
|
||||
// ),
|
||||
// padding: const EdgeInsets.all(16),
|
||||
// foregroundColor:
|
||||
// theme.colorScheme.error,
|
||||
// ),
|
||||
|
|
@ -576,15 +555,11 @@ class ChatView extends StatelessWidget {
|
|||
// Icons.archive_outlined,
|
||||
// ),
|
||||
// onPressed: controller.leaveChat,
|
||||
// label: Text(
|
||||
// L10n.of(context).leave,
|
||||
// ),
|
||||
// label: Text(L10n.of(context).leave),
|
||||
// ),
|
||||
// TextButton.icon(
|
||||
// style: TextButton.styleFrom(
|
||||
// padding: const EdgeInsets.all(
|
||||
// 16,
|
||||
// ),
|
||||
// padding: const EdgeInsets.all(16),
|
||||
// ),
|
||||
// icon: const Icon(
|
||||
// Icons.forum_outlined,
|
||||
|
|
@ -597,7 +572,7 @@ class ChatView extends StatelessWidget {
|
|||
// ],
|
||||
// )
|
||||
// : Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// ReplyDisplay(controller),
|
||||
// ChatInputRow(controller),
|
||||
|
|
@ -622,9 +597,7 @@ class ChatView extends StatelessWidget {
|
|||
controller: controller,
|
||||
),
|
||||
if (controller.room.isActivityFinished)
|
||||
LoadActivitySummaryWidget(
|
||||
room: controller.room,
|
||||
),
|
||||
LoadActivitySummaryWidget(room: controller.room),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
|
|
@ -635,7 +608,7 @@ class ChatView extends StatelessWidget {
|
|||
ValueListenableBuilder(
|
||||
valueListenable:
|
||||
controller.activityController.hasRainedConfetti,
|
||||
builder: (context, hasRained, __) {
|
||||
builder: (context, hasRained, _) {
|
||||
return hasRained
|
||||
? const SizedBox()
|
||||
: StarRainWidget(
|
||||
|
|
@ -650,10 +623,7 @@ class ChatView extends StatelessWidget {
|
|||
// Container(
|
||||
// color: theme.scaffoldBackgroundColor.withAlpha(230),
|
||||
// alignment: Alignment.center,
|
||||
// child: const Icon(
|
||||
// Icons.upload_outlined,
|
||||
// size: 100,
|
||||
// ),
|
||||
// child: const Icon(Icons.upload_outlined, size: 100),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
|
|
|
|||
|
|
@ -15,11 +15,9 @@ class EncryptionButton extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return StreamBuilder<SyncUpdate>(
|
||||
stream: Matrix.of(context)
|
||||
.client
|
||||
.onSync
|
||||
.stream
|
||||
.where((s) => s.deviceLists != null),
|
||||
stream: Matrix.of(
|
||||
context,
|
||||
).client.onSync.stream.where((s) => s.deviceLists != null),
|
||||
builder: (context, snapshot) {
|
||||
final shouldBeEncrypted = room.joinRules != JoinRules.public;
|
||||
return FutureBuilder<EncryptionHealthState>(
|
||||
|
|
|
|||
|
|
@ -13,21 +13,16 @@ import 'package:fluffychat/widgets/avatar.dart';
|
|||
|
||||
extension EventInfoDialogExtension on Event {
|
||||
void showInfoDialog(BuildContext context) => showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
EventInfoDialog(l10n: L10n.of(context), event: this),
|
||||
);
|
||||
context: context,
|
||||
builder: (context) => EventInfoDialog(l10n: L10n.of(context), event: this),
|
||||
);
|
||||
}
|
||||
|
||||
class EventInfoDialog extends StatelessWidget {
|
||||
final Event event;
|
||||
final L10n l10n;
|
||||
|
||||
const EventInfoDialog({
|
||||
required this.event,
|
||||
required this.l10n,
|
||||
super.key,
|
||||
});
|
||||
const EventInfoDialog({required this.event, required this.l10n, super.key});
|
||||
|
||||
String prettyJson(MatrixEvent event) {
|
||||
const decoder = JsonDecoder();
|
||||
|
|
@ -75,15 +70,9 @@ class EventInfoDialog extends StatelessWidget {
|
|||
trailing: IconButton(
|
||||
icon: const Icon(Icons.copy),
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: prettyJson(event),
|
||||
),
|
||||
);
|
||||
Clipboard.setData(ClipboardData(text: prettyJson(event)));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context).copiedToClipboard),
|
||||
),
|
||||
SnackBar(content: Text(L10n.of(context).copiedToClipboard)),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -99,9 +88,7 @@ class EventInfoDialog extends StatelessWidget {
|
|||
scrollDirection: Axis.horizontal,
|
||||
child: SelectableText(
|
||||
prettyJson(MatrixEvent.fromJson(event.toJson())),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
style: TextStyle(color: theme.colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -118,9 +105,7 @@ class EventInfoDialog extends StatelessWidget {
|
|||
scrollDirection: Axis.horizontal,
|
||||
child: SelectableText(
|
||||
prettyJson(originalSource),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
style: TextStyle(color: theme.colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -128,12 +128,13 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
// onPressed: () {
|
||||
// audioPlayer.pause();
|
||||
// audioPlayer.dispose();
|
||||
// matrix.voiceMessageEventId.value =
|
||||
// matrix.audioPlayer = null;
|
||||
// matrix.voiceMessageEventId.value = matrix.audioPlayer =
|
||||
// null;
|
||||
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// ScaffoldMessenger.of(matrix.context)
|
||||
// .clearMaterialBanners();
|
||||
// ScaffoldMessenger.of(
|
||||
// matrix.context,
|
||||
// ).clearMaterialBanners();
|
||||
// });
|
||||
// },
|
||||
// icon: const Icon(Icons.close_outlined),
|
||||
|
|
@ -163,15 +164,16 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
// #Pangea
|
||||
// matrix.voiceMessageEventId.value != widget.event.eventId
|
||||
matrix.voiceMessageEventId.value != widget.eventId
|
||||
// Pangea#
|
||||
? null
|
||||
: matrix.audioPlayer;
|
||||
// Pangea#
|
||||
? null
|
||||
: matrix.audioPlayer;
|
||||
if (currentPlayer != null) {
|
||||
// #Pangea
|
||||
currentPlayer.setSpeed(playbackSpeed);
|
||||
_onAudioStateChanged?.cancel();
|
||||
_onAudioStateChanged =
|
||||
matrix.audioPlayer!.playerStateStream.listen((state) {
|
||||
_onAudioStateChanged = matrix.audioPlayer!.playerStateStream.listen((
|
||||
state,
|
||||
) {
|
||||
if (state.processingState == ProcessingState.completed) {
|
||||
matrix.audioPlayer!.stop();
|
||||
matrix.audioPlayer!.seek(Duration.zero);
|
||||
|
|
@ -214,8 +216,9 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
? (progress) {
|
||||
final progressPercentage = progress / fileSize;
|
||||
setState(() {
|
||||
_downloadProgress =
|
||||
progressPercentage < 1 ? progressPercentage : null;
|
||||
_downloadProgress = progressPercentage < 1
|
||||
? progressPercentage
|
||||
: null;
|
||||
});
|
||||
}
|
||||
: null,
|
||||
|
|
@ -261,11 +264,9 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
});
|
||||
} catch (e, s) {
|
||||
Logs().v('Could not download audio file', e, s);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(e.toLocalizedString(context)),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
rethrow;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
|
|
@ -277,11 +278,12 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
|
||||
final audioPlayer = matrix.audioPlayer = AudioPlayer();
|
||||
|
||||
// #Pangea
|
||||
// #Pangea
|
||||
audioPlayer.setSpeed(playbackSpeed);
|
||||
_onAudioStateChanged?.cancel();
|
||||
_onAudioStateChanged =
|
||||
matrix.audioPlayer!.playerStateStream.listen((state) {
|
||||
_onAudioStateChanged = matrix.audioPlayer!.playerStateStream.listen((
|
||||
state,
|
||||
) {
|
||||
if (state.processingState == ProcessingState.completed) {
|
||||
matrix.audioPlayer!.stop();
|
||||
matrix.audioPlayer!.seek(Duration.zero);
|
||||
|
|
@ -316,9 +318,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
// Pangea#
|
||||
|
||||
audioPlayer.play().onError(
|
||||
ErrorReporter(context, 'Unable to play audio message')
|
||||
.onErrorCallback,
|
||||
);
|
||||
ErrorReporter(context, 'Unable to play audio message').onErrorCallback,
|
||||
);
|
||||
}
|
||||
|
||||
void _toggleSpeed() async {
|
||||
|
|
@ -337,8 +338,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
default:
|
||||
setState(() => playbackSpeed = 1.0);
|
||||
}
|
||||
// Pangea#
|
||||
if (audioPlayer == null) return;
|
||||
// Pangea#
|
||||
switch (audioPlayer.speed) {
|
||||
// #Pangea
|
||||
// case 1.0:
|
||||
|
|
@ -377,7 +378,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
|
||||
List<int>? _getWaveform() {
|
||||
// #Pangea
|
||||
final eventWaveForm = widget.matrixFile?.waveform ??
|
||||
final eventWaveForm =
|
||||
widget.matrixFile?.waveform ??
|
||||
widget.event?.content
|
||||
.tryGetMap<String, dynamic>('org.matrix.msc1767.audio')
|
||||
?.tryGetList<int>('waveform');
|
||||
|
|
@ -406,8 +408,9 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
void _onPlayerChange() {
|
||||
if (matrix.audioPlayer == null) return;
|
||||
_onAudioStateChanged?.cancel();
|
||||
_onAudioStateChanged =
|
||||
matrix.audioPlayer?.playerStateStream.listen((state) {
|
||||
_onAudioStateChanged = matrix.audioPlayer?.playerStateStream.listen((
|
||||
state,
|
||||
) {
|
||||
if (state.processingState == ProcessingState.completed) {
|
||||
matrix.audioPlayer?.stop();
|
||||
matrix.audioPlayer?.seek(Duration.zero);
|
||||
|
|
@ -464,10 +467,12 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
valueListenable: matrix.voiceMessageEventId,
|
||||
builder: (context, eventId, _) {
|
||||
// #Pangea
|
||||
// final audioPlayer =
|
||||
// eventId != widget.event.eventId ? null : matrix.audioPlayer;
|
||||
final audioPlayer =
|
||||
eventId != widget.eventId ? null : matrix.audioPlayer;
|
||||
// final audioPlayer = eventId != widget.event.eventId
|
||||
// ? null
|
||||
// : matrix.audioPlayer;
|
||||
final audioPlayer = eventId != widget.eventId
|
||||
? null
|
||||
: matrix.audioPlayer;
|
||||
// Pangea#
|
||||
|
||||
// #Pangea
|
||||
|
|
@ -494,7 +499,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
audioPlayer?.position.inMilliseconds.toDouble() ?? 0.0;
|
||||
if (currentPosition > maxPosition) currentPosition = maxPosition;
|
||||
|
||||
final wavePosition = (currentPosition / maxPosition) *
|
||||
final wavePosition =
|
||||
(currentPosition / maxPosition) *
|
||||
AudioPlayerWidget.wavesCount;
|
||||
|
||||
final statusText = audioPlayer == null
|
||||
|
|
@ -503,15 +509,15 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
return Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
width: buttonSize,
|
||||
|
|
@ -556,9 +562,11 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
),
|
||||
child: Row(
|
||||
children: [
|
||||
for (var i = 0;
|
||||
i < AudioPlayerWidget.wavesCount;
|
||||
i++)
|
||||
for (
|
||||
var i = 0;
|
||||
i < AudioPlayerWidget.wavesCount;
|
||||
i++
|
||||
)
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 32,
|
||||
|
|
@ -566,13 +574,14 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
child: Container(
|
||||
margin:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 1,
|
||||
),
|
||||
horizontal: 1,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: i < wavePosition
|
||||
? widget.color
|
||||
: widget.color
|
||||
.withAlpha(128),
|
||||
: widget.color.withAlpha(
|
||||
128,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(64),
|
||||
),
|
||||
|
|
@ -588,11 +597,13 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
height: 32,
|
||||
child: Slider(
|
||||
// #Pangea
|
||||
// thumbColor: widget.event.senderId ==
|
||||
// thumbColor:
|
||||
// widget.event.senderId ==
|
||||
// widget.event.room.client.userID
|
||||
// ? theme.colorScheme.onPrimary
|
||||
// : theme.colorScheme.primary,
|
||||
thumbColor: widget.senderId ==
|
||||
thumbColor:
|
||||
widget.senderId ==
|
||||
Matrix.of(context).client.userID
|
||||
? widget.color
|
||||
: theme.colorScheme.onSurface,
|
||||
|
|
@ -609,13 +620,13 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
onChanged: !widget.enableClicks
|
||||
? null
|
||||
: (position) => audioPlayer == null
|
||||
? _onButtonTap()
|
||||
: audioPlayer.seek(
|
||||
Duration(
|
||||
milliseconds:
|
||||
position.round(),
|
||||
? _onButtonTap()
|
||||
: audioPlayer.seek(
|
||||
Duration(
|
||||
milliseconds: position
|
||||
.round(),
|
||||
),
|
||||
),
|
||||
),
|
||||
// onChanged: (position) => audioPlayer == null
|
||||
// ? _onButtonTap()
|
||||
// : audioPlayer.seek(
|
||||
|
|
@ -643,21 +654,20 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
// ),
|
||||
Text(
|
||||
statusText,
|
||||
style: TextStyle(
|
||||
color: widget.color,
|
||||
fontSize: 12,
|
||||
),
|
||||
style: TextStyle(color: widget.color, fontSize: 12),
|
||||
),
|
||||
// Pangea#
|
||||
const SizedBox(width: 8),
|
||||
// #Pangea
|
||||
Material(
|
||||
color: widget.color.withAlpha(64),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
onTap: !widget.enableClicks ? null : _toggleSpeed,
|
||||
child: SizedBox(
|
||||
width: 32,
|
||||
|
|
@ -684,8 +694,9 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
// ),
|
||||
// secondChild: Material(
|
||||
// color: widget.color.withAlpha(64),
|
||||
// borderRadius:
|
||||
// BorderRadius.circular(AppConfig.borderRadius),
|
||||
// borderRadius: BorderRadius.circular(
|
||||
// AppConfig.borderRadius,
|
||||
// ),
|
||||
// child: InkWell(
|
||||
// borderRadius: BorderRadius.circular(
|
||||
// AppConfig.borderRadius,
|
||||
|
|
@ -725,8 +736,9 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
),
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context).scale(1),
|
||||
textScaleFactor: MediaQuery.textScalerOf(
|
||||
context,
|
||||
).scale(1),
|
||||
style: TextStyle(
|
||||
color: widget.color,
|
||||
fontSize: widget.fontSize,
|
||||
|
|
@ -806,4 +818,5 @@ class BytesAudioSource extends StreamAudioSource {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -37,13 +37,10 @@ class _CuteContentState extends State<CuteContent> {
|
|||
return GestureDetector(
|
||||
onTap: addOverlay,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: .min,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
Text(
|
||||
widget.event.text,
|
||||
style: const TextStyle(fontSize: 150),
|
||||
),
|
||||
Text(widget.event.text, style: const TextStyle(fontSize: 150)),
|
||||
if (label != null) Text(label),
|
||||
],
|
||||
),
|
||||
|
|
@ -112,10 +109,7 @@ class _CuteEventOverlayState extends State<CuteEventOverlay>
|
|||
with TickerProviderStateMixin {
|
||||
final List<Size> items = List.generate(
|
||||
50,
|
||||
(index) => Size(
|
||||
Random().nextDouble(),
|
||||
4 + (Random().nextDouble() * 4),
|
||||
),
|
||||
(index) => Size(Random().nextDouble(), 4 + (Random().nextDouble() * 4)),
|
||||
);
|
||||
|
||||
AnimationController? controller;
|
||||
|
|
@ -150,14 +144,13 @@ class _CuteEventOverlayState extends State<CuteEventOverlay>
|
|||
.map(
|
||||
(position) => Positioned(
|
||||
left: position.width * width,
|
||||
bottom: (height *
|
||||
bottom:
|
||||
(height *
|
||||
.25 *
|
||||
position.height *
|
||||
(controller?.value ?? 0)) -
|
||||
_CuteOverlayContent.size,
|
||||
child: _CuteOverlayContent(
|
||||
emoji: widget.emoji,
|
||||
),
|
||||
child: _CuteOverlayContent(emoji: widget.emoji),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
|
@ -186,10 +179,7 @@ class _CuteOverlayContent extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return SizedBox.square(
|
||||
dimension: size,
|
||||
child: Text(
|
||||
emoji,
|
||||
style: const TextStyle(fontSize: 48),
|
||||
),
|
||||
child: Text(emoji, style: const TextStyle(fontSize: 48)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,7 @@ class BurstPainter extends CustomPainter {
|
|||
final List<BurstParticle> particles;
|
||||
final double progress;
|
||||
|
||||
BurstPainter({
|
||||
required this.particles,
|
||||
required this.progress,
|
||||
});
|
||||
BurstPainter({required this.particles, required this.progress});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
|
|
|
|||
|
|
@ -157,20 +157,17 @@ class HtmlMessage extends StatelessWidget {
|
|||
};
|
||||
|
||||
// #Pangea
|
||||
List<PangeaToken>? get tokens =>
|
||||
pangeaMessageEvent?.messageDisplayRepresentation?.tokens
|
||||
?.where(
|
||||
(t) =>
|
||||
!["SYM"].contains(t.pos) &&
|
||||
!t.lemma.text.contains(RegExp(r'[0-9]')),
|
||||
)
|
||||
.toList();
|
||||
List<PangeaToken>? get tokens => pangeaMessageEvent
|
||||
?.messageDisplayRepresentation
|
||||
?.tokens
|
||||
?.where(
|
||||
(t) =>
|
||||
!["SYM"].contains(t.pos) &&
|
||||
!t.lemma.text.contains(RegExp(r'[0-9]')),
|
||||
)
|
||||
.toList();
|
||||
|
||||
PangeaToken? getToken(
|
||||
String text,
|
||||
int offset,
|
||||
int length,
|
||||
) =>
|
||||
PangeaToken? getToken(String text, int offset, int length) =>
|
||||
tokens?.firstWhereOrNull(
|
||||
(token) => token.text.offset == offset && token.text.length == length,
|
||||
);
|
||||
|
|
@ -204,11 +201,9 @@ class HtmlMessage extends StatelessWidget {
|
|||
replyTagIndex,
|
||||
);
|
||||
if (closingReplyTagIndex != -1) {
|
||||
result.replaceRange(
|
||||
replyTagIndex,
|
||||
closingReplyTagIndex + 1,
|
||||
[result.sublist(replyTagIndex, closingReplyTagIndex + 1).join()],
|
||||
);
|
||||
result.replaceRange(replyTagIndex, closingReplyTagIndex + 1, [
|
||||
result.sublist(replyTagIndex, closingReplyTagIndex + 1).join(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -240,10 +235,10 @@ class HtmlMessage extends StatelessWidget {
|
|||
}
|
||||
|
||||
final int tokenLength = tokenSpanText.characters.length;
|
||||
final before =
|
||||
result[substringIndex].characters.take(tokenIndex).toString();
|
||||
final after = result[substringIndex]
|
||||
.characters
|
||||
final before = result[substringIndex].characters
|
||||
.take(tokenIndex)
|
||||
.toString();
|
||||
final after = result[substringIndex].characters
|
||||
.skip(tokenIndex + tokenLength)
|
||||
.toString();
|
||||
|
||||
|
|
@ -372,7 +367,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
if (node is! dom.Element) {
|
||||
return TextSpan(text: node.text);
|
||||
}
|
||||
final style = atomOneDarkTheme[node.className.split('-').last] ??
|
||||
final style =
|
||||
atomOneDarkTheme[node.className.split('-').last] ??
|
||||
atomOneDarkTheme['root'];
|
||||
|
||||
return TextSpan(
|
||||
|
|
@ -382,15 +378,15 @@ class HtmlMessage extends StatelessWidget {
|
|||
}
|
||||
|
||||
/// Transforms a Node to an InlineSpan.
|
||||
// #Pangea
|
||||
// InlineSpan _renderHtml(dom.Node node, BuildContext context, {int depth = 1}) {
|
||||
InlineSpan _renderHtml(
|
||||
dom.Node node,
|
||||
// #Pangea
|
||||
// BuildContext context, {
|
||||
BuildContext context,
|
||||
TextStyle textStyle, {
|
||||
// Pangea#
|
||||
int depth = 1,
|
||||
}) {
|
||||
// Pangea#
|
||||
// We must not render elements nested more than 100 elements deep:
|
||||
if (depth >= 100) return const TextSpan();
|
||||
|
||||
|
|
@ -414,7 +410,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
// #Pangea
|
||||
double fontSize = this.fontSize;
|
||||
if (readingAssistanceMode == ReadingAssistanceMode.practiceMode) {
|
||||
fontSize = (overlayController != null && overlayController!.maxWidth > 600
|
||||
fontSize =
|
||||
(overlayController != null && overlayController!.maxWidth > 600
|
||||
? Theme.of(context).textTheme.titleLarge?.fontSize
|
||||
: Theme.of(context).textTheme.bodyLarge?.fontSize) ??
|
||||
this.fontSize;
|
||||
|
|
@ -422,13 +419,13 @@ class HtmlMessage extends StatelessWidget {
|
|||
|
||||
final existingStyle = pangeaMessageEvent != null
|
||||
? textStyle
|
||||
.merge(
|
||||
AppConfig.messageTextStyle(
|
||||
pangeaMessageEvent!.event,
|
||||
textColor,
|
||||
),
|
||||
)
|
||||
.copyWith(fontSize: fontSize)
|
||||
.merge(
|
||||
AppConfig.messageTextStyle(
|
||||
pangeaMessageEvent!.event,
|
||||
textColor,
|
||||
),
|
||||
)
|
||||
.copyWith(fontSize: fontSize)
|
||||
: textStyle.copyWith(fontSize: fontSize);
|
||||
|
||||
final renderer = TokenRenderingUtil();
|
||||
|
|
@ -439,8 +436,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
|
||||
final newTokens =
|
||||
pangeaMessageEvent != null && !pangeaMessageEvent!.ownMessage
|
||||
? TokensUtil.getNewTokensByEvent(pangeaMessageEvent!)
|
||||
: [];
|
||||
? TokensUtil.getNewTokensByEvent(pangeaMessageEvent!)
|
||||
: [];
|
||||
// Pangea#
|
||||
|
||||
switch (node.localName) {
|
||||
|
|
@ -461,7 +458,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
: false;
|
||||
|
||||
final isNew = token != null && newTokens.contains(token.text);
|
||||
final isFirstNewToken = isNew &&
|
||||
final isFirstNewToken =
|
||||
isNew &&
|
||||
controller.buttonEventID == event.eventId &&
|
||||
newTokens.first == token.text;
|
||||
final showShimmer =
|
||||
|
|
@ -479,8 +477,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
WidgetSpan(
|
||||
alignment:
|
||||
readingAssistanceMode == ReadingAssistanceMode.practiceMode
|
||||
? PlaceholderAlignment.bottom
|
||||
: PlaceholderAlignment.middle,
|
||||
? PlaceholderAlignment.bottom
|
||||
: PlaceholderAlignment.middle,
|
||||
child: Column(
|
||||
children: [
|
||||
if (token != null && overlayController != null)
|
||||
|
|
@ -507,10 +505,10 @@ class HtmlMessage extends StatelessWidget {
|
|||
CompositedTransformTarget(
|
||||
link: token != null
|
||||
? MatrixState.pAnyState
|
||||
.layerLinkAndKey(
|
||||
"message-token-${token.text.uniqueKey}-${event.eventId}",
|
||||
)
|
||||
.link
|
||||
.layerLinkAndKey(
|
||||
"message-token-${token.text.uniqueKey}-${event.eventId}",
|
||||
)
|
||||
.link
|
||||
: LayerLink(),
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
|
|
@ -532,14 +530,15 @@ class HtmlMessage extends StatelessWidget {
|
|||
pangeaMessageEvent?.textDirection,
|
||||
underlineColor:
|
||||
TokenRenderingUtil.underlineColor(
|
||||
underlineColor,
|
||||
selected: selected,
|
||||
highlighted: highlighted,
|
||||
isNew: isNew,
|
||||
practiceMode: readingAssistanceMode ==
|
||||
ReadingAssistanceMode.practiceMode,
|
||||
hovered: hovered,
|
||||
),
|
||||
underlineColor,
|
||||
selected: selected,
|
||||
highlighted: highlighted,
|
||||
isNew: isNew,
|
||||
practiceMode:
|
||||
readingAssistanceMode ==
|
||||
ReadingAssistanceMode.practiceMode,
|
||||
hovered: hovered,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -559,8 +558,10 @@ class HtmlMessage extends StatelessWidget {
|
|||
),
|
||||
curve: Curves.easeOut,
|
||||
child: SizedBox(
|
||||
height: overlayController!
|
||||
.practiceController.practiceMode !=
|
||||
height:
|
||||
overlayController!
|
||||
.practiceController
|
||||
.practiceMode !=
|
||||
MessagePracticeMode.noneSelected
|
||||
? 4.0
|
||||
: 0.0,
|
||||
|
|
@ -589,7 +590,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
final user = room.unsafeGetUserFromMemoryOrFallback(matrixId);
|
||||
return WidgetSpan(
|
||||
// #Pangea
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
alignment: .middle,
|
||||
// Pangea#
|
||||
child: MatrixPill(
|
||||
key: Key('user_pill_$matrixId'),
|
||||
|
|
@ -611,7 +612,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
: this.room.client.getRoomByAlias(matrixId);
|
||||
return WidgetSpan(
|
||||
// #Pangea
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
alignment: .middle,
|
||||
// Pangea#
|
||||
child: MatrixPill(
|
||||
name: room?.getLocalizedDisplayname() ?? matrixId,
|
||||
|
|
@ -642,9 +643,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
node.nodes,
|
||||
context,
|
||||
// #Pangea
|
||||
textStyle.merge(
|
||||
linkStyle.copyWith(height: 1.25),
|
||||
),
|
||||
textStyle.merge(linkStyle.copyWith(height: 1.25)),
|
||||
// Pangea#
|
||||
depth: depth,
|
||||
),
|
||||
|
|
@ -664,9 +663,9 @@ class HtmlMessage extends StatelessWidget {
|
|||
final isCheckbox = node.className == 'task-list-item';
|
||||
final checkboxIndex = isCheckbox
|
||||
? node.rootElement
|
||||
.getElementsByClassName('task-list-item')
|
||||
.indexOf(node) +
|
||||
1
|
||||
.getElementsByClassName('task-list-item')
|
||||
.indexOf(node) +
|
||||
1
|
||||
: null;
|
||||
final checkedByReaction = !isCheckbox
|
||||
? null
|
||||
|
|
@ -689,17 +688,13 @@ class HtmlMessage extends StatelessWidget {
|
|||
if (node.parent?.localName == 'ul')
|
||||
// #Pangea
|
||||
// const TextSpan(text: '• '),
|
||||
TextSpan(
|
||||
text: '• ',
|
||||
style: existingStyle,
|
||||
),
|
||||
TextSpan(text: '• ', style: existingStyle),
|
||||
// Pangea#
|
||||
if (node.parent?.localName == 'ol')
|
||||
TextSpan(
|
||||
text:
|
||||
'${(node.parent?.nodes.whereType<dom.Element>().toList().indexOf(node) ?? 0) + (int.tryParse(node.parent?.attributes['start'] ?? '1') ?? 1)}. ',
|
||||
// #Pangea
|
||||
// style: textStyle,
|
||||
style: existingStyle,
|
||||
// Pangea#
|
||||
),
|
||||
|
|
@ -715,7 +710,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
activeColor: textColor.withAlpha(64),
|
||||
value:
|
||||
staticallyChecked || checkedByReaction != null,
|
||||
onChanged: eventId == null ||
|
||||
onChanged:
|
||||
eventId == null ||
|
||||
checkboxIndex == null ||
|
||||
staticallyChecked ||
|
||||
!room.canSendDefaultMessages ||
|
||||
|
|
@ -724,28 +720,29 @@ class HtmlMessage extends StatelessWidget {
|
|||
room.client.userID)
|
||||
? null
|
||||
: (_) => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => checkedByReaction != null
|
||||
? room.redactEvent(
|
||||
checkedByReaction.eventId,
|
||||
)
|
||||
: room.checkCheckbox(
|
||||
eventId,
|
||||
checkboxIndex,
|
||||
),
|
||||
),
|
||||
context: context,
|
||||
future: () => checkedByReaction != null
|
||||
? room.redactEvent(
|
||||
checkedByReaction.eventId,
|
||||
)
|
||||
: room.checkCheckbox(
|
||||
eventId,
|
||||
checkboxIndex,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// ..._renderWithLineBreaks(node.nodes, context, depth: depth),
|
||||
..._renderWithLineBreaks(
|
||||
node.nodes,
|
||||
context,
|
||||
// #Pangea
|
||||
textStyle,
|
||||
// Pangea#
|
||||
depth: depth,
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
style: TextStyle(fontSize: fontSize, color: textColor),
|
||||
),
|
||||
|
|
@ -757,12 +754,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: textColor,
|
||||
width: 5,
|
||||
),
|
||||
),
|
||||
border: Border(left: BorderSide(color: textColor, width: 5)),
|
||||
),
|
||||
child: Text.rich(
|
||||
// #Pangea
|
||||
|
|
@ -788,7 +780,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
);
|
||||
case 'code':
|
||||
final isInline = node.parent?.localName != 'pre';
|
||||
final lang = node.className
|
||||
final lang =
|
||||
node.className
|
||||
.split(' ')
|
||||
.singleWhereOrNull(
|
||||
(className) => className.startsWith('language-'),
|
||||
|
|
@ -796,8 +789,9 @@ class HtmlMessage extends StatelessWidget {
|
|||
?.split('language-')
|
||||
.last ??
|
||||
'md';
|
||||
final highlightedHtml =
|
||||
highlight.parse(node.text, language: lang).toHtml();
|
||||
final highlightedHtml = highlight
|
||||
.parse(node.text, language: lang)
|
||||
.toHtml();
|
||||
final element = parser.parse(highlightedHtml).body;
|
||||
if (element == null) {
|
||||
return const TextSpan(text: 'Unable to render code block!');
|
||||
|
|
@ -813,14 +807,9 @@ class HtmlMessage extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: isInline
|
||||
? const EdgeInsets.symmetric(horizontal: 4.0)
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 4.0,
|
||||
),
|
||||
: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [_renderCodeBlockNode(element)],
|
||||
),
|
||||
TextSpan(children: [_renderCodeBlockNode(element)]),
|
||||
selectionColor: hightlightTextColor.withAlpha(128),
|
||||
),
|
||||
),
|
||||
|
|
@ -888,10 +877,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
node,
|
||||
context,
|
||||
textStyle.merge(
|
||||
TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: textColor,
|
||||
),
|
||||
TextStyle(fontSize: fontSize, color: textColor),
|
||||
),
|
||||
depth: depth,
|
||||
),
|
||||
|
|
@ -908,10 +894,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: textColor,
|
||||
),
|
||||
style: TextStyle(fontSize: fontSize, color: textColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -957,16 +940,13 @@ class HtmlMessage extends StatelessWidget {
|
|||
default:
|
||||
// #Pangea
|
||||
final style = switch (node.localName) {
|
||||
'body' => TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: textColor,
|
||||
),
|
||||
'body' => TextStyle(fontSize: fontSize, color: textColor),
|
||||
'a' => linkStyle,
|
||||
'strong' => const TextStyle(fontWeight: FontWeight.bold),
|
||||
'em' || 'i' => const TextStyle(fontStyle: FontStyle.italic),
|
||||
'del' ||
|
||||
'strikethrough' =>
|
||||
const TextStyle(decoration: TextDecoration.lineThrough),
|
||||
'del' || 'strikethrough' => const TextStyle(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
'u' => const TextStyle(decoration: TextDecoration.underline),
|
||||
'h1' => TextStyle(fontSize: fontSize * 1.6, height: 2),
|
||||
'h2' => TextStyle(fontSize: fontSize * 1.5, height: 2),
|
||||
|
|
@ -975,11 +955,12 @@ class HtmlMessage extends StatelessWidget {
|
|||
'h5' => TextStyle(fontSize: fontSize * 1.2, height: 1.75),
|
||||
'h6' => TextStyle(fontSize: fontSize * 1.1, height: 1.5),
|
||||
'span' => TextStyle(
|
||||
color: node.attributes['color']?.hexToColor ??
|
||||
node.attributes['data-mx-color']?.hexToColor ??
|
||||
textColor,
|
||||
backgroundColor: node.attributes['data-mx-bg-color']?.hexToColor,
|
||||
),
|
||||
color:
|
||||
node.attributes['color']?.hexToColor ??
|
||||
node.attributes['data-mx-color']?.hexToColor ??
|
||||
textColor,
|
||||
backgroundColor: node.attributes['data-mx-bg-color']?.hexToColor,
|
||||
),
|
||||
'sup' => const TextStyle(fontFeatures: [FontFeature.superscripts()]),
|
||||
'sub' => const TextStyle(fontFeatures: [FontFeature.subscripts()]),
|
||||
_ => null,
|
||||
|
|
@ -1020,9 +1001,9 @@ class HtmlMessage extends StatelessWidget {
|
|||
child: SizedBox(
|
||||
height:
|
||||
overlayController!.practiceController.practiceMode !=
|
||||
MessagePracticeMode.noneSelected
|
||||
? 4.0
|
||||
: 0.0,
|
||||
MessagePracticeMode.noneSelected
|
||||
? 4.0
|
||||
: 0.0,
|
||||
width: 0,
|
||||
),
|
||||
),
|
||||
|
|
@ -1032,17 +1013,13 @@ class HtmlMessage extends StatelessWidget {
|
|||
);
|
||||
// return TextSpan(
|
||||
// style: switch (node.localName) {
|
||||
// 'body' => TextStyle(
|
||||
// fontSize: fontSize,
|
||||
// color: textColor,
|
||||
// ),
|
||||
// 'body' => TextStyle(fontSize: fontSize, color: textColor),
|
||||
// 'a' => linkStyle,
|
||||
// 'strong' => const TextStyle(fontWeight: FontWeight.bold),
|
||||
// 'em' || 'i' => const TextStyle(fontStyle: FontStyle.italic),
|
||||
// 'del' ||
|
||||
// 's' ||
|
||||
// 'strikethrough' =>
|
||||
// const TextStyle(decoration: TextDecoration.lineThrough),
|
||||
// 'del' || 's' || 'strikethrough' => const TextStyle(
|
||||
// decoration: TextDecoration.lineThrough,
|
||||
// ),
|
||||
// 'u' => const TextStyle(decoration: TextDecoration.underline),
|
||||
// 'h1' => TextStyle(fontSize: fontSize * 1.6, height: 2),
|
||||
// 'h2' => TextStyle(fontSize: fontSize * 1.5, height: 2),
|
||||
|
|
@ -1051,25 +1028,19 @@ class HtmlMessage extends StatelessWidget {
|
|||
// 'h5' => TextStyle(fontSize: fontSize * 1.2, height: 1.75),
|
||||
// 'h6' => TextStyle(fontSize: fontSize * 1.1, height: 1.5),
|
||||
// 'span' => TextStyle(
|
||||
// color: node.attributes['color']?.hexToColor ??
|
||||
// node.attributes['data-mx-color']?.hexToColor ??
|
||||
// textColor,
|
||||
// backgroundColor:
|
||||
// node.attributes['data-mx-bg-color']?.hexToColor,
|
||||
// ),
|
||||
// color:
|
||||
// node.attributes['color']?.hexToColor ??
|
||||
// node.attributes['data-mx-color']?.hexToColor ??
|
||||
// textColor,
|
||||
// backgroundColor: node.attributes['data-mx-bg-color']?.hexToColor,
|
||||
// ),
|
||||
// 'sup' => const TextStyle(
|
||||
// fontFeatures: [FontFeature.superscripts()],
|
||||
// ),
|
||||
// 'sub' => const TextStyle(
|
||||
// fontFeatures: [FontFeature.subscripts()],
|
||||
// ),
|
||||
// fontFeatures: [FontFeature.superscripts()],
|
||||
// ),
|
||||
// 'sub' => const TextStyle(fontFeatures: [FontFeature.subscripts()]),
|
||||
// _ => null,
|
||||
// },
|
||||
// children: _renderWithLineBreaks(
|
||||
// node.nodes,
|
||||
// context,
|
||||
// depth: depth,
|
||||
// ),
|
||||
// children: _renderWithLineBreaks(node.nodes, context, depth: depth),
|
||||
// );
|
||||
// Pangea#
|
||||
}
|
||||
|
|
@ -1081,12 +1052,10 @@ class HtmlMessage extends StatelessWidget {
|
|||
// final element = parser.parse(html).body ?? dom.Element.html('');
|
||||
// return Text.rich(
|
||||
// _renderHtml(element, context),
|
||||
// style: TextStyle(
|
||||
// fontSize: fontSize,
|
||||
// color: textColor,
|
||||
// ),
|
||||
// maxLines: limitHeight ? 64 : null,Add commentMore actions
|
||||
// style: TextStyle(fontSize: fontSize, color: textColor),
|
||||
// maxLines: limitHeight ? 64 : null,
|
||||
// overflow: TextOverflow.fade,
|
||||
// selectionColor: textColor.withAlpha(128)
|
||||
// );
|
||||
final parsed = parser.parse(_addTokenTags()).body ?? dom.Element.html('');
|
||||
return GestureDetector(
|
||||
|
|
@ -1105,15 +1074,9 @@ class HtmlMessage extends StatelessWidget {
|
|||
_renderHtml(
|
||||
parsed,
|
||||
context,
|
||||
TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: textColor,
|
||||
TextStyle(fontSize: fontSize, color: textColor),
|
||||
),
|
||||
style: TextStyle(fontSize: fontSize, color: textColor),
|
||||
maxLines: limitHeight ? 64 : null,
|
||||
overflow: TextOverflow.fade,
|
||||
selectionColor: textColor.withAlpha(128),
|
||||
|
|
@ -1180,13 +1143,9 @@ class MatrixPill extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// Avatar(
|
||||
// mxContent: avatar,
|
||||
// name: name,
|
||||
// size: 16,
|
||||
// ),
|
||||
// Avatar(mxContent: avatar, name: name, size: 16),
|
||||
// const SizedBox(width: 6),
|
||||
// Text(
|
||||
// name,
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ class ImageBubble extends StatelessWidget {
|
|||
Widget _buildPlaceholder(BuildContext context) {
|
||||
final String blurHashString =
|
||||
event.infoMap['xyz.amorgan.blurhash'] is String
|
||||
? event.infoMap['xyz.amorgan.blurhash']
|
||||
: 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';
|
||||
? event.infoMap['xyz.amorgan.blurhash']
|
||||
: 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';
|
||||
return SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
|
|
@ -69,11 +69,8 @@ class ImageBubble extends StatelessWidget {
|
|||
if (!tapToView) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => ImageViewer(
|
||||
event,
|
||||
timeline: timeline,
|
||||
outerContext: context,
|
||||
),
|
||||
builder: (_) =>
|
||||
ImageViewer(event, timeline: timeline, outerContext: context),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +92,7 @@ class ImageBubble extends StatelessWidget {
|
|||
}
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Material(
|
||||
|
|
@ -115,7 +112,8 @@ class ImageBubble extends StatelessWidget {
|
|||
child: Hero(
|
||||
tag: event.eventId,
|
||||
// #Pangea
|
||||
child: event.content['url'] is String &&
|
||||
child:
|
||||
event.content['url'] is String &&
|
||||
!(event.content['url'] as String).startsWith('mxc')
|
||||
? CachedNetworkImage(
|
||||
imageUrl: event.content['url'] as String,
|
||||
|
|
@ -154,22 +152,21 @@ class ImageBubble extends StatelessWidget {
|
|||
SizedBox(
|
||||
width: width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ class Message extends StatelessWidget {
|
|||
event.room.activityPlan != null) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: controller.activityController.showInstructions,
|
||||
builder: (context, show, __) {
|
||||
builder: (context, show, _) {
|
||||
return ActivitySummary(
|
||||
inChat: true,
|
||||
activity: event.room.activityPlan!,
|
||||
|
|
@ -177,7 +177,6 @@ class Message extends StatelessWidget {
|
|||
return ActivityRolesEvent(event: event);
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
return StateMessage(event, onExpand: onExpand, isCollapsed: isCollapsed);
|
||||
}
|
||||
|
||||
|
|
@ -191,10 +190,12 @@ class Message extends StatelessWidget {
|
|||
final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft;
|
||||
|
||||
var color = theme.colorScheme.surfaceContainerHigh;
|
||||
final displayTime = event.type == EventTypes.RoomCreate ||
|
||||
final displayTime =
|
||||
event.type == EventTypes.RoomCreate ||
|
||||
nextEvent == null ||
|
||||
!event.originServerTs.sameEnvironment(nextEvent!.originServerTs);
|
||||
final nextEventSameSender = nextEvent != null &&
|
||||
final nextEventSameSender =
|
||||
nextEvent != null &&
|
||||
{
|
||||
EventTypes.Message,
|
||||
EventTypes.Sticker,
|
||||
|
|
@ -203,7 +204,8 @@ class Message extends StatelessWidget {
|
|||
nextEvent!.senderId == event.senderId &&
|
||||
!displayTime;
|
||||
|
||||
final previousEventSameSender = previousEvent != null &&
|
||||
final previousEventSameSender =
|
||||
previousEvent != null &&
|
||||
{
|
||||
EventTypes.Message,
|
||||
EventTypes.Sticker,
|
||||
|
|
@ -213,26 +215,28 @@ class Message extends StatelessWidget {
|
|||
previousEvent!.originServerTs.sameEnvironment(event.originServerTs);
|
||||
|
||||
// #Pangea
|
||||
// final textColor =
|
||||
// ownMessage ? theme.onBubbleColor : theme.colorScheme.onSurface;
|
||||
// final textColor = ownMessage
|
||||
// ? theme.onBubbleColor
|
||||
// : theme.colorScheme.onSurface;
|
||||
final textColor = ownMessage
|
||||
? ThemeData.dark().colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface;
|
||||
|
||||
// final linkColor = ownMessage
|
||||
// ? theme.brightness == Brightness.light
|
||||
// ? theme.colorScheme.primaryFixed
|
||||
// : theme.colorScheme.onTertiaryContainer
|
||||
// ? theme.colorScheme.primaryFixed
|
||||
// : theme.colorScheme.onTertiaryContainer
|
||||
// : theme.colorScheme.primary;
|
||||
final linkColor = theme.brightness == Brightness.light
|
||||
? theme.colorScheme.primary
|
||||
: ownMessage
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface;
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface;
|
||||
// Pangea#
|
||||
|
||||
final rowMainAxisAlignment =
|
||||
ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start;
|
||||
final rowMainAxisAlignment = ownMessage
|
||||
? MainAxisAlignment.end
|
||||
: MainAxisAlignment.start;
|
||||
|
||||
final displayEvent = event.getDisplayEvent(timeline);
|
||||
const hardCorner = Radius.circular(4);
|
||||
|
|
@ -240,12 +244,15 @@ class Message extends StatelessWidget {
|
|||
final borderRadius = BorderRadius.only(
|
||||
topLeft: !ownMessage && nextEventSameSender ? hardCorner : roundedCorner,
|
||||
topRight: ownMessage && nextEventSameSender ? hardCorner : roundedCorner,
|
||||
bottomLeft:
|
||||
!ownMessage && previousEventSameSender ? hardCorner : roundedCorner,
|
||||
bottomRight:
|
||||
ownMessage && previousEventSameSender ? hardCorner : roundedCorner,
|
||||
bottomLeft: !ownMessage && previousEventSameSender
|
||||
? hardCorner
|
||||
: roundedCorner,
|
||||
bottomRight: ownMessage && previousEventSameSender
|
||||
? hardCorner
|
||||
: roundedCorner,
|
||||
);
|
||||
final noBubble = ({
|
||||
final noBubble =
|
||||
({
|
||||
MessageTypes.Video,
|
||||
MessageTypes.Image,
|
||||
MessageTypes.Sticker,
|
||||
|
|
@ -260,8 +267,9 @@ class Message extends StatelessWidget {
|
|||
|
||||
if (ownMessage) {
|
||||
// #Pangea
|
||||
// color =
|
||||
// displayEvent.status.isError ? Colors.redAccent : theme.bubbleColor;
|
||||
// color = displayEvent.status.isError
|
||||
// ? Colors.redAccent
|
||||
// : theme.bubbleColor;
|
||||
color = displayEvent.status.isError
|
||||
? Colors.redAccent
|
||||
: Color.alphaBlend(
|
||||
|
|
@ -278,10 +286,7 @@ class Message extends StatelessWidget {
|
|||
if (singleSelected) {
|
||||
sentReactions.addAll(
|
||||
event
|
||||
.aggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.reaction,
|
||||
)
|
||||
.aggregatedEvents(timeline, RelationshipTypes.reaction)
|
||||
.where(
|
||||
(event) =>
|
||||
event.senderId == event.room.client.userID &&
|
||||
|
|
@ -296,11 +301,15 @@ class Message extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
final showReceiptsRow =
|
||||
event.hasAggregatedEvents(timeline, RelationshipTypes.reaction);
|
||||
final showReceiptsRow = event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.reaction,
|
||||
);
|
||||
|
||||
final threadChildren =
|
||||
event.aggregatedEvents(timeline, RelationshipTypes.thread);
|
||||
final threadChildren = event.aggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.thread,
|
||||
);
|
||||
|
||||
// #Pangea
|
||||
// final showReactionPicker =
|
||||
|
|
@ -314,9 +323,7 @@ class Message extends StatelessWidget {
|
|||
key: ValueKey(event.eventId),
|
||||
background: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Center(
|
||||
child: Icon(Icons.check_outlined),
|
||||
),
|
||||
child: Center(child: Icon(Icons.check_outlined)),
|
||||
),
|
||||
direction: AppSettings.swipeRightToLeftToReply.value
|
||||
? SwipeDirection.endToStart
|
||||
|
|
@ -333,9 +340,8 @@ class Message extends StatelessWidget {
|
|||
bottom: previousEventSameSender ? 1.0 : 4.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
ownMessage ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: ownMessage ? .end : .start,
|
||||
children: <Widget>[
|
||||
// #Pangea
|
||||
// if (displayTime || selected)
|
||||
|
|
@ -349,8 +355,9 @@ class Message extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius * 2),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius * 2,
|
||||
),
|
||||
color: theme.colorScheme.surface.withAlpha(128),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
|
@ -417,13 +424,13 @@ class Message extends StatelessWidget {
|
|||
),
|
||||
color: selected || highlightMarker
|
||||
? theme.colorScheme.secondaryContainer
|
||||
.withAlpha(128)
|
||||
.withAlpha(128)
|
||||
: Colors.transparent,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisAlignment: rowMainAxisAlignment,
|
||||
children: [
|
||||
// #Pangea
|
||||
|
|
@ -451,18 +458,17 @@ class Message extends StatelessWidget {
|
|||
child: SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: event.status ==
|
||||
EventStatus.error
|
||||
child:
|
||||
event.status == EventStatus.error
|
||||
? const Icon(
|
||||
Icons.error,
|
||||
color: Colors.red,
|
||||
)
|
||||
: event.fileSendingStatus != null
|
||||
? const CircularProgressIndicator
|
||||
.adaptive(
|
||||
strokeWidth: 1,
|
||||
)
|
||||
: null,
|
||||
? const CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 1,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -470,20 +476,21 @@ class Message extends StatelessWidget {
|
|||
FutureBuilder<User?>(
|
||||
future: event.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final user = snapshot.data ??
|
||||
final user =
|
||||
snapshot.data ??
|
||||
event.senderFromMemoryOrFallback;
|
||||
return Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
onTap: () =>
|
||||
showMemberActionsPopupMenu(
|
||||
context: context,
|
||||
user: user,
|
||||
onMention: onMention,
|
||||
// #Pangea
|
||||
room: controller.room,
|
||||
// Pangea#
|
||||
),
|
||||
context: context,
|
||||
user: user,
|
||||
onMention: onMention,
|
||||
// #Pangea
|
||||
room: controller.room,
|
||||
// Pangea#
|
||||
),
|
||||
presenceUserId: user.stateKey,
|
||||
presenceBackgroundColor: wallpaperMode
|
||||
? Colors.transparent
|
||||
|
|
@ -491,23 +498,22 @@ class Message extends StatelessWidget {
|
|||
// #Pangea
|
||||
miniIcon:
|
||||
user.id == BotName.byEnvironment
|
||||
? BotSettingsLanguageIcon(
|
||||
user: user,
|
||||
)
|
||||
: null,
|
||||
? BotSettingsLanguageIcon(
|
||||
user: user,
|
||||
)
|
||||
: null,
|
||||
presenceOffset:
|
||||
user.id == BotName.byEnvironment
|
||||
? const Offset(0, 0)
|
||||
: null,
|
||||
? const Offset(0, 0)
|
||||
: null,
|
||||
// Pangea#
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
if (!nextEventSameSender)
|
||||
Padding(
|
||||
|
|
@ -515,16 +521,16 @@ class Message extends StatelessWidget {
|
|||
left: 8.0,
|
||||
bottom: 4,
|
||||
),
|
||||
child: ownMessage ||
|
||||
child:
|
||||
ownMessage ||
|
||||
event.room.isDirectChat
|
||||
? const SizedBox(height: 12)
|
||||
: FutureBuilder<User?>(
|
||||
future:
|
||||
event.fetchSenderUser(),
|
||||
builder:
|
||||
(context, snapshot) {
|
||||
final displayname = snapshot
|
||||
.data
|
||||
future: event
|
||||
.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final displayname =
|
||||
snapshot.data
|
||||
?.calcDisplayname() ??
|
||||
event
|
||||
.senderFromMemoryOrFallback
|
||||
|
|
@ -534,38 +540,39 @@ class Message extends StatelessWidget {
|
|||
// displayname,
|
||||
controller.room
|
||||
.senderDisplayName(
|
||||
snapshot.data ??
|
||||
event
|
||||
.senderFromMemoryOrFallback,
|
||||
),
|
||||
snapshot.data ??
|
||||
event
|
||||
.senderFromMemoryOrFallback,
|
||||
),
|
||||
// Pangea#
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
color: (theme.brightness ==
|
||||
color:
|
||||
(theme.brightness ==
|
||||
Brightness
|
||||
.light
|
||||
? displayname
|
||||
.color
|
||||
.color
|
||||
: displayname
|
||||
.lightColorText),
|
||||
.lightColorText),
|
||||
shadows:
|
||||
!wallpaperMode
|
||||
? null
|
||||
: [
|
||||
const Shadow(
|
||||
offset:
|
||||
Offset(
|
||||
? null
|
||||
: [
|
||||
const Shadow(
|
||||
offset:
|
||||
Offset(
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
blurRadius:
|
||||
3,
|
||||
color: Colors
|
||||
.black,
|
||||
),
|
||||
],
|
||||
blurRadius:
|
||||
3,
|
||||
color: Colors
|
||||
.black,
|
||||
),
|
||||
],
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow
|
||||
|
|
@ -576,8 +583,9 @@ class Message extends StatelessWidget {
|
|||
),
|
||||
Container(
|
||||
alignment: alignment,
|
||||
padding:
|
||||
const EdgeInsets.only(left: 8),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8,
|
||||
),
|
||||
child: GestureDetector(
|
||||
// #Pangea
|
||||
onTap: () =>
|
||||
|
|
@ -595,49 +603,50 @@ class Message extends StatelessWidget {
|
|||
opacity: animateIn
|
||||
? 0
|
||||
: event.messageType ==
|
||||
MessageTypes
|
||||
.BadEncrypted ||
|
||||
event.status.isSending
|
||||
? 0.5
|
||||
: 1,
|
||||
MessageTypes
|
||||
.BadEncrypted ||
|
||||
event.status.isSending
|
||||
? 0.5
|
||||
: 1,
|
||||
duration: FluffyThemes
|
||||
.animationDuration,
|
||||
curve:
|
||||
FluffyThemes.animationCurve,
|
||||
// #Pangea
|
||||
child:
|
||||
SelectionContainer.disabled(
|
||||
child: SelectionContainer.disabled(
|
||||
child: MouseRegion(
|
||||
cursor:
|
||||
SystemMouseCursors.click,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: controller
|
||||
.depressMessageButton,
|
||||
builder: (
|
||||
context,
|
||||
depressed,
|
||||
child,
|
||||
) =>
|
||||
PressableButton(
|
||||
buttonHeight: 5,
|
||||
depressed: !isButton ||
|
||||
builder:
|
||||
(
|
||||
context,
|
||||
depressed,
|
||||
borderRadius:
|
||||
borderRadius,
|
||||
onPressed: () {
|
||||
showToolbar(
|
||||
pangeaMessageEvent,
|
||||
);
|
||||
},
|
||||
color: color,
|
||||
visible:
|
||||
isButton && !noBubble,
|
||||
builder:
|
||||
(context, _, __) =>
|
||||
child!,
|
||||
),
|
||||
// Pangea#
|
||||
child,
|
||||
) => PressableButton(
|
||||
buttonHeight: 5,
|
||||
depressed:
|
||||
!isButton ||
|
||||
depressed,
|
||||
borderRadius:
|
||||
borderRadius,
|
||||
onPressed: () {
|
||||
showToolbar(
|
||||
pangeaMessageEvent,
|
||||
);
|
||||
},
|
||||
color: color,
|
||||
visible:
|
||||
isButton &&
|
||||
!noBubble,
|
||||
builder:
|
||||
(context, _, _) =>
|
||||
child!,
|
||||
),
|
||||
|
||||
// Pangea#
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: noBubble
|
||||
|
|
@ -649,8 +658,7 @@ class Message extends StatelessWidget {
|
|||
clipBehavior:
|
||||
Clip.antiAlias,
|
||||
// #Pangea
|
||||
child:
|
||||
CompositedTransformTarget(
|
||||
child: CompositedTransformTarget(
|
||||
link: MatrixState
|
||||
.pAnyState
|
||||
.layerLinkAndKey(
|
||||
|
|
@ -680,98 +688,87 @@ class Message extends StatelessWidget {
|
|||
)
|
||||
.key,
|
||||
// Pangea#
|
||||
decoration:
|
||||
BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius
|
||||
.circular(
|
||||
AppConfig
|
||||
.borderRadius,
|
||||
),
|
||||
BorderRadius.circular(
|
||||
AppConfig
|
||||
.borderRadius,
|
||||
),
|
||||
),
|
||||
constraints:
|
||||
const BoxConstraints(
|
||||
maxWidth: FluffyThemes
|
||||
.columnWidth *
|
||||
1.5,
|
||||
),
|
||||
maxWidth:
|
||||
FluffyThemes
|
||||
.columnWidth *
|
||||
1.5,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize:
|
||||
MainAxisSize
|
||||
.min,
|
||||
.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment
|
||||
.start,
|
||||
children: <Widget>[
|
||||
if (event
|
||||
.inReplyToEventId(
|
||||
if (event.inReplyToEventId(
|
||||
includingFallback:
|
||||
false,
|
||||
) !=
|
||||
null)
|
||||
FutureBuilder<
|
||||
Event?>(
|
||||
Event?
|
||||
>(
|
||||
future: event
|
||||
.getReplyEvent(
|
||||
timeline,
|
||||
),
|
||||
builder: (
|
||||
BuildContext
|
||||
context,
|
||||
snapshot,
|
||||
) {
|
||||
final replyEvent = snapshot
|
||||
.hasData
|
||||
? snapshot
|
||||
.data!
|
||||
: Event(
|
||||
eventId: event.inReplyToEventId() ?? '\$fake_event_id',
|
||||
content: {
|
||||
'msgtype': 'm.text',
|
||||
'body': '...',
|
||||
},
|
||||
senderId: event.senderId,
|
||||
type: 'm.room.message',
|
||||
room: event.room,
|
||||
status: EventStatus.sent,
|
||||
originServerTs: DateTime.now(),
|
||||
);
|
||||
return Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
left:
|
||||
16,
|
||||
right:
|
||||
16,
|
||||
top:
|
||||
8,
|
||||
timeline,
|
||||
),
|
||||
child:
|
||||
Material(
|
||||
color:
|
||||
Colors.transparent,
|
||||
borderRadius:
|
||||
ReplyContent.borderRadius,
|
||||
child:
|
||||
InkWell(
|
||||
borderRadius:
|
||||
ReplyContent.borderRadius,
|
||||
onTap: () =>
|
||||
scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
builder:
|
||||
(
|
||||
BuildContext
|
||||
context,
|
||||
snapshot,
|
||||
) {
|
||||
final replyEvent =
|
||||
snapshot.hasData
|
||||
? snapshot.data!
|
||||
: Event(
|
||||
eventId:
|
||||
event.inReplyToEventId() ??
|
||||
'\$fake_event_id',
|
||||
content: {
|
||||
'msgtype': 'm.text',
|
||||
'body': '...',
|
||||
},
|
||||
senderId: event.senderId,
|
||||
type: 'm.room.message',
|
||||
room: event.room,
|
||||
status: EventStatus.sent,
|
||||
originServerTs: DateTime.now(),
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
),
|
||||
child:
|
||||
AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage: ownMessage,
|
||||
timeline: timeline,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: ReplyContent.borderRadius,
|
||||
child: InkWell(
|
||||
borderRadius: ReplyContent.borderRadius,
|
||||
onTap: () => scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
),
|
||||
child: AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage: ownMessage,
|
||||
timeline: timeline,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
MessageContent(
|
||||
displayEvent,
|
||||
|
|
@ -800,14 +797,12 @@ class Message extends StatelessWidget {
|
|||
),
|
||||
if (event
|
||||
.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes
|
||||
.edit,
|
||||
))
|
||||
timeline,
|
||||
RelationshipTypes
|
||||
.edit,
|
||||
))
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets
|
||||
.only(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom:
|
||||
8.0,
|
||||
left:
|
||||
|
|
@ -825,23 +820,18 @@ class Message extends StatelessWidget {
|
|||
Icon(
|
||||
Icons
|
||||
.edit_outlined,
|
||||
color:
|
||||
textColor.withAlpha(
|
||||
color: textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
size:
|
||||
14,
|
||||
),
|
||||
Text(
|
||||
displayEvent
|
||||
.originServerTs
|
||||
.localizedTimeShort(
|
||||
displayEvent.originServerTs.localizedTimeShort(
|
||||
context,
|
||||
),
|
||||
style:
|
||||
TextStyle(
|
||||
color:
|
||||
textColor.withAlpha(
|
||||
style: TextStyle(
|
||||
color: textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
fontSize:
|
||||
|
|
@ -876,46 +866,43 @@ class Message extends StatelessWidget {
|
|||
// ? Padding(
|
||||
// padding:
|
||||
// const EdgeInsets.all(
|
||||
// 4.0,
|
||||
// ),
|
||||
// 4.0,
|
||||
// ),
|
||||
// child: Material(
|
||||
// elevation: 4,
|
||||
// borderRadius:
|
||||
// BorderRadius.circular(
|
||||
// AppConfig.borderRadius,
|
||||
// ),
|
||||
// AppConfig
|
||||
// .borderRadius,
|
||||
// ),
|
||||
// shadowColor: theme
|
||||
// .colorScheme.surface
|
||||
// .colorScheme
|
||||
// .surface
|
||||
// .withAlpha(128),
|
||||
// child:
|
||||
// SingleChildScrollView(
|
||||
// child: SingleChildScrollView(
|
||||
// scrollDirection:
|
||||
// Axis.horizontal,
|
||||
// child: Row(
|
||||
// mainAxisSize:
|
||||
// MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// ...AppConfig
|
||||
// .defaultReactions
|
||||
// .map(
|
||||
// (emoji) =>
|
||||
// IconButton(
|
||||
// ...AppConfig.defaultReactions.map(
|
||||
// (
|
||||
// emoji,
|
||||
// ) => IconButton(
|
||||
// padding:
|
||||
// EdgeInsets
|
||||
// .zero,
|
||||
// icon: Center(
|
||||
// child:
|
||||
// Opacity(
|
||||
// opacity: sentReactions
|
||||
// .contains(
|
||||
// emoji,
|
||||
// )
|
||||
// child: Opacity(
|
||||
// opacity:
|
||||
// sentReactions.contains(
|
||||
// emoji,
|
||||
// )
|
||||
// ? 0.33
|
||||
// : 1,
|
||||
// child: Text(
|
||||
// emoji,
|
||||
// style:
|
||||
// const TextStyle(
|
||||
// style: const TextStyle(
|
||||
// fontSize:
|
||||
// 20,
|
||||
// ),
|
||||
|
|
@ -927,19 +914,20 @@ class Message extends StatelessWidget {
|
|||
// ),
|
||||
// onPressed:
|
||||
// sentReactions
|
||||
// .contains(
|
||||
// emoji,
|
||||
// )
|
||||
// ? null
|
||||
// : () {
|
||||
// onSelect(
|
||||
// event,
|
||||
// );
|
||||
// event.room.sendReaction(
|
||||
// event.eventId,
|
||||
// emoji,
|
||||
// );
|
||||
// },
|
||||
// .contains(
|
||||
// emoji,
|
||||
// )
|
||||
// ? null
|
||||
// : () {
|
||||
// onSelect(
|
||||
// event,
|
||||
// );
|
||||
// event.room.sendReaction(
|
||||
// event
|
||||
// .eventId,
|
||||
// emoji,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// IconButton(
|
||||
|
|
@ -950,72 +938,55 @@ class Message extends StatelessWidget {
|
|||
// tooltip: L10n.of(
|
||||
// context,
|
||||
// ).customReaction,
|
||||
// onPressed:
|
||||
// () async {
|
||||
// final emoji =
|
||||
// await showAdaptiveBottomSheet<
|
||||
// String>(
|
||||
// onPressed: () async {
|
||||
// final emoji = await showAdaptiveBottomSheet<String>(
|
||||
// context:
|
||||
// context,
|
||||
// builder:
|
||||
// (context) =>
|
||||
// Scaffold(
|
||||
// appBar:
|
||||
// AppBar(
|
||||
// title:
|
||||
// Text(
|
||||
// L10n.of(context)
|
||||
// .customReaction,
|
||||
// ),
|
||||
// leading:
|
||||
// CloseButton(
|
||||
// onPressed:
|
||||
// () =>
|
||||
// Navigator.of(
|
||||
// builder: (context) => Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: Text(
|
||||
// L10n.of(
|
||||
// context,
|
||||
// ).pop(
|
||||
// null,
|
||||
// ),
|
||||
// ).customReaction,
|
||||
// ),
|
||||
// leading: CloseButton(
|
||||
// onPressed: () => Navigator.of(
|
||||
// context,
|
||||
// ).pop(null),
|
||||
// ),
|
||||
// ),
|
||||
// body:
|
||||
// SizedBox(
|
||||
// body: SizedBox(
|
||||
// height: double
|
||||
// .infinity,
|
||||
// child:
|
||||
// EmojiPicker(
|
||||
// onEmojiSelected: (
|
||||
// _,
|
||||
// emoji,
|
||||
// ) =>
|
||||
// Navigator.of(
|
||||
// context,
|
||||
// ).pop(
|
||||
// emoji
|
||||
// .emoji,
|
||||
// ),
|
||||
// config:
|
||||
// Config(
|
||||
// locale:
|
||||
// Localizations.localeOf(context),
|
||||
// emojiViewConfig:
|
||||
// const EmojiViewConfig(
|
||||
// child: EmojiPicker(
|
||||
// onEmojiSelected:
|
||||
// (
|
||||
// _,
|
||||
// emoji,
|
||||
// ) =>
|
||||
// Navigator.of(
|
||||
// context,
|
||||
// ).pop(
|
||||
// emoji.emoji,
|
||||
// ),
|
||||
// config: Config(
|
||||
// locale: Localizations.localeOf(
|
||||
// context,
|
||||
// ),
|
||||
// emojiViewConfig: const EmojiViewConfig(
|
||||
// backgroundColor:
|
||||
// Colors.transparent,
|
||||
// ),
|
||||
// bottomActionBarConfig:
|
||||
// const BottomActionBarConfig(
|
||||
// bottomActionBarConfig: const BottomActionBarConfig(
|
||||
// enabled:
|
||||
// false,
|
||||
// ),
|
||||
// categoryViewConfig:
|
||||
// CategoryViewConfig(
|
||||
// categoryViewConfig: CategoryViewConfig(
|
||||
// initCategory:
|
||||
// Category.SMILEYS,
|
||||
// backspaceColor:
|
||||
// theme.colorScheme.primary,
|
||||
// iconColor:
|
||||
// theme.colorScheme.primary.withAlpha(
|
||||
// iconColor: theme.colorScheme.primary.withAlpha(
|
||||
// 128,
|
||||
// ),
|
||||
// iconColorSelected:
|
||||
|
|
@ -1025,10 +996,8 @@ class Message extends StatelessWidget {
|
|||
// backgroundColor:
|
||||
// theme.colorScheme.surface,
|
||||
// ),
|
||||
// skinToneConfig:
|
||||
// SkinToneConfig(
|
||||
// dialogBackgroundColor:
|
||||
// Color.lerp(
|
||||
// skinToneConfig: SkinToneConfig(
|
||||
// dialogBackgroundColor: Color.lerp(
|
||||
// theme.colorScheme.surface,
|
||||
// theme.colorScheme.primaryContainer,
|
||||
// 0.75,
|
||||
|
|
@ -1047,17 +1016,18 @@ class Message extends StatelessWidget {
|
|||
// }
|
||||
// if (sentReactions
|
||||
// .contains(
|
||||
// emoji,
|
||||
// )) {
|
||||
// emoji,
|
||||
// )) {
|
||||
// return;
|
||||
// }
|
||||
// onSelect(event);
|
||||
|
||||
// await event.room
|
||||
// .sendReaction(
|
||||
// event.eventId,
|
||||
// emoji,
|
||||
// );
|
||||
// event
|
||||
// .eventId,
|
||||
// emoji,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
|
|
@ -1095,6 +1065,7 @@ class Message extends StatelessWidget {
|
|||
// child: MessageReactions(event, timeline),
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
if (enterThread != null)
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
|
|
@ -1125,10 +1096,7 @@ class Message extends StatelessWidget {
|
|||
onPressed: () => enterThread(event.eventId),
|
||||
icon: const Icon(Icons.message),
|
||||
label: Text(
|
||||
'${L10n.of(context).countReplies(threadChildren.length)} | ${threadChildren.first.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
withSenderNamePrefix: true,
|
||||
)}',
|
||||
'${L10n.of(context).countReplies(threadChildren.length)} | ${threadChildren.first.calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)), withSenderNamePrefix: true)}',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
|
@ -1136,6 +1104,7 @@ class Message extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
!showReceiptsRow
|
||||
? const SizedBox.shrink()
|
||||
: Padding(
|
||||
|
|
@ -1174,8 +1143,9 @@ class Message extends StatelessWidget {
|
|||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 3),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 3,
|
||||
),
|
||||
color: theme.colorScheme.surface.withAlpha(128),
|
||||
),
|
||||
child: Text(
|
||||
|
|
@ -1246,8 +1216,10 @@ class BubblePainter extends CustomPainter {
|
|||
final scrollableRect = Offset.zero & scrollableBox.size;
|
||||
final bubbleBox = context.findRenderObject() as RenderBox;
|
||||
|
||||
final origin =
|
||||
bubbleBox.localToGlobal(Offset.zero, ancestor: scrollableBox);
|
||||
final origin = bubbleBox.localToGlobal(
|
||||
Offset.zero,
|
||||
ancestor: scrollableBox,
|
||||
);
|
||||
final paint = Paint()
|
||||
..shader = ui.Gradient.linear(
|
||||
scrollableRect.topCenter,
|
||||
|
|
|
|||
|
|
@ -73,9 +73,7 @@ class MessageContent extends StatelessWidget {
|
|||
// if (event.content['can_request_session'] != true) {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(
|
||||
// content: Text(
|
||||
// event.calcLocalizedBodyFallback(MatrixLocals(l10n)),
|
||||
// ),
|
||||
// content: Text(event.calcLocalizedBodyFallback(MatrixLocals(l10n))),
|
||||
// ),
|
||||
// );
|
||||
// return;
|
||||
|
|
@ -114,20 +112,16 @@ class MessageContent extends StatelessWidget {
|
|||
// trailing: const Icon(Icons.lock_outlined),
|
||||
// ),
|
||||
// const Divider(),
|
||||
// Text(
|
||||
// event.calcLocalizedBodyFallback(
|
||||
// MatrixLocals(l10n),
|
||||
// ),
|
||||
// ),
|
||||
// Text(event.calcLocalizedBodyFallback(MatrixLocals(l10n))),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
void onClick(PangeaToken token) {
|
||||
token = pangeaMessageEvent?.messageDisplayRepresentation
|
||||
token =
|
||||
pangeaMessageEvent?.messageDisplayRepresentation
|
||||
?.getClosestNonPunctToken(token) ??
|
||||
token;
|
||||
|
||||
|
|
@ -157,8 +151,9 @@ class MessageContent extends StatelessWidget {
|
|||
case MessageTypes.Image:
|
||||
case MessageTypes.Sticker:
|
||||
if (event.redacted) continue textmessage;
|
||||
final maxSize =
|
||||
event.messageType == MessageTypes.Sticker ? 128.0 : 256.0;
|
||||
final maxSize = event.messageType == MessageTypes.Sticker
|
||||
? 128.0
|
||||
: 256.0;
|
||||
final w = event.content
|
||||
.tryGetMap<String, Object?>('info')
|
||||
?.tryGet<int>('w');
|
||||
|
|
@ -193,12 +188,12 @@ class MessageContent extends StatelessWidget {
|
|||
return CuteContent(event);
|
||||
case MessageTypes.Audio:
|
||||
if (PlatformInfos.isMobile ||
|
||||
PlatformInfos.isMacOS ||
|
||||
PlatformInfos.isWeb
|
||||
// Disabled until https://github.com/bleonard252/just_audio_mpv/issues/3
|
||||
// is fixed
|
||||
// || PlatformInfos.isLinux
|
||||
) {
|
||||
PlatformInfos.isMacOS ||
|
||||
PlatformInfos.isWeb
|
||||
// Disabled until https://github.com/bleonard252/just_audio_mpv/issues/3
|
||||
// is fixed
|
||||
// || PlatformInfos.isLinux
|
||||
) {
|
||||
return AudioPlayerWidget(
|
||||
event,
|
||||
color: textColor,
|
||||
|
|
@ -244,8 +239,9 @@ class MessageContent extends StatelessWidget {
|
|||
// );
|
||||
// Pangea#
|
||||
case MessageTypes.Location:
|
||||
final geoUri =
|
||||
Uri.tryParse(event.content.tryGet<String>('geo_uri')!);
|
||||
final geoUri = Uri.tryParse(
|
||||
event.content.tryGet<String>('geo_uri')!,
|
||||
);
|
||||
if (geoUri != null && geoUri.scheme == 'geo') {
|
||||
final latlong = geoUri.path
|
||||
.split(';')
|
||||
|
|
@ -257,7 +253,7 @@ class MessageContent extends StatelessWidget {
|
|||
latlong.first != null &&
|
||||
latlong.last != null) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
MapBubble(
|
||||
latitude: latlong.first!,
|
||||
|
|
@ -266,8 +262,10 @@ class MessageContent extends StatelessWidget {
|
|||
const SizedBox(height: 6),
|
||||
OutlinedButton.icon(
|
||||
icon: Icon(Icons.location_on_outlined, color: textColor),
|
||||
onPressed:
|
||||
UrlLauncher(context, geoUri.toString()).launchUrl,
|
||||
onPressed: UrlLauncher(
|
||||
context,
|
||||
geoUri.toString(),
|
||||
).launchUrl,
|
||||
label: Text(
|
||||
L10n.of(context).openInMaps,
|
||||
style: TextStyle(color: textColor),
|
||||
|
|
@ -299,25 +297,25 @@ class MessageContent extends StatelessWidget {
|
|||
html = '* $html';
|
||||
}
|
||||
|
||||
final bigEmotes = event.onlyEmotes &&
|
||||
final bigEmotes =
|
||||
event.onlyEmotes &&
|
||||
event.numberEmotes > 0 &&
|
||||
event.numberEmotes <= 3;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: HtmlMessage(
|
||||
html: html,
|
||||
textColor: textColor,
|
||||
room: event.room,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize *
|
||||
(bigEmotes ? 5 : 1),
|
||||
limitHeight: !selected,
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
|
|
@ -335,7 +333,8 @@ class MessageContent extends StatelessWidget {
|
|||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
nextEvent: nextEvent,
|
||||
prevEvent: prevEvent,
|
||||
onClick: event.isActivityMessage ||
|
||||
onClick:
|
||||
event.isActivityMessage ||
|
||||
readingAssistanceMode ==
|
||||
ReadingAssistanceMode.practiceMode
|
||||
? null
|
||||
|
|
@ -418,16 +417,14 @@ class RedactionWidget extends StatelessWidget {
|
|||
future: event.redactedBecause?.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final reason = event.redactedBecause?.content.tryGet<String>('reason');
|
||||
final redactedBy = snapshot.data?.calcDisplayname() ??
|
||||
final redactedBy =
|
||||
snapshot.data?.calcDisplayname() ??
|
||||
event.redactedBecause?.senderId.localpart ??
|
||||
L10n.of(context).user;
|
||||
return _ButtonContent(
|
||||
label: reason == null
|
||||
? L10n.of(context).redactedBy(redactedBy)
|
||||
: L10n.of(context).redactedByBecause(
|
||||
redactedBy,
|
||||
reason,
|
||||
),
|
||||
: L10n.of(context).redactedByBecause(redactedBy, reason),
|
||||
icon: '🗑️',
|
||||
textColor: buttonTextColor.withAlpha(128),
|
||||
onPressed: () => onInfoTab!(event),
|
||||
|
|
@ -456,18 +453,12 @@ class _ButtonContent extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: InkWell(
|
||||
onTap: onPressed,
|
||||
child: Text(
|
||||
'$icon $label',
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: fontSize,
|
||||
),
|
||||
style: TextStyle(color: textColor, fontSize: fontSize),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,15 +27,15 @@ class MessageDownloadContent extends StatelessWidget {
|
|||
final filetype = (filename.contains('.')
|
||||
? filename.split('.').last.toUpperCase()
|
||||
: event.content
|
||||
.tryGetMap<String, dynamic>('info')
|
||||
?.tryGet<String>('mimetype')
|
||||
?.toUpperCase() ??
|
||||
'UNKNOWN');
|
||||
.tryGetMap<String, dynamic>('info')
|
||||
?.tryGet<String>('mimetype')
|
||||
?.toUpperCase() ??
|
||||
'UNKNOWN');
|
||||
final sizeString = event.sizeString ?? '?MB';
|
||||
final fileDescription = event.fileDescription;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Material(
|
||||
|
|
@ -47,7 +47,7 @@ class MessageDownloadContent extends StatelessWidget {
|
|||
width: 400,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
spacing: 16,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
|
|
@ -56,8 +56,8 @@ class MessageDownloadContent extends StatelessWidget {
|
|||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Text(
|
||||
filename,
|
||||
|
|
@ -93,13 +93,15 @@ class MessageDownloadContent extends StatelessWidget {
|
|||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@ class MessageReactions extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final allReactionEvents =
|
||||
event.aggregatedEvents(timeline, RelationshipTypes.reaction);
|
||||
final allReactionEvents = event.aggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.reaction,
|
||||
);
|
||||
final reactionMap = <String, _ReactionEntry>{};
|
||||
final client = Matrix.of(context).client;
|
||||
|
||||
|
|
@ -113,7 +115,7 @@ class _Reaction extends StatelessWidget {
|
|||
Widget content;
|
||||
if (reactionKey.startsWith('mxc://')) {
|
||||
content = Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
MxcImage(
|
||||
uri: Uri.parse(reactionKey),
|
||||
|
|
@ -190,17 +192,14 @@ class _AdaptableReactorsDialog extends StatelessWidget {
|
|||
final Client? client;
|
||||
final _ReactionEntry? reactionEntry;
|
||||
|
||||
const _AdaptableReactorsDialog({
|
||||
this.client,
|
||||
this.reactionEntry,
|
||||
});
|
||||
const _AdaptableReactorsDialog({this.client, this.reactionEntry});
|
||||
|
||||
Future<bool?> show(BuildContext context) => showAdaptiveDialog(
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
barrierDismissible: true,
|
||||
useRootNavigator: false,
|
||||
);
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
barrierDismissible: true,
|
||||
useRootNavigator: false,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -226,9 +225,6 @@ class _AdaptableReactorsDialog extends StatelessWidget {
|
|||
|
||||
final title = Center(child: Text(reactionEntry!.key));
|
||||
|
||||
return AlertDialog.adaptive(
|
||||
title: title,
|
||||
content: body,
|
||||
);
|
||||
return AlertDialog.adaptive(title: title, content: body);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,8 +74,10 @@ class _PangeaMessageReactionsState extends State<PangeaMessageReactions> {
|
|||
}
|
||||
|
||||
void _updateReactionMap() {
|
||||
final allReactionEvents = widget.event
|
||||
.aggregatedEvents(widget.timeline, RelationshipTypes.reaction);
|
||||
final allReactionEvents = widget.event.aggregatedEvents(
|
||||
widget.timeline,
|
||||
RelationshipTypes.reaction,
|
||||
);
|
||||
final newReactionMap = <String, _ReactionEntry>{};
|
||||
|
||||
for (final e in allReactionEvents) {
|
||||
|
|
@ -220,14 +222,8 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin {
|
|||
duration: const Duration(milliseconds: 400),
|
||||
vsync: this,
|
||||
);
|
||||
_bounceOutAnimation = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _bounceOutController,
|
||||
curve: Curves.easeInBack,
|
||||
),
|
||||
_bounceOutAnimation = Tween<double>(begin: 1.0, end: 0).animate(
|
||||
CurvedAnimation(parent: _bounceOutController, curve: Curves.easeInBack),
|
||||
);
|
||||
|
||||
_burstController = AnimationController(
|
||||
|
|
@ -237,12 +233,7 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin {
|
|||
_burstAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _burstController,
|
||||
curve: Curves.easeOut,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _burstController, curve: Curves.easeOut));
|
||||
|
||||
_growController = AnimationController(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
|
|
@ -250,25 +241,33 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin {
|
|||
);
|
||||
_growScale = TweenSequence([
|
||||
TweenSequenceItem(
|
||||
tween: Tween<double>(begin: 0.6, end: 1.18)
|
||||
.chain(CurveTween(curve: Curves.easeOutBack)),
|
||||
tween: Tween<double>(
|
||||
begin: 0.6,
|
||||
end: 1.18,
|
||||
).chain(CurveTween(curve: Curves.easeOutBack)),
|
||||
weight: 60,
|
||||
),
|
||||
TweenSequenceItem(
|
||||
tween: Tween<double>(begin: 1.18, end: 1.0)
|
||||
.chain(CurveTween(curve: Curves.easeIn)),
|
||||
tween: Tween<double>(
|
||||
begin: 1.18,
|
||||
end: 1.0,
|
||||
).chain(CurveTween(curve: Curves.easeIn)),
|
||||
weight: 40,
|
||||
),
|
||||
]).animate(_growController);
|
||||
_growOffset = TweenSequence([
|
||||
TweenSequenceItem(
|
||||
tween: Tween<double>(begin: 0.0, end: -10.0)
|
||||
.chain(CurveTween(curve: Curves.easeOut)),
|
||||
tween: Tween<double>(
|
||||
begin: 0.0,
|
||||
end: -10.0,
|
||||
).chain(CurveTween(curve: Curves.easeOut)),
|
||||
weight: 60,
|
||||
),
|
||||
TweenSequenceItem(
|
||||
tween: Tween<double>(begin: -10.0, end: 0.0)
|
||||
.chain(CurveTween(curve: Curves.easeIn)),
|
||||
tween: Tween<double>(
|
||||
begin: -10.0,
|
||||
end: 0.0,
|
||||
).chain(CurveTween(curve: Curves.easeIn)),
|
||||
weight: 40,
|
||||
),
|
||||
]).animate(_growController);
|
||||
|
|
@ -391,11 +390,13 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin {
|
|||
AnimatedBuilder(
|
||||
animation: Listenable.merge([_bounceOutAnimation, _growController]),
|
||||
builder: (context, child) {
|
||||
final isGrowing = _growController.isAnimating ||
|
||||
final isGrowing =
|
||||
_growController.isAnimating ||
|
||||
(_growController.value > 0 && _growController.value < 1.0);
|
||||
final isBouncing = _bounceOutController.isAnimating;
|
||||
final scale =
|
||||
isGrowing ? _growScale.value : _bounceOutAnimation.value;
|
||||
final scale = isGrowing
|
||||
? _growScale.value
|
||||
: _bounceOutAnimation.value;
|
||||
final offsetY = isGrowing ? _growOffset.value : 0.0;
|
||||
|
||||
return AnimatedSize(
|
||||
|
|
@ -440,7 +441,8 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin {
|
|||
color: widget.reacted == true
|
||||
? theme.colorScheme.primary
|
||||
: theme
|
||||
.colorScheme.surfaceContainerHigh,
|
||||
.colorScheme
|
||||
.surfaceContainerHigh,
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
|
@ -503,17 +505,14 @@ class _AdaptableReactorsDialog extends StatelessWidget {
|
|||
final Client? client;
|
||||
final _ReactionEntry? reactionEntry;
|
||||
|
||||
const _AdaptableReactorsDialog({
|
||||
this.client,
|
||||
this.reactionEntry,
|
||||
});
|
||||
const _AdaptableReactorsDialog({this.client, this.reactionEntry});
|
||||
|
||||
Future<bool?> show(BuildContext context) => showAdaptiveDialog(
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
barrierDismissible: true,
|
||||
useRootNavigator: true,
|
||||
);
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
barrierDismissible: true,
|
||||
useRootNavigator: true,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -542,9 +541,6 @@ class _AdaptableReactorsDialog extends StatelessWidget {
|
|||
|
||||
final title = Center(child: Text(reactionEntry!.key));
|
||||
|
||||
return AlertDialog.adaptive(
|
||||
title: title,
|
||||
content: body,
|
||||
);
|
||||
return AlertDialog.adaptive(title: title, content: body);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,8 @@ class PollWidget extends StatelessWidget {
|
|||
super.key,
|
||||
});
|
||||
|
||||
void _endPoll(BuildContext context) => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => event.endPoll(),
|
||||
);
|
||||
void _endPoll(BuildContext context) =>
|
||||
showFutureLoadingDialog(context: context, future: () => event.endPoll());
|
||||
|
||||
void _toggleVote(
|
||||
BuildContext context,
|
||||
|
|
@ -60,20 +58,19 @@ class PollWidget extends StatelessWidget {
|
|||
}
|
||||
final responses = event.getPollResponses(timeline);
|
||||
final pollHasBeenEnded = event.getPollHasBeenEnded(timeline);
|
||||
final canVote = event.room.canSendEvent(PollEventContent.responseType) &&
|
||||
final canVote =
|
||||
event.room.canSendEvent(PollEventContent.responseType) &&
|
||||
!pollHasBeenEnded;
|
||||
final maxPolls = responses.length;
|
||||
final answersVisible =
|
||||
eventContent.pollStartContent.kind == PollKind.disclosed ||
|
||||
pollHasBeenEnded;
|
||||
pollHasBeenEnded;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
|
|
@ -82,13 +79,15 @@ class PollWidget extends StatelessWidget {
|
|||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
|
|
@ -97,99 +96,101 @@ class PollWidget extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Divider(color: linkColor.withAlpha(64)),
|
||||
...eventContent.pollStartContent.answers.map(
|
||||
(answer) {
|
||||
final votedUserIds = responses.entries
|
||||
.where((entry) => entry.value.contains(answer.id))
|
||||
.map((entry) => entry.key)
|
||||
.toSet();
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: CheckboxListTile.adaptive(
|
||||
value: responses[event.room.client.userID!]
|
||||
?.contains(answer.id) ??
|
||||
false,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
checkboxScaleFactor: 1.5,
|
||||
checkboxShape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
...eventContent.pollStartContent.answers.map((answer) {
|
||||
final votedUserIds = responses.entries
|
||||
.where((entry) => entry.value.contains(answer.id))
|
||||
.map((entry) => entry.key)
|
||||
.toSet();
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: CheckboxListTile.adaptive(
|
||||
value:
|
||||
responses[event.room.client.userID!]?.contains(answer.id) ??
|
||||
false,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
checkboxScaleFactor: 1.5,
|
||||
checkboxShape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
onChanged: !canVote
|
||||
? null
|
||||
: (_) => _toggleVote(
|
||||
context,
|
||||
answer.id,
|
||||
eventContent.pollStartContent.maxSelections,
|
||||
),
|
||||
title: Text(
|
||||
answer.mText,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize:
|
||||
AppConfig.messageFontSize *
|
||||
AppSettings.fontSizeFactor.value,
|
||||
),
|
||||
onChanged: !canVote
|
||||
? null
|
||||
: (_) => _toggleVote(
|
||||
context,
|
||||
answer.id,
|
||||
eventContent.pollStartContent.maxSelections,
|
||||
),
|
||||
title: Text(
|
||||
answer.mText,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppConfig.messageFontSize *
|
||||
AppSettings.fontSizeFactor.value,
|
||||
),
|
||||
),
|
||||
subtitle: answersVisible
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context)
|
||||
.countVotes(votedUserIds.length),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize:
|
||||
),
|
||||
subtitle: answersVisible
|
||||
? Column(
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(
|
||||
context,
|
||||
).countVotes(votedUserIds.length),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize:
|
||||
12 * AppSettings.fontSizeFactor.value,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
...votedUserIds.map((userId) {
|
||||
final user = event.room
|
||||
.getState(EventTypes.RoomMember, userId)
|
||||
?.asUser(event.room);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 2.0,
|
||||
),
|
||||
child: Avatar(
|
||||
mxContent: user?.avatarUrl,
|
||||
name:
|
||||
user?.calcDisplayname() ??
|
||||
userId.localpart,
|
||||
size:
|
||||
12 * AppSettings.fontSizeFactor.value,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
...votedUserIds.map((userId) {
|
||||
final user = event.room
|
||||
.getState(EventTypes.RoomMember, userId)
|
||||
?.asUser(event.room);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 2.0,
|
||||
),
|
||||
child: Avatar(
|
||||
mxContent: user?.avatarUrl,
|
||||
name: user?.calcDisplayname() ??
|
||||
userId.localpart,
|
||||
size: 12 *
|
||||
AppSettings.fontSizeFactor.value,
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(width: 2),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(width: 2),
|
||||
],
|
||||
),
|
||||
LinearProgressIndicator(
|
||||
color: linkColor,
|
||||
backgroundColor: linkColor.withAlpha(128),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
value: maxPolls == 0
|
||||
? 0
|
||||
: votedUserIds.length / maxPolls,
|
||||
),
|
||||
LinearProgressIndicator(
|
||||
color: linkColor,
|
||||
backgroundColor: linkColor.withAlpha(128),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
value: maxPolls == 0
|
||||
? 0
|
||||
: votedUserIds.length / maxPolls,
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (!pollHasBeenEnded && event.senderId == event.room.client.userID)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
|
|
|
|||
|
|
@ -9,22 +9,22 @@ class ReactionListener {
|
|||
StreamSubscription? _reactionSub;
|
||||
|
||||
ReactionListener({required this.event, required this.onUpdate}) {
|
||||
_reactionSub = event.room.client.onSync.stream.where(
|
||||
(update) {
|
||||
final room = event.room;
|
||||
final timelineEvents = update.rooms?.join?[room.id]?.timeline?.events;
|
||||
if (timelineEvents == null) return false;
|
||||
_reactionSub = event.room.client.onSync.stream
|
||||
.where((update) {
|
||||
final room = event.room;
|
||||
final timelineEvents = update.rooms?.join?[room.id]?.timeline?.events;
|
||||
if (timelineEvents == null) return false;
|
||||
|
||||
final eventID = event.eventId;
|
||||
return timelineEvents.any(
|
||||
(e) =>
|
||||
e.type == EventTypes.Redaction ||
|
||||
(e.type == EventTypes.Reaction &&
|
||||
Event.fromMatrixEvent(e, room).relationshipEventId ==
|
||||
eventID),
|
||||
);
|
||||
},
|
||||
).listen(onUpdate);
|
||||
final eventID = event.eventId;
|
||||
return timelineEvents.any(
|
||||
(e) =>
|
||||
e.type == EventTypes.Redaction ||
|
||||
(e.type == EventTypes.Reaction &&
|
||||
Event.fromMatrixEvent(e, room).relationshipEventId ==
|
||||
eventID),
|
||||
);
|
||||
})
|
||||
.listen(onUpdate);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
|
|
|
|||
|
|
@ -29,27 +29,28 @@ class ReplyContent extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
|
||||
final timeline = this.timeline;
|
||||
final displayEvent =
|
||||
timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent;
|
||||
final displayEvent = timeline != null
|
||||
? replyEvent.getDisplayEvent(timeline)
|
||||
: replyEvent;
|
||||
final fontSize =
|
||||
AppConfig.messageFontSize * AppSettings.fontSizeFactor.value;
|
||||
final color = theme.brightness == Brightness.dark
|
||||
// Pangea#
|
||||
// #Pangea
|
||||
? ownMessage
|
||||
? theme.colorScheme.tertiaryContainer
|
||||
: theme.colorScheme.onTertiaryContainer
|
||||
? theme.colorScheme.tertiaryContainer
|
||||
: theme.colorScheme.onTertiaryContainer
|
||||
: theme.colorScheme.tertiary;
|
||||
// ? theme.colorScheme.onTertiaryContainer
|
||||
// : ownMessage
|
||||
// ? theme.colorScheme.tertiaryContainer
|
||||
// : theme.colorScheme.tertiary;
|
||||
// ? theme.colorScheme.tertiaryContainer
|
||||
// : theme.colorScheme.tertiary;
|
||||
// Pangea#
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: borderRadius,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 5,
|
||||
|
|
@ -62,8 +63,8 @@ class ReplyContent extends StatelessWidget {
|
|||
const SizedBox(width: 6),
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisAlignment: .center,
|
||||
children: <Widget>[
|
||||
FutureBuilder<User?>(
|
||||
initialData: displayEvent.senderFromMemoryOrFallback,
|
||||
|
|
@ -105,8 +106,8 @@ class ReplyContent extends StatelessWidget {
|
|||
// color: theme.brightness == Brightness.dark
|
||||
// ? theme.colorScheme.onSurface
|
||||
// : ownMessage
|
||||
// ? theme.colorScheme.onTertiary
|
||||
// : theme.colorScheme.onSurface,
|
||||
// ? theme.colorScheme.onTertiary
|
||||
// : theme.colorScheme.onSurface,
|
||||
color: ownMessage
|
||||
? ThemeData.dark().colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface,
|
||||
|
|
|
|||
|
|
@ -36,14 +36,14 @@ class RoomCreationStateEventState extends State<RoomCreationStateEvent> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_memberSubscription = event.room.client.onRoomState.stream.where(
|
||||
(u) {
|
||||
return u.roomId == event.room.id &&
|
||||
u.state.type == EventTypes.RoomMember;
|
||||
},
|
||||
).listen((_) {
|
||||
if (_members > 1) setState(() {});
|
||||
});
|
||||
_memberSubscription = event.room.client.onRoomState.stream
|
||||
.where((u) {
|
||||
return u.roomId == event.room.id &&
|
||||
u.state.type == EventTypes.RoomMember;
|
||||
})
|
||||
.listen((_) {
|
||||
if (_members > 1) setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -75,7 +75,7 @@ class RoomCreationStateEventState extends State<RoomCreationStateEvent> {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: event.room.avatar,
|
||||
|
|
@ -106,21 +106,13 @@ class RoomCreationStateEventState extends State<RoomCreationStateEvent> {
|
|||
const SizedBox(height: 16.0),
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.clickMessage,
|
||||
padding: EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
top: 16.0,
|
||||
),
|
||||
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0),
|
||||
animate: false,
|
||||
),
|
||||
if (_members <= 1 && InstructionsEnum.clickMessage.isToggledOff)
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.emptyChatWarning,
|
||||
padding: EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
top: 16.0,
|
||||
),
|
||||
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0),
|
||||
animate: false,
|
||||
),
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@ class StateMessage extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(4),
|
||||
child: Material(
|
||||
color: theme.colorScheme.surface.withAlpha(128),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 3),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 3,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
|
|
@ -50,7 +51,8 @@ class StateMessage extends StatelessWidget {
|
|||
// text: event.calcLocalizedBodyFallback(
|
||||
// MatrixLocals(L10n.of(context)),
|
||||
// ),
|
||||
text: (event.type == EventTypes.RoomMember) &&
|
||||
text:
|
||||
(event.type == EventTypes.RoomMember) &&
|
||||
(event.roomMemberChangeType ==
|
||||
RoomMemberChangeType.leave) &&
|
||||
(event.stateKey ==
|
||||
|
|
|
|||
|
|
@ -47,8 +47,10 @@ class EventVideoPlayer extends StatelessWidget {
|
|||
PlatformInfos.supportsVideoPlayer && _supportedFormat;
|
||||
// Pangea#
|
||||
|
||||
final blurHash = (event.infoMap as Map<String, dynamic>)
|
||||
.tryGet<String>('xyz.amorgan.blurhash') ??
|
||||
final blurHash =
|
||||
(event.infoMap as Map<String, dynamic>).tryGet<String>(
|
||||
'xyz.amorgan.blurhash',
|
||||
) ??
|
||||
fallbackBlurHash;
|
||||
final fileDescription = event.fileDescription;
|
||||
const maxDimension = 300.0;
|
||||
|
|
@ -61,11 +63,12 @@ class EventVideoPlayer extends StatelessWidget {
|
|||
final height = videoHeight / modifier;
|
||||
|
||||
final durationInt = infoMap?.tryGet<int>('duration');
|
||||
final duration =
|
||||
durationInt == null ? null : Duration(milliseconds: durationInt);
|
||||
final duration = durationInt == null
|
||||
? null
|
||||
: Duration(milliseconds: durationInt);
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Material(
|
||||
|
|
@ -140,22 +143,21 @@ class EventVideoPlayer extends StatelessWidget {
|
|||
SizedBox(
|
||||
width: width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppSettings.fontSizeFactor.value *
|
||||
fontSize:
|
||||
AppSettings.fontSizeFactor.value *
|
||||
AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
|
|
|
|||
|
|
@ -58,11 +58,11 @@ class InputBar extends StatelessWidget {
|
|||
this.autofocus,
|
||||
this.textInputAction,
|
||||
this.readOnly = false,
|
||||
required this.suggestionEmojis,
|
||||
// #Pangea
|
||||
required this.choreographer,
|
||||
required this.showNextMatch,
|
||||
// Pangea#
|
||||
required this.suggestionEmojis,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -81,10 +81,7 @@ class InputBar extends StatelessWidget {
|
|||
// final commandSearch = commandMatch[1]!.toLowerCase();
|
||||
// for (final command in room.client.commands.keys) {
|
||||
// if (command.contains(commandSearch)) {
|
||||
// ret.add({
|
||||
// 'type': 'command',
|
||||
// 'name': command,
|
||||
// });
|
||||
// ret.add({'type': 'command', 'name': command});
|
||||
// }
|
||||
|
||||
// if (ret.length > maxResults) return ret;
|
||||
|
|
@ -127,8 +124,8 @@ class InputBar extends StatelessWidget {
|
|||
'type': 'emote',
|
||||
'name': emote.key,
|
||||
'pack': packSearch,
|
||||
'pack_avatar_url':
|
||||
emotePacks[packSearch]!.pack.avatarUrl?.toString(),
|
||||
'pack_avatar_url': emotePacks[packSearch]!.pack.avatarUrl
|
||||
?.toString(),
|
||||
'pack_display_name':
|
||||
emotePacks[packSearch]!.pack.displayName ?? packSearch,
|
||||
'mxc': emote.value.url.toString(),
|
||||
|
|
@ -179,8 +176,9 @@ class InputBar extends StatelessWidget {
|
|||
for (final user in room.getParticipants()) {
|
||||
if ((user.displayName != null &&
|
||||
(user.displayName!.toLowerCase().contains(userSearch) ||
|
||||
slugify(user.displayName!.toLowerCase())
|
||||
.contains(userSearch))) ||
|
||||
slugify(
|
||||
user.displayName!.toLowerCase(),
|
||||
).contains(userSearch))) ||
|
||||
user.id.split(':')[0].toLowerCase().contains(userSearch)) {
|
||||
ret.add({
|
||||
'type': 'user',
|
||||
|
|
@ -278,11 +276,7 @@ class InputBar extends StatelessWidget {
|
|||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
title: Text(label, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -300,7 +294,7 @@ class InputBar extends StatelessWidget {
|
|||
isThumbnail: false,
|
||||
),
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
crossAxisAlignment: .center,
|
||||
children: <Widget>[
|
||||
Text(suggestion['name']!),
|
||||
Expanded(
|
||||
|
|
@ -331,7 +325,8 @@ class InputBar extends StatelessWidget {
|
|||
onTap: () => onSelected(suggestion),
|
||||
leading: Avatar(
|
||||
mxContent: url,
|
||||
name: suggestion.tryGet<String>('displayname') ??
|
||||
name:
|
||||
suggestion.tryGet<String>('displayname') ??
|
||||
suggestion.tryGet<String>('mxid'),
|
||||
size: size,
|
||||
client: client,
|
||||
|
|
@ -343,8 +338,10 @@ class InputBar extends StatelessWidget {
|
|||
}
|
||||
|
||||
String insertSuggestion(Map<String, String?> suggestion) {
|
||||
final replaceText =
|
||||
controller!.text.substring(0, controller!.selection.baseOffset);
|
||||
final replaceText = controller!.text.substring(
|
||||
0,
|
||||
controller!.selection.baseOffset,
|
||||
);
|
||||
var startText = '';
|
||||
final afterText = replaceText == controller!.text
|
||||
? ''
|
||||
|
|
@ -434,12 +431,7 @@ class InputBar extends StatelessWidget {
|
|||
if (match.updatedMatch.isITStart) {
|
||||
choreographer.itController.openIT(controller!.text);
|
||||
} else {
|
||||
OverlayUtil.showIGCMatch(
|
||||
match,
|
||||
choreographer,
|
||||
context,
|
||||
showNextMatch,
|
||||
);
|
||||
OverlayUtil.showIGCMatch(match, choreographer, context, showNextMatch);
|
||||
|
||||
// rebuild the text field to highlight the newly selected match
|
||||
choreographer.textController.setSystemText(
|
||||
|
|
@ -483,9 +475,9 @@ class InputBar extends StatelessWidget {
|
|||
optionsBuilder: getSuggestions,
|
||||
// #Pangea
|
||||
// fieldViewBuilder: (context, controller, focusNode, _) => TextField(
|
||||
fieldViewBuilder: (context, __, focusNode, _) => ValueListenableBuilder(
|
||||
fieldViewBuilder: (context, _, focusNode, _) => ValueListenableBuilder(
|
||||
valueListenable: choreographer.itController.open,
|
||||
builder: (context, _, __) {
|
||||
builder: (context, _, _) {
|
||||
return TextField(
|
||||
// Pangea#
|
||||
controller: controller,
|
||||
|
|
@ -511,10 +503,7 @@ class InputBar extends StatelessWidget {
|
|||
bytes: data,
|
||||
name: content.uri.split('/').last,
|
||||
);
|
||||
room.sendFileEvent(
|
||||
file,
|
||||
shrinkImageMaxDimension: 1600,
|
||||
);
|
||||
room.sendFileEvent(file, shrinkImageMaxDimension: 1600);
|
||||
},
|
||||
),
|
||||
minLines: minLines,
|
||||
|
|
@ -524,7 +513,9 @@ class InputBar extends StatelessWidget {
|
|||
autofocus: autofocus!,
|
||||
inputFormatters: [
|
||||
// #Pangea
|
||||
// LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
|
||||
//LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
|
||||
//setting max character count to 1000
|
||||
//after max, nothing else can be typed
|
||||
LengthLimitingTextInputFormatter(1000),
|
||||
// Pangea#
|
||||
],
|
||||
|
|
@ -535,11 +526,14 @@ class InputBar extends StatelessWidget {
|
|||
},
|
||||
// #Pangea
|
||||
// maxLength: AppSettings.textMessageMaxLength.value,
|
||||
// decoration: decoration!,
|
||||
// decoration: decoration,
|
||||
decoration: decoration.copyWith(
|
||||
hint: StreamBuilder(
|
||||
stream: MatrixState
|
||||
.pangeaController.userController.languageStream.stream,
|
||||
.pangeaController
|
||||
.userController
|
||||
.languageStream
|
||||
.stream,
|
||||
builder: (context, _) => SizedBox(
|
||||
height: 24,
|
||||
child: ShrinkableText(
|
||||
|
|
@ -548,8 +542,8 @@ class InputBar extends StatelessWidget {
|
|||
: _defaultHintText(context),
|
||||
maxWidth: double.infinity,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ class PinnedEvents extends StatelessWidget {
|
|||
(event) => AdaptiveModalAction(
|
||||
value: event?.eventId ?? '',
|
||||
icon: const Icon(Icons.push_pin_outlined),
|
||||
label: event?.calcLocalizedBodyFallback(
|
||||
label:
|
||||
event?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
withSenderNamePrefix: true,
|
||||
hideReply: true,
|
||||
|
|
@ -68,7 +69,8 @@ class PinnedEvents extends StatelessWidget {
|
|||
builder: (context, snapshot) {
|
||||
final event = snapshot.data;
|
||||
return ChatAppBarListTile(
|
||||
title: event?.calcLocalizedBodyFallback(
|
||||
title:
|
||||
event?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
withSenderNamePrefix: true,
|
||||
hideReply: true,
|
||||
|
|
@ -86,12 +88,12 @@ class PinnedEvents extends StatelessWidget {
|
|||
// : null,
|
||||
tooltip:
|
||||
controller.room.canChangeStateEvent(EventTypes.RoomPinnedEvents)
|
||||
? L10n.of(context).unpin
|
||||
: null,
|
||||
? L10n.of(context).unpin
|
||||
: null,
|
||||
onPressed:
|
||||
controller.room.canChangeStateEvent(EventTypes.RoomPinnedEvents)
|
||||
? () => controller.unpinEvent(event!.eventId)
|
||||
: null,
|
||||
? () => controller.unpinEvent(event!.eventId)
|
||||
: null,
|
||||
// Pangea#
|
||||
),
|
||||
onTap: () => _displayPinnedEventsDialog(context),
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ class RecordingInputRow extends StatelessWidget {
|
|||
builder: (context, constraints) {
|
||||
const width = 4;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
mainAxisSize: .min,
|
||||
mainAxisAlignment: .end,
|
||||
children: state.amplitudeTimeline.reversed
|
||||
.take((constraints.maxWidth / (width + 2)).floor())
|
||||
.toList()
|
||||
|
|
|
|||
|
|
@ -27,10 +27,7 @@ class EmptyAudioException implements Exception {}
|
|||
class RecordingViewModel extends StatefulWidget {
|
||||
final Widget Function(BuildContext, RecordingViewModelState) builder;
|
||||
|
||||
const RecordingViewModel({
|
||||
required this.builder,
|
||||
super.key,
|
||||
});
|
||||
const RecordingViewModel({required this.builder, super.key});
|
||||
|
||||
@override
|
||||
RecordingViewModelState createState() => RecordingViewModelState();
|
||||
|
|
@ -78,10 +75,10 @@ class RecordingViewModelState extends State<RecordingViewModel> {
|
|||
// ? AudioEncoder.wav
|
||||
// // Everywhere else we use opus if supported by the platform:
|
||||
// : !PlatformInfos
|
||||
// .isIOS && // Blocked by https://github.com/llfbandit/record/issues/560
|
||||
// await audioRecorder.isEncoderSupported(AudioEncoder.opus)
|
||||
// ? AudioEncoder.opus
|
||||
// : AudioEncoder.aacLc;
|
||||
// .isIOS && // Blocked by https://github.com/llfbandit/record/issues/560
|
||||
// await audioRecorder.isEncoderSupported(AudioEncoder.opus)
|
||||
// ? AudioEncoder.opus
|
||||
// : AudioEncoder.aacLc;
|
||||
const codec = AudioEncoder.wav;
|
||||
// Pangea#
|
||||
fileName =
|
||||
|
|
@ -136,8 +133,9 @@ class RecordingViewModelState extends State<RecordingViewModel> {
|
|||
|
||||
void _subscribe() {
|
||||
_recorderSubscription?.cancel();
|
||||
_recorderSubscription =
|
||||
Timer.periodic(const Duration(milliseconds: 100), (_) async {
|
||||
_recorderSubscription = Timer.periodic(const Duration(milliseconds: 100), (
|
||||
_,
|
||||
) async {
|
||||
final amplitude = await _audioRecorder!.getAmplitude();
|
||||
var value = 100 + amplitude.current * 2;
|
||||
value = value < 1 ? 1 : value;
|
||||
|
|
@ -188,7 +186,8 @@ class RecordingViewModelState extends State<RecordingViewModel> {
|
|||
int duration,
|
||||
List<int> waveform,
|
||||
String? fileName,
|
||||
) onSend,
|
||||
)
|
||||
onSend,
|
||||
) async {
|
||||
_recorderSubscription?.cancel();
|
||||
final path = await _audioRecorder?.stop();
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ class ReplyDisplay extends StatelessWidget {
|
|||
|
||||
// #Pangea
|
||||
return ListenableBuilder(
|
||||
listenable:
|
||||
Listenable.merge([controller.replyEvent, controller.editEvent]),
|
||||
builder: (context, __) {
|
||||
listenable: Listenable.merge([
|
||||
controller.replyEvent,
|
||||
controller.editEvent,
|
||||
]),
|
||||
builder: (context, _) {
|
||||
final editEvent = controller.editEvent.value;
|
||||
final replyEvent = controller.replyEvent.value;
|
||||
// Pangea#
|
||||
|
|
@ -34,9 +36,7 @@ class ReplyDisplay extends StatelessWidget {
|
|||
? 56
|
||||
: 0,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
),
|
||||
decoration: BoxDecoration(color: theme.colorScheme.onInverseSurface),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
|
|
@ -85,10 +85,7 @@ class _EditContent extends StatelessWidget {
|
|||
}
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.edit,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
Icon(Icons.edit, color: theme.colorScheme.primary),
|
||||
Container(width: 15.0),
|
||||
// #Pangea
|
||||
// Text(
|
||||
|
|
@ -99,9 +96,7 @@ class _EditContent extends StatelessWidget {
|
|||
// ),
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// maxLines: 1,
|
||||
// style: TextStyle(
|
||||
// color: theme.textTheme.bodyMedium!.color,
|
||||
// ),
|
||||
// style: TextStyle(color: theme.textTheme.bodyMedium!.color),
|
||||
// ),
|
||||
Flexible(
|
||||
child: Text(
|
||||
|
|
@ -113,9 +108,7 @@ class _EditContent extends StatelessWidget {
|
|||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
color: theme.textTheme.bodyMedium!.color,
|
||||
),
|
||||
style: TextStyle(color: theme.textTheme.bodyMedium!.color),
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -20,14 +20,16 @@ class SeenByRow extends StatelessWidget {
|
|||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedContainer(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: FluffyThemes.maxTimelineWidth),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.maxTimelineWidth,
|
||||
),
|
||||
height: seenByUsers.isEmpty ? 0 : 24,
|
||||
duration: seenByUsers.isEmpty
|
||||
? Duration.zero
|
||||
: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
alignment: controller.timeline!.events.isNotEmpty &&
|
||||
alignment:
|
||||
controller.timeline!.events.isNotEmpty &&
|
||||
controller.timeline!.events.first.senderId ==
|
||||
Matrix.of(context).client.userID
|
||||
? Alignment.topRight
|
||||
|
|
@ -40,15 +42,15 @@ class SeenByRow extends StatelessWidget {
|
|||
? seenByUsers.sublist(0, maxAvatars)
|
||||
: seenByUsers)
|
||||
.map(
|
||||
(user) => Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
// #Pangea
|
||||
userId: user.id,
|
||||
// Pangea#
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
(user) => Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
size: 16,
|
||||
// #Pangea
|
||||
userId: user.id,
|
||||
// Pangea#
|
||||
),
|
||||
),
|
||||
if (seenByUsers.length > maxAvatars)
|
||||
SizedBox(
|
||||
width: 16,
|
||||
|
|
|
|||
|
|
@ -119,8 +119,9 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
if (e.error != MatrixError.M_LIMIT_EXCEEDED || retryAfterMs == null) {
|
||||
rethrow;
|
||||
}
|
||||
final retryAfterDuration =
|
||||
Duration(milliseconds: retryAfterMs + 1000);
|
||||
final retryAfterDuration = Duration(
|
||||
milliseconds: retryAfterMs + 1000,
|
||||
);
|
||||
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
|
|
@ -164,8 +165,9 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
}
|
||||
|
||||
Future<String> _calcCombinedFileSize() async {
|
||||
final lengths =
|
||||
await Future.wait(widget.files.map((file) => file.length()));
|
||||
final lengths = await Future.wait(
|
||||
widget.files.map((file) => file.length()),
|
||||
);
|
||||
return lengths.fold<double>(0, (p, length) => p + length).sizeString;
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +218,7 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
width: 256,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
if (uniqueFileType == 'image')
|
||||
|
|
@ -243,8 +245,8 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
final bytes = snapshot.data;
|
||||
if (bytes == null) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator
|
||||
.adaptive(),
|
||||
child:
|
||||
CircularProgressIndicator.adaptive(),
|
||||
);
|
||||
}
|
||||
if (snapshot.error != null) {
|
||||
|
|
@ -272,8 +274,11 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
: null,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, e, s) {
|
||||
Logs()
|
||||
.w('Unable to preview image', e, s);
|
||||
Logs().w(
|
||||
'Unable to preview image',
|
||||
e,
|
||||
s,
|
||||
);
|
||||
return const Center(
|
||||
child: SizedBox(
|
||||
width: 256,
|
||||
|
|
@ -303,17 +308,17 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
uniqueFileType == null
|
||||
? Icons.description_outlined
|
||||
: uniqueFileType == 'video'
|
||||
? Icons.video_file_outlined
|
||||
: uniqueFileType == 'audio'
|
||||
? Icons.audio_file_outlined
|
||||
: Icons.description_outlined,
|
||||
? Icons.video_file_outlined
|
||||
: uniqueFileType == 'audio'
|
||||
? Icons.audio_file_outlined
|
||||
: Icons.description_outlined,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Text(
|
||||
fileName,
|
||||
|
|
@ -347,10 +352,12 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
// Workaround for SwitchListTile.adaptive crashes in CupertinoDialog
|
||||
if ({'image', 'video'}.contains(uniqueFileType))
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
crossAxisAlignment: .center,
|
||||
children: [
|
||||
if ({TargetPlatform.iOS, TargetPlatform.macOS}
|
||||
.contains(theme.platform))
|
||||
if ({
|
||||
TargetPlatform.iOS,
|
||||
TargetPlatform.macOS,
|
||||
}.contains(theme.platform))
|
||||
CupertinoSwitch(
|
||||
value: compressionSupported && compress,
|
||||
onChanged: compressionSupported
|
||||
|
|
@ -367,11 +374,11 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).compress,
|
||||
|
|
@ -430,9 +437,7 @@ extension on ScaffoldMessengerState {
|
|||
const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(title),
|
||||
|
|
|
|||
|
|
@ -14,10 +14,7 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
|||
class SendLocationDialog extends StatefulWidget {
|
||||
final Room room;
|
||||
|
||||
const SendLocationDialog({
|
||||
required this.room,
|
||||
super.key,
|
||||
});
|
||||
const SendLocationDialog({required this.room, super.key});
|
||||
|
||||
@override
|
||||
SendLocationDialogState createState() => SendLocationDialogState();
|
||||
|
|
@ -102,12 +99,13 @@ class SendLocationDialogState extends State<SendLocationDialog> {
|
|||
} else if (denied) {
|
||||
contentWidget = Text(L10n.of(context).locationPermissionDeniedNotice);
|
||||
} else if (error != null) {
|
||||
contentWidget =
|
||||
Text(L10n.of(context).errorObtainingLocation(error.toString()));
|
||||
contentWidget = Text(
|
||||
L10n.of(context).errorObtainingLocation(error.toString()),
|
||||
);
|
||||
} else {
|
||||
contentWidget = Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: .min,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
const CupertinoActivityIndicator(),
|
||||
const SizedBox(width: 12),
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _StartPollBottomSheetState extends State<StartPollBottomSheet> {
|
|||
} catch (e, s) {
|
||||
Logs().w('Unable to create poll', e, s);
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(e.toLocalizedString(context))),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
}
|
||||
}
|
||||
|
||||
void _updateCanCreate([dynamic _]) {
|
||||
final newCanCreate = _bodyController.text.trim().isNotEmpty &&
|
||||
final newCanCreate =
|
||||
_bodyController.text.trim().isNotEmpty &&
|
||||
!_answers.any((controller) => controller.text.trim().isEmpty);
|
||||
if (_canCreate != newCanCreate) {
|
||||
setState(() {
|
||||
|
|
@ -71,9 +72,7 @@ class _StartPollBottomSheetState extends State<StartPollBottomSheet> {
|
|||
const maxAnswers = 10;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: CloseButton(
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
leading: CloseButton(onPressed: Navigator.of(context).pop),
|
||||
title: Text(L10n.of(context).startPoll),
|
||||
),
|
||||
body: ListView(
|
||||
|
|
@ -119,8 +118,8 @@ class _StartPollBottomSheetState extends State<StartPollBottomSheet> {
|
|||
icon: const Icon(Icons.add_outlined),
|
||||
onPressed: _answers.length < maxAnswers
|
||||
? () => setState(() {
|
||||
_answers.add(TextEditingController());
|
||||
})
|
||||
_answers.add(TextEditingController());
|
||||
})
|
||||
: null,
|
||||
label: Text(L10n.of(context).addAnswerOption),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -37,15 +37,17 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||
final filteredImagePackImageEntried = pack.images.entries.toList();
|
||||
if (searchFilter?.isNotEmpty ?? false) {
|
||||
filteredImagePackImageEntried.removeWhere(
|
||||
(e) => !(e.key.toLowerCase().contains(searchFilter!.toLowerCase()) ||
|
||||
(e.value.body
|
||||
?.toLowerCase()
|
||||
.contains(searchFilter!.toLowerCase()) ??
|
||||
false)),
|
||||
(e) =>
|
||||
!(e.key.toLowerCase().contains(searchFilter!.toLowerCase()) ||
|
||||
(e.value.body?.toLowerCase().contains(
|
||||
searchFilter!.toLowerCase(),
|
||||
) ??
|
||||
false)),
|
||||
);
|
||||
}
|
||||
final imageKeys =
|
||||
filteredImagePackImageEntried.map((e) => e.key).toList();
|
||||
final imageKeys = filteredImagePackImageEntried
|
||||
.map((e) => e.key)
|
||||
.toList();
|
||||
if (imageKeys.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
|
@ -81,8 +83,9 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||
key: ValueKey(image.url.toString()),
|
||||
onTap: () {
|
||||
// copy the image
|
||||
final imageCopy =
|
||||
ImagePackImageContent.fromJson(image.toJson().copy());
|
||||
final imageCopy = ImagePackImageContent.fromJson(
|
||||
image.toJson().copy(),
|
||||
);
|
||||
// set the body, if it doesn't exist, to the key
|
||||
imageCopy.body ??= imageKeys[imageIndex];
|
||||
widget.onSelected(imageCopy);
|
||||
|
|
@ -136,7 +139,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||
SliverFillRemaining(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Text(L10n.of(context).noEmotesFound),
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ class TypingIndicators extends StatelessWidget {
|
|||
return StreamBuilder<Object>(
|
||||
stream: controller.room.client.onSync.stream.where(
|
||||
(syncUpdate) =>
|
||||
syncUpdate.rooms?.join?[controller.room.id]?.ephemeral
|
||||
?.any((ephemeral) => ephemeral.type == 'm.typing') ??
|
||||
syncUpdate.rooms?.join?[controller.room.id]?.ephemeral?.any(
|
||||
(ephemeral) => ephemeral.type == 'm.typing',
|
||||
) ??
|
||||
false,
|
||||
),
|
||||
builder: (context, _) {
|
||||
|
|
@ -33,22 +34,21 @@ class TypingIndicators extends StatelessWidget {
|
|||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedContainer(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: FluffyThemes.maxTimelineWidth),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.maxTimelineWidth,
|
||||
),
|
||||
height: typingUsers.isEmpty ? 0 : avatarSize + 8,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
alignment: controller.timeline!.events.isNotEmpty &&
|
||||
alignment:
|
||||
controller.timeline!.events.isNotEmpty &&
|
||||
controller.timeline!.events.first.senderId ==
|
||||
Matrix.of(context).client.userID
|
||||
? Alignment.topRight
|
||||
: Alignment.topLeft,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 4.0,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
|
|
@ -123,17 +123,14 @@ class __TypingDotsState extends State<_TypingDots> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
_timer = Timer.periodic(
|
||||
animationDuration,
|
||||
(_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_tick = (_tick + 1) % 4;
|
||||
});
|
||||
},
|
||||
);
|
||||
_timer = Timer.periodic(animationDuration, (_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_tick = (_tick + 1) % 4;
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +146,7 @@ class __TypingDotsState extends State<_TypingDots> {
|
|||
const size = 8.0;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
for (var i = 1; i <= 3; i++)
|
||||
AnimatedContainer(
|
||||
|
|
|
|||
|
|
@ -28,15 +28,15 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
bool guestAccessLoading = false;
|
||||
Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!;
|
||||
Set<Room> get knownSpaceParents => {
|
||||
...room.client.rooms.where(
|
||||
(space) =>
|
||||
space.isSpace &&
|
||||
space.spaceChildren.any((child) => child.roomId == room.id),
|
||||
),
|
||||
...room.spaceParents
|
||||
.map((parent) => room.client.getRoomById(parent.roomId ?? ''))
|
||||
.whereType<Room>(),
|
||||
};
|
||||
...room.client.rooms.where(
|
||||
(space) =>
|
||||
space.isSpace &&
|
||||
space.spaceChildren.any((child) => child.roomId == room.id),
|
||||
),
|
||||
...room.spaceParents
|
||||
.map((parent) => room.client.getRoomById(parent.roomId ?? ''))
|
||||
.whereType<Room>(),
|
||||
};
|
||||
|
||||
String get roomVersion =>
|
||||
room
|
||||
|
|
@ -92,8 +92,11 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
// #Pangea
|
||||
// await room.setJoinRules(
|
||||
// newJoinRules,
|
||||
// allowConditionRoomIds: {JoinRules.restricted, JoinRules.knockRestricted}
|
||||
// .contains(newJoinRules)
|
||||
// allowConditionRoomIds:
|
||||
// {
|
||||
// JoinRules.restricted,
|
||||
// JoinRules.knockRestricted,
|
||||
// }.contains(newJoinRules)
|
||||
// ? knownSpaceParents.map((parent) => parent.id).toList()
|
||||
// : null,
|
||||
// );
|
||||
|
|
@ -104,13 +107,9 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
} catch (e, s) {
|
||||
Logs().w('Unable to change join rules', e, s);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
|
|
@ -132,13 +131,9 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
} catch (e, s) {
|
||||
Logs().w('Unable to change history visibility', e, s);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
|
|
@ -160,13 +155,9 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
} catch (e, s) {
|
||||
Logs().w('Unable to change guest access', e, s);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
|
|
@ -225,8 +216,11 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
newRoom = room.client.getRoomById(newRoomId);
|
||||
}
|
||||
|
||||
if ({JoinRules.invite, JoinRules.knock, JoinRules.knockRestricted}
|
||||
.contains(room.joinRules)) {
|
||||
if ({
|
||||
JoinRules.invite,
|
||||
JoinRules.knock,
|
||||
JoinRules.knockRestricted,
|
||||
}.contains(room.joinRules)) {
|
||||
final users = await room.requestParticipants([
|
||||
Membership.join,
|
||||
Membership.invite,
|
||||
|
|
@ -291,7 +285,8 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
cancelLabel: L10n.of(context).no,
|
||||
);
|
||||
|
||||
final altAliases = room
|
||||
final altAliases =
|
||||
room
|
||||
.getState(EventTypes.RoomCanonicalAlias)
|
||||
?.content
|
||||
.tryGetList<String>('alt_aliases')
|
||||
|
|
@ -307,17 +302,13 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.client.setRoomStateWithKey(
|
||||
room.id,
|
||||
EventTypes.RoomCanonicalAlias,
|
||||
'',
|
||||
{
|
||||
'alias': canonicalAliasConsent == OkCancelResult.ok
|
||||
? alias
|
||||
: room.canonicalAlias,
|
||||
if (altAliases.isNotEmpty) 'alt_aliases': altAliases.toList(),
|
||||
},
|
||||
),
|
||||
future: () => room.client
|
||||
.setRoomStateWithKey(room.id, EventTypes.RoomCanonicalAlias, '', {
|
||||
'alias': canonicalAliasConsent == OkCancelResult.ok
|
||||
? alias
|
||||
: room.canonicalAlias,
|
||||
if (altAliases.isNotEmpty) 'alt_aliases': altAliases.toList(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -344,13 +335,9 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
} catch (e, s) {
|
||||
Logs().w('Unable to change visibility', e, s);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
|
|
|
|||
|
|
@ -24,17 +24,19 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
),
|
||||
body: MaxWidthBody(
|
||||
child: StreamBuilder<Object>(
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == controller.room.id),
|
||||
stream: room.client.onRoomState.stream.where(
|
||||
(update) => update.roomId == controller.room.id,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
final canonicalAlias = room.canonicalAlias;
|
||||
final altAliases = room
|
||||
final altAliases =
|
||||
room
|
||||
.getState(EventTypes.RoomCanonicalAlias)
|
||||
?.content
|
||||
.tryGetList<String>('alt_aliases') ??
|
||||
[];
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
|
|
@ -47,12 +49,13 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
),
|
||||
RadioGroup<HistoryVisibility>(
|
||||
groupValue: room.historyVisibility,
|
||||
onChanged: controller.historyVisibilityLoading ||
|
||||
onChanged:
|
||||
controller.historyVisibilityLoading ||
|
||||
!room.canChangeHistoryVisibility
|
||||
? (_) {}
|
||||
: controller.setHistoryVisibility,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
for (final historyVisibility in HistoryVisibility.values)
|
||||
RadioListTile<HistoryVisibility>.adaptive(
|
||||
|
|
@ -80,12 +83,13 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
groupValue: room.joinRules,
|
||||
onChanged: controller.setJoinRule,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
for (final joinRule in controller.availableJoinRules)
|
||||
if (joinRule != JoinRules.private)
|
||||
RadioListTile<JoinRules>.adaptive(
|
||||
enabled: !controller.joinRulesLoading &&
|
||||
enabled:
|
||||
!controller.joinRulesLoading &&
|
||||
room.canChangeJoinRules,
|
||||
title: Text(
|
||||
joinRule.localizedString(
|
||||
|
|
@ -99,8 +103,10 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
if ({JoinRules.public, JoinRules.knock}
|
||||
.contains(room.joinRules)) ...[
|
||||
if ({
|
||||
JoinRules.public,
|
||||
JoinRules.knock,
|
||||
}.contains(room.joinRules)) ...[
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).areGuestsAllowedToJoin,
|
||||
|
|
@ -114,11 +120,12 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
groupValue: room.guestAccess,
|
||||
onChanged: controller.setGuestAccess,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
for (final guestAccess in GuestAccess.values)
|
||||
RadioListTile<GuestAccess>.adaptive(
|
||||
enabled: !controller.guestAccessLoading &&
|
||||
enabled:
|
||||
!controller.guestAccessLoading &&
|
||||
room.canChangeGuestAccess,
|
||||
title: Text(
|
||||
guestAccess.getLocalizedString(
|
||||
|
|
@ -148,9 +155,10 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
if (canonicalAlias.isNotEmpty)
|
||||
_AliasListTile(
|
||||
alias: canonicalAlias,
|
||||
onDelete: room.canChangeStateEvent(
|
||||
EventTypes.RoomCanonicalAlias,
|
||||
)
|
||||
onDelete:
|
||||
room.canChangeStateEvent(
|
||||
EventTypes.RoomCanonicalAlias,
|
||||
)
|
||||
? () => controller.deleteAlias(canonicalAlias)
|
||||
: null,
|
||||
isCanonicalAlias: true,
|
||||
|
|
@ -158,9 +166,10 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
for (final alias in altAliases)
|
||||
_AliasListTile(
|
||||
alias: alias,
|
||||
onDelete: room.canChangeStateEvent(
|
||||
EventTypes.RoomCanonicalAlias,
|
||||
)
|
||||
onDelete:
|
||||
room.canChangeStateEvent(
|
||||
EventTypes.RoomCanonicalAlias,
|
||||
)
|
||||
? () => controller.deleteAlias(alias)
|
||||
: null,
|
||||
),
|
||||
|
|
@ -172,10 +181,11 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
return const SizedBox.shrink();
|
||||
}
|
||||
localAddresses.remove(room.canonicalAlias);
|
||||
localAddresses
|
||||
.removeWhere((alias) => altAliases.contains(alias));
|
||||
localAddresses.removeWhere(
|
||||
(alias) => altAliases.contains(alias),
|
||||
);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: localAddresses
|
||||
.map(
|
||||
(alias) => _AliasListTile(
|
||||
|
|
@ -257,10 +267,7 @@ class _AliasListTile extends StatelessWidget {
|
|||
? const Icon(Icons.star)
|
||||
: const Icon(Icons.link_outlined),
|
||||
title: InkWell(
|
||||
onTap: () => FluffyShare.share(
|
||||
'https://matrix.to/#/$alias',
|
||||
context,
|
||||
),
|
||||
onTap: () => FluffyShare.share('https://matrix.to/#/$alias', context),
|
||||
child: SelectableText(
|
||||
alias,
|
||||
style: TextStyle(
|
||||
|
|
|
|||
|
|
@ -66,12 +66,15 @@ class ChatDetailsController extends State<ChatDetails>
|
|||
_loadSummaries();
|
||||
|
||||
_languageSubscription = MatrixState
|
||||
.pangeaController.userController.languageStream.stream
|
||||
.pangeaController
|
||||
.userController
|
||||
.languageStream
|
||||
.stream
|
||||
.listen((update) {
|
||||
if (update.prevBaseLang != update.baseLang) {
|
||||
_loadCourseInfo();
|
||||
}
|
||||
});
|
||||
if (update.prevBaseLang != update.baseLang) {
|
||||
_loadCourseInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -115,11 +118,7 @@ class ChatDetailsController extends State<ChatDetails>
|
|||
// Pangea#
|
||||
okLabel: L10n.of(context).ok,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
initialText: room.getLocalizedDisplayname(
|
||||
MatrixLocals(
|
||||
L10n.of(context),
|
||||
),
|
||||
),
|
||||
initialText: room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
);
|
||||
if (input == null) return;
|
||||
final success = await showFutureLoadingDialog(
|
||||
|
|
@ -153,13 +152,11 @@ class ChatDetailsController extends State<ChatDetails>
|
|||
);
|
||||
// final success = await showFutureLoadingDialog(
|
||||
// context: context,
|
||||
// future: () => room.setDescription(input.single),
|
||||
// future: () => room.setDescription(input),
|
||||
// );
|
||||
// if (success.error == null) {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(
|
||||
// content: Text(L10n.of(context).chatDescriptionHasBeenChanged),
|
||||
// ),
|
||||
// SnackBar(content: Text(L10n.of(context).chatDescriptionHasBeenChanged)),
|
||||
// );
|
||||
// }
|
||||
// Pangea#
|
||||
|
|
@ -213,10 +210,7 @@ class ChatDetailsController extends State<ChatDetails>
|
|||
imageQuality: 50,
|
||||
);
|
||||
if (result == null) return;
|
||||
file = MatrixFile(
|
||||
bytes: await result.readAsBytes(),
|
||||
name: result.path,
|
||||
);
|
||||
file = MatrixFile(bytes: await result.readAsBytes(), name: result.path);
|
||||
} else {
|
||||
final picked = await selectFiles(
|
||||
context,
|
||||
|
|
@ -258,8 +252,9 @@ class ChatDetailsController extends State<ChatDetails>
|
|||
return L10n.of(context).enterNumber;
|
||||
}
|
||||
if (int.parse(value) < (room.summary.mJoinedMemberCount ?? 1)) {
|
||||
return L10n.of(context)
|
||||
.chatCapacitySetTooLow(room.summary.mJoinedMemberCount ?? 1);
|
||||
return L10n.of(
|
||||
context,
|
||||
).chatCapacitySetTooLow(room.summary.mJoinedMemberCount ?? 1);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
|
@ -275,9 +270,7 @@ class ChatDetailsController extends State<ChatDetails>
|
|||
);
|
||||
if (success.error == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context).chatCapacityHasBeenChanged),
|
||||
),
|
||||
SnackBar(content: Text(L10n.of(context).chatCapacityHasBeenChanged)),
|
||||
);
|
||||
setState(() {});
|
||||
}
|
||||
|
|
@ -309,26 +302,21 @@ class ChatDetailsController extends State<ChatDetails>
|
|||
context: context,
|
||||
future: () async {
|
||||
final newRoomId = await Matrix.of(context).client.createGroupChat(
|
||||
visibility: sdk.Visibility.private,
|
||||
groupName: names,
|
||||
initialState: [
|
||||
RoomDefaults.defaultPowerLevels(
|
||||
Matrix.of(context).client.userID!,
|
||||
),
|
||||
await Matrix.of(context).client.pangeaJoinRules(
|
||||
'knock_restricted',
|
||||
allow: roomId != null
|
||||
? [
|
||||
{
|
||||
"type": "m.room_membership",
|
||||
"room_id": roomId,
|
||||
}
|
||||
]
|
||||
: null,
|
||||
),
|
||||
],
|
||||
enableEncryption: false,
|
||||
);
|
||||
visibility: sdk.Visibility.private,
|
||||
groupName: names,
|
||||
initialState: [
|
||||
RoomDefaults.defaultPowerLevels(Matrix.of(context).client.userID!),
|
||||
await Matrix.of(context).client.pangeaJoinRules(
|
||||
'knock_restricted',
|
||||
allow: roomId != null
|
||||
? [
|
||||
{"type": "m.room_membership", "room_id": roomId},
|
||||
]
|
||||
: null,
|
||||
),
|
||||
],
|
||||
enableEncryption: false,
|
||||
);
|
||||
final client = Matrix.of(context).client;
|
||||
Room? room = client.getRoomById(newRoomId);
|
||||
if (room == null) {
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||
final room = Matrix.of(context).client.getRoomById(controller.roomId!);
|
||||
if (room == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).oopsSomethingWentWrong),
|
||||
),
|
||||
appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)),
|
||||
body: Center(
|
||||
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
|
||||
),
|
||||
|
|
@ -42,13 +40,15 @@ class ChatDetailsView extends StatelessWidget {
|
|||
final roomAvatar = room.avatar;
|
||||
|
||||
return StreamBuilder(
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == room.id),
|
||||
stream: room.client.onRoomState.stream.where(
|
||||
(update) => update.roomId == room.id,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
var members = room.getParticipants().toList()
|
||||
..sort((b, a) => a.powerLevel.compareTo(b.powerLevel));
|
||||
members = members.take(10).toList();
|
||||
final actualMembersCount = (room.summary.mInvitedMemberCount ?? 0) +
|
||||
final actualMembersCount =
|
||||
(room.summary.mInvitedMemberCount ?? 0) +
|
||||
(room.summary.mJoinedMemberCount ?? 0);
|
||||
final canRequestMoreMembers = members.length < actualMembersCount;
|
||||
final iconColor = theme.textTheme.bodyLarge!.color;
|
||||
|
|
@ -57,7 +57,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||
);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: controller.widget.embeddedCloseButton ??
|
||||
leading:
|
||||
controller.widget.embeddedCloseButton ??
|
||||
const Center(child: BackButton()),
|
||||
elevation: theme.appBarTheme.elevation,
|
||||
actions: <Widget>[
|
||||
|
|
@ -65,19 +66,15 @@ class ChatDetailsView extends StatelessWidget {
|
|||
IconButton(
|
||||
tooltip: L10n.of(context).share,
|
||||
icon: const Icon(Icons.qr_code_rounded),
|
||||
onPressed: () => showQrCodeViewer(
|
||||
context,
|
||||
room.canonicalAlias,
|
||||
),
|
||||
onPressed: () =>
|
||||
showQrCodeViewer(context, room.canonicalAlias),
|
||||
)
|
||||
else if (directChatMatrixID != null)
|
||||
IconButton(
|
||||
tooltip: L10n.of(context).share,
|
||||
icon: const Icon(Icons.qr_code_rounded),
|
||||
onPressed: () => showQrCodeViewer(
|
||||
context,
|
||||
directChatMatrixID,
|
||||
),
|
||||
onPressed: () =>
|
||||
showQrCodeViewer(context, directChatMatrixID),
|
||||
),
|
||||
if (controller.widget.embeddedCloseButton == null)
|
||||
ChatSettingsPopupMenu(room, false),
|
||||
|
|
@ -92,7 +89,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||
itemCount: members.length + 1 + (canRequestMoreMembers ? 1 : 0),
|
||||
itemBuilder: (BuildContext context, int i) => i == 0
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
crossAxisAlignment: .stretch,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: [
|
||||
|
|
@ -103,9 +100,9 @@ class ChatDetailsView extends StatelessWidget {
|
|||
Hero(
|
||||
tag:
|
||||
controller.widget.embeddedCloseButton !=
|
||||
null
|
||||
? 'embedded_content_banner'
|
||||
: 'content_banner',
|
||||
null
|
||||
? 'embedded_content_banner'
|
||||
: 'content_banner',
|
||||
child: Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
|
|
@ -115,10 +112,10 @@ class ChatDetailsView extends StatelessWidget {
|
|||
size: Avatar.defaultSize * 2.5,
|
||||
onTap: roomAvatar != null
|
||||
? () => showDialog(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
MxcImageViewer(roomAvatar),
|
||||
)
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
MxcImageViewer(roomAvatar),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
|
|
@ -142,8 +139,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: .center,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () => room.isDirectChat
|
||||
|
|
@ -151,20 +148,20 @@ class ChatDetailsView extends StatelessWidget {
|
|||
: room.canChangeStateEvent(
|
||||
EventTypes.RoomName,
|
||||
)
|
||||
? controller.setDisplaynameAction()
|
||||
: FluffyShare.share(
|
||||
displayname,
|
||||
context,
|
||||
copyOnly: true,
|
||||
),
|
||||
? controller.setDisplaynameAction()
|
||||
: FluffyShare.share(
|
||||
displayname,
|
||||
context,
|
||||
copyOnly: true,
|
||||
),
|
||||
icon: Icon(
|
||||
room.isDirectChat
|
||||
? Icons.chat_bubble_outline
|
||||
: room.canChangeStateEvent(
|
||||
EventTypes.RoomName,
|
||||
)
|
||||
? Icons.edit_outlined
|
||||
: Icons.copy_outlined,
|
||||
? Icons.edit_outlined
|
||||
: Icons.copy_outlined,
|
||||
size: 16,
|
||||
),
|
||||
style: TextButton.styleFrom(
|
||||
|
|
@ -197,9 +194,9 @@ class ChatDetailsView extends StatelessWidget {
|
|||
iconColor: theme.colorScheme.secondary,
|
||||
),
|
||||
label: Text(
|
||||
L10n.of(context).countParticipants(
|
||||
actualMembersCount,
|
||||
),
|
||||
L10n.of(
|
||||
context,
|
||||
).countParticipants(actualMembersCount),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// style: const TextStyle(fontSize: 12),
|
||||
|
|
@ -223,13 +220,14 @@ class ChatDetailsView extends StatelessWidget {
|
|||
),
|
||||
trailing:
|
||||
room.canChangeStateEvent(EventTypes.RoomTopic)
|
||||
? IconButton(
|
||||
onPressed: controller.setTopicAction,
|
||||
tooltip:
|
||||
L10n.of(context).setChatDescription,
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
)
|
||||
: null,
|
||||
? IconButton(
|
||||
onPressed: controller.setTopicAction,
|
||||
tooltip: L10n.of(
|
||||
context,
|
||||
).setChatDescription,
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
|
@ -239,8 +237,9 @@ class ChatDetailsView extends StatelessWidget {
|
|||
text: room.topic.isEmpty
|
||||
? L10n.of(context).noChatDescriptionYet
|
||||
: room.topic,
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context).scale(1),
|
||||
textScaleFactor: MediaQuery.textScalerOf(
|
||||
context,
|
||||
).scale(1),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: const TextStyle(
|
||||
color: Colors.blueAccent,
|
||||
|
|
@ -272,14 +271,13 @@ class ChatDetailsView extends StatelessWidget {
|
|||
Icons.admin_panel_settings_outlined,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
L10n.of(context).accessAndVisibility,
|
||||
),
|
||||
title: Text(L10n.of(context).accessAndVisibility),
|
||||
subtitle: Text(
|
||||
L10n.of(context).accessAndVisibilityDescription,
|
||||
),
|
||||
onTap: () => context
|
||||
.push('/rooms/${room.id}/details/access'),
|
||||
onTap: () => context.push(
|
||||
'/rooms/${room.id}/details/access',
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
),
|
||||
ListTile(
|
||||
|
|
@ -291,21 +289,20 @@ class ChatDetailsView extends StatelessWidget {
|
|||
backgroundColor:
|
||||
theme.colorScheme.surfaceContainer,
|
||||
foregroundColor: iconColor,
|
||||
child: const Icon(
|
||||
Icons.tune_outlined,
|
||||
),
|
||||
child: const Icon(Icons.tune_outlined),
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
onTap: () => context
|
||||
.push('/rooms/${room.id}/details/permissions'),
|
||||
onTap: () => context.push(
|
||||
'/rooms/${room.id}/details/permissions',
|
||||
),
|
||||
),
|
||||
],
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).countParticipants(
|
||||
actualMembersCount,
|
||||
),
|
||||
L10n.of(
|
||||
context,
|
||||
).countParticipants(actualMembersCount),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
|
@ -329,25 +326,25 @@ class ChatDetailsView extends StatelessWidget {
|
|||
],
|
||||
)
|
||||
: i < members.length + 1
|
||||
? ParticipantListItem(members[i - 1])
|
||||
: ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).loadCountMoreParticipants(
|
||||
(actualMembersCount - members.length),
|
||||
),
|
||||
),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
child: const Icon(
|
||||
Icons.group_outlined,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
onTap: () => context.push(
|
||||
'/rooms/${controller.roomId!}/details/members',
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
? ParticipantListItem(members[i - 1])
|
||||
: ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).loadCountMoreParticipants(
|
||||
(actualMembersCount - members.length),
|
||||
),
|
||||
),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
child: const Icon(
|
||||
Icons.group_outlined,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
onTap: () => context.push(
|
||||
'/rooms/${controller.roomId!}/details/members',
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -37,11 +37,7 @@ mixin ChatDownloadProvider {
|
|||
await room.download(type, context);
|
||||
} on EmptyChatException {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
L10n.of(context).emptyChatDownloadWarning,
|
||||
),
|
||||
),
|
||||
SnackBar(content: Text(L10n.of(context).emptyChatDownloadWarning)),
|
||||
);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ class ParticipantListItem extends StatelessWidget {
|
|||
final permissionBatch = user.powerLevel >= 100
|
||||
? L10n.of(context).admin
|
||||
: user.powerLevel >= 50
|
||||
? L10n.of(context).moderator
|
||||
: '';
|
||||
? L10n.of(context).moderator
|
||||
: '';
|
||||
|
||||
return ListTile(
|
||||
onTap: () => showMemberActionsPopupMenu(context: context, user: user),
|
||||
|
|
@ -43,17 +43,12 @@ class ParticipantListItem extends StatelessWidget {
|
|||
),
|
||||
if (permissionBatch.isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: user.powerLevel >= 100
|
||||
? theme.colorScheme.tertiary
|
||||
: theme.colorScheme.tertiaryContainer,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
child: Text(
|
||||
permissionBatch,
|
||||
|
|
@ -67,8 +62,10 @@ class ParticipantListItem extends StatelessWidget {
|
|||
membershipBatch == null
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: 8,
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.secondaryContainer,
|
||||
|
|
@ -87,10 +84,7 @@ class ParticipantListItem extends StatelessWidget {
|
|||
),
|
||||
// #Pangea
|
||||
subtitle: LevelDisplayName(userId: user.id),
|
||||
// subtitle: Text(
|
||||
// user.id,
|
||||
// maxLines: 1,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// subtitle: Text(user.id, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
// Pangea#
|
||||
leading: Opacity(
|
||||
opacity: user.membership == Membership.join ? 1 : 0.5,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
),
|
||||
body: MaxWidthBody(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
secondary: CircleAvatar(
|
||||
|
|
@ -76,16 +76,14 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).deviceKeys,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == controller.room.id),
|
||||
builder: (context, snapshot) =>
|
||||
FutureBuilder<List<DeviceKeys>>(
|
||||
stream: room.client.onRoomState.stream.where(
|
||||
(update) => update.roomId == controller.room.id,
|
||||
),
|
||||
builder: (context, snapshot) => FutureBuilder<List<DeviceKeys>>(
|
||||
future: room.getUserDeviceKeys(),
|
||||
builder: (BuildContext context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
|
|
@ -108,20 +106,21 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: deviceKeys.length,
|
||||
itemBuilder: (BuildContext context, int i) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
if (i == 0 ||
|
||||
deviceKeys[i].userId !=
|
||||
deviceKeys[i - 1].userId) ...[
|
||||
const Divider(),
|
||||
FutureBuilder(
|
||||
future: room.client
|
||||
.getUserProfile(deviceKeys[i].userId),
|
||||
future: room.client.getUserProfile(
|
||||
deviceKeys[i].userId,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
final displayname =
|
||||
snapshot.data?.displayname ??
|
||||
deviceKeys[i].userId.localpart ??
|
||||
deviceKeys[i].userId;
|
||||
deviceKeys[i].userId.localpart ??
|
||||
deviceKeys[i].userId;
|
||||
return ListTile(
|
||||
leading: Avatar(
|
||||
name: displayname,
|
||||
|
|
@ -146,14 +145,14 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
deviceKeys[i].verified
|
||||
? L10n.of(context).verified
|
||||
: deviceKeys[i].blocked
|
||||
? L10n.of(context).blocked
|
||||
: L10n.of(context).unverified,
|
||||
? L10n.of(context).blocked
|
||||
: L10n.of(context).unverified,
|
||||
style: TextStyle(
|
||||
color: deviceKeys[i].verified
|
||||
? Colors.green
|
||||
: deviceKeys[i].blocked
|
||||
? Colors.red
|
||||
: Colors.orange,
|
||||
? Colors.red
|
||||
: Colors.orange,
|
||||
),
|
||||
),
|
||||
const Text(' | ID: '),
|
||||
|
|
@ -185,9 +184,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
child: Center(
|
||||
child: Text(
|
||||
L10n.of(context).encryptionNotEnabled,
|
||||
style: const TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
style: const TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -47,13 +47,7 @@ enum PopupMenuAction {
|
|||
archive,
|
||||
}
|
||||
|
||||
enum ActiveFilter {
|
||||
allChats,
|
||||
messages,
|
||||
groups,
|
||||
unread,
|
||||
spaces,
|
||||
}
|
||||
enum ActiveFilter { allChats, messages, groups, unread, spaces }
|
||||
|
||||
extension LocalizedActiveFilter on ActiveFilter {
|
||||
String toLocalizedString(BuildContext context) {
|
||||
|
|
@ -118,8 +112,8 @@ class ChatListController extends State<ChatList>
|
|||
// }
|
||||
|
||||
// void clearActiveSpace() => setState(() {
|
||||
// _activeSpaceId = null;
|
||||
// });
|
||||
// _activeSpaceId = null;
|
||||
// });
|
||||
void clearActiveSpace() => context.go("/rooms");
|
||||
void setActiveSpace(String spaceId) =>
|
||||
context.go("/rooms/spaces/$spaceId/details");
|
||||
|
|
@ -153,13 +147,13 @@ class ChatListController extends State<ChatList>
|
|||
inviteEvent == null
|
||||
? L10n.of(context).inviteForMe
|
||||
: inviteEvent.content.tryGet<String>('reason') ??
|
||||
L10n.of(context).youInvitedBy(
|
||||
room
|
||||
.unsafeGetUserFromMemoryOrFallback(
|
||||
inviteEvent.senderId,
|
||||
)
|
||||
.calcDisplayname(i18n: matrixLocals),
|
||||
),
|
||||
L10n.of(context).youInvitedBy(
|
||||
room
|
||||
.unsafeGetUserFromMemoryOrFallback(
|
||||
inviteEvent.senderId,
|
||||
)
|
||||
.calcDisplayname(i18n: matrixLocals),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
|
|
@ -223,9 +217,7 @@ class ChatListController extends State<ChatList>
|
|||
|
||||
if (room.membership == Membership.ban) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context).youHaveBeenBannedFromThisChat),
|
||||
),
|
||||
SnackBar(content: Text(L10n.of(context).youHaveBeenBannedFromThisChat)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -275,11 +267,9 @@ class ChatListController extends State<ChatList>
|
|||
}
|
||||
}
|
||||
|
||||
List<Room> get filteredRooms => Matrix.of(context)
|
||||
.client
|
||||
.rooms
|
||||
.where(getRoomFilterByActiveFilter(activeFilter))
|
||||
.toList();
|
||||
List<Room> get filteredRooms => Matrix.of(
|
||||
context,
|
||||
).client.rooms.where(getRoomFilterByActiveFilter(activeFilter)).toList();
|
||||
|
||||
bool isSearchMode = false;
|
||||
Future<QueryPublicRoomsResponse>? publicRoomsResponse;
|
||||
|
|
@ -338,8 +328,9 @@ class ChatListController extends State<ChatList>
|
|||
|
||||
if (searchQuery.isValidMatrixId &&
|
||||
searchQuery.sigil == '#' &&
|
||||
roomSearchResult.chunk
|
||||
.any((room) => room.canonicalAlias == searchQuery) ==
|
||||
roomSearchResult.chunk.any(
|
||||
(room) => room.canonicalAlias == searchQuery,
|
||||
) ==
|
||||
false) {
|
||||
final response = await client.getRoomIdByAlias(searchQuery);
|
||||
final roomId = response.roomId;
|
||||
|
|
@ -362,13 +353,9 @@ class ChatListController extends State<ChatList>
|
|||
);
|
||||
} catch (e, s) {
|
||||
Logs().w('Searching has crashed', e, s);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
}
|
||||
if (!isSearchMode) return;
|
||||
setState(() {
|
||||
|
|
@ -452,22 +439,17 @@ class ChatListController extends State<ChatList>
|
|||
showScaffoldDialog(
|
||||
context: context,
|
||||
builder: (context) => ShareScaffoldDialog(
|
||||
items: files.map(
|
||||
(file) {
|
||||
if ({
|
||||
SharedMediaType.text,
|
||||
SharedMediaType.url,
|
||||
}.contains(file.type)) {
|
||||
return TextShareItem(file.path);
|
||||
}
|
||||
return FileShareItem(
|
||||
XFile(
|
||||
file.path.replaceFirst('file://', ''),
|
||||
mimeType: file.mimeType,
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
items: files.map((file) {
|
||||
if ({SharedMediaType.text, SharedMediaType.url}.contains(file.type)) {
|
||||
return TextShareItem(file.path);
|
||||
}
|
||||
return FileShareItem(
|
||||
XFile(
|
||||
file.path.replaceFirst('file://', ''),
|
||||
mimeType: file.mimeType,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -491,24 +473,25 @@ class ChatListController extends State<ChatList>
|
|||
.listen(_processIncomingSharedMedia, onError: print);
|
||||
|
||||
// For sharing images coming from outside the app while the app is closed
|
||||
ReceiveSharingIntent.instance
|
||||
.getInitialMedia()
|
||||
.then(_processIncomingSharedMedia);
|
||||
ReceiveSharingIntent.instance.getInitialMedia().then(
|
||||
_processIncomingSharedMedia,
|
||||
);
|
||||
|
||||
// #Pangea
|
||||
// // For receiving shared Uris
|
||||
// _intentUriStreamSubscription =
|
||||
// AppLinks().uriLinkStream.listen(_processIncomingUris);
|
||||
// _intentUriStreamSubscription = AppLinks().uriLinkStream.listen(
|
||||
// _processIncomingUris,
|
||||
// );
|
||||
// Pangea#
|
||||
|
||||
if (PlatformInfos.isAndroid) {
|
||||
final shortcuts = FlutterShortcuts();
|
||||
shortcuts.initialize().then(
|
||||
(_) => shortcuts.listenAction((action) {
|
||||
if (!mounted) return;
|
||||
UrlLauncher(context, action).launchUrl();
|
||||
}),
|
||||
);
|
||||
(_) => shortcuts.listenAction((action) {
|
||||
if (!mounted) return;
|
||||
UrlLauncher(context, action).launchUrl();
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -535,8 +518,9 @@ class ChatListController extends State<ChatList>
|
|||
_hackyWebRTCFixForWeb();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
if (mounted) {
|
||||
searchServer =
|
||||
Matrix.of(context).store.getString(_serverStoreNamespace);
|
||||
searchServer = Matrix.of(
|
||||
context,
|
||||
).store.getString(_serverStoreNamespace);
|
||||
Matrix.of(context).backgroundPush?.setupPush();
|
||||
UpdateNotifier.showUpdateSnackBar(context);
|
||||
// #Pangea
|
||||
|
|
@ -551,61 +535,54 @@ class ChatListController extends State<ChatList>
|
|||
});
|
||||
|
||||
//#Pangea
|
||||
_invitedSpaceSubscription = Matrix.of(context)
|
||||
.client
|
||||
.onSync
|
||||
.stream
|
||||
_invitedSpaceSubscription = Matrix.of(context).client.onSync.stream
|
||||
.where((event) => event.rooms?.invite != null)
|
||||
.listen((event) async {
|
||||
for (final inviteEntry in event.rooms!.invite!.entries) {
|
||||
if (inviteEntry.value.inviteState == null) continue;
|
||||
final isSpace = inviteEntry.value.inviteState!.any(
|
||||
(event) =>
|
||||
event.type == EventTypes.RoomCreate &&
|
||||
event.content['type'] == 'm.space',
|
||||
);
|
||||
final isAnalytics = inviteEntry.value.inviteState!.any(
|
||||
(event) =>
|
||||
event.type == EventTypes.RoomCreate &&
|
||||
event.content['type'] == PangeaRoomTypes.analytics,
|
||||
);
|
||||
|
||||
if (isSpace) {
|
||||
final spaceId = inviteEntry.key;
|
||||
final space = Matrix.of(context).client.getRoomById(
|
||||
spaceId,
|
||||
);
|
||||
|
||||
if (space?.classCode?.toLowerCase() ==
|
||||
SpaceCodeRepo.recentCode?.toLowerCase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (space != null) {
|
||||
chatListHandleSpaceTap(
|
||||
context,
|
||||
space,
|
||||
for (final inviteEntry in event.rooms!.invite!.entries) {
|
||||
if (inviteEntry.value.inviteState == null) continue;
|
||||
final isSpace = inviteEntry.value.inviteState!.any(
|
||||
(event) =>
|
||||
event.type == EventTypes.RoomCreate &&
|
||||
event.content['type'] == 'm.space',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isAnalytics) {
|
||||
final analyticsRoom =
|
||||
Matrix.of(context).client.getRoomById(inviteEntry.key);
|
||||
try {
|
||||
await analyticsRoom?.join();
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(
|
||||
m: "Failed to join analytics room",
|
||||
e: err,
|
||||
s: s,
|
||||
data: {"analyticsRoom": analyticsRoom?.id},
|
||||
final isAnalytics = inviteEntry.value.inviteState!.any(
|
||||
(event) =>
|
||||
event.type == EventTypes.RoomCreate &&
|
||||
event.content['type'] == PangeaRoomTypes.analytics,
|
||||
);
|
||||
|
||||
if (isSpace) {
|
||||
final spaceId = inviteEntry.key;
|
||||
final space = Matrix.of(context).client.getRoomById(spaceId);
|
||||
|
||||
if (space?.classCode?.toLowerCase() ==
|
||||
SpaceCodeRepo.recentCode?.toLowerCase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (space != null) {
|
||||
chatListHandleSpaceTap(context, space);
|
||||
}
|
||||
}
|
||||
|
||||
if (isAnalytics) {
|
||||
final analyticsRoom = Matrix.of(
|
||||
context,
|
||||
).client.getRoomById(inviteEntry.key);
|
||||
try {
|
||||
await analyticsRoom?.join();
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(
|
||||
m: "Failed to join analytics room",
|
||||
e: err,
|
||||
s: s,
|
||||
data: {"analyticsRoom": analyticsRoom?.id},
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
MatrixState.pangeaController.subscriptionController.subscriptionNotifier
|
||||
.addListener(_onSubscribe);
|
||||
|
|
@ -619,39 +596,39 @@ class ChatListController extends State<ChatList>
|
|||
_roomCapacitySubscription ??= client.onSync.stream
|
||||
.where((u) => u.rooms?.join != null)
|
||||
.listen((update) async {
|
||||
final roomUpdates = update.rooms!.join!.entries;
|
||||
for (final entry in roomUpdates) {
|
||||
final roomID = entry.key;
|
||||
final roomUpdate = entry.value;
|
||||
if (roomUpdate.timeline?.events == null) continue;
|
||||
final events = roomUpdate.timeline!.events;
|
||||
final memberEvents = events!.where(
|
||||
(event) =>
|
||||
event.type == EventTypes.RoomMember &&
|
||||
event.senderId == client.userID,
|
||||
);
|
||||
if (memberEvents.isEmpty) continue;
|
||||
final room = client.getRoomById(roomID);
|
||||
if (room == null ||
|
||||
room.isSpace ||
|
||||
room.isHiddenRoom ||
|
||||
room.capacity == null ||
|
||||
(room.summary.mJoinedMemberCount ?? 1) <= room.capacity!) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
await room.leave();
|
||||
if (GoRouterState.of(context).uri.toString().contains(roomID)) {
|
||||
NavigationUtil.goToSpaceRoute(null, [], context);
|
||||
final roomUpdates = update.rooms!.join!.entries;
|
||||
for (final entry in roomUpdates) {
|
||||
final roomID = entry.key;
|
||||
final roomUpdate = entry.value;
|
||||
if (roomUpdate.timeline?.events == null) continue;
|
||||
final events = roomUpdate.timeline!.events;
|
||||
final memberEvents = events!.where(
|
||||
(event) =>
|
||||
event.type == EventTypes.RoomMember &&
|
||||
event.senderId == client.userID,
|
||||
);
|
||||
if (memberEvents.isEmpty) continue;
|
||||
final room = client.getRoomById(roomID);
|
||||
if (room == null ||
|
||||
room.isSpace ||
|
||||
room.isHiddenRoom ||
|
||||
room.capacity == null ||
|
||||
(room.summary.mJoinedMemberCount ?? 1) <= room.capacity!) {
|
||||
continue;
|
||||
}
|
||||
throw L10n.of(context).roomFull;
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
await room.leave();
|
||||
if (GoRouterState.of(context).uri.toString().contains(roomID)) {
|
||||
NavigationUtil.goToSpaceRoute(null, [], context);
|
||||
}
|
||||
throw L10n.of(context).roomFull;
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_joinInvitedSpaces();
|
||||
|
|
@ -667,9 +644,9 @@ class ChatListController extends State<ChatList>
|
|||
}
|
||||
|
||||
Future<void> _joinInvitedSpaces() async {
|
||||
final invitedSpaces = Matrix.of(context).client.rooms.where(
|
||||
(r) => r.isSpace && r.membership == Membership.invite,
|
||||
);
|
||||
final invitedSpaces = Matrix.of(
|
||||
context,
|
||||
).client.rooms.where((r) => r.isSpace && r.membership == Membership.invite);
|
||||
|
||||
for (final space in invitedSpaces) {
|
||||
await showInviteDialog(space, context);
|
||||
|
|
@ -693,11 +670,7 @@ class ChatListController extends State<ChatList>
|
|||
}
|
||||
|
||||
// #Pangea
|
||||
void chatContextAction(
|
||||
Room room,
|
||||
BuildContext posContext, [
|
||||
Room? space,
|
||||
]) =>
|
||||
void chatContextAction(Room room, BuildContext posContext, [Room? space]) =>
|
||||
chatContextMenuAction(
|
||||
room,
|
||||
posContext,
|
||||
|
|
@ -726,8 +699,9 @@ class ChatListController extends State<ChatList>
|
|||
// Offset.zero & overlay.size,
|
||||
// );
|
||||
|
||||
// final displayname =
|
||||
// room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)));
|
||||
// final displayname = room.getLocalizedDisplayname(
|
||||
// MatrixLocals(L10n.of(context)),
|
||||
// );
|
||||
|
||||
// final spacesWithPowerLevels = room.client.rooms
|
||||
// .where(
|
||||
|
|
@ -745,19 +719,17 @@ class ChatListController extends State<ChatList>
|
|||
// PopupMenuItem(
|
||||
// value: ChatContextAction.open,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// spacing: 12.0,
|
||||
// children: [
|
||||
// Avatar(
|
||||
// mxContent: room.avatar,
|
||||
// name: displayname,
|
||||
// ),
|
||||
// Avatar(mxContent: room.avatar, name: displayname),
|
||||
// ConstrainedBox(
|
||||
// constraints: const BoxConstraints(maxWidth: 128),
|
||||
// child: Text(
|
||||
// displayname,
|
||||
// style:
|
||||
// TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
// style: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.onSurface,
|
||||
// ),
|
||||
// maxLines: 2,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// ),
|
||||
|
|
@ -770,7 +742,7 @@ class ChatListController extends State<ChatList>
|
|||
// PopupMenuItem(
|
||||
// value: ChatContextAction.goToSpace,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// Avatar(
|
||||
// mxContent: space.avatar,
|
||||
|
|
@ -790,7 +762,7 @@ class ChatListController extends State<ChatList>
|
|||
// PopupMenuItem(
|
||||
// value: ChatContextAction.mute,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// room.pushRuleState == PushRuleState.notify
|
||||
|
|
@ -809,7 +781,7 @@ class ChatListController extends State<ChatList>
|
|||
// PopupMenuItem(
|
||||
// value: ChatContextAction.markUnread,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// room.markedUnread
|
||||
|
|
@ -828,7 +800,7 @@ class ChatListController extends State<ChatList>
|
|||
// PopupMenuItem(
|
||||
// value: ChatContextAction.favorite,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined,
|
||||
|
|
@ -846,7 +818,7 @@ class ChatListController extends State<ChatList>
|
|||
// PopupMenuItem(
|
||||
// value: ChatContextAction.addToSpace,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// const Icon(Icons.group_work_outlined),
|
||||
// const SizedBox(width: 12),
|
||||
|
|
@ -858,7 +830,7 @@ class ChatListController extends State<ChatList>
|
|||
// PopupMenuItem(
|
||||
// value: ChatContextAction.leave,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// Icons.delete_outlined,
|
||||
|
|
@ -880,7 +852,7 @@ class ChatListController extends State<ChatList>
|
|||
// PopupMenuItem(
|
||||
// value: ChatContextAction.block,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// Icons.block_outlined,
|
||||
|
|
@ -963,8 +935,9 @@ class ChatListController extends State<ChatList>
|
|||
// .map(
|
||||
// (space) => AdaptiveModalAction(
|
||||
// value: space,
|
||||
// label: space
|
||||
// .getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
// label: space.getLocalizedDisplayname(
|
||||
// MatrixLocals(L10n.of(context)),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// .toList(),
|
||||
|
|
@ -1032,8 +1005,9 @@ class ChatListController extends State<ChatList>
|
|||
// if (client.prevBatch == null) {
|
||||
if (client.onSync.value?.nextBatch == null) {
|
||||
// Pangea#
|
||||
await client.onSyncStatus.stream
|
||||
.firstWhere((status) => status.status == SyncStatus.finished);
|
||||
await client.onSyncStatus.stream.firstWhere(
|
||||
(status) => status.status == SyncStatus.finished,
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
|
|
@ -1050,8 +1024,9 @@ class ChatListController extends State<ChatList>
|
|||
});
|
||||
|
||||
// #Pangea
|
||||
// if (client.userDeviceKeys[client.userID!]?.deviceKeys.values
|
||||
// .any((device) => !device.verified && !device.blocked) ??
|
||||
// if (client.userDeviceKeys[client.userID!]?.deviceKeys.values.any(
|
||||
// (device) => !device.verified && !device.blocked,
|
||||
// ) ??
|
||||
// false) {
|
||||
// late final ScaffoldFeatureController controller;
|
||||
// final theme = Theme.of(context);
|
||||
|
|
@ -1063,9 +1038,7 @@ class ChatListController extends State<ChatList>
|
|||
// closeIconColor: theme.colorScheme.onErrorContainer,
|
||||
// content: Text(
|
||||
// L10n.of(context).oneOfYourDevicesIsNotVerified,
|
||||
// style: TextStyle(
|
||||
// color: theme.colorScheme.onErrorContainer,
|
||||
// ),
|
||||
// style: TextStyle(color: theme.colorScheme.onErrorContainer),
|
||||
// ),
|
||||
// action: SnackBarAction(
|
||||
// onPressed: () {
|
||||
|
|
@ -1112,11 +1085,12 @@ class ChatListController extends State<ChatList>
|
|||
// setState(() {
|
||||
// _activeSpaceId = null;
|
||||
// Matrix.of(context).activeBundle = bundle;
|
||||
// if (!Matrix.of(context)
|
||||
// .currentBundle!
|
||||
// .any((client) => client == Matrix.of(context).client)) {
|
||||
// Matrix.of(context)
|
||||
// .setActiveClient(Matrix.of(context).currentBundle!.first);
|
||||
// if (!Matrix.of(
|
||||
// context,
|
||||
// ).currentBundle!.any((client) => client == Matrix.of(context).client)) {
|
||||
// Matrix.of(
|
||||
// context,
|
||||
// ).setActiveClient(Matrix.of(context).currentBundle!.first);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
|
@ -1124,9 +1098,9 @@ class ChatListController extends State<ChatList>
|
|||
|
||||
void editBundlesForAccount(String? userId, String? activeBundle) async {
|
||||
final l10n = L10n.of(context);
|
||||
final client = Matrix.of(context)
|
||||
.widget
|
||||
.clients[Matrix.of(context).getClientIndexByMatrixId(userId!)];
|
||||
final client = Matrix.of(
|
||||
context,
|
||||
).widget.clients[Matrix.of(context).getClientIndexByMatrixId(userId!)];
|
||||
final action = await showModalActionPopup<EditBundleAction>(
|
||||
context: context,
|
||||
title: L10n.of(context).editBundlesForAccount,
|
||||
|
|
@ -1171,10 +1145,9 @@ class ChatListController extends State<ChatList>
|
|||
|
||||
String? get secureActiveBundle {
|
||||
if (Matrix.of(context).activeBundle == null ||
|
||||
!Matrix.of(context)
|
||||
.accountBundles
|
||||
.keys
|
||||
.contains(Matrix.of(context).activeBundle)) {
|
||||
!Matrix.of(
|
||||
context,
|
||||
).accountBundles.keys.contains(Matrix.of(context).activeBundle)) {
|
||||
return Matrix.of(context).accountBundles.keys.first;
|
||||
}
|
||||
return Matrix.of(context).activeBundle;
|
||||
|
|
@ -1200,11 +1173,7 @@ class ChatListController extends State<ChatList>
|
|||
|
||||
enum EditBundleAction { addToBundle, removeFromBundle }
|
||||
|
||||
enum InviteActions {
|
||||
accept,
|
||||
decline,
|
||||
block,
|
||||
}
|
||||
enum InviteActions { accept, decline, block }
|
||||
|
||||
enum ChatContextAction {
|
||||
open,
|
||||
|
|
@ -1223,4 +1192,5 @@ enum ChatContextAction {
|
|||
|
||||
// #Pangea
|
||||
enum InviteAction { accept, decline, block }
|
||||
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -76,9 +76,7 @@ class ChatListViewBody extends StatelessWidget {
|
|||
const dummyChatCount = 4;
|
||||
final filter = controller.searchController.text.toLowerCase();
|
||||
return StreamBuilder(
|
||||
key: ValueKey(
|
||||
client.userID.toString(),
|
||||
),
|
||||
key: ValueKey(client.userID.toString()),
|
||||
stream: client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
|
|
@ -97,173 +95,173 @@ class ChatListViewBody extends StatelessWidget {
|
|||
),
|
||||
// Pangea#
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
if (controller.isSearchMode) ...[
|
||||
// #Pangea
|
||||
// SearchTitle(
|
||||
// title: L10n.of(context).publicRooms,
|
||||
// icon: const Icon(Icons.explore_outlined),
|
||||
// ),
|
||||
// PublicRoomsHorizontalList(publicRooms: publicRooms),
|
||||
// SearchTitle(
|
||||
// title: L10n.of(context).publicSpaces,
|
||||
// icon: const Icon(Icons.workspaces_outlined),
|
||||
// ),
|
||||
// PublicRoomsHorizontalList(publicRooms: publicSpaces),
|
||||
// SearchTitle(
|
||||
// title: L10n.of(context).users,
|
||||
// icon: const Icon(Icons.group_outlined),
|
||||
// ),
|
||||
// AnimatedContainer(
|
||||
// clipBehavior: Clip.hardEdge,
|
||||
// decoration: const BoxDecoration(),
|
||||
// height: userSearchResult == null ||
|
||||
// userSearchResult.results.isEmpty
|
||||
// ? 0
|
||||
// : 106,
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// child: userSearchResult == null
|
||||
// ? null
|
||||
// : 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: () => UserDialog.show(
|
||||
// context: context,
|
||||
// profile: userSearchResult.results[i],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
delegate: SliverChildListDelegate([
|
||||
if (controller.isSearchMode) ...[
|
||||
// #Pangea
|
||||
// if (!controller.isSearchMode &&
|
||||
// AppSettings.showPresences.value)
|
||||
// GestureDetector(
|
||||
// onLongPress: () => controller.dismissStatusList(),
|
||||
// child: StatusMessageList(
|
||||
// onStatusEdit: controller.setStatus,
|
||||
// ),
|
||||
// ),
|
||||
// if (client.rooms.isNotEmpty && !controller.isSearchMode)
|
||||
// SizedBox(
|
||||
// height: 64,
|
||||
// child: ListView(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 12.0,
|
||||
// vertical: 12.0,
|
||||
// ),
|
||||
// shrinkWrap: true,
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// children: [
|
||||
// if (AppSettings.separateChatTypes.value)
|
||||
// ActiveFilter.messages
|
||||
// else
|
||||
// ActiveFilter.allChats,
|
||||
// ActiveFilter.groups,
|
||||
// ActiveFilter.unread,
|
||||
// if (spaceDelegateCandidates.isNotEmpty &&
|
||||
// !AppSettings.displayNavigationRail.value &&
|
||||
// !FluffyThemes.isColumnMode(context))
|
||||
// ActiveFilter.spaces,
|
||||
// ]
|
||||
// .map(
|
||||
// (filter) => Padding(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 4.0,
|
||||
// ),
|
||||
// child: FilterChip(
|
||||
// selected: filter == controller.activeFilter,
|
||||
// onSelected: (_) =>
|
||||
// controller.setActiveFilter(filter),
|
||||
// label:
|
||||
// Text(filter.toLocalizedString(context)),
|
||||
// ),
|
||||
// SearchTitle(
|
||||
// title: L10n.of(context).publicRooms,
|
||||
// icon: const Icon(Icons.explore_outlined),
|
||||
// ),
|
||||
// PublicRoomsHorizontalList(publicRooms: publicRooms),
|
||||
// SearchTitle(
|
||||
// title: L10n.of(context).publicSpaces,
|
||||
// icon: const Icon(Icons.workspaces_outlined),
|
||||
// ),
|
||||
// PublicRoomsHorizontalList(publicRooms: publicSpaces),
|
||||
// SearchTitle(
|
||||
// title: L10n.of(context).users,
|
||||
// icon: const Icon(Icons.group_outlined),
|
||||
// ),
|
||||
// AnimatedContainer(
|
||||
// clipBehavior: Clip.hardEdge,
|
||||
// decoration: const BoxDecoration(),
|
||||
// height:
|
||||
// userSearchResult == null ||
|
||||
// userSearchResult.results.isEmpty
|
||||
// ? 0
|
||||
// : 106,
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// child: userSearchResult == null
|
||||
// ? null
|
||||
// : 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: () => UserDialog.show(
|
||||
// context: context,
|
||||
// profile: userSearchResult.results[i],
|
||||
// ),
|
||||
// )
|
||||
// .toList(),
|
||||
// ),
|
||||
// ),
|
||||
// if (controller.isSearchMode)
|
||||
// SearchTitle(
|
||||
// title: L10n.of(context).chats,
|
||||
// icon: const Icon(Icons.forum_outlined),
|
||||
// ),
|
||||
// if (client.prevBatch != null &&
|
||||
// rooms.isEmpty &&
|
||||
// !controller.isSearchMode) ...[
|
||||
// Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// Stack(
|
||||
// alignment: Alignment.center,
|
||||
// children: [
|
||||
// const Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// DummyChatListItem(
|
||||
// opacity: 0.5,
|
||||
// animate: false,
|
||||
// ),
|
||||
// DummyChatListItem(
|
||||
// opacity: 0.3,
|
||||
// animate: false,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// Icon(
|
||||
// CupertinoIcons.chat_bubble_text_fill,
|
||||
// size: 128,
|
||||
// color: theme.colorScheme.secondary,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: Text(
|
||||
// client.rooms.isEmpty
|
||||
// ? L10n.of(context).noChatsFoundHere
|
||||
// : L10n.of(context).noMoreChatsFound,
|
||||
// textAlign: TextAlign.center,
|
||||
// style: TextStyle(
|
||||
// fontSize: 18,
|
||||
// color: theme.colorScheme.secondary,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
if (controller.isSearchMode &&
|
||||
rooms
|
||||
.where(
|
||||
(room) => room
|
||||
.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
)
|
||||
.toLowerCase()
|
||||
.contains(filter),
|
||||
)
|
||||
.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsetsGeometry.all(16.0),
|
||||
child: Text(
|
||||
L10n.of(context).emptyChatSearch,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
// #Pangea
|
||||
// if (!controller.isSearchMode &&
|
||||
// AppSettings.showPresences.value)
|
||||
// GestureDetector(
|
||||
// onLongPress: () => controller.dismissStatusList(),
|
||||
// child: StatusMessageList(
|
||||
// onStatusEdit: controller.setStatus,
|
||||
// ),
|
||||
// ),
|
||||
// if (client.rooms.isNotEmpty && !controller.isSearchMode)
|
||||
// SizedBox(
|
||||
// height: 64,
|
||||
// child: ListView(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 12.0,
|
||||
// vertical: 12.0,
|
||||
// ),
|
||||
// shrinkWrap: true,
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// children:
|
||||
// [
|
||||
// if (AppSettings.separateChatTypes.value)
|
||||
// ActiveFilter.messages
|
||||
// else
|
||||
// ActiveFilter.allChats,
|
||||
// ActiveFilter.groups,
|
||||
// ActiveFilter.unread,
|
||||
// if (spaceDelegateCandidates.isNotEmpty &&
|
||||
// !AppSettings
|
||||
// .displayNavigationRail
|
||||
// .value &&
|
||||
// !FluffyThemes.isColumnMode(context))
|
||||
// ActiveFilter.spaces,
|
||||
// ]
|
||||
// .map(
|
||||
// (filter) => Padding(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 4.0,
|
||||
// ),
|
||||
// child: FilterChip(
|
||||
// selected:
|
||||
// filter == controller.activeFilter,
|
||||
// onSelected: (_) =>
|
||||
// controller.setActiveFilter(filter),
|
||||
// label: Text(
|
||||
// filter.toLocalizedString(context),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// .toList(),
|
||||
// ),
|
||||
// ),
|
||||
// if (controller.isSearchMode)
|
||||
// SearchTitle(
|
||||
// title: L10n.of(context).chats,
|
||||
// icon: const Icon(Icons.forum_outlined),
|
||||
// ),
|
||||
// if (client.prevBatch != null &&
|
||||
// rooms.isEmpty &&
|
||||
// !controller.isSearchMode) ...[
|
||||
// Column(
|
||||
// mainAxisAlignment: .center,
|
||||
// children: [
|
||||
// Stack(
|
||||
// alignment: Alignment.center,
|
||||
// children: [
|
||||
// const Column(
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// DummyChatListItem(opacity: 0.5, animate: false),
|
||||
// DummyChatListItem(opacity: 0.3, animate: false),
|
||||
// ],
|
||||
// ),
|
||||
// Icon(
|
||||
// CupertinoIcons.chat_bubble_text_fill,
|
||||
// size: 128,
|
||||
// color: theme.colorScheme.secondary,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: Text(
|
||||
// client.rooms.isEmpty
|
||||
// ? L10n.of(context).noChatsFoundHere
|
||||
// : L10n.of(context).noMoreChatsFound,
|
||||
// textAlign: TextAlign.center,
|
||||
// style: TextStyle(
|
||||
// fontSize: 18,
|
||||
// color: theme.colorScheme.secondary,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
if (controller.isSearchMode &&
|
||||
rooms
|
||||
.where(
|
||||
(room) => room
|
||||
.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
)
|
||||
.toLowerCase()
|
||||
.contains(filter),
|
||||
)
|
||||
.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsetsGeometry.all(16.0),
|
||||
child: Text(
|
||||
L10n.of(context).emptyChatSearch,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
]),
|
||||
),
|
||||
if (client.prevBatch == null)
|
||||
SliverList(
|
||||
|
|
@ -302,17 +300,16 @@ class ChatListViewBody extends StatelessWidget {
|
|||
vertical: 1,
|
||||
),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: ListTile(
|
||||
leading: const BotFace(
|
||||
expression: BotExpression.idle,
|
||||
width: Avatar.defaultSize,
|
||||
),
|
||||
trailing: const Icon(
|
||||
Icons.chat_bubble_outline,
|
||||
),
|
||||
trailing: const Icon(Icons.chat_bubble_outline),
|
||||
title: Text(L10n.of(context).directMessageBotTitle),
|
||||
subtitle: Text(L10n.of(context).directMessageBotDesc),
|
||||
onTap: () async {
|
||||
|
|
@ -338,8 +335,9 @@ class ChatListViewBody extends StatelessWidget {
|
|||
vertical: 1,
|
||||
),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
|
|
@ -366,8 +364,7 @@ class ChatListViewBody extends StatelessWidget {
|
|||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
final roomId = await Matrix.of(context)
|
||||
.client
|
||||
final roomId = await Matrix.of(context).client
|
||||
.startDirectChat(
|
||||
Environment.supportUserId,
|
||||
enableEncryption: false,
|
||||
|
|
@ -391,10 +388,7 @@ class ChatListViewBody extends StatelessWidget {
|
|||
}
|
||||
|
||||
class PublicRoomsHorizontalList extends StatelessWidget {
|
||||
const PublicRoomsHorizontalList({
|
||||
super.key,
|
||||
required this.publicRooms,
|
||||
});
|
||||
const PublicRoomsHorizontalList({super.key, required this.publicRooms});
|
||||
|
||||
final List<PublishedRoomsChunk>? publicRooms;
|
||||
|
||||
|
|
@ -413,7 +407,8 @@ class PublicRoomsHorizontalList extends StatelessWidget {
|
|||
scrollDirection: Axis.horizontal,
|
||||
itemCount: publicRooms.length,
|
||||
itemBuilder: (context, i) => _SearchItem(
|
||||
title: publicRooms[i].name ??
|
||||
title:
|
||||
publicRooms[i].name ??
|
||||
publicRooms[i].canonicalAlias?.localpart ??
|
||||
L10n.of(context).group,
|
||||
avatar: publicRooms[i].avatarUrl,
|
||||
|
|
@ -444,31 +439,26 @@ class _SearchItem extends StatelessWidget {
|
|||
|
||||
@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,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
onTap: onPressed,
|
||||
child: SizedBox(
|
||||
width: 84,
|
||||
child: Column(
|
||||
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,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// #Pangea
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
// import 'package:matrix/matrix.dart';
|
||||
|
|
@ -35,19 +34,19 @@
|
|||
// title: StreamBuilder(
|
||||
// stream: client.onSyncStatus.stream,
|
||||
// builder: (context, snapshot) {
|
||||
// final status = client.onSyncStatus.value ??
|
||||
// final status =
|
||||
// client.onSyncStatus.value ??
|
||||
// const SyncStatusUpdate(SyncStatus.waitingForResponse);
|
||||
// final hide = client.onSync.value != null &&
|
||||
// final hide =
|
||||
// client.onSync.value != null &&
|
||||
// status.status != SyncStatus.error &&
|
||||
// client.prevBatch != null;
|
||||
// return TextField(
|
||||
// controller: controller.searchController,
|
||||
// focusNode: controller.searchFocusNode,
|
||||
// textInputAction: TextInputAction.search,
|
||||
// onChanged: (text) => controller.onSearchEnter(
|
||||
// text,
|
||||
// globalSearch: globalSearch,
|
||||
// ),
|
||||
// onChanged: (text) =>
|
||||
// controller.onSearchEnter(text, globalSearch: globalSearch),
|
||||
// decoration: InputDecoration(
|
||||
// filled: true,
|
||||
// fillColor: theme.colorScheme.secondaryContainer,
|
||||
|
|
@ -67,19 +66,19 @@
|
|||
// ),
|
||||
// prefixIcon: hide
|
||||
// ? controller.isSearchMode
|
||||
// ? IconButton(
|
||||
// tooltip: L10n.of(context).cancel,
|
||||
// icon: const Icon(Icons.close_outlined),
|
||||
// onPressed: controller.cancelSearch,
|
||||
// color: theme.colorScheme.onPrimaryContainer,
|
||||
// )
|
||||
// : IconButton(
|
||||
// onPressed: controller.startSearch,
|
||||
// icon: Icon(
|
||||
// Icons.search_outlined,
|
||||
// ? IconButton(
|
||||
// tooltip: L10n.of(context).cancel,
|
||||
// icon: const Icon(Icons.close_outlined),
|
||||
// onPressed: controller.cancelSearch,
|
||||
// color: theme.colorScheme.onPrimaryContainer,
|
||||
// ),
|
||||
// )
|
||||
// )
|
||||
// : IconButton(
|
||||
// onPressed: controller.startSearch,
|
||||
// icon: Icon(
|
||||
// Icons.search_outlined,
|
||||
// color: theme.colorScheme.onPrimaryContainer,
|
||||
// ),
|
||||
// )
|
||||
// : Container(
|
||||
// margin: const EdgeInsets.all(12),
|
||||
// width: 8,
|
||||
|
|
@ -98,37 +97,34 @@
|
|||
// ),
|
||||
// suffixIcon: controller.isSearchMode && globalSearch
|
||||
// ? controller.isSearching
|
||||
// ? const Padding(
|
||||
// padding: EdgeInsets.symmetric(
|
||||
// vertical: 10.0,
|
||||
// horizontal: 12,
|
||||
// ),
|
||||
// child: SizedBox.square(
|
||||
// dimension: 24,
|
||||
// child: CircularProgressIndicator.adaptive(
|
||||
// strokeWidth: 2,
|
||||
// ? const Padding(
|
||||
// padding: EdgeInsets.symmetric(
|
||||
// vertical: 10.0,
|
||||
// horizontal: 12,
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// : TextButton.icon(
|
||||
// onPressed: controller.setServer,
|
||||
// style: TextButton.styleFrom(
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(99),
|
||||
// child: SizedBox.square(
|
||||
// dimension: 24,
|
||||
// child: CircularProgressIndicator.adaptive(
|
||||
// strokeWidth: 2,
|
||||
// ),
|
||||
// ),
|
||||
// textStyle: const TextStyle(fontSize: 12),
|
||||
// ),
|
||||
// icon: const Icon(Icons.edit_outlined, size: 16),
|
||||
// label: Text(
|
||||
// controller.searchServer ??
|
||||
// Matrix.of(context).client.homeserver!.host,
|
||||
// maxLines: 2,
|
||||
// ),
|
||||
// )
|
||||
// : SizedBox(
|
||||
// width: 0,
|
||||
// child: ClientChooserButton(controller),
|
||||
// ),
|
||||
// )
|
||||
// : TextButton.icon(
|
||||
// onPressed: controller.setServer,
|
||||
// style: TextButton.styleFrom(
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(99),
|
||||
// ),
|
||||
// textStyle: const TextStyle(fontSize: 12),
|
||||
// ),
|
||||
// icon: const Icon(Icons.edit_outlined, size: 16),
|
||||
// label: Text(
|
||||
// controller.searchServer ??
|
||||
// Matrix.of(context).client.homeserver!.host,
|
||||
// maxLines: 2,
|
||||
// ),
|
||||
// )
|
||||
// : SizedBox(width: 0, child: ClientChooserButton(controller)),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
|
|
@ -139,4 +135,3 @@
|
|||
// @override
|
||||
// Size get preferredSize => const Size.fromHeight(56);
|
||||
// }
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -56,8 +56,9 @@ class ChatListItem extends StatelessWidget {
|
|||
final directChatMatrixId = room.directChatMatrixID;
|
||||
final isDirectChat = directChatMatrixId != null;
|
||||
final hasNotifications = room.notificationCount > 0;
|
||||
final backgroundColor =
|
||||
activeChat ? theme.colorScheme.secondaryContainer : null;
|
||||
final backgroundColor = activeChat
|
||||
? theme.colorScheme.secondaryContainer
|
||||
: null;
|
||||
final displayname = room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
);
|
||||
|
|
@ -78,10 +79,7 @@ class ChatListItem extends StatelessWidget {
|
|||
final space = this.space;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 1,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
|
|
@ -110,7 +108,8 @@ class ChatListItem extends StatelessWidget {
|
|||
child: Avatar(
|
||||
border: BorderSide(
|
||||
width: 2,
|
||||
color: backgroundColor ??
|
||||
color:
|
||||
backgroundColor ??
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
|
@ -132,14 +131,15 @@ class ChatListItem extends StatelessWidget {
|
|||
child: Avatar(
|
||||
border: space == null
|
||||
? room.isSpace
|
||||
? BorderSide(
|
||||
width: 1,
|
||||
color: theme.dividerColor,
|
||||
)
|
||||
: null
|
||||
? BorderSide(
|
||||
width: 1,
|
||||
color: theme.dividerColor,
|
||||
)
|
||||
: null
|
||||
: BorderSide(
|
||||
width: 2,
|
||||
color: backgroundColor ??
|
||||
color:
|
||||
backgroundColor ??
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
// #Pangea
|
||||
|
|
@ -148,7 +148,8 @@ class ChatListItem extends StatelessWidget {
|
|||
// AppConfig.borderRadius / 4,
|
||||
// )
|
||||
// : null,
|
||||
borderRadius: borderRadius ??
|
||||
borderRadius:
|
||||
borderRadius ??
|
||||
(room.isSpace
|
||||
? BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
|
|
@ -208,10 +209,7 @@ class ChatListItem extends StatelessWidget {
|
|||
if (isMuted)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 4.0),
|
||||
child: Icon(
|
||||
Icons.notifications_off_outlined,
|
||||
size: 16,
|
||||
),
|
||||
child: Icon(Icons.notifications_off_outlined, size: 16),
|
||||
),
|
||||
if (room.isFavourite)
|
||||
Padding(
|
||||
|
|
@ -228,8 +226,9 @@ class ChatListItem extends StatelessWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: Text(
|
||||
room.latestEventReceivedTime
|
||||
.localizedTimeShort(context),
|
||||
room.latestEventReceivedTime.localizedTimeShort(
|
||||
context,
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
|
|
@ -239,8 +238,8 @@ class ChatListItem extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisAlignment: .center,
|
||||
children: <Widget>[
|
||||
if (typingText.isEmpty &&
|
||||
ownMessage &&
|
||||
|
|
@ -266,134 +265,129 @@ class ChatListItem extends StatelessWidget {
|
|||
),
|
||||
)
|
||||
: room.lastEvent?.relationshipType ==
|
||||
RelationshipTypes.thread
|
||||
? Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
RelationshipTypes.thread
|
||||
? Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
),
|
||||
margin: const EdgeInsets.only(right: 4.0),
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.message_outlined,
|
||||
size: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
L10n.of(context).thread,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
margin: const EdgeInsets.only(right: 4.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.message_outlined,
|
||||
size: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
L10n.of(context).thread,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
Expanded(
|
||||
child: room.isSpace && room.membership == Membership.join
|
||||
? Text(
|
||||
// #Pangea
|
||||
// L10n.of(context)
|
||||
// .countChats(room.spaceChildren.length),
|
||||
// L10n.of(
|
||||
// context,
|
||||
// ).countChats(room.spaceChildren.length),
|
||||
L10n.of(context).countChats(room.spaceChildCount),
|
||||
// Pangea#
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
)
|
||||
: typingText.isNotEmpty
|
||||
? Text(
|
||||
typingText,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
)
|
||||
// #Pangea
|
||||
: room.lastEvent != null
|
||||
? ChatListItemSubtitle(
|
||||
room: room,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
? Text(
|
||||
typingText,
|
||||
style: TextStyle(color: theme.colorScheme.primary),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
)
|
||||
// #Pangea
|
||||
: room.lastEvent != null
|
||||
? ChatListItemSubtitle(
|
||||
room: room,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
// Pangea#
|
||||
: FutureBuilder(
|
||||
key: ValueKey(
|
||||
'${lastEvent?.eventId}_${lastEvent?.type}_${lastEvent?.redacted}',
|
||||
),
|
||||
future: needLastEventSender
|
||||
? lastEvent.calcLocalizedBody(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix:
|
||||
(!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId),
|
||||
)
|
||||
// Pangea#
|
||||
: FutureBuilder(
|
||||
key: ValueKey(
|
||||
'${lastEvent?.eventId}_${lastEvent?.type}_${lastEvent?.redacted}',
|
||||
),
|
||||
future: needLastEventSender
|
||||
? lastEvent.calcLocalizedBody(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix:
|
||||
(!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent
|
||||
?.senderId),
|
||||
)
|
||||
: null,
|
||||
initialData:
|
||||
lastEvent?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix: (!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId),
|
||||
),
|
||||
builder: (context, snapshot) => Text(
|
||||
room.membership == Membership.invite
|
||||
? room
|
||||
.getState(
|
||||
EventTypes.RoomMember,
|
||||
room.client.userID!,
|
||||
)
|
||||
?.content
|
||||
.tryGet<String>('reason') ??
|
||||
(isDirectChat
|
||||
? L10n.of(context)
|
||||
.newChatRequest
|
||||
// #Pangea
|
||||
// : L10n.of(context)
|
||||
// .inviteGroupChat)
|
||||
: L10n.of(context).inviteChat)
|
||||
// Pangea#
|
||||
: snapshot.data ??
|
||||
L10n.of(context).noMessagesYet,
|
||||
softWrap: false,
|
||||
maxLines:
|
||||
room.notificationCount >= 1 ? 2 : 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: unread || room.hasNewMessages
|
||||
? theme.colorScheme.onSurface
|
||||
: theme.colorScheme.outline,
|
||||
decoration:
|
||||
room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
: null,
|
||||
initialData: lastEvent?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix:
|
||||
(!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId),
|
||||
),
|
||||
builder: (context, snapshot) => Text(
|
||||
room.membership == Membership.invite
|
||||
? room
|
||||
.getState(
|
||||
EventTypes.RoomMember,
|
||||
room.client.userID!,
|
||||
)
|
||||
?.content
|
||||
.tryGet<String>('reason') ??
|
||||
(isDirectChat
|
||||
? L10n.of(context).newChatRequest
|
||||
// #Pangea
|
||||
// : L10n.of(context).inviteGroupChat)
|
||||
: L10n.of(context).inviteChat)
|
||||
// Pangea#
|
||||
: snapshot.data ??
|
||||
L10n.of(context).noMessagesYet,
|
||||
softWrap: false,
|
||||
maxLines: room.notificationCount >= 1 ? 2 : 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: unread || room.hasNewMessages
|
||||
? theme.colorScheme.onSurface
|
||||
: theme.colorScheme.outline,
|
||||
decoration: room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
UnreadBubble(room: room),
|
||||
|
|
@ -402,27 +396,27 @@ class ChatListItem extends StatelessWidget {
|
|||
onTap: onTap,
|
||||
trailing: onForget == null
|
||||
? room.membership == Membership.invite
|
||||
? IconButton(
|
||||
tooltip: L10n.of(context).declineInvitation,
|
||||
icon: const Icon(Icons.delete_forever_outlined),
|
||||
color: theme.colorScheme.error,
|
||||
onPressed: () async {
|
||||
final consent = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).declineInvitation,
|
||||
message: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).yes,
|
||||
isDestructive: true,
|
||||
);
|
||||
if (consent != OkCancelResult.ok) return;
|
||||
if (!context.mounted) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.leave,
|
||||
);
|
||||
},
|
||||
)
|
||||
: null
|
||||
? IconButton(
|
||||
tooltip: L10n.of(context).declineInvitation,
|
||||
icon: const Icon(Icons.delete_forever_outlined),
|
||||
color: theme.colorScheme.error,
|
||||
onPressed: () async {
|
||||
final consent = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).declineInvitation,
|
||||
message: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).yes,
|
||||
isDestructive: true,
|
||||
);
|
||||
if (consent != OkCancelResult.ok) return;
|
||||
if (!context.mounted) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.leave,
|
||||
);
|
||||
},
|
||||
)
|
||||
: null
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.delete_outlined),
|
||||
onPressed: onForget,
|
||||
|
|
|
|||
|
|
@ -36,10 +36,7 @@ class ChatListView extends StatelessWidget {
|
|||
// onGoToChats: controller.clearActiveSpace,
|
||||
// onGoToSpaceId: controller.setActiveSpace,
|
||||
// ),
|
||||
// Container(
|
||||
// color: Theme.of(context).dividerColor,
|
||||
// width: 1,
|
||||
// ),
|
||||
// Container(color: Theme.of(context).dividerColor, width: 1),
|
||||
// ],
|
||||
// Pangea#
|
||||
Expanded(
|
||||
|
|
@ -52,8 +49,8 @@ class ChatListView extends StatelessWidget {
|
|||
// body: ChatListViewBody(controller),
|
||||
body: ChatListViewBodyWrapper(controller: controller),
|
||||
// Pangea#
|
||||
floatingActionButton: !controller.isSearchMode &&
|
||||
controller.activeSpaceId == null
|
||||
floatingActionButton:
|
||||
!controller.isSearchMode && controller.activeSpaceId == null
|
||||
? FloatingActionButton.extended(
|
||||
onPressed: () => context.go('/rooms/newprivatechat'),
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@
|
|||
// (a, b) => a!.isValidMatrixId == b!.isValidMatrixId
|
||||
// ? 0
|
||||
// : a.isValidMatrixId && !b.isValidMatrixId
|
||||
// ? -1
|
||||
// : 1,
|
||||
// ? -1
|
||||
// : 1,
|
||||
// );
|
||||
// return <PopupMenuEntry<Object>>[
|
||||
// PopupMenuItem(
|
||||
|
|
@ -97,8 +97,8 @@
|
|||
// PopupMenuItem(
|
||||
// value: null,
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// crossAxisAlignment: .start,
|
||||
// mainAxisSize: .min,
|
||||
// children: [
|
||||
// Text(
|
||||
// bundle!,
|
||||
|
|
@ -123,7 +123,8 @@
|
|||
// children: [
|
||||
// Avatar(
|
||||
// mxContent: snapshot.data?.avatarUrl,
|
||||
// name: snapshot.data?.displayName ??
|
||||
// name:
|
||||
// snapshot.data?.displayName ??
|
||||
// client.userID!.localpart,
|
||||
// size: 32,
|
||||
// ),
|
||||
|
|
@ -193,10 +194,7 @@
|
|||
// );
|
||||
// }
|
||||
|
||||
// void _clientSelected(
|
||||
// Object object,
|
||||
// BuildContext context,
|
||||
// ) async {
|
||||
// void _clientSelected(Object object, BuildContext context) async {
|
||||
// if (object is Client) {
|
||||
// controller.setActiveClient(object);
|
||||
// } else if (object is String) {
|
||||
|
|
|
|||
|
|
@ -59,13 +59,13 @@ class NaviRailItem extends StatelessWidget {
|
|||
// #Pangea
|
||||
// return SizedBox(
|
||||
// height: 72,
|
||||
// width: FluffyThemes.navRailWidth,
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: width - (isColumnMode ? 16.0 : 12.0),
|
||||
width: width,
|
||||
// width: FluffyThemes.navRailWidth,
|
||||
// Pangea#
|
||||
child: Stack(
|
||||
children: [
|
||||
|
|
@ -76,8 +76,8 @@ class NaviRailItem extends StatelessWidget {
|
|||
child: AnimatedContainer(
|
||||
width: isSelected
|
||||
? FluffyThemes.isColumnMode(context)
|
||||
? 8
|
||||
: 4
|
||||
? 8
|
||||
: 4
|
||||
: 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
|
|
@ -97,10 +97,10 @@ class NaviRailItem extends StatelessWidget {
|
|||
curve: FluffyThemes.animationCurve,
|
||||
// #Pangea
|
||||
// child: Material(
|
||||
// borderRadius: borderRadius,
|
||||
// color: isSelected
|
||||
// ? theme.colorScheme.primaryContainer
|
||||
// : theme.colorScheme.surfaceContainerHigh,
|
||||
// borderRadius: borderRadius,
|
||||
// color: isSelected
|
||||
// ? theme.colorScheme.primaryContainer
|
||||
// : theme.colorScheme.surfaceContainerHigh,
|
||||
child: UnreadRoomsBadge(
|
||||
filter: unreadBadgeFilter ?? (_) => false,
|
||||
badgePosition: BadgePosition.topEnd(
|
||||
|
|
@ -110,7 +110,8 @@ class NaviRailItem extends StatelessWidget {
|
|||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor ??
|
||||
color:
|
||||
backgroundColor ??
|
||||
(isSelected
|
||||
? theme.colorScheme.primaryContainer
|
||||
: theme.colorScheme.surfaceContainerHigh),
|
||||
|
|
@ -151,6 +152,7 @@ class NaviRailItem extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
if (expanded)
|
||||
Flexible(
|
||||
child: Container(
|
||||
|
|
@ -174,6 +176,7 @@ class NaviRailItem extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -22,14 +22,8 @@ class SearchTitle extends StatelessWidget {
|
|||
|
||||
return Material(
|
||||
shape: Border(
|
||||
top: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
bottom: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
top: BorderSide(color: theme.dividerColor, width: 1),
|
||||
bottom: BorderSide(color: theme.dividerColor, width: 1),
|
||||
),
|
||||
color: color ?? theme.colorScheme.surface,
|
||||
child: InkWell(
|
||||
|
|
@ -38,10 +32,7 @@ class SearchTitle extends StatelessWidget {
|
|||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: IconTheme(
|
||||
data: theme.iconTheme.copyWith(size: 16),
|
||||
child: Row(
|
||||
|
|
|
|||
|
|
@ -28,12 +28,7 @@ enum AddRoomType { chat, subspace }
|
|||
|
||||
enum SpaceChildAction { edit, moveToSpace, removeFromSpace }
|
||||
|
||||
enum SpaceActions {
|
||||
settings,
|
||||
invite,
|
||||
members,
|
||||
leave,
|
||||
}
|
||||
enum SpaceActions { settings, invite, members, leave }
|
||||
|
||||
class SpaceView extends StatefulWidget {
|
||||
final String spaceId;
|
||||
|
|
@ -124,8 +119,9 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
} catch (e, s) {
|
||||
Logs().w('Unable to load hierarchy', e, s);
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
|
|
@ -141,9 +137,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
builder: (_) => PublicRoomDialog(
|
||||
chunk: item,
|
||||
via: space?.spaceChildren
|
||||
.firstWhereOrNull(
|
||||
(child) => child.roomId == item.roomId,
|
||||
)
|
||||
.firstWhereOrNull((child) => child.roomId == item.roomId)
|
||||
?.via,
|
||||
),
|
||||
);
|
||||
|
|
@ -224,8 +218,9 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
if (roomType == AddRoomType.subspace) {
|
||||
roomId = await client.createSpace(
|
||||
name: names,
|
||||
visibility:
|
||||
isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private,
|
||||
visibility: isPublicSpace
|
||||
? sdk.Visibility.public
|
||||
: sdk.Visibility.private,
|
||||
);
|
||||
} else {
|
||||
roomId = await client.createGroupChat(
|
||||
|
|
@ -234,8 +229,9 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
preset: isPublicSpace
|
||||
? CreateRoomPreset.publicChat
|
||||
: CreateRoomPreset.privateChat,
|
||||
visibility:
|
||||
isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private,
|
||||
visibility: isPublicSpace
|
||||
? sdk.Visibility.public
|
||||
: sdk.Visibility.private,
|
||||
initialState: isPublicSpace
|
||||
? null
|
||||
: [
|
||||
|
|
@ -289,7 +285,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: SpaceChildAction.moveToSpace,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.move_down_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -300,7 +296,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: SpaceChildAction.edit,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.edit_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -311,7 +307,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: SpaceChildAction.removeFromSpace,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.group_remove_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -344,8 +340,9 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
.map(
|
||||
(space) => AdaptiveModalAction(
|
||||
value: space,
|
||||
label: space
|
||||
.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
label: space.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
|
@ -392,19 +389,12 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
final displayname =
|
||||
room?.getLocalizedDisplayname() ?? L10n.of(context).nothingFound;
|
||||
const avatarSize = Avatar.defaultSize / 1.5;
|
||||
final isAdmin = room?.canChangeStateEvent(
|
||||
EventTypes.SpaceChild,
|
||||
) ==
|
||||
true;
|
||||
final isAdmin = room?.canChangeStateEvent(EventTypes.SpaceChild) == true;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: FluffyThemes.isColumnMode(context)
|
||||
? null
|
||||
: Center(
|
||||
child: CloseButton(
|
||||
onPressed: widget.onBack,
|
||||
),
|
||||
),
|
||||
: Center(child: CloseButton(onPressed: widget.onBack)),
|
||||
automaticallyImplyLeading: false,
|
||||
titleSpacing: FluffyThemes.isColumnMode(context) ? null : 0,
|
||||
title: ListTile(
|
||||
|
|
@ -432,7 +422,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: AddRoomType.chat,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.group_add_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -443,7 +433,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: AddRoomType.subspace,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.workspaces_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -460,7 +450,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: SpaceActions.settings,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.settings_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -471,7 +461,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: SpaceActions.invite,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.person_add_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -482,7 +472,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: SpaceActions.members,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.group_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -497,7 +487,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
PopupMenuItem(
|
||||
value: SpaceActions.leave,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.delete_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -510,12 +500,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
],
|
||||
),
|
||||
body: room == null
|
||||
? const Center(
|
||||
child: Icon(
|
||||
Icons.search_outlined,
|
||||
size: 80,
|
||||
),
|
||||
)
|
||||
? const Center(child: Icon(Icons.search_outlined, size: 80))
|
||||
: StreamBuilder(
|
||||
stream: room.client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
|
|
@ -573,7 +558,8 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
);
|
||||
}
|
||||
final item = _discoveredChildren[i];
|
||||
final displayname = item.name ??
|
||||
final displayname =
|
||||
item.name ??
|
||||
item.canonicalAlias ??
|
||||
L10n.of(context).emptyChat;
|
||||
if (!displayname.toLowerCase().contains(filter)) {
|
||||
|
|
@ -589,27 +575,31 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
vertical: 1,
|
||||
),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: joinedRoom != null &&
|
||||
color:
|
||||
joinedRoom != null &&
|
||||
widget.activeChat == joinedRoom.id
|
||||
? theme.colorScheme.secondaryContainer
|
||||
: Colors.transparent,
|
||||
child: HoverBuilder(
|
||||
builder: (context, hovered) => ListTile(
|
||||
visualDensity:
|
||||
const VisualDensity(vertical: -0.5),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 8),
|
||||
visualDensity: const VisualDensity(
|
||||
vertical: -0.5,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
),
|
||||
onTap: joinedRoom != null
|
||||
? () => widget.onChatTab(joinedRoom!)
|
||||
: () => _joinChildRoom(item),
|
||||
onLongPress: isAdmin
|
||||
? () => _showSpaceChildEditMenu(
|
||||
context,
|
||||
item.roomId,
|
||||
)
|
||||
context,
|
||||
item.roomId,
|
||||
)
|
||||
: null,
|
||||
leading: hovered && isAdmin
|
||||
? SizedBox.square(
|
||||
|
|
@ -618,16 +608,18 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
splashRadius: avatarSize,
|
||||
iconSize: 14,
|
||||
style: IconButton.styleFrom(
|
||||
foregroundColor: theme.colorScheme
|
||||
foregroundColor: theme
|
||||
.colorScheme
|
||||
.onTertiaryContainer,
|
||||
backgroundColor: theme
|
||||
.colorScheme.tertiaryContainer,
|
||||
.colorScheme
|
||||
.tertiaryContainer,
|
||||
),
|
||||
onPressed: () =>
|
||||
_showSpaceChildEditMenu(
|
||||
context,
|
||||
item.roomId,
|
||||
),
|
||||
context,
|
||||
item.roomId,
|
||||
),
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
),
|
||||
)
|
||||
|
|
@ -637,11 +629,13 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
name: '#',
|
||||
backgroundColor:
|
||||
theme.colorScheme.surfaceContainer,
|
||||
textColor: item.name?.darkColor ??
|
||||
textColor:
|
||||
item.name?.darkColor ??
|
||||
theme.colorScheme.onSurface,
|
||||
border: item.roomType == 'm.space'
|
||||
? BorderSide(
|
||||
color: theme.colorScheme
|
||||
color: theme
|
||||
.colorScheme
|
||||
.surfaceContainerHighest,
|
||||
)
|
||||
: null,
|
||||
|
|
|
|||
|
|
@ -13,10 +13,7 @@ import '../../widgets/adaptive_dialogs/user_dialog.dart';
|
|||
class StatusMessageList extends StatelessWidget {
|
||||
final void Function() onStatusEdit;
|
||||
|
||||
const StatusMessageList({
|
||||
required this.onStatusEdit,
|
||||
super.key,
|
||||
});
|
||||
const StatusMessageList({required this.onStatusEdit, super.key});
|
||||
|
||||
static const double height = 116;
|
||||
|
||||
|
|
@ -24,10 +21,7 @@ class StatusMessageList extends StatelessWidget {
|
|||
final client = Matrix.of(context).client;
|
||||
if (profile.userId == client.userID) return onStatusEdit();
|
||||
|
||||
UserDialog.show(
|
||||
context: context,
|
||||
profile: profile,
|
||||
);
|
||||
UserDialog.show(context: context, profile: profile);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -56,8 +50,9 @@ class StatusMessageList extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
final presences =
|
||||
snapshot.data?.where(isInterestingPresence).toList();
|
||||
final presences = snapshot.data
|
||||
?.where(isInterestingPresence)
|
||||
.toList();
|
||||
|
||||
// If no other presences than the own entry is interesting, we
|
||||
// hide the presence header.
|
||||
|
|
@ -131,7 +126,8 @@ class PresenceAvatar extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
|
||||
final profile = snapshot.data;
|
||||
final displayName = profile?.displayName ??
|
||||
final displayName =
|
||||
profile?.displayName ??
|
||||
presence.userid.localpart ??
|
||||
presence.userid;
|
||||
final statusMsg = presence.statusMsg;
|
||||
|
|
@ -163,11 +159,13 @@ class PresenceAvatar extends StatelessWidget {
|
|||
decoration: BoxDecoration(
|
||||
// #Pangea
|
||||
// gradient: presence.gradient,
|
||||
gradient: gradient ??
|
||||
gradient:
|
||||
gradient ??
|
||||
(showPresence ? presence.gradient : null),
|
||||
// Pangea#
|
||||
borderRadius:
|
||||
BorderRadius.circular(avatarSize),
|
||||
borderRadius: BorderRadius.circular(
|
||||
avatarSize,
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Container(
|
||||
|
|
@ -175,8 +173,9 @@ class PresenceAvatar extends StatelessWidget {
|
|||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface,
|
||||
borderRadius:
|
||||
BorderRadius.circular(avatarSize),
|
||||
borderRadius: BorderRadius.circular(
|
||||
avatarSize,
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
child: Avatar(
|
||||
|
|
@ -217,7 +216,7 @@ class PresenceAvatar extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
// #Pangea
|
||||
if (floatingIndicator != null) floatingIndicator!,
|
||||
?floatingIndicator,
|
||||
// Pangea#
|
||||
if (statusMsg != null) ...[
|
||||
Positioned(
|
||||
|
|
@ -226,9 +225,8 @@ class PresenceAvatar extends StatelessWidget {
|
|||
right: 8,
|
||||
child: Column(
|
||||
spacing: 2,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Material(
|
||||
elevation: statusMsgBubbleElevation,
|
||||
|
|
@ -254,8 +252,9 @@ class PresenceAvatar extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 8.0),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8.0,
|
||||
),
|
||||
child: Material(
|
||||
color: statusMsgBubbleColor,
|
||||
elevation: statusMsgBubbleElevation,
|
||||
|
|
@ -270,8 +269,9 @@ class PresenceAvatar extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 13.0),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 13.0,
|
||||
),
|
||||
child: Material(
|
||||
color: statusMsgBubbleColor,
|
||||
elevation: statusMsgBubbleElevation,
|
||||
|
|
@ -304,9 +304,7 @@ class PresenceAvatar extends StatelessWidget {
|
|||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
),
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -320,10 +318,12 @@ class PresenceAvatar extends StatelessWidget {
|
|||
|
||||
extension on Client {
|
||||
Set<String> get interestingPresences {
|
||||
final allHeroes = rooms.map((room) => room.summary.mHeroes).fold(
|
||||
<String>{},
|
||||
(previousValue, element) => previousValue..addAll(element ?? {}),
|
||||
);
|
||||
final allHeroes = rooms
|
||||
.map((room) => room.summary.mHeroes)
|
||||
.fold(
|
||||
<String>{},
|
||||
(previousValue, element) => previousValue..addAll(element ?? {}),
|
||||
);
|
||||
allHeroes.add(userID!);
|
||||
return allHeroes;
|
||||
}
|
||||
|
|
@ -341,31 +341,23 @@ extension on CachedPresence {
|
|||
|
||||
LinearGradient get gradient => presence.isOnline == true
|
||||
? LinearGradient(
|
||||
colors: [
|
||||
Colors.green,
|
||||
Colors.green.shade200,
|
||||
Colors.green.shade900,
|
||||
],
|
||||
colors: [Colors.green, Colors.green.shade200, Colors.green.shade900],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: presence.isUnavailable
|
||||
? LinearGradient(
|
||||
colors: [
|
||||
Colors.yellow,
|
||||
Colors.yellow.shade200,
|
||||
Colors.yellow.shade900,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: LinearGradient(
|
||||
colors: [
|
||||
Colors.grey,
|
||||
Colors.grey.shade200,
|
||||
Colors.grey.shade900,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
? LinearGradient(
|
||||
colors: [
|
||||
Colors.yellow,
|
||||
Colors.yellow.shade200,
|
||||
Colors.yellow.shade900,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: LinearGradient(
|
||||
colors: [Colors.grey, Colors.grey.shade200, Colors.grey.shade900],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ class UnreadBubble extends StatelessWidget {
|
|||
final hasNotifications = room.notificationCount > 0;
|
||||
final unreadBubbleSize = unread || room.hasNewMessages
|
||||
? room.notificationCount > 0
|
||||
? 20.0
|
||||
: 14.0
|
||||
? 20.0
|
||||
: 14.0
|
||||
: 0.0;
|
||||
return AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
|
|
@ -27,13 +27,13 @@ class UnreadBubble extends StatelessWidget {
|
|||
width: !hasNotifications && !unread && !room.hasNewMessages
|
||||
? 0
|
||||
: (unreadBubbleSize - 9) * room.notificationCount.toString().length +
|
||||
9,
|
||||
9,
|
||||
decoration: BoxDecoration(
|
||||
color: room.highlightCount > 0
|
||||
? theme.colorScheme.error
|
||||
: hasNotifications || room.markedUnread
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primaryContainer,
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
child: hasNotifications
|
||||
|
|
@ -43,8 +43,8 @@ class UnreadBubble extends StatelessWidget {
|
|||
color: room.highlightCount > 0
|
||||
? theme.colorScheme.onError
|
||||
: hasNotifications
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onPrimaryContainer,
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onPrimaryContainer,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -15,11 +15,7 @@ class ChatMembersPage extends StatefulWidget {
|
|||
|
||||
// #Pangea
|
||||
// const ChatMembersPage({required this.roomId, super.key});
|
||||
const ChatMembersPage({
|
||||
required this.roomId,
|
||||
this.filter,
|
||||
super.key,
|
||||
});
|
||||
const ChatMembersPage({required this.roomId, this.filter, super.key});
|
||||
// Pangea#
|
||||
|
||||
@override
|
||||
|
|
@ -58,8 +54,7 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
void setFilter([dynamic _]) async {
|
||||
final filter = filterController.text.toLowerCase().trim();
|
||||
|
||||
final members = this
|
||||
.members
|
||||
final members = this.members
|
||||
?.where((member) => member.membership == membershipFilter)
|
||||
.toList();
|
||||
|
||||
|
|
@ -71,14 +66,15 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
return;
|
||||
}
|
||||
setState(() {
|
||||
filteredMembers = members
|
||||
?.where(
|
||||
(user) =>
|
||||
user.displayName?.toLowerCase().contains(filter) ??
|
||||
user.id.toLowerCase().contains(filter),
|
||||
)
|
||||
.toList()
|
||||
?..sort((b, a) => a.powerLevel.compareTo(b.powerLevel));
|
||||
filteredMembers =
|
||||
members
|
||||
?.where(
|
||||
(user) =>
|
||||
user.displayName?.toLowerCase().contains(filter) ??
|
||||
user.id.toLowerCase().contains(filter),
|
||||
)
|
||||
.toList()
|
||||
?..sort((b, a) => a.powerLevel.compareTo(b.powerLevel));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -88,8 +84,7 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
setState(() {
|
||||
error = null;
|
||||
});
|
||||
final participants = await Matrix.of(context)
|
||||
.client
|
||||
final participants = await Matrix.of(context).client
|
||||
.getRoomById(widget.roomId)
|
||||
?.requestParticipants(
|
||||
// #Pangea
|
||||
|
|
@ -107,9 +102,7 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
|
||||
// #Pangea
|
||||
final availableFilters = (participants ?? [])
|
||||
.map(
|
||||
(p) => p.membership,
|
||||
)
|
||||
.map((p) => p.membership)
|
||||
.toSet();
|
||||
|
||||
if (availableFilters.length == 1 &&
|
||||
|
|
@ -123,8 +116,11 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
});
|
||||
setFilter();
|
||||
} catch (e, s) {
|
||||
Logs()
|
||||
.d('Unable to request participants. Try again in 3 seconds...', e, s);
|
||||
Logs().d(
|
||||
'Unable to request participants. Try again in 3 seconds...',
|
||||
e,
|
||||
s,
|
||||
);
|
||||
setState(() {
|
||||
error = e;
|
||||
});
|
||||
|
|
@ -138,14 +134,12 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
super.initState();
|
||||
refreshMembers();
|
||||
|
||||
_updateSub = Matrix.of(context)
|
||||
.client
|
||||
.onSync
|
||||
.stream
|
||||
_updateSub = Matrix.of(context).client.onSync.stream
|
||||
.where(
|
||||
(syncUpdate) =>
|
||||
syncUpdate.rooms?.join?[widget.roomId]?.timeline?.events
|
||||
?.any((state) => state.type == EventTypes.RoomMember) ??
|
||||
syncUpdate.rooms?.join?[widget.roomId]?.timeline?.events?.any(
|
||||
(state) => state.type == EventTypes.RoomMember,
|
||||
) ??
|
||||
false,
|
||||
)
|
||||
.listen(refreshMembers);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,12 @@ class ChatMembersView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final room =
|
||||
Matrix.of(context).client.getRoomById(controller.widget.roomId);
|
||||
final room = Matrix.of(
|
||||
context,
|
||||
).client.getRoomById(controller.widget.roomId);
|
||||
if (room == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).oopsSomethingWentWrong),
|
||||
),
|
||||
appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)),
|
||||
body: Center(
|
||||
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
|
||||
),
|
||||
|
|
@ -32,7 +31,8 @@ class ChatMembersView extends StatelessWidget {
|
|||
|
||||
final members = controller.filteredMembers;
|
||||
|
||||
final roomCount = (room.summary.mJoinedMemberCount ?? 0) +
|
||||
final roomCount =
|
||||
(room.summary.mJoinedMemberCount ?? 0) +
|
||||
(room.summary.mInvitedMemberCount ?? 0);
|
||||
|
||||
final error = controller.error;
|
||||
|
|
@ -41,16 +41,12 @@ class ChatMembersView extends StatelessWidget {
|
|||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const Center(child: BackButton()),
|
||||
title: Text(
|
||||
L10n.of(context).countParticipants(roomCount),
|
||||
),
|
||||
title: Text(L10n.of(context).countParticipants(roomCount)),
|
||||
actions: [
|
||||
if (room.canInvite)
|
||||
IconButton(
|
||||
onPressed: () => context.go('/rooms/${room.id}/invite'),
|
||||
icon: const Icon(
|
||||
Icons.person_add_outlined,
|
||||
),
|
||||
icon: const Icon(Icons.person_add_outlined),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -62,7 +58,7 @@ class ChatMembersView extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.error_outline),
|
||||
Text(error.toLocalizedString(context)),
|
||||
|
|
@ -77,120 +73,117 @@ class ChatMembersView extends StatelessWidget {
|
|||
),
|
||||
)
|
||||
: members == null
|
||||
? const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: members.length + 1,
|
||||
itemBuilder: (context, i) {
|
||||
if (i == 0) {
|
||||
final availableFilters = Membership.values
|
||||
.where(
|
||||
(membership) =>
|
||||
controller.members?.any(
|
||||
(member) => member.membership == membership,
|
||||
) ??
|
||||
false,
|
||||
)
|
||||
.toList();
|
||||
availableFilters
|
||||
.sort((a, b) => a == Membership.join ? -1 : 1);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: controller.filterController,
|
||||
onChanged: controller.setFilter,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor:
|
||||
theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
hintText: L10n.of(context).search,
|
||||
? const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: members.length + 1,
|
||||
itemBuilder: (context, i) {
|
||||
if (i == 0) {
|
||||
final availableFilters = Membership.values
|
||||
.where(
|
||||
(membership) =>
|
||||
controller.members?.any(
|
||||
(member) => member.membership == membership,
|
||||
) ??
|
||||
false,
|
||||
)
|
||||
.toList();
|
||||
availableFilters.sort(
|
||||
(a, b) => a == Membership.join ? -1 : 1,
|
||||
);
|
||||
return Column(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: controller.filterController,
|
||||
onChanged: controller.setFilter,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
hintText: L10n.of(context).search,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (availableFilters.length > 1)
|
||||
SizedBox(
|
||||
height: 64,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 12.0,
|
||||
),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: availableFilters.length,
|
||||
itemBuilder: (context, i) => Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
child: FilterChip(
|
||||
label: Text(switch (availableFilters[i]) {
|
||||
Membership.ban => L10n.of(context).banned,
|
||||
Membership.invite =>
|
||||
L10n.of(context).countInvited(
|
||||
room.summary.mInvitedMemberCount ??
|
||||
controller.members
|
||||
?.where(
|
||||
(member) =>
|
||||
member.membership ==
|
||||
Membership.invite,
|
||||
)
|
||||
.length ??
|
||||
0,
|
||||
),
|
||||
Membership.join =>
|
||||
L10n.of(context).countParticipants(
|
||||
room.summary.mJoinedMemberCount ??
|
||||
controller.members
|
||||
?.where(
|
||||
(member) =>
|
||||
member.membership ==
|
||||
Membership.join,
|
||||
)
|
||||
.length ??
|
||||
0,
|
||||
),
|
||||
Membership.knock => L10n.of(
|
||||
context,
|
||||
).knocking,
|
||||
Membership.leave => L10n.of(
|
||||
context,
|
||||
).leftTheChat,
|
||||
}),
|
||||
selected:
|
||||
controller.membershipFilter ==
|
||||
availableFilters[i],
|
||||
onSelected: (_) => controller
|
||||
.setMembershipFilter(availableFilters[i]),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (availableFilters.length > 1)
|
||||
SizedBox(
|
||||
height: 64,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 12.0,
|
||||
),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: availableFilters.length,
|
||||
itemBuilder: (context, i) => Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
child: FilterChip(
|
||||
label: Text(
|
||||
switch (availableFilters[i]) {
|
||||
Membership.ban =>
|
||||
L10n.of(context).banned,
|
||||
Membership.invite =>
|
||||
L10n.of(context).countInvited(
|
||||
room.summary
|
||||
.mInvitedMemberCount ??
|
||||
controller.members
|
||||
?.where(
|
||||
(member) =>
|
||||
member.membership ==
|
||||
Membership.invite,
|
||||
)
|
||||
.length ??
|
||||
0,
|
||||
),
|
||||
Membership.join =>
|
||||
L10n.of(context).countParticipants(
|
||||
room.summary.mJoinedMemberCount ??
|
||||
controller.members
|
||||
?.where(
|
||||
(member) =>
|
||||
member.membership ==
|
||||
Membership.join,
|
||||
)
|
||||
.length ??
|
||||
0,
|
||||
),
|
||||
Membership.knock =>
|
||||
L10n.of(context).knocking,
|
||||
Membership.leave =>
|
||||
L10n.of(context).leftTheChat,
|
||||
},
|
||||
),
|
||||
selected: controller.membershipFilter ==
|
||||
availableFilters[i],
|
||||
onSelected: (_) =>
|
||||
controller.setMembershipFilter(
|
||||
availableFilters[i],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
i--;
|
||||
return ParticipantListItem(members[i]);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
i--;
|
||||
return ParticipantListItem(members[i]);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
|||
}) async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
if (!room.canSendEvent(EventTypes.RoomPowerLevels)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(L10n.of(context).noPermission)),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(L10n.of(context).noPermission)));
|
||||
return;
|
||||
}
|
||||
newLevel ??= await showPermissionChooser(
|
||||
|
|
@ -72,12 +72,13 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
|||
}
|
||||
|
||||
Stream get onChanged => Matrix.of(context).client.onSync.stream.where(
|
||||
(e) =>
|
||||
(e.rooms?.join?.containsKey(roomId) ?? false) &&
|
||||
(e.rooms!.join![roomId!]?.timeline?.events
|
||||
?.any((s) => s.type == EventTypes.RoomPowerLevels) ??
|
||||
false),
|
||||
);
|
||||
(e) =>
|
||||
(e.rooms?.join?.containsKey(roomId) ?? false) &&
|
||||
(e.rooms!.join![roomId!]?.timeline?.events?.any(
|
||||
(s) => s.type == EventTypes.RoomPowerLevels,
|
||||
) ??
|
||||
false),
|
||||
);
|
||||
|
||||
// #Pangea
|
||||
Map<String, dynamic> get defaultPowerLevels {
|
||||
|
|
@ -96,10 +97,7 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
|||
return room.isSpace ? spacePowerLevels : chatPowerLevels;
|
||||
}
|
||||
|
||||
int getDefaultValue(
|
||||
String permissionKey, {
|
||||
String? category,
|
||||
}) {
|
||||
int getDefaultValue(String permissionKey, {String? category}) {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||
if (room == null) return 0;
|
||||
final powerLevelsContent = Map<String, Object?>.from(
|
||||
|
|
|
|||
|
|
@ -65,11 +65,12 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
(key, value) => MapEntry(key, controller.getDefaultValue(key)),
|
||||
);
|
||||
|
||||
Map<String, int?> missingEventsPowerLevels = Map<String, int?>.from(
|
||||
defaults.tryGetMap<String, int?>('events') ?? {},
|
||||
)..removeWhere(
|
||||
(k, v) => v is! int || eventsPowerLevels.containsKey(k),
|
||||
);
|
||||
Map<String, int?> missingEventsPowerLevels =
|
||||
Map<String, int?>.from(
|
||||
defaults.tryGetMap<String, int?>('events') ?? {},
|
||||
)..removeWhere(
|
||||
(k, v) => v is! int || eventsPowerLevels.containsKey(k),
|
||||
);
|
||||
|
||||
missingEventsPowerLevels = missingEventsPowerLevels.map(
|
||||
(key, value) => MapEntry(
|
||||
|
|
@ -95,9 +96,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.info_outlined),
|
||||
subtitle: Text(
|
||||
L10n.of(context).chatPermissionsDescription,
|
||||
),
|
||||
subtitle: Text(L10n.of(context).chatPermissionsDescription),
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
|
|
@ -113,7 +112,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
for (final entry in powerLevels.entries)
|
||||
PermissionsListTile(
|
||||
|
|
@ -148,13 +147,13 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
const key = 'rooms';
|
||||
final value =
|
||||
powerLevelsContent.containsKey('notifications')
|
||||
? powerLevelsContent
|
||||
.tryGetMap<String, Object?>(
|
||||
'notifications',
|
||||
)
|
||||
?.tryGet<int>('rooms') ??
|
||||
0
|
||||
: 0;
|
||||
? powerLevelsContent
|
||||
.tryGetMap<String, Object?>(
|
||||
'notifications',
|
||||
)
|
||||
?.tryGet<int>('rooms') ??
|
||||
0
|
||||
: 0;
|
||||
return PermissionsListTile(
|
||||
permissionKey: key,
|
||||
permission: value,
|
||||
|
|
|
|||
|
|
@ -116,8 +116,8 @@ class PermissionsListTile extends StatelessWidget {
|
|||
final color = permission >= 100
|
||||
? Colors.orangeAccent
|
||||
: permission >= 50
|
||||
? Colors.blueAccent
|
||||
: Colors.greenAccent;
|
||||
? Colors.blueAccent
|
||||
: Colors.greenAccent;
|
||||
return ListTile(
|
||||
title: Text(
|
||||
getLocalizedPowerLevelString(context),
|
||||
|
|
@ -150,14 +150,12 @@ class PermissionsListTile extends StatelessWidget {
|
|||
DropdownMenuItem(
|
||||
value: permission >= 100 ? permission : 100,
|
||||
child: Text(
|
||||
L10n.of(context)
|
||||
.adminLevel(permission >= 100 ? permission : 100),
|
||||
L10n.of(
|
||||
context,
|
||||
).adminLevel(permission >= 100 ? permission : 100),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: null,
|
||||
child: Text(L10n.of(context).custom),
|
||||
),
|
||||
DropdownMenuItem(value: null, child: Text(L10n.of(context).custom)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -11,10 +11,8 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
|||
class ChatSearchFilesTab extends StatelessWidget {
|
||||
final Room room;
|
||||
final Stream<(List<Event>, String?)>? searchStream;
|
||||
final void Function({
|
||||
String? prevBatch,
|
||||
List<Event>? previousSearchResult,
|
||||
}) startSearch;
|
||||
final void Function({String? prevBatch, List<Event>? previousSearchResult})
|
||||
startSearch;
|
||||
|
||||
const ChatSearchFilesTab({
|
||||
required this.room,
|
||||
|
|
@ -32,15 +30,13 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
final events = snapshot.data?.$1;
|
||||
if (searchStream == null || events == null) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
const CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
L10n.of(context).searchIn(
|
||||
room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
),
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -49,7 +45,7 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
|
||||
if (events.isEmpty) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
const Icon(Icons.file_present_outlined, size: 64),
|
||||
const SizedBox(height: 8),
|
||||
|
|
@ -68,9 +64,7 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
return const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -90,35 +84,35 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
prevBatch: nextBatch,
|
||||
previousSearchResult: events,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.arrow_downward_outlined,
|
||||
),
|
||||
icon: const Icon(Icons.arrow_downward_outlined),
|
||||
label: Text(L10n.of(context).searchMore),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
final event = events[i];
|
||||
final filename = event.content.tryGet<String>('filename') ??
|
||||
final filename =
|
||||
event.content.tryGet<String>('filename') ??
|
||||
event.content.tryGet<String>('body') ??
|
||||
L10n.of(context).unknownEvent('File');
|
||||
final filetype = (filename.contains('.')
|
||||
? filename.split('.').last.toUpperCase()
|
||||
: event.content
|
||||
.tryGetMap<String, dynamic>('info')
|
||||
?.tryGet<String>('mimetype')
|
||||
?.toUpperCase() ??
|
||||
'UNKNOWN');
|
||||
.tryGetMap<String, dynamic>('info')
|
||||
?.tryGet<String>('mimetype')
|
||||
?.toUpperCase() ??
|
||||
'UNKNOWN');
|
||||
final sizeString = event.sizeString;
|
||||
final prevEvent = i > 0 ? events[i - 1] : null;
|
||||
final sameEnvironment = prevEvent == null
|
||||
? false
|
||||
: prevEvent.originServerTs
|
||||
.sameEnvironment(event.originServerTs);
|
||||
: prevEvent.originServerTs.sameEnvironment(
|
||||
event.originServerTs,
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
if (!sameEnvironment) ...[
|
||||
Row(
|
||||
|
|
@ -148,8 +142,9 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
const SizedBox(height: 4),
|
||||
],
|
||||
Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: ListTile(
|
||||
|
|
|
|||
|
|
@ -13,10 +13,8 @@ import 'package:fluffychat/widgets/mxc_image.dart';
|
|||
class ChatSearchImagesTab extends StatelessWidget {
|
||||
final Room room;
|
||||
final Stream<(List<Event>, String?)>? searchStream;
|
||||
final void Function({
|
||||
String? prevBatch,
|
||||
List<Event>? previousSearchResult,
|
||||
}) startSearch;
|
||||
final void Function({String? prevBatch, List<Event>? previousSearchResult})
|
||||
startSearch;
|
||||
|
||||
const ChatSearchImagesTab({
|
||||
required this.room,
|
||||
|
|
@ -35,15 +33,13 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
final events = snapshot.data?.$1;
|
||||
if (searchStream == null || events == null) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
const CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
L10n.of(context).searchIn(
|
||||
room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
),
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -51,7 +47,7 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
}
|
||||
if (events.isEmpty) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
const Icon(Icons.photo_outlined, size: 64),
|
||||
const SizedBox(height: 8),
|
||||
|
|
@ -80,9 +76,7 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
return const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -102,9 +96,7 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
prevBatch: nextBatch,
|
||||
previousSearchResult: events,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.arrow_downward_outlined,
|
||||
),
|
||||
icon: const Icon(Icons.arrow_downward_outlined),
|
||||
label: Text(L10n.of(context).searchMore),
|
||||
),
|
||||
),
|
||||
|
|
@ -113,16 +105,13 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
|
||||
final monthEvents = eventsByMonthList[i].value;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
child: Container(height: 1, color: theme.dividerColor),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
|
|
@ -135,10 +124,7 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
child: Container(height: 1, color: theme.dividerColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -150,39 +136,35 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
clipBehavior: Clip.hardEdge,
|
||||
padding: const EdgeInsets.all(padding),
|
||||
crossAxisCount: 3,
|
||||
children: monthEvents.map(
|
||||
(event) {
|
||||
if (event.messageType == MessageTypes.Video) {
|
||||
return Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: borderRadius,
|
||||
child: EventVideoPlayer(event),
|
||||
);
|
||||
}
|
||||
return InkWell(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => ImageViewer(
|
||||
event,
|
||||
outerContext: context,
|
||||
),
|
||||
),
|
||||
children: monthEvents.map((event) {
|
||||
if (event.messageType == MessageTypes.Video) {
|
||||
return Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: borderRadius,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: borderRadius,
|
||||
child: MxcImage(
|
||||
event: event,
|
||||
width: 128,
|
||||
height: 128,
|
||||
fit: BoxFit.cover,
|
||||
animated: true,
|
||||
isThumbnail: true,
|
||||
),
|
||||
),
|
||||
child: EventVideoPlayer(event),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
}
|
||||
return InkWell(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
ImageViewer(event, outerContext: context),
|
||||
),
|
||||
borderRadius: borderRadius,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: borderRadius,
|
||||
child: MxcImage(
|
||||
event: event,
|
||||
width: 128,
|
||||
height: 128,
|
||||
fit: BoxFit.cover,
|
||||
animated: true,
|
||||
isThumbnail: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,10 +14,8 @@ class ChatSearchMessageTab extends StatelessWidget {
|
|||
final String searchQuery;
|
||||
final Room room;
|
||||
final Stream<(List<Event>, String?)>? searchStream;
|
||||
final void Function({
|
||||
String? prevBatch,
|
||||
List<Event>? previousSearchResult,
|
||||
}) startSearch;
|
||||
final void Function({String? prevBatch, List<Event>? previousSearchResult})
|
||||
startSearch;
|
||||
|
||||
const ChatSearchMessageTab({
|
||||
required this.searchQuery,
|
||||
|
|
@ -36,15 +34,13 @@ class ChatSearchMessageTab extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
if (searchStream == null) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
const Icon(Icons.search_outlined, size: 64),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
L10n.of(context).searchIn(
|
||||
room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
),
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -63,19 +59,15 @@ class ChatSearchMessageTab extends StatelessWidget {
|
|||
return SelectionArea(
|
||||
child: ListView.separated(
|
||||
itemCount: events.length + 1,
|
||||
separatorBuilder: (context, _) => Divider(
|
||||
color: theme.dividerColor,
|
||||
height: 1,
|
||||
),
|
||||
separatorBuilder: (context, _) =>
|
||||
Divider(color: theme.dividerColor, height: 1),
|
||||
itemBuilder: (context, i) {
|
||||
if (i == events.length) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -95,9 +87,7 @@ class ChatSearchMessageTab extends StatelessWidget {
|
|||
prevBatch: nextBatch,
|
||||
previousSearchResult: events,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.arrow_downward_outlined,
|
||||
),
|
||||
icon: const Icon(Icons.arrow_downward_outlined),
|
||||
label: Text(L10n.of(context).searchMore),
|
||||
),
|
||||
),
|
||||
|
|
@ -142,19 +132,18 @@ class _MessageSearchResultListTile extends StatelessWidget {
|
|||
return ListTile(
|
||||
title: Row(
|
||||
children: [
|
||||
// #Pangea
|
||||
// Avatar(mxContent: sender.avatarUrl, name: displayname, size: 16),
|
||||
Avatar(
|
||||
mxContent: sender.avatarUrl,
|
||||
name: displayname,
|
||||
// #Pangea
|
||||
userId: sender.id,
|
||||
// Pangea#
|
||||
size: 16,
|
||||
),
|
||||
// Pangea#
|
||||
const SizedBox(width: 8),
|
||||
// #Pangea
|
||||
// Text(
|
||||
// displayname,
|
||||
// ),
|
||||
// Text(displayname),
|
||||
// Expanded(
|
||||
// child: Text(
|
||||
// ' | ${event.originServerTs.localizedTimeShort(context)}',
|
||||
|
|
@ -188,24 +177,17 @@ class _MessageSearchResultListTile extends StatelessWidget {
|
|||
.calcLocalizedBodyFallback(
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
MatrixLocals(
|
||||
L10n.of(context),
|
||||
),
|
||||
MatrixLocals(L10n.of(context)),
|
||||
)
|
||||
.trim(),
|
||||
maxLines: 7,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.chevron_right_outlined,
|
||||
),
|
||||
icon: const Icon(Icons.chevron_right_outlined),
|
||||
// #Pangea
|
||||
// onPressed: () => context.go(
|
||||
// '/${Uri(
|
||||
// pathSegments: ['rooms', room.id],
|
||||
// queryParameters: {'event': event.eventId},
|
||||
// )}',
|
||||
// '/${Uri(pathSegments: ['rooms', room.id], queryParameters: {'event': event.eventId})}',
|
||||
// ),
|
||||
onPressed: () => NavigationUtil.goToSpaceRoute(
|
||||
room.id,
|
||||
|
|
|
|||
|
|
@ -75,11 +75,10 @@ class ChatSearchController extends State<ChatSearchPage>
|
|||
.map(
|
||||
(result) => (
|
||||
<String, Event>{
|
||||
for (final event in result.$1) event.eventId: event,
|
||||
// #Pangea
|
||||
// }.values.toList(),
|
||||
}
|
||||
.values
|
||||
for (final event in result.$1) event.eventId: event,
|
||||
// #Pangea
|
||||
// }.values.toList(),
|
||||
}.values
|
||||
.toList()
|
||||
.where(
|
||||
(e) => !e.hasAggregatedEvents(
|
||||
|
|
|
|||
|
|
@ -48,9 +48,7 @@ class ChatSearchView extends StatelessWidget {
|
|||
if (FluffyThemes.isThreeColumnMode(context))
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: TextField(
|
||||
controller: controller.searchController,
|
||||
onSubmitted: (_) => controller.restartSearch(),
|
||||
|
|
|
|||
|
|
@ -59,10 +59,7 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||
.tryGetMap<String, Object?>('org.matrix.msc2965.authentication')
|
||||
?.tryGet<String>('account');
|
||||
if (accountManageUrl != null) {
|
||||
launchUrlString(
|
||||
accountManageUrl,
|
||||
mode: LaunchMode.inAppBrowserView,
|
||||
);
|
||||
launchUrlString(accountManageUrl, mode: LaunchMode.inAppBrowserView);
|
||||
return;
|
||||
}
|
||||
if (await showOkCancelAlertDialog(
|
||||
|
|
@ -86,10 +83,7 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||
context: context,
|
||||
delay: false,
|
||||
future: () => matrix.client.uiaRequestBackground(
|
||||
(auth) => matrix.client.deleteDevices(
|
||||
deviceIds,
|
||||
auth: auth,
|
||||
),
|
||||
(auth) => matrix.client.deleteDevices(deviceIds, auth: auth),
|
||||
),
|
||||
);
|
||||
reload();
|
||||
|
|
@ -106,9 +100,9 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||
if (displayName == null) return;
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => Matrix.of(context)
|
||||
.client
|
||||
.updateDevice(device.deviceId, displayName: displayName),
|
||||
future: () => Matrix.of(
|
||||
context,
|
||||
).client.updateDevice(device.deviceId, displayName: displayName),
|
||||
);
|
||||
if (success.error == null) {
|
||||
reload();
|
||||
|
|
@ -130,8 +124,10 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||
.deviceKeys[device.deviceId]!
|
||||
.startVerification();
|
||||
req.onUpdate = () {
|
||||
if ({KeyVerificationState.error, KeyVerificationState.done}
|
||||
.contains(req.state)) {
|
||||
if ({
|
||||
KeyVerificationState.error,
|
||||
KeyVerificationState.done,
|
||||
}.contains(req.state)) {
|
||||
setState(() {});
|
||||
}
|
||||
};
|
||||
|
|
@ -162,9 +158,7 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||
bool _isOwnDevice(Device userDevice) =>
|
||||
userDevice.deviceId == Matrix.of(context).client.deviceID;
|
||||
|
||||
Device? get thisDevice => devices!.firstWhereOrNull(
|
||||
_isOwnDevice,
|
||||
);
|
||||
Device? get thisDevice => devices!.firstWhereOrNull(_isOwnDevice);
|
||||
|
||||
List<Device> get notThisDevice => List<Device>.from(devices!)
|
||||
..removeWhere(_isOwnDevice)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class DevicesSettingsView extends StatelessWidget {
|
|||
if (snapshot.hasError) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
const Icon(Icons.error_outlined),
|
||||
Text(snapshot.error.toString()),
|
||||
|
|
@ -47,7 +47,7 @@ class DevicesSettingsView extends StatelessWidget {
|
|||
itemBuilder: (BuildContext context, int i) {
|
||||
if (i == 0) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
if (controller.chatBackupEnabled == false)
|
||||
Padding(
|
||||
|
|
@ -57,8 +57,9 @@ class DevicesSettingsView extends StatelessWidget {
|
|||
child: Icon(Icons.info_outlined),
|
||||
),
|
||||
subtitle: Text(
|
||||
L10n.of(context)
|
||||
.noticeChatBackupDeviceVerification,
|
||||
L10n.of(
|
||||
context,
|
||||
).noticeChatBackupDeviceVerification,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,13 +9,7 @@ import '../../utils/date_time_extension.dart';
|
|||
import '../../utils/matrix_sdk_extensions/device_extension.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
|
||||
enum UserDeviceListItemAction {
|
||||
rename,
|
||||
remove,
|
||||
verify,
|
||||
block,
|
||||
unblock,
|
||||
}
|
||||
enum UserDeviceListItemAction { rename, remove, verify, block, unblock }
|
||||
|
||||
class UserDeviceListItem extends StatelessWidget {
|
||||
final Device userDevice;
|
||||
|
|
@ -38,7 +32,8 @@ class UserDeviceListItem extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = Matrix.of(context).client;
|
||||
final keys = client.userDeviceKeys[Matrix.of(context).client.userID]
|
||||
final keys = client
|
||||
.userDeviceKeys[Matrix.of(context).client.userID]
|
||||
?.deviceKeys[userDevice.deviceId];
|
||||
final isOwnDevice = userDevice.deviceId == client.deviceID;
|
||||
|
||||
|
|
@ -113,10 +108,10 @@ class UserDeviceListItem extends StatelessWidget {
|
|||
backgroundColor: keys == null
|
||||
? Colors.grey[700]
|
||||
: keys.blocked
|
||||
? Colors.red
|
||||
: keys.verified
|
||||
? Colors.green
|
||||
: Colors.orange,
|
||||
? Colors.red
|
||||
: keys.verified
|
||||
? Colors.green
|
||||
: Colors.orange,
|
||||
child: Icon(userDevice.icon),
|
||||
),
|
||||
title: Text(
|
||||
|
|
@ -126,8 +121,9 @@ class UserDeviceListItem extends StatelessWidget {
|
|||
),
|
||||
subtitle: Text(
|
||||
L10n.of(context).lastActiveAgo(
|
||||
DateTime.fromMillisecondsSinceEpoch(userDevice.lastSeenTs ?? 0)
|
||||
.localizedTimeShort(context),
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
userDevice.lastSeenTs ?? 0,
|
||||
).localizedTimeShort(context),
|
||||
),
|
||||
style: const TextStyle(fontWeight: FontWeight.w300),
|
||||
),
|
||||
|
|
@ -137,14 +133,14 @@ class UserDeviceListItem extends StatelessWidget {
|
|||
keys.blocked
|
||||
? L10n.of(context).blocked
|
||||
: keys.verified
|
||||
? L10n.of(context).verified
|
||||
: L10n.of(context).unverified,
|
||||
? L10n.of(context).verified
|
||||
: L10n.of(context).unverified,
|
||||
style: TextStyle(
|
||||
color: keys.blocked
|
||||
? Colors.red
|
||||
: keys.verified
|
||||
? Colors.green
|
||||
: Colors.orange,
|
||||
? Colors.green
|
||||
: Colors.orange,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -70,9 +70,7 @@ class _StreamView extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.black54,
|
||||
),
|
||||
decoration: const BoxDecoration(color: Colors.black54),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
|
|
@ -133,9 +131,8 @@ class Calling extends StatefulWidget {
|
|||
class MyCallingPage extends State<Calling> {
|
||||
Room? get room => call.room;
|
||||
|
||||
String get displayName => call.room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(widget.context)),
|
||||
);
|
||||
String get displayName =>
|
||||
call.room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)));
|
||||
|
||||
String get callId => widget.callId;
|
||||
|
||||
|
|
@ -219,10 +216,7 @@ class MyCallingPage extends State<Calling> {
|
|||
}
|
||||
|
||||
void cleanUp() {
|
||||
Timer(
|
||||
const Duration(seconds: 2),
|
||||
() => widget.onClear?.call(),
|
||||
);
|
||||
Timer(const Duration(seconds: 2), () => widget.onClear?.call());
|
||||
if (call.type == CallType.kVideo) {
|
||||
try {
|
||||
unawaited(WakelockPlus.disable());
|
||||
|
|
@ -295,8 +289,9 @@ class MyCallingPage extends State<Calling> {
|
|||
androidNotificationOptions: AndroidNotificationOptions(
|
||||
channelId: 'notification_channel_id',
|
||||
channelName: 'Foreground Notification',
|
||||
channelDescription:
|
||||
L10n.of(widget.context).foregroundServiceRunning,
|
||||
channelDescription: L10n.of(
|
||||
widget.context,
|
||||
).foregroundServiceRunning,
|
||||
),
|
||||
iosNotificationOptions: const IOSNotificationOptions(),
|
||||
foregroundTaskOptions: ForegroundTaskOptions(
|
||||
|
|
@ -434,9 +429,7 @@ class MyCallingPage extends State<Calling> {
|
|||
hangupButton,
|
||||
];
|
||||
case CallState.kEnded:
|
||||
return <Widget>[
|
||||
hangupButton,
|
||||
];
|
||||
return <Widget>[hangupButton];
|
||||
case CallState.kFledgling:
|
||||
case CallState.kWaitLocalMedia:
|
||||
case CallState.kCreateOffer:
|
||||
|
|
@ -458,28 +451,20 @@ class MyCallingPage extends State<Calling> {
|
|||
if (call.localHold || call.remoteOnHold) {
|
||||
var title = '';
|
||||
if (call.localHold) {
|
||||
title = '${call.room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(widget.context)),
|
||||
)} held the call.';
|
||||
title =
|
||||
'${call.room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)))} held the call.';
|
||||
} else if (call.remoteOnHold) {
|
||||
title = 'You held the call.';
|
||||
}
|
||||
stackWidgets.add(
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.pause,
|
||||
size: 48.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
const Icon(Icons.pause, size: 48.0, color: Colors.white),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
style: const TextStyle(color: Colors.white, fontSize: 24.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -488,7 +473,8 @@ class MyCallingPage extends State<Calling> {
|
|||
return stackWidgets;
|
||||
}
|
||||
|
||||
var primaryStream = call.remoteScreenSharingStream ??
|
||||
var primaryStream =
|
||||
call.remoteScreenSharingStream ??
|
||||
call.localScreenSharingStream ??
|
||||
call.remoteUserMediaStream ??
|
||||
call.localUserMediaStream;
|
||||
|
|
@ -527,8 +513,10 @@ class MyCallingPage extends State<Calling> {
|
|||
SizedBox(
|
||||
width: _localVideoWidth,
|
||||
height: _localVideoHeight,
|
||||
child:
|
||||
_StreamView(remoteUserMediaStream!, matrixClient: widget.client),
|
||||
child: _StreamView(
|
||||
remoteUserMediaStream!,
|
||||
matrixClient: widget.client,
|
||||
),
|
||||
),
|
||||
);
|
||||
secondaryStreamViews.add(const SizedBox(height: 10));
|
||||
|
|
@ -569,9 +557,7 @@ class MyCallingPage extends State<Calling> {
|
|||
child: Container(
|
||||
width: _localVideoWidth,
|
||||
margin: _localVideoMargin,
|
||||
child: Column(
|
||||
children: secondaryStreamViews,
|
||||
),
|
||||
child: Column(children: secondaryStreamViews),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -592,16 +578,14 @@ class MyCallingPage extends State<Calling> {
|
|||
width: 320.0,
|
||||
height: 150.0,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
mainAxisAlignment: .spaceAround,
|
||||
children: _buildActionButtons(isFloating),
|
||||
),
|
||||
),
|
||||
body: OrientationBuilder(
|
||||
builder: (BuildContext context, Orientation orientation) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.black87,
|
||||
),
|
||||
decoration: const BoxDecoration(color: Colors.black87),
|
||||
child: Stack(
|
||||
children: [
|
||||
..._buildContent(orientation, isFloating),
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@ class PIPView extends StatefulWidget {
|
|||
final double? floatingHeight;
|
||||
final bool avoidKeyboard;
|
||||
|
||||
final Widget Function(
|
||||
BuildContext context,
|
||||
bool isFloating,
|
||||
) builder;
|
||||
final Widget Function(BuildContext context, bool isFloating) builder;
|
||||
|
||||
const PIPView({
|
||||
super.key,
|
||||
|
|
@ -95,10 +92,7 @@ class PIPViewState extends State<PIPView> with TickerProviderStateMixin {
|
|||
void _onPanUpdate(DragUpdateDetails details) {
|
||||
if (!_isDragging) return;
|
||||
setState(() {
|
||||
_dragOffset = _dragOffset.translate(
|
||||
details.delta.dx,
|
||||
details.delta.dy,
|
||||
);
|
||||
_dragOffset = _dragOffset.translate(details.delta.dx, details.delta.dy);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -182,9 +176,7 @@ class PIPViewState extends State<PIPView> with TickerProviderStateMixin {
|
|||
_dragAnimationController,
|
||||
]),
|
||||
builder: (context, child) {
|
||||
final animationCurve = CurveTween(
|
||||
curve: Curves.easeInOutQuad,
|
||||
);
|
||||
final animationCurve = CurveTween(curve: Curves.easeInOutQuad);
|
||||
final dragAnimationValue = animationCurve.transform(
|
||||
_dragAnimationController.value,
|
||||
);
|
||||
|
|
@ -265,21 +257,13 @@ class PIPViewState extends State<PIPView> with TickerProviderStateMixin {
|
|||
}
|
||||
}
|
||||
|
||||
enum PIPViewCorner {
|
||||
topLeft,
|
||||
topRight,
|
||||
bottomLeft,
|
||||
bottomRight,
|
||||
}
|
||||
enum PIPViewCorner { topLeft, topRight, bottomLeft, bottomRight }
|
||||
|
||||
class _CornerDistance {
|
||||
final PIPViewCorner corner;
|
||||
final double distance;
|
||||
|
||||
_CornerDistance({
|
||||
required this.corner,
|
||||
required this.distance,
|
||||
});
|
||||
_CornerDistance({required this.corner, required this.distance});
|
||||
}
|
||||
|
||||
PIPViewCorner _calculateNearestCorner({
|
||||
|
|
@ -288,15 +272,9 @@ PIPViewCorner _calculateNearestCorner({
|
|||
}) {
|
||||
_CornerDistance calculateDistance(PIPViewCorner corner) {
|
||||
final distance = offsets[corner]!
|
||||
.translate(
|
||||
-offset.dx,
|
||||
-offset.dy,
|
||||
)
|
||||
.translate(-offset.dx, -offset.dy)
|
||||
.distanceSquared;
|
||||
return _CornerDistance(
|
||||
corner: corner,
|
||||
distance: distance,
|
||||
);
|
||||
return _CornerDistance(corner: corner, distance: distance);
|
||||
}
|
||||
|
||||
final distances = PIPViewCorner.values.map(calculateDistance).toList();
|
||||
|
|
|
|||
|
|
@ -44,8 +44,10 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||
/// well-known information and forwards to the login page depending on the
|
||||
/// login type.
|
||||
Future<void> checkHomeserverAction({bool legacyPasswordLogin = false}) async {
|
||||
final homeserverInput =
|
||||
homeserverController.text.trim().toLowerCase().replaceAll(' ', '-');
|
||||
final homeserverInput = homeserverController.text
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replaceAll(' ', '-');
|
||||
|
||||
if (homeserverInput.isEmpty) {
|
||||
final client = await Matrix.of(context).getLoginClient();
|
||||
|
|
@ -115,14 +117,12 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||
|
||||
void ssoLoginAction() async {
|
||||
final redirectUrl = kIsWeb
|
||||
? Uri.parse(html.window.location.href)
|
||||
.resolveUri(
|
||||
Uri(pathSegments: ['auth.html']),
|
||||
)
|
||||
.toString()
|
||||
? Uri.parse(
|
||||
html.window.location.href,
|
||||
).resolveUri(Uri(pathSegments: ['auth.html'])).toString()
|
||||
: isDefaultPlatform
|
||||
? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login'
|
||||
: 'http://localhost:3001//login';
|
||||
? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login'
|
||||
: 'http://localhost:3001//login';
|
||||
final client = await Matrix.of(context).getLoginClient();
|
||||
final url = client.homeserver!.replace(
|
||||
path: '/_matrix/client/v3/login/sso/redirect',
|
||||
|
|
|
|||
|
|
@ -14,18 +14,16 @@ import 'homeserver_picker.dart';
|
|||
class HomeserverPickerView extends StatelessWidget {
|
||||
final HomeserverPickerController controller;
|
||||
|
||||
const HomeserverPickerView(
|
||||
this.controller, {
|
||||
super.key,
|
||||
});
|
||||
const HomeserverPickerView(this.controller, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return LoginScaffold(
|
||||
enforceMobileMode:
|
||||
Matrix.of(context).widget.clients.any((client) => client.isLogged()),
|
||||
enforceMobileMode: Matrix.of(
|
||||
context,
|
||||
).widget.clients.any((client) => client.isLogged()),
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
|
|
@ -41,7 +39,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
PopupMenuItem(
|
||||
value: MoreLoginActions.importBackup,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.import_export_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -52,7 +50,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
PopupMenuItem(
|
||||
value: MoreLoginActions.privacy,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.privacy_tip_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -63,7 +61,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
PopupMenuItem(
|
||||
value: MoreLoginActions.about,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.info_outlined),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -99,8 +97,9 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||
child: SelectableLinkify(
|
||||
text: L10n.of(context).appIntroduction,
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context).scale(1),
|
||||
textScaleFactor: MediaQuery.textScalerOf(
|
||||
context,
|
||||
).scale(1),
|
||||
textAlign: TextAlign.center,
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
|
|
@ -113,8 +112,8 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .stretch,
|
||||
children: [
|
||||
TextField(
|
||||
onSubmitted: (_) =>
|
||||
|
|
@ -146,11 +145,13 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
L10n.of(context).whatIsAHomeserver,
|
||||
),
|
||||
content: Linkify(
|
||||
text: L10n.of(context)
|
||||
.homeserverDescription,
|
||||
text: L10n.of(
|
||||
context,
|
||||
).homeserverDescription,
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context)
|
||||
.scale(1),
|
||||
MediaQuery.textScalerOf(
|
||||
context,
|
||||
).scale(1),
|
||||
options: const LinkifyOptions(
|
||||
humanize: false,
|
||||
),
|
||||
|
|
@ -168,8 +169,9 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
Uri.https('servers.joinmatrix.org'),
|
||||
),
|
||||
child: Text(
|
||||
L10n.of(context)
|
||||
.discoverHomeservers,
|
||||
L10n.of(
|
||||
context,
|
||||
).discoverHomeservers,
|
||||
),
|
||||
),
|
||||
AdaptiveDialogAction(
|
||||
|
|
@ -205,8 +207,8 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
onPressed: controller.isLoading
|
||||
? null
|
||||
: () => controller.checkHomeserverAction(
|
||||
legacyPasswordLogin: true,
|
||||
),
|
||||
legacyPasswordLogin: true,
|
||||
),
|
||||
child: Text(L10n.of(context).loginWithMatrixId),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ class ImageViewerController extends State<ImageViewer> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
allEvents = widget.timeline?.events
|
||||
allEvents =
|
||||
widget.timeline?.events
|
||||
.where(
|
||||
(event) => {
|
||||
MessageTypes.Image,
|
||||
|
|
@ -44,8 +45,9 @@ class ImageViewerController extends State<ImageViewer> {
|
|||
.reversed
|
||||
.toList() ??
|
||||
[widget.event];
|
||||
var index =
|
||||
allEvents.indexWhere((event) => event.eventId == widget.event.eventId);
|
||||
var index = allEvents.indexWhere(
|
||||
(event) => event.eventId == widget.event.eventId,
|
||||
);
|
||||
if (index < 0) index = 0;
|
||||
pageController = PageController(initialPage: index);
|
||||
}
|
||||
|
|
@ -93,11 +95,10 @@ class ImageViewerController extends State<ImageViewer> {
|
|||
|
||||
/// Forward this image to another room.
|
||||
void forwardAction() => showScaffoldDialog(
|
||||
context: context,
|
||||
builder: (context) => ShareScaffoldDialog(
|
||||
items: [ContentShareItem(currentEvent.content)],
|
||||
),
|
||||
);
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
ShareScaffoldDialog(items: [ContentShareItem(currentEvent.content)]),
|
||||
);
|
||||
|
||||
/// Save this file with a system call.
|
||||
void saveFileAction(BuildContext context) => currentEvent.saveFile(context);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ class ImageViewerView extends StatelessWidget {
|
|||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
if (controller.canGoBack)
|
||||
Padding(
|
||||
|
|
|
|||
|
|
@ -20,10 +20,7 @@ import '../../widgets/mxc_image.dart';
|
|||
class EventVideoPlayer extends StatefulWidget {
|
||||
final Event event;
|
||||
|
||||
const EventVideoPlayer(
|
||||
this.event, {
|
||||
super.key,
|
||||
});
|
||||
const EventVideoPlayer(this.event, {super.key});
|
||||
|
||||
@override
|
||||
EventVideoPlayerState createState() => EventVideoPlayerState();
|
||||
|
|
@ -71,8 +68,9 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
: (progress) {
|
||||
final progressPercentage = progress / fileSize;
|
||||
setState(() {
|
||||
_downloadProgress =
|
||||
progressPercentage < 1 ? progressPercentage : null;
|
||||
_downloadProgress = progressPercentage < 1
|
||||
? progressPercentage
|
||||
: null;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
@ -117,11 +115,9 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
);
|
||||
});
|
||||
} on IOException catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(e.toLocalizedString(context)),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
// #Pangea
|
||||
setState(() => _error = e);
|
||||
// Pangea#
|
||||
|
|
@ -159,8 +155,10 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasThumbnail = widget.event.hasThumbnail;
|
||||
final blurHash = (widget.event.infoMap as Map<String, dynamic>)
|
||||
.tryGet<String>('xyz.amorgan.blurhash') ??
|
||||
final blurHash =
|
||||
(widget.event.infoMap as Map<String, dynamic>).tryGet<String>(
|
||||
'xyz.amorgan.blurhash',
|
||||
) ??
|
||||
fallbackBlurHash;
|
||||
final infoMap = widget.event.content.tryGetMap<String, Object?>('info');
|
||||
final videoWidth = infoMap?.tryGet<int>('w') ?? 400;
|
||||
|
|
|
|||
|
|
@ -12,10 +12,7 @@ import '../../utils/localized_exception_extension.dart';
|
|||
|
||||
class InvitationSelection extends StatefulWidget {
|
||||
final String roomId;
|
||||
const InvitationSelection({
|
||||
super.key,
|
||||
required this.roomId,
|
||||
});
|
||||
const InvitationSelection({super.key, required this.roomId});
|
||||
|
||||
@override
|
||||
InvitationSelectionController createState() =>
|
||||
|
|
@ -47,8 +44,8 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
|||
.toList();
|
||||
contacts.sort(
|
||||
(a, b) => a.calcDisplayname().toLowerCase().compareTo(
|
||||
b.calcDisplayname().toLowerCase(),
|
||||
),
|
||||
b.calcDisplayname().toLowerCase(),
|
||||
),
|
||||
);
|
||||
return contacts;
|
||||
}
|
||||
|
|
@ -91,9 +88,9 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
|||
try {
|
||||
response = await matrix.client.searchUserDirectory(text, limit: 10);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text((e).toLocalizedString(context))),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text((e).toLocalizedString(context))));
|
||||
return;
|
||||
} finally {
|
||||
setState(() => loading = false);
|
||||
|
|
|
|||
|
|
@ -16,13 +16,12 @@ class InvitationSelectionView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final room =
|
||||
Matrix.of(context).client.getRoomById(controller.widget.roomId);
|
||||
final room = Matrix.of(
|
||||
context,
|
||||
).client.getRoomById(controller.widget.roomId);
|
||||
if (room == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).oopsSomethingWentWrong),
|
||||
),
|
||||
appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)),
|
||||
body: Center(
|
||||
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
|
||||
),
|
||||
|
|
@ -76,11 +75,14 @@ class InvitationSelectionView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
StreamBuilder<Object>(
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == room.id),
|
||||
stream: room.client.onRoomState.stream.where(
|
||||
(update) => update.roomId == room.id,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
final participants =
|
||||
room.getParticipants().map((user) => user.id).toSet();
|
||||
final participants = room
|
||||
.getParticipants()
|
||||
.map((user) => user.id)
|
||||
.toSet();
|
||||
return controller.foundProfiles.isNotEmpty
|
||||
? ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
|
|
@ -88,17 +90,21 @@ class InvitationSelectionView extends StatelessWidget {
|
|||
itemCount: controller.foundProfiles.length,
|
||||
itemBuilder: (BuildContext context, int i) =>
|
||||
_InviteContactListTile(
|
||||
profile: controller.foundProfiles[i],
|
||||
isMember: participants
|
||||
.contains(controller.foundProfiles[i].userId),
|
||||
onTap: () => controller.inviteAction(
|
||||
context,
|
||||
controller.foundProfiles[i].userId,
|
||||
controller.foundProfiles[i].displayName ??
|
||||
controller.foundProfiles[i].userId.localpart ??
|
||||
L10n.of(context).user,
|
||||
),
|
||||
),
|
||||
profile: controller.foundProfiles[i],
|
||||
isMember: participants.contains(
|
||||
controller.foundProfiles[i].userId,
|
||||
),
|
||||
onTap: () => controller.inviteAction(
|
||||
context,
|
||||
controller.foundProfiles[i].userId,
|
||||
controller.foundProfiles[i].displayName ??
|
||||
controller
|
||||
.foundProfiles[i]
|
||||
.userId
|
||||
.localpart ??
|
||||
L10n.of(context).user,
|
||||
),
|
||||
),
|
||||
)
|
||||
: FutureBuilder<List<User>>(
|
||||
future: controller.getContacts(context),
|
||||
|
|
@ -117,23 +123,26 @@ class InvitationSelectionView extends StatelessWidget {
|
|||
itemCount: contacts.length,
|
||||
itemBuilder: (BuildContext context, int i) =>
|
||||
_InviteContactListTile(
|
||||
user: contacts[i],
|
||||
profile: Profile(
|
||||
avatarUrl: contacts[i].avatarUrl,
|
||||
displayName: contacts[i].displayName ??
|
||||
contacts[i].id.localpart ??
|
||||
L10n.of(context).user,
|
||||
userId: contacts[i].id,
|
||||
),
|
||||
isMember: participants.contains(contacts[i].id),
|
||||
onTap: () => controller.inviteAction(
|
||||
context,
|
||||
contacts[i].id,
|
||||
contacts[i].displayName ??
|
||||
contacts[i].id.localpart ??
|
||||
L10n.of(context).user,
|
||||
),
|
||||
),
|
||||
user: contacts[i],
|
||||
profile: Profile(
|
||||
avatarUrl: contacts[i].avatarUrl,
|
||||
displayName:
|
||||
contacts[i].displayName ??
|
||||
contacts[i].id.localpart ??
|
||||
L10n.of(context).user,
|
||||
userId: contacts[i].id,
|
||||
),
|
||||
isMember: participants.contains(
|
||||
contacts[i].id,
|
||||
),
|
||||
onTap: () => controller.inviteAction(
|
||||
context,
|
||||
contacts[i].id,
|
||||
contacts[i].displayName ??
|
||||
contacts[i].id.localpart ??
|
||||
L10n.of(context).user,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -169,10 +178,7 @@ class _InviteContactListTile extends StatelessWidget {
|
|||
mxContent: profile.avatarUrl,
|
||||
name: profile.displayName,
|
||||
presenceUserId: profile.userId,
|
||||
onTap: () => UserDialog.show(
|
||||
context: context,
|
||||
profile: profile,
|
||||
),
|
||||
onTap: () => UserDialog.show(context: context, profile: profile),
|
||||
),
|
||||
title: Text(
|
||||
profile.displayName ?? profile.userId.localpart ?? l10n.user,
|
||||
|
|
@ -183,9 +189,7 @@ class _InviteContactListTile extends StatelessWidget {
|
|||
profile.userId,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
style: TextStyle(color: theme.colorScheme.secondary),
|
||||
),
|
||||
trailing: TextButton.icon(
|
||||
onPressed: isMember ? null : onTap,
|
||||
|
|
|
|||
|
|
@ -15,17 +15,14 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
|||
|
||||
class KeyVerificationDialog extends StatefulWidget {
|
||||
Future<bool?> show(BuildContext context) => showAdaptiveDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
barrierDismissible: false,
|
||||
);
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
barrierDismissible: false,
|
||||
);
|
||||
|
||||
final KeyVerification request;
|
||||
|
||||
const KeyVerificationDialog({
|
||||
super.key,
|
||||
required this.request,
|
||||
});
|
||||
const KeyVerificationDialog({super.key, required this.request});
|
||||
|
||||
@override
|
||||
KeyVerificationPageState createState() => KeyVerificationPageState();
|
||||
|
|
@ -57,8 +54,10 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
void dispose() {
|
||||
widget.request.onUpdate =
|
||||
originalOnUpdate; // don't want to get updates anymore
|
||||
if (![KeyVerificationState.error, KeyVerificationState.done]
|
||||
.contains(widget.request.state)) {
|
||||
if (![
|
||||
KeyVerificationState.error,
|
||||
KeyVerificationState.done,
|
||||
].contains(widget.request.state)) {
|
||||
widget.request.cancel('m.user');
|
||||
}
|
||||
super.dispose();
|
||||
|
|
@ -98,8 +97,9 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
final theme = Theme.of(context);
|
||||
|
||||
User? user;
|
||||
final directChatId =
|
||||
widget.request.client.getDirectChatFromUserId(widget.request.userId);
|
||||
final directChatId = widget.request.client.getDirectChatFromUserId(
|
||||
widget.request.userId,
|
||||
);
|
||||
if (directChatId != null) {
|
||||
user = widget.request.client
|
||||
.getRoomById(directChatId)!
|
||||
|
|
@ -122,7 +122,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
body = Container(
|
||||
margin: const EdgeInsets.only(left: 8.0, right: 8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
L10n.of(context).askSSSSSign,
|
||||
|
|
@ -152,17 +152,13 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
);
|
||||
buttons.add(
|
||||
AdaptiveDialogAction(
|
||||
child: Text(
|
||||
L10n.of(context).submit,
|
||||
),
|
||||
child: Text(L10n.of(context).submit),
|
||||
onPressed: () => checkInput(textEditingController.text),
|
||||
),
|
||||
);
|
||||
buttons.add(
|
||||
AdaptiveDialogAction(
|
||||
child: Text(
|
||||
L10n.of(context).skip,
|
||||
),
|
||||
child: Text(L10n.of(context).skip),
|
||||
onPressed: () => widget.request.openSSSS(skip: true),
|
||||
),
|
||||
);
|
||||
|
|
@ -170,7 +166,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
case KeyVerificationState.askAccept:
|
||||
title = Text(L10n.of(context).newVerificationRequest);
|
||||
body = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Avatar(
|
||||
|
|
@ -179,16 +175,14 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
size: Avatar.defaultSize * 2,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
L10n.of(context).askVerificationRequest(displayName),
|
||||
),
|
||||
Text(L10n.of(context).askVerificationRequest(displayName)),
|
||||
],
|
||||
);
|
||||
buttons.add(
|
||||
AdaptiveDialogAction(
|
||||
onPressed: () => widget.request.rejectVerification().then(
|
||||
(_) => Navigator.of(context, rootNavigator: false).pop(false),
|
||||
),
|
||||
(_) => Navigator.of(context, rootNavigator: false).pop(false),
|
||||
),
|
||||
child: Text(
|
||||
L10n.of(context).reject,
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
|
|
@ -211,10 +205,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: user?.avatarUrl,
|
||||
name: displayName,
|
||||
),
|
||||
Avatar(mxContent: user?.avatarUrl, name: displayName),
|
||||
const SizedBox(
|
||||
width: Avatar.defaultSize + 2,
|
||||
height: Avatar.defaultSize + 2,
|
||||
|
|
@ -258,16 +249,15 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
title = Text(L10n.of(context).compareNumbersMatch);
|
||||
final numbers = widget.request.sasNumbers;
|
||||
final numbstr = '${numbers[0]}-${numbers[1]}-${numbers[2]}';
|
||||
compareWidget =
|
||||
TextSpan(text: numbstr, style: const TextStyle(fontSize: 40));
|
||||
compareWidget = TextSpan(
|
||||
text: numbstr,
|
||||
style: const TextStyle(fontSize: 40),
|
||||
);
|
||||
}
|
||||
body = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
Text.rich(
|
||||
compareWidget,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text.rich(compareWidget, textAlign: TextAlign.center),
|
||||
],
|
||||
);
|
||||
buttons.add(
|
||||
|
|
@ -291,15 +281,12 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
? L10n.of(context).waitingPartnerEmoji
|
||||
: L10n.of(context).waitingPartnerNumbers;
|
||||
body = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 16),
|
||||
const CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
acceptText,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(acceptText, textAlign: TextAlign.center),
|
||||
],
|
||||
);
|
||||
break;
|
||||
|
|
@ -315,9 +302,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
);
|
||||
buttons.add(
|
||||
AdaptiveDialogAction(
|
||||
child: Text(
|
||||
L10n.of(context).close,
|
||||
),
|
||||
child: Text(L10n.of(context).close),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: false).pop(true),
|
||||
),
|
||||
|
|
@ -326,7 +311,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
case KeyVerificationState.error:
|
||||
title = const Text('');
|
||||
body = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 16),
|
||||
Icon(Icons.cancel, color: theme.colorScheme.error, size: 64.0),
|
||||
|
|
@ -340,9 +325,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
);
|
||||
buttons.add(
|
||||
AdaptiveDialogAction(
|
||||
child: Text(
|
||||
L10n.of(context).close,
|
||||
),
|
||||
child: Text(L10n.of(context).close),
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: false).pop(false),
|
||||
),
|
||||
|
|
@ -355,9 +338,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
content: SizedBox(
|
||||
height: 256,
|
||||
width: 256,
|
||||
child: ListView(
|
||||
children: [body],
|
||||
),
|
||||
child: ListView(children: [body]),
|
||||
),
|
||||
actions: buttons,
|
||||
);
|
||||
|
|
@ -399,7 +380,7 @@ class _Emoji extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
Text(emoji.emoji, style: const TextStyle(fontSize: 50)),
|
||||
Padding(
|
||||
|
|
|
|||
|
|
@ -20,10 +20,7 @@ class Login extends StatefulWidget {
|
|||
// const Login({required this.client, super.key});
|
||||
final bool withEmail;
|
||||
|
||||
const Login({
|
||||
super.key,
|
||||
this.withEmail = false,
|
||||
});
|
||||
const Login({super.key, this.withEmail = false});
|
||||
// Pangea#
|
||||
|
||||
@override
|
||||
|
|
@ -58,22 +55,24 @@ class LoginController extends State<Login> {
|
|||
// TODO: implement initState
|
||||
super.initState();
|
||||
loadingSignIn = true;
|
||||
checkHomeServerAction().then((client) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
loadingSignIn = false;
|
||||
this.client = client;
|
||||
checkHomeServerAction()
|
||||
.then((client) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
loadingSignIn = false;
|
||||
this.client = client;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catchError((e) {
|
||||
final String err = e.toString();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
loadingSignIn = false;
|
||||
passwordError = err.toLocalizedString(context);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}).catchError((e) {
|
||||
final String err = e.toString();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
loadingSignIn = false;
|
||||
passwordError = err.toLocalizedString(context);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
usernameController.addListener(() => setState(() {}));
|
||||
passwordController.addListener(() => setState(() {}));
|
||||
|
|
@ -194,8 +193,9 @@ class LoginController extends State<Login> {
|
|||
// final dialogResult = await showOkCancelAlertDialog(
|
||||
// context: context,
|
||||
// useRootNavigator: false,
|
||||
// title: L10n.of(context)
|
||||
// .noMatrixServer(newDomain.toString(), oldHomeserver.toString()),
|
||||
// title: L10n.of(
|
||||
// context,
|
||||
// ).noMatrixServer(newDomain.toString(), oldHomeserver.toString()),
|
||||
// okLabel: L10n.of(context).ok,
|
||||
// cancelLabel: L10n.of(context).cancel,
|
||||
// );
|
||||
|
|
@ -228,8 +228,10 @@ class LoginController extends State<Login> {
|
|||
return client;
|
||||
}
|
||||
|
||||
final String homeServer =
|
||||
AppConfig.defaultHomeserver.trim().toLowerCase().replaceAll(' ', '-');
|
||||
final String homeServer = AppConfig.defaultHomeserver
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replaceAll(' ', '-');
|
||||
var homeserver = Uri.parse(homeServer);
|
||||
if (homeserver.scheme.isEmpty) {
|
||||
homeserver = Uri.https(homeServer, '');
|
||||
|
|
@ -257,8 +259,9 @@ class LoginController extends State<Login> {
|
|||
message: L10n.of(context).enterAnEmailAddress,
|
||||
okLabel: L10n.of(context).ok,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
initialText:
|
||||
usernameController.text.isEmail ? usernameController.text : '',
|
||||
initialText: usernameController.text.isEmail
|
||||
? usernameController.text
|
||||
: '',
|
||||
hintText: L10n.of(context).enterAnEmailAddress,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
);
|
||||
|
|
@ -366,9 +369,10 @@ class LoginController extends State<Login> {
|
|||
// #Pangea
|
||||
// extension on String {
|
||||
extension LoginExtension on String {
|
||||
// Pangea#
|
||||
static final RegExp _phoneRegex =
|
||||
RegExp(r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$');
|
||||
// Pangea#
|
||||
static final RegExp _phoneRegex = RegExp(
|
||||
r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$',
|
||||
);
|
||||
static final RegExp _emailRegex = RegExp(r'(.+)@(.+)\.(.+)');
|
||||
|
||||
bool get isEmail => _emailRegex.hasMatch(this);
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@
|
|||
// final titleParts = title.split(homeserver);
|
||||
|
||||
// return LoginScaffold(
|
||||
// enforceMobileMode:
|
||||
// Matrix.of(context).widget.clients.any((client) => client.isLogged()),
|
||||
// enforceMobileMode: Matrix.of(
|
||||
// context,
|
||||
// ).widget.clients.any((client) => client.isLogged()),
|
||||
// appBar: AppBar(
|
||||
// leading:
|
||||
// controller.loadingSignIn ? null : const Center(child: BackButton()),
|
||||
// automaticallyImplyLeading: !controller.loadingSignIn,
|
||||
// titleSpacing: !controller.loadingSignIn ? 0 : null,
|
||||
// leading: controller.loading ? null : const Center(child: BackButton()),
|
||||
// automaticallyImplyLeading: !controller.loading,
|
||||
// titleSpacing: !controller.loading ? 0 : null,
|
||||
// title: Text.rich(
|
||||
// TextSpan(
|
||||
// children: [
|
||||
|
|
@ -56,14 +56,14 @@
|
|||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
// child: TextField(
|
||||
// readOnly: controller.loadingSignIn,
|
||||
// readOnly: controller.loading,
|
||||
// autocorrect: false,
|
||||
// autofocus: true,
|
||||
// onChanged: controller.checkWellKnownWithCoolDown,
|
||||
// controller: controller.usernameController,
|
||||
// textInputAction: TextInputAction.next,
|
||||
// keyboardType: TextInputType.emailAddress,
|
||||
// autofillHints: controller.loadingSignIn
|
||||
// autofillHints: controller.loading
|
||||
// ? null
|
||||
// : [AutofillHints.username],
|
||||
// decoration: InputDecoration(
|
||||
|
|
@ -79,9 +79,9 @@
|
|||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
// child: TextField(
|
||||
// readOnly: controller.loadingSignIn,
|
||||
// readOnly: controller.loading,
|
||||
// autocorrect: false,
|
||||
// autofillHints: controller.loadingSignIn
|
||||
// autofillHints: controller.loading
|
||||
// ? null
|
||||
// : [AutofillHints.password],
|
||||
// controller: controller.passwordController,
|
||||
|
|
@ -114,9 +114,8 @@
|
|||
// backgroundColor: theme.colorScheme.primary,
|
||||
// foregroundColor: theme.colorScheme.onPrimary,
|
||||
// ),
|
||||
// onPressed:
|
||||
// controller.loadingSignIn ? null : controller.login,
|
||||
// child: controller.loadingSignIn
|
||||
// onPressed: controller.loading ? null : controller.login,
|
||||
// child: controller.loading
|
||||
// ? const LinearProgressIndicator()
|
||||
// : Text(L10n.of(context).login),
|
||||
// ),
|
||||
|
|
@ -125,7 +124,7 @@
|
|||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
// child: TextButton(
|
||||
// onPressed: controller.loadingSignIn
|
||||
// onPressed: controller.loading
|
||||
// ? () {}
|
||||
// : controller.passwordForgotten,
|
||||
// style: TextButton.styleFrom(
|
||||
|
|
|
|||
|
|
@ -8,27 +8,12 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/new_group/new_group_view.dart';
|
||||
import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart';
|
||||
import 'package:fluffychat/pangea/extensions/join_rule_extension.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/spaces/client_spaces_extension.dart';
|
||||
import 'package:fluffychat/utils/file_selector.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class NewGroup extends StatefulWidget {
|
||||
// #Pangea
|
||||
final String? spaceId;
|
||||
// Pangea#
|
||||
final CreateGroupType createGroupType;
|
||||
const NewGroup({
|
||||
// #Pangea
|
||||
this.spaceId,
|
||||
// Pangea#
|
||||
this.createGroupType = CreateGroupType.group,
|
||||
super.key,
|
||||
});
|
||||
const NewGroup({this.createGroupType = CreateGroupType.group, super.key});
|
||||
|
||||
@override
|
||||
NewGroupController createState() => NewGroupController();
|
||||
|
|
@ -37,14 +22,8 @@ class NewGroup extends StatefulWidget {
|
|||
class NewGroupController extends State<NewGroup> {
|
||||
TextEditingController nameController = TextEditingController();
|
||||
|
||||
// #Pangea
|
||||
// bool publicGroup = false;
|
||||
// bool groupCanBeFound = false;
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
bool get canSubmit => nameController.text.trim().isNotEmpty;
|
||||
// Pangea#
|
||||
bool publicGroup = false;
|
||||
bool groupCanBeFound = false;
|
||||
|
||||
Uint8List? avatar;
|
||||
|
||||
|
|
@ -62,27 +41,10 @@ class NewGroupController extends State<NewGroup> {
|
|||
void setCreateGroupType(Set<CreateGroupType> b) =>
|
||||
setState(() => _createGroupType = b.single);
|
||||
|
||||
// #Pangea
|
||||
// void setPublicGroup(bool b) =>
|
||||
// setState(() => publicGroup = groupCanBeFound = b);
|
||||
void setPublicGroup(bool b) =>
|
||||
setState(() => publicGroup = groupCanBeFound = b);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
nameController.addListener(() => setState(() {}));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
nameController.dispose();
|
||||
focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
// #Pangea
|
||||
// void setGroupCanBeFound(bool b) => setState(() => groupCanBeFound = b);
|
||||
// Pangea#
|
||||
void setGroupCanBeFound(bool b) => setState(() => groupCanBeFound = b);
|
||||
|
||||
void selectPhoto() async {
|
||||
final photo = await selectFiles(
|
||||
|
|
@ -101,134 +63,56 @@ class NewGroupController extends State<NewGroup> {
|
|||
Future<void> _createGroup() async {
|
||||
if (!mounted) return;
|
||||
final roomId = await Matrix.of(context).client.createGroupChat(
|
||||
// #Pangea
|
||||
// visibility:
|
||||
// groupCanBeFound ? sdk.Visibility.public : sdk.Visibility.private,
|
||||
// preset: publicGroup
|
||||
// ? sdk.CreateRoomPreset.publicChat
|
||||
// : sdk.CreateRoomPreset.privateChat,
|
||||
preset: sdk.CreateRoomPreset.publicChat,
|
||||
visibility: sdk.Visibility.private,
|
||||
// Pangea#
|
||||
groupName:
|
||||
nameController.text.isNotEmpty ? nameController.text : null,
|
||||
initialState: [
|
||||
if (avatar != null)
|
||||
sdk.StateEvent(
|
||||
type: sdk.EventTypes.RoomAvatar,
|
||||
content: {'url': avatarUrl.toString()},
|
||||
),
|
||||
// #Pangea
|
||||
RoomDefaults.defaultPowerLevels(
|
||||
Matrix.of(context).client.userID!,
|
||||
),
|
||||
await Matrix.of(context).client.pangeaJoinRules(
|
||||
widget.spaceId != null
|
||||
? 'knock_restricted'
|
||||
: JoinRules.public
|
||||
.toString()
|
||||
.replaceAll('JoinRules.', ''),
|
||||
allow: widget.spaceId != null
|
||||
? [
|
||||
{
|
||||
"type": "m.room_membership",
|
||||
"room_id": widget.spaceId,
|
||||
}
|
||||
]
|
||||
: null,
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
// #Pangea
|
||||
enableEncryption: false,
|
||||
// Pangea#
|
||||
);
|
||||
visibility: groupCanBeFound
|
||||
? sdk.Visibility.public
|
||||
: sdk.Visibility.private,
|
||||
preset: publicGroup
|
||||
? sdk.CreateRoomPreset.publicChat
|
||||
: sdk.CreateRoomPreset.privateChat,
|
||||
groupName: nameController.text.isNotEmpty ? nameController.text : null,
|
||||
initialState: [
|
||||
if (avatar != null)
|
||||
sdk.StateEvent(
|
||||
type: sdk.EventTypes.RoomAvatar,
|
||||
content: {'url': avatarUrl.toString()},
|
||||
),
|
||||
],
|
||||
);
|
||||
if (!mounted) return;
|
||||
// #Pangea
|
||||
final client = Matrix.of(context).client;
|
||||
Room? room = client.getRoomById(roomId);
|
||||
if (room == null) {
|
||||
await client.waitForRoomInSync(roomId);
|
||||
room = client.getRoomById(roomId);
|
||||
}
|
||||
if (room == null) return;
|
||||
|
||||
if (widget.spaceId != null) {
|
||||
try {
|
||||
final space = client.getRoomById(widget.spaceId!);
|
||||
await space?.addToSpace(room.id);
|
||||
if (room.pangeaSpaceParents.isEmpty) {
|
||||
await client.waitForRoomInSync(roomId);
|
||||
}
|
||||
} catch (err) {
|
||||
ErrorHandler.logError(
|
||||
e: "Failed to add room to space",
|
||||
data: {"spaceId": widget.spaceId, "error": err},
|
||||
);
|
||||
}
|
||||
}
|
||||
context.go('/rooms/$roomId/invite');
|
||||
// Pangea#
|
||||
}
|
||||
|
||||
Future<void> _createSpace() async {
|
||||
if (!mounted) return;
|
||||
// #Pangea
|
||||
// final spaceId = await Matrix.of(context).client.createRoom(
|
||||
// preset: publicGroup
|
||||
// ? sdk.CreateRoomPreset.publicChat
|
||||
// : sdk.CreateRoomPreset.privateChat,
|
||||
// creationContent: {'type': RoomCreationTypes.mSpace},
|
||||
// visibility: publicGroup ? sdk.Visibility.public : null,
|
||||
// roomAliasName: publicGroup
|
||||
// ? nameController.text.trim().toLowerCase().replaceAll(' ', '_')
|
||||
// : null,
|
||||
// name: nameController.text.trim(),
|
||||
// powerLevelContentOverride: {'events_default': 100},
|
||||
// initialState: [
|
||||
// if (avatar != null)
|
||||
// sdk.StateEvent(
|
||||
// type: sdk.EventTypes.RoomAvatar,
|
||||
// content: {'url': avatarUrl.toString()},
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// if (!mounted) return;
|
||||
// context.pop<String>(spaceId);
|
||||
final spaceId = await Matrix.of(context).client.createPangeaSpace(
|
||||
name: nameController.text,
|
||||
visibility: sdk.Visibility.private,
|
||||
joinRules: sdk.JoinRules.knock,
|
||||
avatarUrl: avatarUrl.toString(),
|
||||
);
|
||||
|
||||
final spaceId = await Matrix.of(context).client.createRoom(
|
||||
preset: publicGroup
|
||||
? sdk.CreateRoomPreset.publicChat
|
||||
: sdk.CreateRoomPreset.privateChat,
|
||||
creationContent: {'type': RoomCreationTypes.mSpace},
|
||||
visibility: publicGroup ? sdk.Visibility.public : null,
|
||||
roomAliasName: publicGroup
|
||||
? nameController.text.trim().toLowerCase().replaceAll(' ', '_')
|
||||
: null,
|
||||
name: nameController.text.trim(),
|
||||
powerLevelContentOverride: {'events_default': 100},
|
||||
initialState: [
|
||||
if (avatar != null)
|
||||
sdk.StateEvent(
|
||||
type: sdk.EventTypes.RoomAvatar,
|
||||
content: {'url': avatarUrl.toString()},
|
||||
),
|
||||
],
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
||||
final room = Matrix.of(context).client.getRoomById(spaceId);
|
||||
if (room == null) return;
|
||||
final spaceCode = room.classCode;
|
||||
if (spaceCode != null) {
|
||||
GoogleAnalytics.createClass(room.name, spaceCode);
|
||||
}
|
||||
|
||||
context.go("/rooms/spaces/$spaceId/details");
|
||||
// Pangea#
|
||||
context.pop<String>(spaceId);
|
||||
}
|
||||
|
||||
void submitAction([dynamic _]) async {
|
||||
final client = Matrix.of(context).client;
|
||||
|
||||
try {
|
||||
// #Pangea
|
||||
if (!formKey.currentState!.validate()) {
|
||||
focusNode.requestFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
// if (nameController.text.trim().isEmpty &&
|
||||
// createGroupType == CreateGroupType.space) {
|
||||
if (!canSubmit) {
|
||||
// Pangea#
|
||||
if (nameController.text.trim().isEmpty &&
|
||||
createGroupType == CreateGroupType.space) {
|
||||
setState(() => error = L10n.of(context).pleaseFillOut);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/new_group/new_group.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
|
||||
|
|
@ -13,14 +14,10 @@ class NewGroupView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// #Pangea
|
||||
// final theme = Theme.of(context);
|
||||
// Pangea#
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final avatar = controller.avatar;
|
||||
// #Pangea
|
||||
// final error = controller.error;
|
||||
// Pangea#
|
||||
final error = controller.error;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Center(
|
||||
|
|
@ -30,47 +27,32 @@ class NewGroupView extends StatelessWidget {
|
|||
),
|
||||
title: Text(
|
||||
controller.createGroupType == CreateGroupType.space
|
||||
// #Pangea
|
||||
// ? L10n.of(context).newSpace
|
||||
? L10n.of(context).newCourse
|
||||
// : L10n.of(context).createGroup,
|
||||
: L10n.of(context).newChat,
|
||||
// Pangea#
|
||||
? L10n.of(context).newSpace
|
||||
: L10n.of(context).createGroup,
|
||||
),
|
||||
),
|
||||
body: MaxWidthBody(
|
||||
// #Pangea
|
||||
showBorder: false,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32.0,
|
||||
right: 32.0,
|
||||
bottom: 32.0,
|
||||
),
|
||||
// Pangea#
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: <Widget>[
|
||||
// #Pangea
|
||||
// Simplying options here
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: SegmentedButton<CreateGroupType>(
|
||||
// selected: {controller.createGroupType},
|
||||
// onSelectionChanged: controller.setCreateGroupType,
|
||||
// segments: [
|
||||
// ButtonSegment(
|
||||
// value: CreateGroupType.group,
|
||||
// label: Text(L10n.of(context).group),
|
||||
// ),
|
||||
// ButtonSegment(
|
||||
// value: CreateGroupType.space,
|
||||
// label: Text(L10n.of(context).space),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 16),
|
||||
// Pangea#
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SegmentedButton<CreateGroupType>(
|
||||
selected: {controller.createGroupType},
|
||||
onSelectionChanged: controller.setCreateGroupType,
|
||||
segments: [
|
||||
ButtonSegment(
|
||||
value: CreateGroupType.group,
|
||||
label: Text(L10n.of(context).group),
|
||||
),
|
||||
ButtonSegment(
|
||||
value: CreateGroupType.space,
|
||||
label: Text(L10n.of(context).space),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(90),
|
||||
onTap: controller.loading ? null : controller.selectPhoto,
|
||||
|
|
@ -92,107 +74,82 @@ class NewGroupView extends StatelessWidget {
|
|||
const SizedBox(height: 32),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
// #Pangea
|
||||
// child: TextField(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: TextFormField(
|
||||
// Pangea#
|
||||
autofocus: true,
|
||||
controller: controller.nameController,
|
||||
autocorrect: false,
|
||||
readOnly: controller.loading,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.people_outlined),
|
||||
labelText:
|
||||
controller.createGroupType == CreateGroupType.space
|
||||
// #Pangea
|
||||
// ? L10n.of(context).spaceName
|
||||
? L10n.of(context).courseName
|
||||
// : L10n.of(context).groupName,
|
||||
: L10n.of(context).chatName,
|
||||
// Pangea#
|
||||
),
|
||||
// #Pangea
|
||||
onFieldSubmitted: (value) {
|
||||
controller.loading ? null : controller.submitAction();
|
||||
},
|
||||
validator: (value) => controller.canSubmit
|
||||
? null
|
||||
: L10n.of(context).pleaseFillOut,
|
||||
focusNode: controller.focusNode,
|
||||
// Pangea#
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
controller: controller.nameController,
|
||||
autocorrect: false,
|
||||
readOnly: controller.loading,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.people_outlined),
|
||||
labelText: controller.createGroupType == CreateGroupType.space
|
||||
? L10n.of(context).spaceName
|
||||
: L10n.of(context).groupName,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// #Pangea
|
||||
// SwitchListTile.adaptive(
|
||||
// contentPadding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
// secondary: const Icon(Icons.public_outlined),
|
||||
// title: Text(
|
||||
// controller.createGroupType == CreateGroupType.space
|
||||
// ? L10n.of(context).spaceIsPublic
|
||||
// : L10n.of(context).groupIsPublic,
|
||||
// ),
|
||||
// value: controller.publicGroup,
|
||||
// onChanged: controller.loading ? null : controller.setPublicGroup,
|
||||
// ),
|
||||
// AnimatedSize(
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// child: controller.publicGroup
|
||||
// ? SwitchListTile.adaptive(
|
||||
// contentPadding:
|
||||
// const EdgeInsets.symmetric(horizontal: 32),
|
||||
// secondary: const Icon(Icons.search_outlined),
|
||||
// title: Text(L10n.of(context).groupCanBeFoundViaSearch),
|
||||
// value: controller.groupCanBeFound,
|
||||
// onChanged: controller.loading
|
||||
// ? null
|
||||
// : controller.setGroupCanBeFound,
|
||||
// )
|
||||
// : const SizedBox.shrink(),
|
||||
// ),
|
||||
// AnimatedSize(
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// child: controller.createGroupType == CreateGroupType.space
|
||||
// ? const SizedBox.shrink()
|
||||
// : SwitchListTile.adaptive(
|
||||
// contentPadding:
|
||||
// const EdgeInsets.symmetric(horizontal: 32),
|
||||
// secondary: Icon(
|
||||
// Icons.lock_outlined,
|
||||
// color: theme.colorScheme.onSurface,
|
||||
// ),
|
||||
// title: Text(
|
||||
// L10n.of(context).enableEncryption,
|
||||
// style: TextStyle(
|
||||
// color: theme.colorScheme.onSurface,
|
||||
// ),
|
||||
// ),
|
||||
// value: !controller.publicGroup,
|
||||
// onChanged: null,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
SwitchListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
secondary: const Icon(Icons.public_outlined),
|
||||
title: Text(
|
||||
controller.createGroupType == CreateGroupType.space
|
||||
? L10n.of(context).spaceIsPublic
|
||||
: L10n.of(context).groupIsPublic,
|
||||
),
|
||||
value: controller.publicGroup,
|
||||
onChanged: controller.loading ? null : controller.setPublicGroup,
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: controller.publicGroup
|
||||
? SwitchListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
secondary: const Icon(Icons.search_outlined),
|
||||
title: Text(L10n.of(context).groupCanBeFoundViaSearch),
|
||||
value: controller.groupCanBeFound,
|
||||
onChanged: controller.loading
|
||||
? null
|
||||
: controller.setGroupCanBeFound,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: controller.createGroupType == CreateGroupType.space
|
||||
? const SizedBox.shrink()
|
||||
: SwitchListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
secondary: Icon(
|
||||
Icons.lock_outlined,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
title: Text(
|
||||
L10n.of(context).enableEncryption,
|
||||
style: TextStyle(color: theme.colorScheme.onSurface),
|
||||
),
|
||||
value: !controller.publicGroup,
|
||||
onChanged: null,
|
||||
),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: controller.createGroupType == CreateGroupType.space
|
||||
? ListTile(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 32),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
trailing: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Icon(Icons.info_outlined),
|
||||
),
|
||||
// #Pangea
|
||||
// subtitle: Text(L10n.of(context).newSpaceDescription),
|
||||
subtitle:
|
||||
Text(L10n.of(context).updatedNewSpaceDescription),
|
||||
// Pangea#
|
||||
subtitle: Text(L10n.of(context).newSpaceDescription),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
|
|
@ -201,42 +158,35 @@ class NewGroupView extends StatelessWidget {
|
|||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed:
|
||||
controller.loading ? null : controller.submitAction,
|
||||
onPressed: controller.loading
|
||||
? null
|
||||
: controller.submitAction,
|
||||
child: controller.loading
|
||||
? const LinearProgressIndicator()
|
||||
: Text(
|
||||
controller.createGroupType == CreateGroupType.space
|
||||
// #Pangea
|
||||
// ? L10n.of(context).createNewSpace
|
||||
? L10n.of(context).createNewCourse
|
||||
// : L10n.of(context).createGroupAndInviteUsers,
|
||||
: L10n.of(context).createChatAndInviteUsers,
|
||||
// Pangea#
|
||||
? L10n.of(context).createNewSpace
|
||||
: L10n.of(context).createGroupAndInviteUsers,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// AnimatedSize(
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// child: error == null
|
||||
// ? const SizedBox.shrink()
|
||||
// : ListTile(
|
||||
// leading: Icon(
|
||||
// Icons.warning_outlined,
|
||||
// color: theme.colorScheme.error,
|
||||
// ),
|
||||
// title: Text(
|
||||
// error.toLocalizedString(context),
|
||||
// style: TextStyle(
|
||||
// color: theme.colorScheme.error,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: error == null
|
||||
? const SizedBox.shrink()
|
||||
: ListTile(
|
||||
leading: Icon(
|
||||
Icons.warning_outlined,
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
title: Text(
|
||||
error.toLocalizedString(context),
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -54,8 +54,9 @@ class NewPrivateChatController extends State<NewPrivateChat> {
|
|||
|
||||
Future<List<Profile>> _searchUser(String searchTerm) async {
|
||||
// #Pangea
|
||||
// final result =
|
||||
// await Matrix.of(context).client.searchUserDirectory(searchTerm);
|
||||
// final result = await Matrix.of(
|
||||
// context,
|
||||
// ).client.searchUserDirectory(searchTerm);
|
||||
final result = await Matrix.of(context).client.searchUser(searchTerm);
|
||||
// Pangea#
|
||||
final profiles = result.results;
|
||||
|
|
@ -79,9 +80,7 @@ class NewPrivateChatController extends State<NewPrivateChat> {
|
|||
if (info.version.sdkInt < 21) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
L10n.of(context).unsupportedAndroidVersionLong,
|
||||
),
|
||||
content: Text(L10n.of(context).unsupportedAndroidVersionLong),
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
@ -99,15 +98,13 @@ class NewPrivateChatController extends State<NewPrivateChat> {
|
|||
await Clipboard.setData(
|
||||
ClipboardData(text: Matrix.of(context).client.userID!),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(L10n.of(context).copiedToClipboard)),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(L10n.of(context).copiedToClipboard)));
|
||||
}
|
||||
|
||||
void openUserModal(Profile profile) => UserDialog.show(
|
||||
context: context,
|
||||
profile: profile,
|
||||
);
|
||||
void openUserModal(Profile profile) =>
|
||||
UserDialog.show(context: context, profile: profile);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => NewPrivateChatView(this);
|
||||
|
|
|
|||
|
|
@ -38,8 +38,10 @@ class NewPrivateChatView extends StatelessWidget {
|
|||
// #Pangea
|
||||
// actions: [
|
||||
// TextButton(
|
||||
// onPressed:
|
||||
// UrlLauncher(context, AppConfig.startChatTutorial).launchUrl,
|
||||
// onPressed: UrlLauncher(
|
||||
// context,
|
||||
// AppConfig.startChatTutorial,
|
||||
// ).launchUrl,
|
||||
// child: Text(L10n.of(context).help),
|
||||
// ),
|
||||
// ],
|
||||
|
|
@ -103,186 +105,196 @@ class NewPrivateChatView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Expanded(
|
||||
child: AnimatedCrossFade(
|
||||
child: AnimatedSwitcher(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
crossFadeState: searchResponse == null
|
||||
? CrossFadeState.showFirst
|
||||
: CrossFadeState.showSecond,
|
||||
firstChild: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: SelectableText.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: L10n.of(context).yourGlobalUserIdIs,
|
||||
child: searchResponse == null
|
||||
? ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 18.0,
|
||||
),
|
||||
TextSpan(
|
||||
text: Matrix.of(context).client.userID,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
child: SelectableText.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: L10n.of(context).yourGlobalUserIdIs,
|
||||
),
|
||||
TextSpan(
|
||||
text: Matrix.of(context).client.userID,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.colorScheme.secondaryContainer,
|
||||
foregroundColor: theme.colorScheme.onSecondaryContainer,
|
||||
child: Icon(Icons.adaptive.share_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).shareInviteLink),
|
||||
onTap: controller.inviteAction,
|
||||
),
|
||||
// #Pangea
|
||||
// ListTile(
|
||||
// leading: CircleAvatar(
|
||||
// backgroundColor: theme.colorScheme.tertiaryContainer,
|
||||
// foregroundColor: theme.colorScheme.onTertiaryContainer,
|
||||
// child: const Icon(Icons.group_add_outlined),
|
||||
// ),
|
||||
// title: Text(L10n.of(context).createGroup),
|
||||
// onTap: () => context.go('/rooms/newgroup'),
|
||||
// ),
|
||||
// Pangea#
|
||||
if (PlatformInfos.isMobile)
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.colorScheme.primaryContainer,
|
||||
foregroundColor: theme.colorScheme.onPrimaryContainer,
|
||||
child: const Icon(Icons.qr_code_scanner_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).scanQrCode),
|
||||
onTap: controller.openScannerAction,
|
||||
),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 64.0,
|
||||
vertical: 24.0,
|
||||
),
|
||||
child: Material(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
side: BorderSide(
|
||||
width: 3,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
color: Colors.transparent,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: InkWell(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
onTap: () => showQrCodeViewer(
|
||||
context,
|
||||
userId,
|
||||
const SizedBox(height: 8),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
theme.colorScheme.secondaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onSecondaryContainer,
|
||||
child: Icon(Icons.adaptive.share_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).shareInviteLink),
|
||||
onTap: controller.inviteAction,
|
||||
),
|
||||
// #Pangea
|
||||
// ListTile(
|
||||
// leading: CircleAvatar(
|
||||
// backgroundColor:
|
||||
// theme.colorScheme.tertiaryContainer,
|
||||
// foregroundColor:
|
||||
// theme.colorScheme.onTertiaryContainer,
|
||||
// child: const Icon(Icons.group_add_outlined),
|
||||
// ),
|
||||
// title: Text(L10n.of(context).createGroup),
|
||||
// onTap: () => context.go('/rooms/newgroup'),
|
||||
// ),
|
||||
// Pangea#
|
||||
if (PlatformInfos.isMobile)
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
child: const Icon(
|
||||
Icons.qr_code_scanner_outlined,
|
||||
),
|
||||
),
|
||||
title: Text(L10n.of(context).scanQrCode),
|
||||
onTap: controller.openScannerAction,
|
||||
),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: ConstrainedBox(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: 200),
|
||||
child: PrettyQrView.data(
|
||||
// #Pangea
|
||||
// data: 'https://matrix.to/#/$userId',
|
||||
data: Environment.frontendURL,
|
||||
// Pangea#
|
||||
decoration: PrettyQrDecoration(
|
||||
shape: PrettyQrSmoothSymbol(
|
||||
roundFactor: 1,
|
||||
color: theme.colorScheme.primary,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 64.0,
|
||||
vertical: 24.0,
|
||||
),
|
||||
child: Material(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
side: BorderSide(
|
||||
width: 3,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
color: Colors.transparent,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
onTap: () =>
|
||||
showQrCodeViewer(context, userId),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
),
|
||||
child: PrettyQrView.data(
|
||||
// #Pangea
|
||||
// data: 'https://matrix.to/#/$userId',
|
||||
data: Environment.frontendURL,
|
||||
// Pangea#
|
||||
decoration: PrettyQrDecoration(
|
||||
shape: PrettyQrSmoothSymbol(
|
||||
roundFactor: 1,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: FutureBuilder(
|
||||
future: searchResponse,
|
||||
builder: (context, snapshot) {
|
||||
final result = snapshot.data;
|
||||
final error = snapshot.error;
|
||||
if (error != null) {
|
||||
return Column(
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
Text(
|
||||
error.toLocalizedString(context),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
OutlinedButton.icon(
|
||||
onPressed: controller.searchUsers,
|
||||
icon: const Icon(Icons.refresh_outlined),
|
||||
label: Text(L10n.of(context).tryAgain),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
if (result == null) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
);
|
||||
}
|
||||
if (result.isEmpty) {
|
||||
return Column(
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
const Icon(Icons.search_outlined, size: 86),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
L10n.of(context).noUsersFoundWithQuery(
|
||||
controller.controller.text,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: result.length,
|
||||
itemBuilder: (context, i) {
|
||||
final contact = result[i];
|
||||
final displayname =
|
||||
contact.displayName ??
|
||||
contact.userId.localpart ??
|
||||
contact.userId;
|
||||
return ListTile(
|
||||
leading: Avatar(
|
||||
name: displayname,
|
||||
mxContent: contact.avatarUrl,
|
||||
presenceUserId: contact.userId,
|
||||
),
|
||||
title: Text(displayname),
|
||||
subtitle: Text(contact.userId),
|
||||
onTap: () => controller.openUserModal(contact),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
secondChild: FutureBuilder(
|
||||
future: searchResponse,
|
||||
builder: (context, snapshot) {
|
||||
final result = snapshot.data;
|
||||
final error = snapshot.error;
|
||||
if (error != null) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
error.toLocalizedString(context),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
OutlinedButton.icon(
|
||||
onPressed: controller.searchUsers,
|
||||
icon: const Icon(Icons.refresh_outlined),
|
||||
label: Text(L10n.of(context).tryAgain),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
if (result == null) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
);
|
||||
}
|
||||
if (result.isEmpty) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.search_outlined, size: 86),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
L10n.of(context).noUsersFoundWithQuery(
|
||||
controller.controller.text,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: result.length,
|
||||
itemBuilder: (context, i) {
|
||||
final contact = result[i];
|
||||
final displayname = contact.displayName ??
|
||||
contact.userId.localpart ??
|
||||
contact.userId;
|
||||
return ListTile(
|
||||
leading: Avatar(
|
||||
name: displayname,
|
||||
mxContent: contact.avatarUrl,
|
||||
presenceUserId: contact.userId,
|
||||
),
|
||||
title: Text(displayname),
|
||||
subtitle: Text(contact.userId),
|
||||
onTap: () => controller.openUserModal(contact),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -30,17 +30,9 @@ class EnableNotificationsController extends State<EnableNotifications> {
|
|||
Future<void> _setProfile() async {
|
||||
final client = Matrix.of(context).client;
|
||||
try {
|
||||
profile = await client.getProfileFromUserId(
|
||||
client.userID!,
|
||||
);
|
||||
profile = await client.getProfileFromUserId(client.userID!);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
'userId': client.userID,
|
||||
},
|
||||
);
|
||||
ErrorHandler.logError(e: e, s: s, data: {'userId': client.userID});
|
||||
} finally {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
|
@ -58,21 +50,14 @@ class EnableNotificationsController extends State<EnableNotifications> {
|
|||
return PangeaLoginScaffold(
|
||||
customAppBar: AppBar(
|
||||
title: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 450,
|
||||
),
|
||||
constraints: const BoxConstraints(maxWidth: 450),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
BackButton(
|
||||
onPressed: () => pLogoutAction(
|
||||
context,
|
||||
bypassWarning: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 40.0,
|
||||
onPressed: () => pLogoutAction(context, bypassWarning: true),
|
||||
),
|
||||
const SizedBox(width: 40.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -90,10 +75,9 @@ class EnableNotificationsController extends State<EnableNotifications> {
|
|||
Matrix.of(context).client.userID?.localpart ??
|
||||
"",
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
L10n.of(context).enableNotificationsTitle,
|
||||
|
|
@ -103,14 +87,13 @@ class EnableNotificationsController extends State<EnableNotifications> {
|
|||
onPressed: _requestNotificationPermission,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
foregroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(L10n.of(context).enableNotificationsDesc),
|
||||
],
|
||||
children: [Text(L10n.of(context).enableNotificationsDesc)],
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
|
|
|
|||
|
|
@ -39,17 +39,9 @@ class SpaceCodeOnboardingState extends State<SpaceCodeOnboarding> {
|
|||
|
||||
Future<void> _setProfile() async {
|
||||
try {
|
||||
profile = await client.getProfileFromUserId(
|
||||
client.userID!,
|
||||
);
|
||||
profile = await client.getProfileFromUserId(client.userID!);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
'userId': client.userID,
|
||||
},
|
||||
);
|
||||
ErrorHandler.logError(e: e, s: s, data: {'userId': client.userID});
|
||||
} finally {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,28 +9,19 @@ import 'package:fluffychat/pangea/login/pages/pangea_login_scaffold.dart';
|
|||
|
||||
class SpaceCodeOnboardingView extends StatelessWidget {
|
||||
final SpaceCodeOnboardingState controller;
|
||||
const SpaceCodeOnboardingView({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
const SpaceCodeOnboardingView({super.key, required this.controller});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PangeaLoginScaffold(
|
||||
customAppBar: AppBar(
|
||||
title: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 450,
|
||||
),
|
||||
constraints: const BoxConstraints(maxWidth: 450),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
BackButton(
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 40.0,
|
||||
),
|
||||
BackButton(onPressed: Navigator.of(context).pop),
|
||||
const SizedBox(width: 40.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -48,10 +39,9 @@ class SpaceCodeOnboardingView extends StatelessWidget {
|
|||
controller.client.userID?.localpart ??
|
||||
"",
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
L10n.of(context).joinSpaceOnboardingDesc,
|
||||
|
|
@ -70,14 +60,13 @@ class SpaceCodeOnboardingView extends StatelessWidget {
|
|||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
foregroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(L10n.of(context).join),
|
||||
],
|
||||
children: [Text(L10n.of(context).join)],
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ class SettingsController extends State<Settings> {
|
|||
bool profileUpdated = false;
|
||||
|
||||
void updateProfile() => setState(() {
|
||||
profileUpdated = true;
|
||||
profileFuture = null;
|
||||
});
|
||||
profileUpdated = true;
|
||||
profileFuture = null;
|
||||
});
|
||||
|
||||
void setDisplaynameAction() async {
|
||||
final profile = await profileFuture;
|
||||
|
|
@ -132,15 +132,9 @@ class SettingsController extends State<Settings> {
|
|||
imageQuality: 50,
|
||||
);
|
||||
if (result == null) return;
|
||||
file = MatrixFile(
|
||||
bytes: await result.readAsBytes(),
|
||||
name: result.path,
|
||||
);
|
||||
file = MatrixFile(bytes: await result.readAsBytes(), name: result.path);
|
||||
} else {
|
||||
final result = await selectFiles(
|
||||
context,
|
||||
type: FileSelectorType.images,
|
||||
);
|
||||
final result = await selectFiles(context, type: FileSelectorType.images);
|
||||
final pickedFile = result.firstOrNull;
|
||||
if (pickedFile == null) return;
|
||||
file = MatrixFile(
|
||||
|
|
@ -182,8 +176,8 @@ class SettingsController extends State<Settings> {
|
|||
await client.encryption?.crossSigning.isCached() ?? false;
|
||||
final needsBootstrap =
|
||||
await client.encryption?.keyManager.isCached() == false ||
|
||||
client.encryption?.crossSigning.enabled == false ||
|
||||
crossSigning == false;
|
||||
client.encryption?.crossSigning.enabled == false ||
|
||||
crossSigning == false;
|
||||
final isUnknownSession = client.isUnknownSession;
|
||||
setState(() {
|
||||
showChatBackupBanner = needsBootstrap || isUnknownSession;
|
||||
|
|
@ -242,9 +236,7 @@ class SettingsController extends State<Settings> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = Matrix.of(context).client;
|
||||
profileFuture ??= client.getProfileFromUserId(
|
||||
client.userID!,
|
||||
);
|
||||
profileFuture ??= client.getProfileFromUserId(client.userID!);
|
||||
return SettingsView(this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import 'package:url_launcher/url_launcher_string.dart';
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/settings/settings.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/local_notifications_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../widgets/mxc_image_viewer.dart';
|
||||
import 'settings.dart';
|
||||
import 'package:fluffychat/widgets/mxc_image_viewer.dart';
|
||||
|
||||
class SettingsView extends StatelessWidget {
|
||||
final SettingsController controller;
|
||||
|
|
@ -28,8 +28,9 @@ class SettingsView extends StatelessWidget {
|
|||
// #Pangea
|
||||
// final showChatBackupBanner = controller.showChatBackupBanner;
|
||||
// Pangea#
|
||||
final activeRoute =
|
||||
GoRouter.of(context).routeInformationProvider.value.uri.path;
|
||||
final activeRoute = GoRouter.of(
|
||||
context,
|
||||
).routeInformationProvider.value.uri.path;
|
||||
// #Pangea
|
||||
// final accountManageUrl = Matrix.of(context)
|
||||
// .client
|
||||
|
|
@ -47,10 +48,7 @@ class SettingsView extends StatelessWidget {
|
|||
// onGoToChats: () => context.go('/rooms'),
|
||||
// onGoToSpaceId: (spaceId) => context.go('/rooms?spaceId=$spaceId'),
|
||||
// ),
|
||||
// Container(
|
||||
// color: Theme.of(context).dividerColor,
|
||||
// width: 1,
|
||||
// ),
|
||||
// Container(color: Theme.of(context).dividerColor, width: 1),
|
||||
// ],
|
||||
// Pangea#
|
||||
Expanded(
|
||||
|
|
@ -61,9 +59,7 @@ class SettingsView extends StatelessWidget {
|
|||
// : AppBar(
|
||||
// title: Text(L10n.of(context).settings),
|
||||
// leading: Center(
|
||||
// child: BackButton(
|
||||
// onPressed: () => context.go('/rooms'),
|
||||
// ),
|
||||
// child: BackButton(onPressed: () => context.go('/rooms')),
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
|
|
@ -80,7 +76,8 @@ class SettingsView extends StatelessWidget {
|
|||
builder: (context, snapshot) {
|
||||
final profile = snapshot.data;
|
||||
final avatar = profile?.avatarUrl;
|
||||
final mxid = Matrix.of(context).client.userID ??
|
||||
final mxid =
|
||||
Matrix.of(context).client.userID ??
|
||||
L10n.of(context).user;
|
||||
final displayname =
|
||||
profile?.displayName ?? mxid.localpart ?? mxid;
|
||||
|
|
@ -106,10 +103,10 @@ class SettingsView extends StatelessWidget {
|
|||
size: Avatar.defaultSize * 2.5,
|
||||
onTap: avatar != null
|
||||
? () => showDialog(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
MxcImageViewer(avatar),
|
||||
)
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
MxcImageViewer(avatar),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
if (profile != null)
|
||||
|
|
@ -130,8 +127,8 @@ class SettingsView extends StatelessWidget {
|
|||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: .center,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: controller.setDisplaynameAction,
|
||||
|
|
@ -148,9 +145,7 @@ class SettingsView extends StatelessWidget {
|
|||
displayname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
|
|
@ -205,16 +200,14 @@ class SettingsView extends StatelessWidget {
|
|||
// title: Text(L10n.of(context).chatBackup),
|
||||
// onChanged: controller.firstRunBootstrapAction,
|
||||
// ),
|
||||
// Divider(
|
||||
// color: theme.dividerColor,
|
||||
// ),
|
||||
// Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.language_outlined),
|
||||
title: Text(L10n.of(context).learningSettings),
|
||||
tileColor:
|
||||
activeRoute.startsWith('/rooms/settings/learning')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
onTap: () => context.go('/rooms/settings/learning'),
|
||||
),
|
||||
// Pangea#
|
||||
|
|
@ -229,16 +222,19 @@ class SettingsView extends StatelessWidget {
|
|||
ListTile(
|
||||
leading: const Icon(Icons.notifications_outlined),
|
||||
title: Text(L10n.of(context).notifications),
|
||||
tileColor: activeRoute
|
||||
.startsWith('/rooms/settings/notifications')
|
||||
tileColor:
|
||||
activeRoute.startsWith(
|
||||
'/rooms/settings/notifications',
|
||||
)
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
onTap: () => context.go('/rooms/settings/notifications'),
|
||||
// #Pangea
|
||||
trailing: ValueListenableBuilder(
|
||||
valueListenable:
|
||||
Matrix.of(context).notifPermissionNotifier,
|
||||
builder: (context, _, __) => FutureBuilder<bool>(
|
||||
valueListenable: Matrix.of(
|
||||
context,
|
||||
).notifPermissionNotifier,
|
||||
builder: (context, _, _) => FutureBuilder<bool>(
|
||||
future: Matrix.of(context).notificationsEnabled,
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.data != false
|
||||
|
|
@ -258,8 +254,8 @@ class SettingsView extends StatelessWidget {
|
|||
onTap: () => context.go('/rooms/settings/devices'),
|
||||
tileColor:
|
||||
activeRoute.startsWith('/rooms/settings/devices')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.forum_outlined),
|
||||
|
|
@ -276,8 +272,8 @@ class SettingsView extends StatelessWidget {
|
|||
onTap: () => context.go('/rooms/settings/subscription'),
|
||||
tileColor:
|
||||
activeRoute.startsWith('/rooms/settings/subscription')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
),
|
||||
// Pangea#
|
||||
ListTile(
|
||||
|
|
@ -286,8 +282,8 @@ class SettingsView extends StatelessWidget {
|
|||
onTap: () => context.go('/rooms/settings/security'),
|
||||
tileColor:
|
||||
activeRoute.startsWith('/rooms/settings/security')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
// #Pangea
|
||||
|
|
@ -299,11 +295,11 @@ class SettingsView extends StatelessWidget {
|
|||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
final roomId =
|
||||
await Matrix.of(context).client.startDirectChat(
|
||||
Environment.supportUserId,
|
||||
enableEncryption: false,
|
||||
);
|
||||
final roomId = await Matrix.of(context).client
|
||||
.startDirectChat(
|
||||
Environment.supportUserId,
|
||||
enableEncryption: false,
|
||||
);
|
||||
context.go('/rooms/$roomId');
|
||||
},
|
||||
);
|
||||
|
|
@ -332,8 +328,9 @@ class SettingsView extends StatelessWidget {
|
|||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content:
|
||||
Text(L10n.of(context).copiedToClipboard),
|
||||
content: Text(
|
||||
L10n.of(context).copiedToClipboard,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -376,8 +373,8 @@ class SettingsView extends StatelessWidget {
|
|||
// onTap: () => context.go('/rooms/settings/homeserver'),
|
||||
// tileColor:
|
||||
// activeRoute.startsWith('/rooms/settings/homeserver')
|
||||
// ? theme.colorScheme.surfaceContainerHigh
|
||||
// : null,
|
||||
// ? theme.colorScheme.surfaceContainerHigh
|
||||
// : null,
|
||||
// ),
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.privacy_tip_outlined),
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ class Settings3PidController extends State<Settings3Pid> {
|
|||
final response = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => Matrix.of(context).client.requestTokenToRegisterEmail(
|
||||
clientSecret,
|
||||
input,
|
||||
Settings3Pid.sendAttempt++,
|
||||
),
|
||||
clientSecret,
|
||||
input,
|
||||
Settings3Pid.sendAttempt++,
|
||||
),
|
||||
);
|
||||
if (response.error != null) return;
|
||||
final ok = await showOkAlertDialog(
|
||||
|
|
@ -55,12 +55,10 @@ class Settings3PidController extends State<Settings3Pid> {
|
|||
context: context,
|
||||
delay: false,
|
||||
future: () => Matrix.of(context).client.uiaRequestBackground(
|
||||
(auth) => Matrix.of(context).client.add3PID(
|
||||
clientSecret,
|
||||
response.result!.sid,
|
||||
auth: auth,
|
||||
),
|
||||
),
|
||||
(auth) => Matrix.of(
|
||||
context,
|
||||
).client.add3PID(clientSecret, response.result!.sid, auth: auth),
|
||||
),
|
||||
// #Pangea
|
||||
showError: (e) => !e.toString().contains("Request has been canceled"),
|
||||
// Pangea#
|
||||
|
|
@ -84,10 +82,9 @@ class Settings3PidController extends State<Settings3Pid> {
|
|||
}
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => Matrix.of(context).client.delete3pidFromAccount(
|
||||
identifier.address,
|
||||
identifier.medium,
|
||||
),
|
||||
future: () => Matrix.of(
|
||||
context,
|
||||
).client.delete3pidFromAccount(identifier.address, identifier.medium),
|
||||
);
|
||||
if (success.error != null) return;
|
||||
setState(() => request = null);
|
||||
|
|
|
|||
|
|
@ -33,67 +33,71 @@ class Settings3PidView extends StatelessWidget {
|
|||
withScrolling: false,
|
||||
child: FutureBuilder<List<ThirdPartyIdentifier>?>(
|
||||
future: controller.request,
|
||||
builder: (
|
||||
BuildContext context,
|
||||
AsyncSnapshot<List<ThirdPartyIdentifier>?> snapshot,
|
||||
) {
|
||||
if (snapshot.hasError) {
|
||||
return Center(
|
||||
child: Text(
|
||||
snapshot.error.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
);
|
||||
}
|
||||
final identifier = snapshot.data!;
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
foregroundColor:
|
||||
identifier.isEmpty ? Colors.orange : Colors.grey,
|
||||
child: Icon(
|
||||
identifier.isEmpty
|
||||
? Icons.warning_outlined
|
||||
: Icons.info_outlined,
|
||||
builder:
|
||||
(
|
||||
BuildContext context,
|
||||
AsyncSnapshot<List<ThirdPartyIdentifier>?> snapshot,
|
||||
) {
|
||||
if (snapshot.hasError) {
|
||||
return Center(
|
||||
child: Text(
|
||||
snapshot.error.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
identifier.isEmpty
|
||||
? L10n.of(context).noPasswordRecoveryDescription
|
||||
: L10n.of(context)
|
||||
.withTheseAddressesRecoveryDescription,
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: identifier.length,
|
||||
itemBuilder: (BuildContext context, int i) => ListTile(
|
||||
);
|
||||
}
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
);
|
||||
}
|
||||
final identifier = snapshot.data!;
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
foregroundColor: Colors.grey,
|
||||
child: Icon(identifier[i].iconData),
|
||||
foregroundColor: identifier.isEmpty
|
||||
? Colors.orange
|
||||
: Colors.grey,
|
||||
child: Icon(
|
||||
identifier.isEmpty
|
||||
? Icons.warning_outlined
|
||||
: Icons.info_outlined,
|
||||
),
|
||||
),
|
||||
title: Text(identifier[i].address),
|
||||
trailing: IconButton(
|
||||
tooltip: L10n.of(context).delete,
|
||||
icon: const Icon(Icons.delete_forever_outlined),
|
||||
color: Colors.red,
|
||||
onPressed: () => controller.delete3Pid(identifier[i]),
|
||||
title: Text(
|
||||
identifier.isEmpty
|
||||
? L10n.of(context).noPasswordRecoveryDescription
|
||||
: L10n.of(
|
||||
context,
|
||||
).withTheseAddressesRecoveryDescription,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
const Divider(),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: identifier.length,
|
||||
itemBuilder: (BuildContext context, int i) => ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
foregroundColor: Colors.grey,
|
||||
child: Icon(identifier[i].iconData),
|
||||
),
|
||||
title: Text(identifier[i].address),
|
||||
trailing: IconButton(
|
||||
tooltip: L10n.of(context).delete,
|
||||
icon: const Icon(Icons.delete_forever_outlined),
|
||||
color: Colors.red,
|
||||
onPressed: () =>
|
||||
controller.delete3Pid(identifier[i]),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,11 +46,7 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
|||
return AlertDialog(
|
||||
title: Text(L10n.of(context).importEmojis),
|
||||
content: _loading
|
||||
? Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: _progress,
|
||||
),
|
||||
)
|
||||
? Center(child: CircularProgressIndicator(value: _progress))
|
||||
: SingleChildScrollView(
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.spaceEvenly,
|
||||
|
|
@ -79,8 +75,8 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
|||
onPressed: _loading
|
||||
? null
|
||||
: _importMap.isNotEmpty
|
||||
? _addEmotePack
|
||||
: null,
|
||||
? _addEmotePack
|
||||
: null,
|
||||
child: Text(L10n.of(context).importNow),
|
||||
),
|
||||
],
|
||||
|
|
@ -91,12 +87,8 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
|||
_importMap = Map.fromEntries(
|
||||
widget.archive.files
|
||||
.where((e) => e.isFile)
|
||||
.map(
|
||||
(e) => MapEntry(e, e.name.emoteNameFromPath),
|
||||
)
|
||||
.sorted(
|
||||
(a, b) => a.value.compareTo(b.value),
|
||||
),
|
||||
.map((e) => MapEntry(e, e.name.emoteNameFromPath))
|
||||
.sorted((a, b) => a.value.compareTo(b.value)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -148,10 +140,7 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
|||
final imageCode = entry.value;
|
||||
|
||||
try {
|
||||
var mxcFile = MatrixImageFile(
|
||||
bytes: file.content,
|
||||
name: file.name,
|
||||
);
|
||||
var mxcFile = MatrixImageFile(bytes: file.content, name: file.name);
|
||||
|
||||
final thumbnail = (await mxcFile.generateThumbnail(
|
||||
nativeImplementations: ClientManager.nativeImplementations,
|
||||
|
|
@ -162,14 +151,12 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
|||
mxcFile = thumbnail;
|
||||
}
|
||||
final uri = await Matrix.of(context).client.uploadContent(
|
||||
mxcFile.bytes,
|
||||
filename: mxcFile.name,
|
||||
contentType: mxcFile.mimeType,
|
||||
);
|
||||
mxcFile.bytes,
|
||||
filename: mxcFile.name,
|
||||
contentType: mxcFile.mimeType,
|
||||
);
|
||||
|
||||
final info = <String, dynamic>{
|
||||
...mxcFile.info,
|
||||
};
|
||||
final info = <String, dynamic>{...mxcFile.info};
|
||||
|
||||
// normalize width / height to 256, required for stickers
|
||||
if (info['w'] is int && info['h'] is int) {
|
||||
|
|
@ -184,9 +171,9 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
|||
}
|
||||
widget.controller.pack!.images[imageCode] =
|
||||
ImagePackImageContent.fromJson(<String, dynamic>{
|
||||
'url': uri.toString(),
|
||||
'info': info,
|
||||
});
|
||||
'url': uri.toString(),
|
||||
'info': info,
|
||||
});
|
||||
successfulUploads.add(file.name);
|
||||
} catch (e) {
|
||||
Logs().d('Could not upload emote $imageCode');
|
||||
|
|
@ -204,8 +191,9 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
|||
// in case we have unhandled / duplicated emotes left, don't pop
|
||||
if (mounted) setState(() {});
|
||||
if (_importMap.isEmpty) {
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => Navigator.of(context).pop());
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => Navigator.of(context).pop(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -250,21 +238,20 @@ class _EmojiImportPreviewState extends State<_EmojiImportPreview> {
|
|||
if (hasError) return _ImageFileError(name: widget.entry.key.name);
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: .min,
|
||||
mainAxisAlignment: .center,
|
||||
crossAxisAlignment: .center,
|
||||
children: [
|
||||
Image.memory(
|
||||
widget.entry.key.content,
|
||||
height: 64,
|
||||
width: 64,
|
||||
errorBuilder: (context, e, s) {
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => _setRenderError());
|
||||
|
||||
return _ImageFileError(
|
||||
name: widget.entry.key.name,
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => _setRenderError(),
|
||||
);
|
||||
|
||||
return _ImageFileError(name: widget.entry.key.name);
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
|
|
@ -323,9 +310,9 @@ class _ImageFileError extends StatelessWidget {
|
|||
child: Tooltip(
|
||||
message: name,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: .start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .center,
|
||||
children: [
|
||||
const Icon(Icons.error),
|
||||
Text(
|
||||
|
|
@ -347,8 +334,7 @@ extension on String {
|
|||
/// Used to compute emote name proposal based on file name
|
||||
String get emoteNameFromPath {
|
||||
// ... removing leading path
|
||||
return split(RegExp(r'[/\\]'))
|
||||
.last
|
||||
return split(RegExp(r'[/\\]')).last
|
||||
// ... removing file extension
|
||||
.split('.')
|
||||
.first
|
||||
|
|
|
|||
|
|
@ -53,10 +53,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
|
||||
final event = key == null
|
||||
? null
|
||||
: room?.getState(
|
||||
'im.ponies.room_emotes',
|
||||
key,
|
||||
);
|
||||
: room?.getState('im.ponies.room_emotes', key);
|
||||
final eventPack = event?.content.tryGetMap<String, Object?>('pack');
|
||||
packDisplayNameController.text =
|
||||
eventPack?.tryGet<String>('display_name') ?? '';
|
||||
|
|
@ -69,13 +66,11 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
|
||||
ImagePackContent _getPack() {
|
||||
final client = Matrix.of(context).client;
|
||||
final event = (room != null
|
||||
final event =
|
||||
(room != null
|
||||
? room!.getState('im.ponies.room_emotes', stateKey ?? '')
|
||||
: client.accountData['im.ponies.user_emotes']) ??
|
||||
BasicEvent(
|
||||
type: 'm.dummy',
|
||||
content: {},
|
||||
);
|
||||
BasicEvent(type: 'm.dummy', content: {});
|
||||
// make sure we work on a *copy* of the event
|
||||
return BasicEvent.fromJson(event.toJson()).parsedImagePackContent;
|
||||
}
|
||||
|
|
@ -122,7 +117,8 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
return;
|
||||
}
|
||||
final client = Matrix.of(context).client;
|
||||
final content = client.accountData['im.ponies.emote_rooms']?.content ??
|
||||
final content =
|
||||
client.accountData['im.ponies.emote_rooms']?.content ??
|
||||
<String, dynamic>{};
|
||||
if (active) {
|
||||
if (content['rooms'] is! Map) {
|
||||
|
|
@ -156,14 +152,15 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
TextEditingController();
|
||||
|
||||
void removeImageAction(String oldImageCode) => setState(() {
|
||||
pack!.images.remove(oldImageCode);
|
||||
showSave = true;
|
||||
});
|
||||
pack!.images.remove(oldImageCode);
|
||||
showSave = true;
|
||||
});
|
||||
|
||||
void toggleUsage(String imageCode, ImagePackUsage usage) {
|
||||
setState(() {
|
||||
final usages =
|
||||
pack!.images[imageCode]!.usage ??= List.from(ImagePackUsage.values);
|
||||
final usages = pack!.images[imageCode]!.usage ??= List.from(
|
||||
ImagePackUsage.values,
|
||||
);
|
||||
if (!usages.remove(usage)) usages.add(usage);
|
||||
showSave = true;
|
||||
});
|
||||
|
|
@ -263,9 +260,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
|
||||
if (packKeys?.contains(name) ?? false) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context).stickerPackNameAlreadyExists),
|
||||
),
|
||||
SnackBar(content: Text(L10n.of(context).stickerPackNameAlreadyExists)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -314,20 +309,19 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
bytes: await pickedFile.readAsBytes(),
|
||||
name: pickedFile.name,
|
||||
);
|
||||
file = await file.generateThumbnail(
|
||||
file =
|
||||
await file.generateThumbnail(
|
||||
nativeImplementations: ClientManager.nativeImplementations,
|
||||
) ??
|
||||
file;
|
||||
final uri = await Matrix.of(context).client.uploadContent(
|
||||
file.bytes,
|
||||
filename: file.name,
|
||||
contentType: file.mimeType,
|
||||
);
|
||||
file.bytes,
|
||||
filename: file.name,
|
||||
contentType: file.mimeType,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
final info = <String, dynamic>{
|
||||
...file.info,
|
||||
};
|
||||
final info = <String, dynamic>{...file.info};
|
||||
// normalize width / height to 256, required for stickers
|
||||
if (info['w'] is int && info['h'] is int) {
|
||||
final ratio = info['w'] / info['h'];
|
||||
|
|
@ -340,11 +334,9 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
}
|
||||
}
|
||||
final imageCode = pickedFile.name.split('.').first;
|
||||
pack!.images[imageCode] =
|
||||
ImagePackImageContent.fromJson(<String, dynamic>{
|
||||
'url': uri.toString(),
|
||||
'info': info,
|
||||
});
|
||||
pack!.images[imageCode] = ImagePackImageContent.fromJson(
|
||||
<String, dynamic>{'url': uri.toString(), 'info': info},
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
@ -361,10 +353,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
}
|
||||
|
||||
Future<void> importEmojiZip() async {
|
||||
final result = await selectFiles(
|
||||
context,
|
||||
type: FileSelectorType.zip,
|
||||
);
|
||||
final result = await selectFiles(context, type: FileSelectorType.zip);
|
||||
|
||||
if (result.isEmpty) return;
|
||||
|
||||
|
|
@ -376,10 +365,8 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
context: context,
|
||||
// breaks [Matrix.of] calls otherwise
|
||||
useRootNavigator: false,
|
||||
builder: (context) => ImportEmoteArchiveDialog(
|
||||
controller: this,
|
||||
archive: archive,
|
||||
),
|
||||
builder: (context) =>
|
||||
ImportEmoteArchiveDialog(controller: this, archive: archive),
|
||||
);
|
||||
setState(() {});
|
||||
}
|
||||
|
|
@ -402,11 +389,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||
);
|
||||
|
||||
archive.addFile(
|
||||
ArchiveFile(
|
||||
name,
|
||||
response.bodyBytes.length,
|
||||
response.bodyBytes,
|
||||
),
|
||||
ArchiveFile(name, response.bodyBytes.length, response.bodyBytes),
|
||||
);
|
||||
}
|
||||
final fileName =
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue