Merge branch 'main' into weblate-fluffychat-translations
This commit is contained in:
commit
f0841fea9d
15 changed files with 665 additions and 483 deletions
|
|
@ -20,7 +20,5 @@
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>MinimumOSVersion</key>
|
|
||||||
<string>13.0</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@ import UIKit
|
||||||
import Flutter
|
import Flutter
|
||||||
|
|
||||||
@main
|
@main
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
|
||||||
override func application(
|
override func application(
|
||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
|
||||||
if #available(iOS 10.0, *) {
|
|
||||||
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
|
|
||||||
}
|
|
||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
|
||||||
|
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,5 +112,26 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<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>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -440,25 +440,16 @@ class ChatListController extends State<ChatList>
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: ChatContextAction.open,
|
value: ChatContextAction.open,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: .min,
|
|
||||||
spacing: 12.0,
|
spacing: 12.0,
|
||||||
children: [
|
children: [
|
||||||
Avatar(mxContent: room.avatar, name: displayname),
|
Avatar(mxContent: room.avatar, name: displayname, size: 24),
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 128),
|
constraints: const BoxConstraints(maxWidth: 200),
|
||||||
child: Text(
|
child: Text(displayname, maxLines: 1, overflow: .ellipsis),
|
||||||
displayname,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const PopupMenuDivider(),
|
|
||||||
if (space != null)
|
if (space != null)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: ChatContextAction.goToSpace,
|
value: ChatContextAction.goToSpace,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/l10n/l10n.dart';
|
import 'package:fluffychat/l10n/l10n.dart';
|
||||||
|
|
@ -81,6 +82,9 @@ class LoginController extends State<Login> {
|
||||||
password: passwordController.text,
|
password: passwordController.text,
|
||||||
initialDeviceDisplayName: PlatformInfos.clientName,
|
initialDeviceDisplayName: PlatformInfos.clientName,
|
||||||
);
|
);
|
||||||
|
if (mounted) {
|
||||||
|
context.go('/backup');
|
||||||
|
}
|
||||||
} on MatrixException catch (exception) {
|
} on MatrixException catch (exception) {
|
||||||
setState(() => passwordError = exception.errorMessage);
|
setState(() => passwordError = exception.errorMessage);
|
||||||
return setState(() => loading = false);
|
return setState(() => loading = false);
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,11 @@ class SignInPage extends StatelessWidget {
|
||||||
? L10n.of(context).createNewAccount
|
? L10n.of(context).createNewAccount
|
||||||
: L10n.of(context).login,
|
: L10n.of(context).login,
|
||||||
),
|
),
|
||||||
bottom: PreferredSize(
|
),
|
||||||
preferredSize: const Size.fromHeight(56 + 60),
|
body: Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: .min,
|
spacing: 16,
|
||||||
crossAxisAlignment: .center,
|
|
||||||
spacing: 12,
|
|
||||||
children: [
|
children: [
|
||||||
SelectableText(
|
SelectableText(
|
||||||
signUp
|
signUp
|
||||||
|
|
@ -66,24 +63,20 @@ class SignInPage extends StatelessWidget {
|
||||||
borderSide: BorderSide.none,
|
borderSide: BorderSide.none,
|
||||||
borderRadius: BorderRadius.circular(99),
|
borderRadius: BorderRadius.circular(99),
|
||||||
),
|
),
|
||||||
errorText: state.publicHomeservers.error
|
errorText: state.publicHomeservers.error?.toLocalizedString(
|
||||||
?.toLocalizedString(context),
|
|
||||||
prefixIcon: const Icon(Icons.search_outlined),
|
|
||||||
hintText: L10n.of(
|
|
||||||
context,
|
context,
|
||||||
).searchOrEnterHomeserverAddress,
|
),
|
||||||
|
prefixIcon: const Icon(Icons.search_outlined),
|
||||||
|
hintText: L10n.of(context).searchOrEnterHomeserverAddress,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
if (state.publicHomeservers.connectionState ==
|
||||||
),
|
ConnectionState.done)
|
||||||
),
|
Expanded(
|
||||||
),
|
|
||||||
),
|
|
||||||
body: state.publicHomeservers.connectionState == ConnectionState.done
|
|
||||||
? Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
|
||||||
child: Material(
|
child: Material(
|
||||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
borderRadius: BorderRadius.circular(
|
||||||
|
AppConfig.borderRadius,
|
||||||
|
),
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
color: theme.colorScheme.surfaceContainerLow,
|
color: theme.colorScheme.surfaceContainerLow,
|
||||||
child: RadioGroup<PublicHomeserverData>(
|
child: RadioGroup<PublicHomeserverData>(
|
||||||
|
|
@ -93,7 +86,7 @@ class SignInPage extends StatelessWidget {
|
||||||
itemCount: publicHomeservers.length,
|
itemCount: publicHomeservers.length,
|
||||||
itemBuilder: (context, i) {
|
itemBuilder: (context, i) {
|
||||||
final server = publicHomeservers[i];
|
final server = publicHomeservers[i];
|
||||||
final homepage = server.homepage;
|
final website = server.website;
|
||||||
return RadioListTile.adaptive(
|
return RadioListTile.adaptive(
|
||||||
value: server,
|
value: server,
|
||||||
radioScaleFactor:
|
radioScaleFactor:
|
||||||
|
|
@ -106,8 +99,10 @@ class SignInPage extends StatelessWidget {
|
||||||
: 1,
|
: 1,
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Text(server.name ?? 'Unknown')),
|
Expanded(
|
||||||
if (homepage != null)
|
child: Text(server.name ?? 'Unknown'),
|
||||||
|
),
|
||||||
|
if (website != null)
|
||||||
SizedBox.square(
|
SizedBox.square(
|
||||||
dimension: 32,
|
dimension: 32,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
|
@ -116,7 +111,7 @@ class SignInPage extends StatelessWidget {
|
||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
launchUrlString(homepage),
|
launchUrlString(website),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -140,7 +135,8 @@ class SignInPage extends StatelessWidget {
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.tertiaryContainer,
|
.tertiaryContainer,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding:
|
||||||
|
const EdgeInsets.symmetric(
|
||||||
horizontal: 6.0,
|
horizontal: 6.0,
|
||||||
vertical: 3.0,
|
vertical: 3.0,
|
||||||
),
|
),
|
||||||
|
|
@ -165,7 +161,8 @@ class SignInPage extends StatelessWidget {
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondaryContainer,
|
.secondaryContainer,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding:
|
||||||
|
const EdgeInsets.symmetric(
|
||||||
horizontal: 6.0,
|
horizontal: 6.0,
|
||||||
vertical: 3.0,
|
vertical: 3.0,
|
||||||
),
|
),
|
||||||
|
|
@ -194,7 +191,11 @@ class SignInPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Center(child: CircularProgressIndicator.adaptive()),
|
else
|
||||||
|
Center(child: CircularProgressIndicator.adaptive()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: AnimatedSize(
|
bottomNavigationBar: AnimatedSize(
|
||||||
duration: FluffyThemes.animationDuration,
|
duration: FluffyThemes.animationDuration,
|
||||||
curve: FluffyThemes.animationCurve,
|
curve: FluffyThemes.animationCurve,
|
||||||
|
|
@ -203,12 +204,14 @@ class SignInPage extends StatelessWidget {
|
||||||
!publicHomeservers.contains(selectedHomserver)
|
!publicHomeservers.contains(selectedHomserver)
|
||||||
? const SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: Material(
|
: Material(
|
||||||
elevation: 8,
|
|
||||||
shadowColor: theme.appBarTheme.shadowColor,
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: theme.colorScheme.primary,
|
||||||
|
foregroundColor: theme.colorScheme.onPrimary,
|
||||||
|
),
|
||||||
onPressed:
|
onPressed:
|
||||||
state.loginLoading.connectionState ==
|
state.loginLoading.connectionState ==
|
||||||
ConnectionState.waiting
|
ConnectionState.waiting
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ int sortHomeservers(PublicHomeserverData a, PublicHomeserverData b) {
|
||||||
int _calcHomeserverScore(PublicHomeserverData homeserver) {
|
int _calcHomeserverScore(PublicHomeserverData homeserver) {
|
||||||
var score = 0;
|
var score = 0;
|
||||||
if (homeserver.description?.isNotEmpty == true) score++;
|
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.languages?.length ?? 0);
|
||||||
score += (homeserver.features?.length ?? 0);
|
score += (homeserver.features?.length ?? 0);
|
||||||
score += (homeserver.onlineStatus ?? 0);
|
score += (homeserver.onlineStatus ?? 0);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
class PublicHomeserverData {
|
class PublicHomeserverData {
|
||||||
final String? name;
|
final String? name;
|
||||||
final String? clientDomain;
|
final String? clientDomain;
|
||||||
final String? homepage;
|
final String? website;
|
||||||
final String? isp;
|
final String? isp;
|
||||||
final String? staffJur;
|
final String? staffJur;
|
||||||
final String? rules;
|
final String? rules;
|
||||||
|
|
@ -26,7 +26,7 @@ class PublicHomeserverData {
|
||||||
PublicHomeserverData({
|
PublicHomeserverData({
|
||||||
this.name,
|
this.name,
|
||||||
this.clientDomain,
|
this.clientDomain,
|
||||||
this.homepage,
|
this.website,
|
||||||
this.isp,
|
this.isp,
|
||||||
this.staffJur,
|
this.staffJur,
|
||||||
this.rules,
|
this.rules,
|
||||||
|
|
@ -53,7 +53,7 @@ class PublicHomeserverData {
|
||||||
return PublicHomeserverData(
|
return PublicHomeserverData(
|
||||||
name: json['name'],
|
name: json['name'],
|
||||||
clientDomain: json['client_domain'],
|
clientDomain: json['client_domain'],
|
||||||
homepage: json['homepage'],
|
website: json['website'],
|
||||||
isp: json['isp'],
|
isp: json['isp'],
|
||||||
staffJur: json['staff_jur'],
|
staffJur: json['staff_jur'],
|
||||||
rules: json['rules'],
|
rules: json['rules'],
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:fluffychat/config/setting_keys.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.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/l10n/l10n.dart';
|
||||||
import 'package:fluffychat/pages/sign_in/view_model/model/public_homeserver_data.dart';
|
import 'package:fluffychat/pages/sign_in/view_model/model/public_homeserver_data.dart';
|
||||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||||
|
|
@ -70,6 +70,7 @@ Future<void> connectToHomeserverFlow(
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
setState(AsyncSnapshot.withData(ConnectionState.done, true));
|
setState(AsyncSnapshot.withData(ConnectionState.done, true));
|
||||||
|
context.go('/backup');
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
setState(AsyncSnapshot.withError(ConnectionState.done, 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:matrix/matrix.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/l10n/l10n.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_ok_cancel_alert_dialog.dart';
|
||||||
|
import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
|
||||||
import '../../config/themes.dart';
|
import '../../config/themes.dart';
|
||||||
import '../../utils/url_launcher.dart';
|
import '../../utils/url_launcher.dart';
|
||||||
import '../avatar.dart';
|
import '../avatar.dart';
|
||||||
|
|
@ -90,15 +92,8 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
final roomLink = roomAlias ?? chunk?.roomId;
|
final roomLink = roomAlias ?? chunk?.roomId;
|
||||||
var copied = false;
|
var copied = false;
|
||||||
return AlertDialog.adaptive(
|
return AlertDialog.adaptive(
|
||||||
title: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 256),
|
|
||||||
child: Text(
|
|
||||||
chunk?.name ?? roomAlias?.localpart ?? chunk?.roomId ?? 'Unknown',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
content: ConstrainedBox(
|
content: ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256),
|
constraints: const BoxConstraints(maxWidth: 256),
|
||||||
child: FutureBuilder<PublishedRoomsChunk>(
|
child: FutureBuilder<PublishedRoomsChunk>(
|
||||||
future: _search(context),
|
future: _search(context),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
|
@ -109,10 +104,38 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
final topic = profile?.topic;
|
final topic = profile?.topic;
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 8,
|
spacing: 16,
|
||||||
mainAxisSize: .min,
|
mainAxisSize: .min,
|
||||||
crossAxisAlignment: .stretch,
|
crossAxisAlignment: .stretch,
|
||||||
children: [
|
children: [
|
||||||
|
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)
|
if (roomLink != null)
|
||||||
HoverBuilder(
|
HoverBuilder(
|
||||||
builder: (context, hovered) => StatefulBuilder(
|
builder: (context, hovered) => StatefulBuilder(
|
||||||
|
|
@ -120,7 +143,9 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(ClipboardData(text: roomLink));
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: roomLink),
|
||||||
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
copied = true;
|
copied = true;
|
||||||
});
|
});
|
||||||
|
|
@ -134,9 +159,10 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
right: 4.0,
|
right: 4.0,
|
||||||
),
|
),
|
||||||
child: AnimatedScale(
|
child: AnimatedScale(
|
||||||
duration:
|
duration: FluffyThemes
|
||||||
FluffyThemes.animationDuration,
|
.animationDuration,
|
||||||
curve: FluffyThemes.animationCurve,
|
curve: FluffyThemes
|
||||||
|
.animationCurve,
|
||||||
scale: hovered
|
scale: hovered
|
||||||
? 1.33
|
? 1.33
|
||||||
: copied
|
: copied
|
||||||
|
|
@ -147,16 +173,17 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
? Icons.check_circle
|
? Icons.check_circle
|
||||||
: Icons.copy,
|
: Icons.copy,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: copied ? Colors.green : null,
|
color: copied
|
||||||
|
? Colors.green
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextSpan(text: roomLink),
|
TextSpan(text: roomLink),
|
||||||
],
|
],
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
style: theme.textTheme.bodyMedium
|
||||||
fontSize: 10,
|
?.copyWith(fontSize: 10),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
|
@ -164,34 +191,33 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Center(
|
|
||||||
child: Avatar(
|
|
||||||
mxContent: avatar,
|
|
||||||
name: profile?.name ?? roomLink,
|
|
||||||
size: Avatar.defaultSize * 2,
|
|
||||||
onTap: avatar != null
|
|
||||||
? () => showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (_) => MxcImageViewer(avatar),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (profile?.numJoinedMembers != null)
|
if (profile?.numJoinedMembers != null)
|
||||||
Text(
|
Text(
|
||||||
L10n.of(
|
L10n.of(context).countParticipants(
|
||||||
context,
|
profile?.numJoinedMembers ?? 0,
|
||||||
).countParticipants(profile?.numJoinedMembers ?? 0),
|
),
|
||||||
style: const TextStyle(fontSize: 10),
|
style: const TextStyle(fontSize: 10),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
if (topic != null && topic.isNotEmpty)
|
if (topic != null && topic.isNotEmpty)
|
||||||
SelectableLinkify(
|
ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(maxHeight: 200),
|
||||||
|
child: Scrollbar(
|
||||||
|
thumbVisibility: true,
|
||||||
|
trackVisibility: true,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: SelectableLinkify(
|
||||||
text: topic,
|
text: topic,
|
||||||
textScaleFactor: MediaQuery.textScalerOf(
|
textScaleFactor: MediaQuery.textScalerOf(
|
||||||
context,
|
context,
|
||||||
).scale(1),
|
).scale(1),
|
||||||
textAlign: TextAlign.center,
|
textAlign: .start,
|
||||||
options: const LinkifyOptions(humanize: false),
|
options: const LinkifyOptions(humanize: false),
|
||||||
linkStyle: TextStyle(
|
linkStyle: TextStyle(
|
||||||
color: theme.colorScheme.primary,
|
color: theme.colorScheme.primary,
|
||||||
|
|
@ -201,33 +227,73 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
onOpen: (url) =>
|
onOpen: (url) =>
|
||||||
UrlLauncher(context, url.url).launchUrl(),
|
UrlLauncher(context, url.url).launchUrl(),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
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/config/themes.dart';
|
||||||
import 'package:fluffychat/l10n/l10n.dart';
|
import 'package:fluffychat/l10n/l10n.dart';
|
||||||
import 'package:fluffychat/utils/date_time_extension.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/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/avatar.dart';
|
||||||
import 'package:fluffychat/widgets/presence_builder.dart';
|
import 'package:fluffychat/widgets/presence_builder.dart';
|
||||||
import '../../utils/url_launcher.dart';
|
import '../../utils/url_launcher.dart';
|
||||||
|
|
@ -17,6 +19,8 @@ import '../hover_builder.dart';
|
||||||
import '../matrix.dart';
|
import '../matrix.dart';
|
||||||
import '../mxc_image_viewer.dart';
|
import '../mxc_image_viewer.dart';
|
||||||
|
|
||||||
|
// ignore: unused_import
|
||||||
|
|
||||||
class UserDialog extends StatelessWidget {
|
class UserDialog extends StatelessWidget {
|
||||||
static Future<void> show({
|
static Future<void> show({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
|
|
@ -37,7 +41,6 @@ class UserDialog extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final dmRoomId = client.getDirectChatFromUserId(profile.userId);
|
|
||||||
final displayname =
|
final displayname =
|
||||||
profile.displayName ??
|
profile.displayName ??
|
||||||
profile.userId.localpart ??
|
profile.userId.localpart ??
|
||||||
|
|
@ -46,13 +49,7 @@ class UserDialog extends StatelessWidget {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final avatar = profile.avatarUrl;
|
final avatar = profile.avatarUrl;
|
||||||
return AlertDialog.adaptive(
|
return AlertDialog.adaptive(
|
||||||
title: ConstrainedBox(
|
content: PresenceBuilder(
|
||||||
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,
|
userId: profile.userId,
|
||||||
client: Matrix.of(context).client,
|
client: Matrix.of(context).client,
|
||||||
builder: (context, presence) {
|
builder: (context, presence) {
|
||||||
|
|
@ -62,21 +59,22 @@ class UserDialog extends StatelessWidget {
|
||||||
final presenceText = presence.currentlyActive == true
|
final presenceText = presence.currentlyActive == true
|
||||||
? L10n.of(context).currentlyActive
|
? L10n.of(context).currentlyActive
|
||||||
: lastActiveTimestamp != null
|
: lastActiveTimestamp != null
|
||||||
? L10n.of(context).lastActiveAgo(
|
? L10n.of(
|
||||||
lastActiveTimestamp.localizedTimeShort(context),
|
context,
|
||||||
)
|
).lastActiveAgo(lastActiveTimestamp.localizedTimeShort(context))
|
||||||
: null;
|
: null;
|
||||||
return SingleChildScrollView(
|
return Column(
|
||||||
child: Column(
|
spacing: 16,
|
||||||
spacing: 8,
|
|
||||||
mainAxisSize: .min,
|
mainAxisSize: .min,
|
||||||
crossAxisAlignment: .stretch,
|
crossAxisAlignment: .stretch,
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Row(
|
||||||
child: Avatar(
|
spacing: 12,
|
||||||
|
children: [
|
||||||
|
Avatar(
|
||||||
mxContent: avatar,
|
mxContent: avatar,
|
||||||
name: displayname,
|
name: displayname,
|
||||||
size: Avatar.defaultSize * 2,
|
size: Avatar.defaultSize * 1.5,
|
||||||
onTap: avatar != null
|
onTap: avatar != null
|
||||||
? () => showDialog(
|
? () => showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -84,7 +82,17 @@ class UserDialog extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: .start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
displayname,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: .ellipsis,
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
HoverBuilder(
|
HoverBuilder(
|
||||||
builder: (context, hovered) => StatefulBuilder(
|
builder: (context, hovered) => StatefulBuilder(
|
||||||
builder: (context, setState) => MouseRegion(
|
builder: (context, setState) => MouseRegion(
|
||||||
|
|
@ -103,9 +111,12 @@ class UserDialog extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 4.0),
|
padding: const EdgeInsets.only(
|
||||||
|
right: 4.0,
|
||||||
|
),
|
||||||
child: AnimatedScale(
|
child: AnimatedScale(
|
||||||
duration: FluffyThemes.animationDuration,
|
duration:
|
||||||
|
FluffyThemes.animationDuration,
|
||||||
curve: FluffyThemes.animationCurve,
|
curve: FluffyThemes.animationCurve,
|
||||||
scale: hovered
|
scale: hovered
|
||||||
? 1.33
|
? 1.33
|
||||||
|
|
@ -117,7 +128,9 @@ class UserDialog extends StatelessWidget {
|
||||||
? Icons.check_circle
|
? Icons.check_circle
|
||||||
: Icons.copy,
|
: Icons.copy,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: copied ? Colors.green : null,
|
color: copied
|
||||||
|
? Colors.green
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -128,6 +141,8 @@ class UserDialog extends StatelessWidget {
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: .ellipsis,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -137,16 +152,29 @@ class UserDialog extends StatelessWidget {
|
||||||
if (presenceText != null)
|
if (presenceText != null)
|
||||||
Text(
|
Text(
|
||||||
presenceText,
|
presenceText,
|
||||||
style: const TextStyle(fontSize: 10),
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
textAlign: TextAlign.center,
|
fontSize: 10,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
if (statusMsg != null)
|
if (statusMsg != null)
|
||||||
SelectableLinkify(
|
ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(maxHeight: 200),
|
||||||
|
child: Scrollbar(
|
||||||
|
thumbVisibility: true,
|
||||||
|
trackVisibility: true,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: SelectableLinkify(
|
||||||
text: statusMsg,
|
text: statusMsg,
|
||||||
textScaleFactor: MediaQuery.textScalerOf(
|
textScaleFactor: MediaQuery.textScalerOf(
|
||||||
context,
|
context,
|
||||||
).scale(1),
|
).scale(1),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.start,
|
||||||
options: const LinkifyOptions(humanize: false),
|
options: const LinkifyOptions(humanize: false),
|
||||||
linkStyle: TextStyle(
|
linkStyle: TextStyle(
|
||||||
color: theme.colorScheme.primary,
|
color: theme.colorScheme.primary,
|
||||||
|
|
@ -156,18 +184,55 @@ class UserDialog extends StatelessWidget {
|
||||||
onOpen: (url) =>
|
onOpen: (url) =>
|
||||||
UrlLauncher(context, url.url).launchUrl(),
|
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),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
actions: [
|
AdaptiveIconTextButton(
|
||||||
if (client.userID != profile.userId) ...[
|
label: L10n.of(context).share,
|
||||||
AdaptiveDialogAction(
|
icon: Icons.adaptive.share,
|
||||||
borderRadius: AdaptiveDialogAction.topRadius,
|
onTap: () => FluffyShare.share(profile.userId, context),
|
||||||
bigButtons: true,
|
),
|
||||||
onPressed: () async {
|
],
|
||||||
|
),
|
||||||
|
AdaptiveDialogInkWell(
|
||||||
|
onTap: () async {
|
||||||
final router = GoRouter.of(context);
|
final router = GoRouter.of(context);
|
||||||
final roomIdResult = await showFutureLoadingDialog(
|
final roomIdResult = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -179,35 +244,14 @@ class UserDialog extends StatelessWidget {
|
||||||
router.go('/rooms/$roomId');
|
router.go('/rooms/$roomId');
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
dmRoomId == null
|
L10n.of(context).sendAMessage,
|
||||||
? L10n.of(context).startConversation
|
style: TextStyle(color: theme.colorScheme.secondary),
|
||||||
: 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 onRoomKeyRequestSub = <String, StreamSubscription>{};
|
||||||
final onKeyVerificationRequestSub = <String, StreamSubscription>{};
|
final onKeyVerificationRequestSub = <String, StreamSubscription>{};
|
||||||
final onNotification = <String, StreamSubscription>{};
|
final onNotification = <String, StreamSubscription>{};
|
||||||
final onLoginStateChanged = <String, StreamSubscription<LoginState>>{};
|
final onLogoutSub = <String, StreamSubscription<LoginState>>{};
|
||||||
final onUiaRequest = <String, StreamSubscription<UiaRequest>>{};
|
final onUiaRequest = <String, StreamSubscription<UiaRequest>>{};
|
||||||
|
|
||||||
String? _cachedPassword;
|
String? _cachedPassword;
|
||||||
|
|
@ -255,15 +255,17 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
onLoginStateChanged[name] ??= c.onLoginStateChanged.stream.listen((state) {
|
onLogoutSub[name] ??= c.onLoginStateChanged.stream
|
||||||
|
.where((state) => state == LoginState.loggedOut)
|
||||||
|
.listen((state) {
|
||||||
final loggedInWithMultipleClients = widget.clients.length > 1;
|
final loggedInWithMultipleClients = widget.clients.length > 1;
|
||||||
if (state == LoginState.loggedOut) {
|
|
||||||
_cancelSubs(c.clientName);
|
_cancelSubs(c.clientName);
|
||||||
widget.clients.remove(c);
|
widget.clients.remove(c);
|
||||||
ClientManager.removeClientNameFromStore(c.clientName, store);
|
ClientManager.removeClientNameFromStore(c.clientName, store);
|
||||||
InitWithRestoreExtension.deleteSessionBackup(name);
|
InitWithRestoreExtension.deleteSessionBackup(name);
|
||||||
}
|
|
||||||
if (loggedInWithMultipleClients && state != LoginState.loggedIn) {
|
if (loggedInWithMultipleClients) {
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
|
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
|
||||||
context,
|
context,
|
||||||
|
|
@ -274,10 +276,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
||||||
if (state != LoginState.loggedIn) {
|
if (state != LoginState.loggedIn) {
|
||||||
FluffyChatApp.router.go('/rooms');
|
FluffyChatApp.router.go('/rooms');
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
FluffyChatApp.router.go(
|
|
||||||
state == LoginState.loggedIn ? '/backup' : '/home',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onUiaRequest[name] ??= c.onUiaRequest.stream.listen(uiaRequestHandler);
|
onUiaRequest[name] ??= c.onUiaRequest.stream.listen(uiaRequestHandler);
|
||||||
|
|
@ -296,8 +294,8 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
||||||
onRoomKeyRequestSub.remove(name);
|
onRoomKeyRequestSub.remove(name);
|
||||||
onKeyVerificationRequestSub[name]?.cancel();
|
onKeyVerificationRequestSub[name]?.cancel();
|
||||||
onKeyVerificationRequestSub.remove(name);
|
onKeyVerificationRequestSub.remove(name);
|
||||||
onLoginStateChanged[name]?.cancel();
|
onLogoutSub[name]?.cancel();
|
||||||
onLoginStateChanged.remove(name);
|
onLogoutSub.remove(name);
|
||||||
onNotification[name]?.cancel();
|
onNotification[name]?.cancel();
|
||||||
onNotification.remove(name);
|
onNotification.remove(name);
|
||||||
}
|
}
|
||||||
|
|
@ -373,7 +371,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
||||||
|
|
||||||
onRoomKeyRequestSub.values.map((s) => s.cancel());
|
onRoomKeyRequestSub.values.map((s) => s.cancel());
|
||||||
onKeyVerificationRequestSub.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());
|
onNotification.values.map((s) => s.cancel());
|
||||||
client.httpClient.close();
|
client.httpClient.close();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,40 +45,22 @@ Future<void> showMemberActionsPopupMenu({
|
||||||
children: [
|
children: [
|
||||||
Avatar(
|
Avatar(
|
||||||
name: displayname,
|
name: displayname,
|
||||||
|
size: 30,
|
||||||
mxContent: user.avatarUrl,
|
mxContent: user.avatarUrl,
|
||||||
presenceUserId: user.id,
|
presenceUserId: user.id,
|
||||||
presenceBackgroundColor: theme.colorScheme.surfaceContainer,
|
presenceBackgroundColor: theme.colorScheme.surfaceContainer,
|
||||||
),
|
),
|
||||||
Column(
|
|
||||||
mainAxisSize: .min,
|
|
||||||
crossAxisAlignment: .start,
|
|
||||||
children: [
|
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 128),
|
constraints: const BoxConstraints(maxWidth: 200),
|
||||||
child: Text(
|
child: Text(
|
||||||
displayname,
|
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,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const PopupMenuDivider(),
|
|
||||||
if (onMention != null)
|
if (onMention != null)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: _MemberActions.mention,
|
value: _MemberActions.mention,
|
||||||
|
|
|
||||||
24
pubspec.lock
24
pubspec.lock
|
|
@ -165,10 +165,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.1"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1112,18 +1112,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.17"
|
version: "0.12.18"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.13.0"
|
||||||
matrix:
|
matrix:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1877,26 +1877,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
|
sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.26.3"
|
version: "1.29.0"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.7"
|
version: "0.7.9"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
|
sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.12"
|
version: "0.6.15"
|
||||||
timezone:
|
timezone:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue