Merge branch 'main' into weblate-fluffychat-translations
This commit is contained in:
commit
f0841fea9d
15 changed files with 678 additions and 496 deletions
|
|
@ -20,7 +20,5 @@
|
|||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>13.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ import UIKit
|
|||
import Flutter
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
if #available(iOS 10.0, *) {
|
||||
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
|
||||
}
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
|
||||
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,5 +112,26 @@
|
|||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneClassName</key>
|
||||
<string>UIWindowScene</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>FlutterSceneDelegate</string>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>flutter</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -440,25 +440,16 @@ class ChatListController extends State<ChatList>
|
|||
PopupMenuItem(
|
||||
value: ChatContextAction.open,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
Avatar(mxContent: room.avatar, name: displayname),
|
||||
Avatar(mxContent: room.avatar, name: displayname, size: 24),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 128),
|
||||
child: Text(
|
||||
displayname,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
constraints: const BoxConstraints(maxWidth: 200),
|
||||
child: Text(displayname, maxLines: 1, overflow: .ellipsis),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
if (space != null)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.goToSpace,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
|
|
@ -81,6 +82,9 @@ class LoginController extends State<Login> {
|
|||
password: passwordController.text,
|
||||
initialDeviceDisplayName: PlatformInfos.clientName,
|
||||
);
|
||||
if (mounted) {
|
||||
context.go('/backup');
|
||||
}
|
||||
} on MatrixException catch (exception) {
|
||||
setState(() => passwordError = exception.errorMessage);
|
||||
return setState(() => loading = false);
|
||||
|
|
|
|||
|
|
@ -37,164 +37,165 @@ class SignInPage extends StatelessWidget {
|
|||
? L10n.of(context).createNewAccount
|
||||
: L10n.of(context).login,
|
||||
),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(56 + 60),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .center,
|
||||
spacing: 12,
|
||||
children: [
|
||||
SelectableText(
|
||||
signUp
|
||||
? L10n.of(context).signUpGreeting
|
||||
: L10n.of(context).signInGreeting,
|
||||
textAlign: .center,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
SelectableText(
|
||||
signUp
|
||||
? L10n.of(context).signUpGreeting
|
||||
: L10n.of(context).signInGreeting,
|
||||
textAlign: .center,
|
||||
),
|
||||
TextField(
|
||||
readOnly:
|
||||
state.publicHomeservers.connectionState ==
|
||||
ConnectionState.waiting,
|
||||
controller: viewModel.filterTextController,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.url,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
TextField(
|
||||
readOnly:
|
||||
state.publicHomeservers.connectionState ==
|
||||
ConnectionState.waiting,
|
||||
controller: viewModel.filterTextController,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.url,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
errorText: state.publicHomeservers.error?.toLocalizedString(
|
||||
context,
|
||||
),
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
hintText: L10n.of(context).searchOrEnterHomeserverAddress,
|
||||
),
|
||||
),
|
||||
if (state.publicHomeservers.connectionState ==
|
||||
ConnectionState.done)
|
||||
Expanded(
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: theme.colorScheme.surfaceContainerLow,
|
||||
child: RadioGroup<PublicHomeserverData>(
|
||||
groupValue: state.selectedHomeserver,
|
||||
onChanged: viewModel.selectHomeserver,
|
||||
child: ListView.builder(
|
||||
itemCount: publicHomeservers.length,
|
||||
itemBuilder: (context, i) {
|
||||
final server = publicHomeservers[i];
|
||||
final website = server.website;
|
||||
return RadioListTile.adaptive(
|
||||
value: server,
|
||||
radioScaleFactor:
|
||||
FluffyThemes.isColumnMode(context) ||
|
||||
{
|
||||
TargetPlatform.iOS,
|
||||
TargetPlatform.macOS,
|
||||
}.contains(theme.platform)
|
||||
? 2
|
||||
: 1,
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(server.name ?? 'Unknown'),
|
||||
),
|
||||
if (website != null)
|
||||
SizedBox.square(
|
||||
dimension: 32,
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.open_in_new_outlined,
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () =>
|
||||
launchUrlString(website),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
spacing: 4.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (server.features?.isNotEmpty == true)
|
||||
Wrap(
|
||||
spacing: 4.0,
|
||||
runSpacing: 4.0,
|
||||
children: [
|
||||
...?server.languages?.map(
|
||||
(language) => Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.tertiaryContainer,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 3.0,
|
||||
),
|
||||
child: Text(
|
||||
language,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onTertiaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
...server.features!.map(
|
||||
(feature) => Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 3.0,
|
||||
),
|
||||
child: Text(
|
||||
feature,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
server.description ?? 'A matrix homeserver',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
errorText: state.publicHomeservers.error
|
||||
?.toLocalizedString(context),
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
hintText: L10n.of(
|
||||
context,
|
||||
).searchOrEnterHomeserverAddress,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
Center(child: CircularProgressIndicator.adaptive()),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: state.publicHomeservers.connectionState == ConnectionState.done
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: theme.colorScheme.surfaceContainerLow,
|
||||
child: RadioGroup<PublicHomeserverData>(
|
||||
groupValue: state.selectedHomeserver,
|
||||
onChanged: viewModel.selectHomeserver,
|
||||
child: ListView.builder(
|
||||
itemCount: publicHomeservers.length,
|
||||
itemBuilder: (context, i) {
|
||||
final server = publicHomeservers[i];
|
||||
final homepage = server.homepage;
|
||||
return RadioListTile.adaptive(
|
||||
value: server,
|
||||
radioScaleFactor:
|
||||
FluffyThemes.isColumnMode(context) ||
|
||||
{
|
||||
TargetPlatform.iOS,
|
||||
TargetPlatform.macOS,
|
||||
}.contains(theme.platform)
|
||||
? 2
|
||||
: 1,
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(child: Text(server.name ?? 'Unknown')),
|
||||
if (homepage != null)
|
||||
SizedBox.square(
|
||||
dimension: 32,
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.open_in_new_outlined,
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () =>
|
||||
launchUrlString(homepage),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
spacing: 4.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (server.features?.isNotEmpty == true)
|
||||
Wrap(
|
||||
spacing: 4.0,
|
||||
runSpacing: 4.0,
|
||||
children: [
|
||||
...?server.languages?.map(
|
||||
(language) => Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.tertiaryContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 3.0,
|
||||
),
|
||||
child: Text(
|
||||
language,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onTertiaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
...server.features!.map(
|
||||
(feature) => Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 3.0,
|
||||
),
|
||||
child: Text(
|
||||
feature,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
server.description ?? 'A matrix homeserver',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Center(child: CircularProgressIndicator.adaptive()),
|
||||
bottomNavigationBar: AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
|
|
@ -203,12 +204,14 @@ class SignInPage extends StatelessWidget {
|
|||
!publicHomeservers.contains(selectedHomserver)
|
||||
? const SizedBox.shrink()
|
||||
: Material(
|
||||
elevation: 8,
|
||||
shadowColor: theme.appBarTheme.shadowColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SafeArea(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: theme.colorScheme.onPrimary,
|
||||
),
|
||||
onPressed:
|
||||
state.loginLoading.connectionState ==
|
||||
ConnectionState.waiting
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ int sortHomeservers(PublicHomeserverData a, PublicHomeserverData b) {
|
|||
int _calcHomeserverScore(PublicHomeserverData homeserver) {
|
||||
var score = 0;
|
||||
if (homeserver.description?.isNotEmpty == true) score++;
|
||||
if (homeserver.homepage?.isNotEmpty == true) score++;
|
||||
if (homeserver.website?.isNotEmpty == true) score++;
|
||||
score += (homeserver.languages?.length ?? 0);
|
||||
score += (homeserver.features?.length ?? 0);
|
||||
score += (homeserver.onlineStatus ?? 0);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
class PublicHomeserverData {
|
||||
final String? name;
|
||||
final String? clientDomain;
|
||||
final String? homepage;
|
||||
final String? website;
|
||||
final String? isp;
|
||||
final String? staffJur;
|
||||
final String? rules;
|
||||
|
|
@ -26,7 +26,7 @@ class PublicHomeserverData {
|
|||
PublicHomeserverData({
|
||||
this.name,
|
||||
this.clientDomain,
|
||||
this.homepage,
|
||||
this.website,
|
||||
this.isp,
|
||||
this.staffJur,
|
||||
this.rules,
|
||||
|
|
@ -53,7 +53,7 @@ class PublicHomeserverData {
|
|||
return PublicHomeserverData(
|
||||
name: json['name'],
|
||||
clientDomain: json['client_domain'],
|
||||
homepage: json['homepage'],
|
||||
website: json['website'],
|
||||
isp: json['isp'],
|
||||
staffJur: json['staff_jur'],
|
||||
rules: json['rules'],
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/sign_in/view_model/model/public_homeserver_data.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
|
|
@ -70,6 +70,7 @@ Future<void> connectToHomeserverFlow(
|
|||
|
||||
if (context.mounted) {
|
||||
setState(AsyncSnapshot.withData(ConnectionState.done, true));
|
||||
context.go('/backup');
|
||||
}
|
||||
} catch (e, s) {
|
||||
setState(AsyncSnapshot.withError(ConnectionState.done, e, s));
|
||||
|
|
|
|||
|
|
@ -82,3 +82,77 @@ class AdaptiveDialogAction extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AdaptiveDialogInkWell extends StatelessWidget {
|
||||
final Widget child;
|
||||
final VoidCallback onTap;
|
||||
final EdgeInsets padding;
|
||||
|
||||
const AdaptiveDialogInkWell({
|
||||
super.key,
|
||||
required this.onTap,
|
||||
required this.child,
|
||||
this.padding = const EdgeInsets.all(16),
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
if ({TargetPlatform.iOS, TargetPlatform.macOS}.contains(theme.platform)) {
|
||||
return CupertinoButton(
|
||||
onPressed: onTap,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
color: theme.colorScheme.surfaceBright,
|
||||
padding: padding,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return Material(
|
||||
color: theme.colorScheme.surfaceBright,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: padding,
|
||||
child: Center(child: child),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AdaptiveIconTextButton extends StatelessWidget {
|
||||
final String label;
|
||||
final IconData icon;
|
||||
final VoidCallback onTap;
|
||||
const AdaptiveIconTextButton({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.icon,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = Theme.of(context).colorScheme.secondary;
|
||||
return Expanded(
|
||||
child: AdaptiveDialogInkWell(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(icon, color: color),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(fontSize: 12, color: color),
|
||||
maxLines: 1,
|
||||
overflow: .ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
|
||||
import '../../config/themes.dart';
|
||||
import '../../utils/url_launcher.dart';
|
||||
import '../avatar.dart';
|
||||
|
|
@ -90,15 +92,8 @@ class PublicRoomDialog extends StatelessWidget {
|
|||
final roomLink = roomAlias ?? chunk?.roomId;
|
||||
var copied = false;
|
||||
return AlertDialog.adaptive(
|
||||
title: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: Text(
|
||||
chunk?.name ?? roomAlias?.localpart ?? chunk?.roomId ?? 'Unknown',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256),
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: FutureBuilder<PublishedRoomsChunk>(
|
||||
future: _search(context),
|
||||
builder: (context, snapshot) {
|
||||
|
|
@ -109,125 +104,196 @@ class PublicRoomDialog extends StatelessWidget {
|
|||
final topic = profile?.topic;
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
spacing: 16,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .stretch,
|
||||
children: [
|
||||
if (roomLink != null)
|
||||
HoverBuilder(
|
||||
builder: (context, hovered) => StatefulBuilder(
|
||||
builder: (context, setState) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: roomLink));
|
||||
setState(() {
|
||||
copied = true;
|
||||
});
|
||||
},
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 4.0,
|
||||
),
|
||||
child: AnimatedScale(
|
||||
duration:
|
||||
FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered
|
||||
? 1.33
|
||||
: copied
|
||||
? 1.25
|
||||
: 1.0,
|
||||
child: Icon(
|
||||
copied
|
||||
? Icons.check_circle
|
||||
: Icons.copy,
|
||||
size: 12,
|
||||
color: copied ? Colors.green : null,
|
||||
Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: avatar,
|
||||
name: profile?.name ?? roomLink,
|
||||
size: Avatar.defaultSize * 1.5,
|
||||
onTap: avatar != null
|
||||
? () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => MxcImageViewer(avatar),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Text(
|
||||
profile?.name ??
|
||||
roomLink ??
|
||||
profile?.roomId ??
|
||||
' - ',
|
||||
maxLines: 1,
|
||||
overflow: .ellipsis,
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (roomLink != null)
|
||||
HoverBuilder(
|
||||
builder: (context, hovered) => StatefulBuilder(
|
||||
builder: (context, setState) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: roomLink),
|
||||
);
|
||||
setState(() {
|
||||
copied = true;
|
||||
});
|
||||
},
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 4.0,
|
||||
),
|
||||
child: AnimatedScale(
|
||||
duration: FluffyThemes
|
||||
.animationDuration,
|
||||
curve: FluffyThemes
|
||||
.animationCurve,
|
||||
scale: hovered
|
||||
? 1.33
|
||||
: copied
|
||||
? 1.25
|
||||
: 1.0,
|
||||
child: Icon(
|
||||
copied
|
||||
? Icons.check_circle
|
||||
: Icons.copy,
|
||||
size: 12,
|
||||
color: copied
|
||||
? Colors.green
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(text: roomLink),
|
||||
],
|
||||
style: theme.textTheme.bodyMedium
|
||||
?.copyWith(fontSize: 10),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(text: roomLink),
|
||||
],
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
|
||||
if (profile?.numJoinedMembers != null)
|
||||
Text(
|
||||
L10n.of(context).countParticipants(
|
||||
profile?.numJoinedMembers ?? 0,
|
||||
),
|
||||
style: const TextStyle(fontSize: 10),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (topic != null && topic.isNotEmpty)
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
child: Scrollbar(
|
||||
thumbVisibility: true,
|
||||
trackVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
child: SelectableLinkify(
|
||||
text: topic,
|
||||
textScaleFactor: MediaQuery.textScalerOf(
|
||||
context,
|
||||
).scale(1),
|
||||
textAlign: .start,
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: theme.colorScheme.primary,
|
||||
),
|
||||
onOpen: (url) =>
|
||||
UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Avatar(
|
||||
mxContent: avatar,
|
||||
name: profile?.name ?? roomLink,
|
||||
size: Avatar.defaultSize * 2,
|
||||
onTap: avatar != null
|
||||
? () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => MxcImageViewer(avatar),
|
||||
)
|
||||
: null,
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: .spaceBetween,
|
||||
spacing: 4,
|
||||
children: [
|
||||
AdaptiveIconTextButton(
|
||||
label: L10n.of(context).report,
|
||||
icon: Icons.gavel_outlined,
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
final reason = await showTextInputDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).whyDoYouWantToReportThis,
|
||||
okLabel: L10n.of(context).report,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
hintText: L10n.of(context).reason,
|
||||
);
|
||||
if (reason == null || reason.isEmpty) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => Matrix.of(context).client.reportRoom(
|
||||
chunk?.roomId ?? roomAlias!,
|
||||
reason,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
AdaptiveIconTextButton(
|
||||
label: L10n.of(context).copy,
|
||||
icon: Icons.copy_outlined,
|
||||
onTap: () =>
|
||||
Clipboard.setData(ClipboardData(text: roomLink!)),
|
||||
),
|
||||
AdaptiveIconTextButton(
|
||||
label: L10n.of(context).share,
|
||||
icon: Icons.adaptive.share,
|
||||
onTap: () => FluffyShare.share(
|
||||
'https://matrix.to/#/$roomLink',
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
AdaptiveDialogInkWell(
|
||||
onTap: () => _joinRoom(context),
|
||||
child: Text(
|
||||
chunk?.joinRule == 'knock' &&
|
||||
Matrix.of(
|
||||
context,
|
||||
).client.getRoomById(chunk!.roomId) ==
|
||||
null
|
||||
? L10n.of(context).knock
|
||||
: chunk?.roomType == 'm.space'
|
||||
? L10n.of(context).joinSpace
|
||||
: L10n.of(context).joinRoom,
|
||||
style: TextStyle(color: theme.colorScheme.secondary),
|
||||
),
|
||||
),
|
||||
if (profile?.numJoinedMembers != null)
|
||||
Text(
|
||||
L10n.of(
|
||||
context,
|
||||
).countParticipants(profile?.numJoinedMembers ?? 0),
|
||||
style: const TextStyle(fontSize: 10),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (topic != null && topic.isNotEmpty)
|
||||
SelectableLinkify(
|
||||
text: topic,
|
||||
textScaleFactor: MediaQuery.textScalerOf(
|
||||
context,
|
||||
).scale(1),
|
||||
textAlign: TextAlign.center,
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: theme.colorScheme.primary,
|
||||
),
|
||||
onOpen: (url) =>
|
||||
UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
AdaptiveDialogAction(
|
||||
bigButtons: true,
|
||||
borderRadius: AdaptiveDialogAction.topRadius,
|
||||
onPressed: () => _joinRoom(context),
|
||||
child: Text(
|
||||
chunk?.joinRule == 'knock' &&
|
||||
Matrix.of(context).client.getRoomById(chunk!.roomId) == null
|
||||
? L10n.of(context).knock
|
||||
: chunk?.roomType == 'm.space'
|
||||
? L10n.of(context).joinSpace
|
||||
: L10n.of(context).joinRoom,
|
||||
),
|
||||
),
|
||||
AdaptiveDialogAction(
|
||||
bigButtons: true,
|
||||
borderRadius: AdaptiveDialogAction.bottomRadius,
|
||||
onPressed: Navigator.of(context).pop,
|
||||
child: Text(L10n.of(context).close),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ import 'package:matrix/matrix.dart';
|
|||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/presence_builder.dart';
|
||||
import '../../utils/url_launcher.dart';
|
||||
|
|
@ -17,6 +19,8 @@ import '../hover_builder.dart';
|
|||
import '../matrix.dart';
|
||||
import '../mxc_image_viewer.dart';
|
||||
|
||||
// ignore: unused_import
|
||||
|
||||
class UserDialog extends StatelessWidget {
|
||||
static Future<void> show({
|
||||
required BuildContext context,
|
||||
|
|
@ -37,7 +41,6 @@ class UserDialog extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = Matrix.of(context).client;
|
||||
final dmRoomId = client.getDirectChatFromUserId(profile.userId);
|
||||
final displayname =
|
||||
profile.displayName ??
|
||||
profile.userId.localpart ??
|
||||
|
|
@ -46,168 +49,209 @@ class UserDialog extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
final avatar = profile.avatarUrl;
|
||||
return AlertDialog.adaptive(
|
||||
title: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: Center(child: Text(displayname, textAlign: TextAlign.center)),
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256),
|
||||
child: PresenceBuilder(
|
||||
userId: profile.userId,
|
||||
client: Matrix.of(context).client,
|
||||
builder: (context, presence) {
|
||||
if (presence == null) return const SizedBox.shrink();
|
||||
final statusMsg = presence.statusMsg;
|
||||
final lastActiveTimestamp = presence.lastActiveTimestamp;
|
||||
final presenceText = presence.currentlyActive == true
|
||||
? L10n.of(context).currentlyActive
|
||||
: lastActiveTimestamp != null
|
||||
? L10n.of(context).lastActiveAgo(
|
||||
lastActiveTimestamp.localizedTimeShort(context),
|
||||
)
|
||||
: null;
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .stretch,
|
||||
content: PresenceBuilder(
|
||||
userId: profile.userId,
|
||||
client: Matrix.of(context).client,
|
||||
builder: (context, presence) {
|
||||
if (presence == null) return const SizedBox.shrink();
|
||||
final statusMsg = presence.statusMsg;
|
||||
final lastActiveTimestamp = presence.lastActiveTimestamp;
|
||||
final presenceText = presence.currentlyActive == true
|
||||
? L10n.of(context).currentlyActive
|
||||
: lastActiveTimestamp != null
|
||||
? L10n.of(
|
||||
context,
|
||||
).lastActiveAgo(lastActiveTimestamp.localizedTimeShort(context))
|
||||
: null;
|
||||
return Column(
|
||||
spacing: 16,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .stretch,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
Center(
|
||||
child: Avatar(
|
||||
mxContent: avatar,
|
||||
name: displayname,
|
||||
size: Avatar.defaultSize * 2,
|
||||
onTap: avatar != null
|
||||
? () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => MxcImageViewer(avatar),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
Avatar(
|
||||
mxContent: avatar,
|
||||
name: displayname,
|
||||
size: Avatar.defaultSize * 1.5,
|
||||
onTap: avatar != null
|
||||
? () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => MxcImageViewer(avatar),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
HoverBuilder(
|
||||
builder: (context, hovered) => StatefulBuilder(
|
||||
builder: (context, setState) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: profile.userId),
|
||||
);
|
||||
setState(() {
|
||||
copied = true;
|
||||
});
|
||||
},
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 4.0),
|
||||
child: AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered
|
||||
? 1.33
|
||||
: copied
|
||||
? 1.25
|
||||
: 1.0,
|
||||
child: Icon(
|
||||
copied
|
||||
? Icons.check_circle
|
||||
: Icons.copy,
|
||||
size: 12,
|
||||
color: copied ? Colors.green : null,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Text(
|
||||
displayname,
|
||||
maxLines: 1,
|
||||
overflow: .ellipsis,
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
HoverBuilder(
|
||||
builder: (context, hovered) => StatefulBuilder(
|
||||
builder: (context, setState) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: profile.userId),
|
||||
);
|
||||
setState(() {
|
||||
copied = true;
|
||||
});
|
||||
},
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 4.0,
|
||||
),
|
||||
child: AnimatedScale(
|
||||
duration:
|
||||
FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered
|
||||
? 1.33
|
||||
: copied
|
||||
? 1.25
|
||||
: 1.0,
|
||||
child: Icon(
|
||||
copied
|
||||
? Icons.check_circle
|
||||
: Icons.copy,
|
||||
size: 12,
|
||||
color: copied
|
||||
? Colors.green
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(text: profile.userId),
|
||||
],
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: .ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
TextSpan(text: profile.userId),
|
||||
],
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
if (presenceText != null)
|
||||
Text(
|
||||
presenceText,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
if (statusMsg != null)
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
child: Scrollbar(
|
||||
thumbVisibility: true,
|
||||
trackVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
child: SelectableLinkify(
|
||||
text: statusMsg,
|
||||
textScaleFactor: MediaQuery.textScalerOf(
|
||||
context,
|
||||
).scale(1),
|
||||
textAlign: TextAlign.start,
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: theme.colorScheme.primary,
|
||||
),
|
||||
onOpen: (url) =>
|
||||
UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (presenceText != null)
|
||||
Text(
|
||||
presenceText,
|
||||
style: const TextStyle(fontSize: 10),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (statusMsg != null)
|
||||
SelectableLinkify(
|
||||
text: statusMsg,
|
||||
textScaleFactor: MediaQuery.textScalerOf(
|
||||
context,
|
||||
).scale(1),
|
||||
textAlign: TextAlign.center,
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: theme.colorScheme.primary,
|
||||
),
|
||||
onOpen: (url) =>
|
||||
UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: .spaceBetween,
|
||||
spacing: 4,
|
||||
children: [
|
||||
AdaptiveIconTextButton(
|
||||
label: L10n.of(context).block,
|
||||
icon: Icons.block_outlined,
|
||||
onTap: () {
|
||||
final router = GoRouter.of(context);
|
||||
Navigator.of(context).pop();
|
||||
router.go(
|
||||
'/rooms/settings/security/ignorelist',
|
||||
extra: profile.userId,
|
||||
);
|
||||
},
|
||||
),
|
||||
AdaptiveIconTextButton(
|
||||
label: L10n.of(context).report,
|
||||
icon: Icons.gavel_outlined,
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
final reason = await showTextInputDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).whyDoYouWantToReportThis,
|
||||
okLabel: L10n.of(context).report,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
hintText: L10n.of(context).reason,
|
||||
);
|
||||
if (reason == null || reason.isEmpty) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => Matrix.of(
|
||||
context,
|
||||
).client.reportUser(profile.userId, reason),
|
||||
);
|
||||
},
|
||||
),
|
||||
AdaptiveIconTextButton(
|
||||
label: L10n.of(context).share,
|
||||
icon: Icons.adaptive.share,
|
||||
onTap: () => FluffyShare.share(profile.userId, context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
AdaptiveDialogInkWell(
|
||||
onTap: () async {
|
||||
final router = GoRouter.of(context);
|
||||
final roomIdResult = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => client.startDirectChat(profile.userId),
|
||||
);
|
||||
final roomId = roomIdResult.result;
|
||||
if (roomId == null) return;
|
||||
if (context.mounted) Navigator.of(context).pop();
|
||||
router.go('/rooms/$roomId');
|
||||
},
|
||||
child: Text(
|
||||
L10n.of(context).sendAMessage,
|
||||
style: TextStyle(color: theme.colorScheme.secondary),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
if (client.userID != profile.userId) ...[
|
||||
AdaptiveDialogAction(
|
||||
borderRadius: AdaptiveDialogAction.topRadius,
|
||||
bigButtons: true,
|
||||
onPressed: () async {
|
||||
final router = GoRouter.of(context);
|
||||
final roomIdResult = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => client.startDirectChat(profile.userId),
|
||||
);
|
||||
final roomId = roomIdResult.result;
|
||||
if (roomId == null) return;
|
||||
if (context.mounted) Navigator.of(context).pop();
|
||||
router.go('/rooms/$roomId');
|
||||
},
|
||||
child: Text(
|
||||
dmRoomId == null
|
||||
? L10n.of(context).startConversation
|
||||
: L10n.of(context).sendAMessage,
|
||||
),
|
||||
),
|
||||
AdaptiveDialogAction(
|
||||
bigButtons: true,
|
||||
borderRadius: AdaptiveDialogAction.centerRadius,
|
||||
onPressed: () {
|
||||
final router = GoRouter.of(context);
|
||||
Navigator.of(context).pop();
|
||||
router.go(
|
||||
'/rooms/settings/security/ignorelist',
|
||||
extra: profile.userId,
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
L10n.of(context).ignoreUser,
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
),
|
||||
),
|
||||
],
|
||||
AdaptiveDialogAction(
|
||||
bigButtons: true,
|
||||
borderRadius: AdaptiveDialogAction.bottomRadius,
|
||||
onPressed: Navigator.of(context).pop,
|
||||
child: Text(L10n.of(context).close),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
final onRoomKeyRequestSub = <String, StreamSubscription>{};
|
||||
final onKeyVerificationRequestSub = <String, StreamSubscription>{};
|
||||
final onNotification = <String, StreamSubscription>{};
|
||||
final onLoginStateChanged = <String, StreamSubscription<LoginState>>{};
|
||||
final onLogoutSub = <String, StreamSubscription<LoginState>>{};
|
||||
final onUiaRequest = <String, StreamSubscription<UiaRequest>>{};
|
||||
|
||||
String? _cachedPassword;
|
||||
|
|
@ -255,31 +255,29 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
context,
|
||||
);
|
||||
});
|
||||
onLoginStateChanged[name] ??= c.onLoginStateChanged.stream.listen((state) {
|
||||
final loggedInWithMultipleClients = widget.clients.length > 1;
|
||||
if (state == LoginState.loggedOut) {
|
||||
_cancelSubs(c.clientName);
|
||||
widget.clients.remove(c);
|
||||
ClientManager.removeClientNameFromStore(c.clientName, store);
|
||||
InitWithRestoreExtension.deleteSessionBackup(name);
|
||||
}
|
||||
if (loggedInWithMultipleClients && state != LoginState.loggedIn) {
|
||||
ScaffoldMessenger.of(
|
||||
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
|
||||
context,
|
||||
).showSnackBar(
|
||||
SnackBar(content: Text(L10n.of(context).oneClientLoggedOut)),
|
||||
);
|
||||
onLogoutSub[name] ??= c.onLoginStateChanged.stream
|
||||
.where((state) => state == LoginState.loggedOut)
|
||||
.listen((state) {
|
||||
final loggedInWithMultipleClients = widget.clients.length > 1;
|
||||
|
||||
if (state != LoginState.loggedIn) {
|
||||
FluffyChatApp.router.go('/rooms');
|
||||
}
|
||||
} else {
|
||||
FluffyChatApp.router.go(
|
||||
state == LoginState.loggedIn ? '/backup' : '/home',
|
||||
);
|
||||
}
|
||||
});
|
||||
_cancelSubs(c.clientName);
|
||||
widget.clients.remove(c);
|
||||
ClientManager.removeClientNameFromStore(c.clientName, store);
|
||||
InitWithRestoreExtension.deleteSessionBackup(name);
|
||||
|
||||
if (loggedInWithMultipleClients) {
|
||||
ScaffoldMessenger.of(
|
||||
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
|
||||
context,
|
||||
).showSnackBar(
|
||||
SnackBar(content: Text(L10n.of(context).oneClientLoggedOut)),
|
||||
);
|
||||
|
||||
if (state != LoginState.loggedIn) {
|
||||
FluffyChatApp.router.go('/rooms');
|
||||
}
|
||||
}
|
||||
});
|
||||
onUiaRequest[name] ??= c.onUiaRequest.stream.listen(uiaRequestHandler);
|
||||
if (PlatformInfos.isWeb || PlatformInfos.isLinux) {
|
||||
c.onSync.stream.first.then((s) {
|
||||
|
|
@ -296,8 +294,8 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
onRoomKeyRequestSub.remove(name);
|
||||
onKeyVerificationRequestSub[name]?.cancel();
|
||||
onKeyVerificationRequestSub.remove(name);
|
||||
onLoginStateChanged[name]?.cancel();
|
||||
onLoginStateChanged.remove(name);
|
||||
onLogoutSub[name]?.cancel();
|
||||
onLogoutSub.remove(name);
|
||||
onNotification[name]?.cancel();
|
||||
onNotification.remove(name);
|
||||
}
|
||||
|
|
@ -373,7 +371,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
|
||||
onRoomKeyRequestSub.values.map((s) => s.cancel());
|
||||
onKeyVerificationRequestSub.values.map((s) => s.cancel());
|
||||
onLoginStateChanged.values.map((s) => s.cancel());
|
||||
onLogoutSub.values.map((s) => s.cancel());
|
||||
onNotification.values.map((s) => s.cancel());
|
||||
client.httpClient.close();
|
||||
|
||||
|
|
|
|||
|
|
@ -45,40 +45,22 @@ Future<void> showMemberActionsPopupMenu({
|
|||
children: [
|
||||
Avatar(
|
||||
name: displayname,
|
||||
size: 30,
|
||||
mxContent: user.avatarUrl,
|
||||
presenceUserId: user.id,
|
||||
presenceBackgroundColor: theme.colorScheme.surfaceContainer,
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 128),
|
||||
child: Text(
|
||||
displayname,
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.labelLarge,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 128),
|
||||
child: Text(
|
||||
user.id,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 10),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 200),
|
||||
child: Text(
|
||||
displayname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
if (onMention != null)
|
||||
PopupMenuItem(
|
||||
value: _MemberActions.mention,
|
||||
|
|
|
|||
24
pubspec.lock
24
pubspec.lock
|
|
@ -165,10 +165,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1112,18 +1112,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.18"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
matrix:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1877,26 +1877,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
|
||||
sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.26.3"
|
||||
version: "1.29.0"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
version: "0.7.9"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
|
||||
sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.12"
|
||||
version: "0.6.15"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue