mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-04 06:17:26 +01:00 
			
		
		
		
	Merge branch 'krille/flutter-2' into 'main'
refactor: Migrate to flutter 2 See merge request famedly/fluffychat!394
This commit is contained in:
		
						commit
						c011b7e156
					
				@ -25,7 +25,6 @@ test:
 | 
			
		||||
 | 
			
		||||
build_web:
 | 
			
		||||
  stage: coverage
 | 
			
		||||
  image: registry.gitlab.com/famedly/containers/flutter-dockerimages:beta
 | 
			
		||||
  before_script: [sudo apt update && sudo apt install curl -y, ./scripts/prepare-web.sh]
 | 
			
		||||
  script: [./scripts/build-web.sh]
 | 
			
		||||
  artifacts:
 | 
			
		||||
@ -157,7 +156,6 @@ pages:
 | 
			
		||||
 | 
			
		||||
build_linux:
 | 
			
		||||
  stage: coverage
 | 
			
		||||
  image: cirrusci/flutter:dev
 | 
			
		||||
  before_script: [sudo apt update && sudo apt install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev -y]
 | 
			
		||||
  script: [./scripts/build-linux.sh]
 | 
			
		||||
  artifacts:
 | 
			
		||||
@ -260,7 +258,6 @@ upload-windows:
 | 
			
		||||
 | 
			
		||||
upload-playstore:
 | 
			
		||||
  extends: .release
 | 
			
		||||
  image: registry.gitlab.com/famedly/containers/flutter-dockerimages:stable
 | 
			
		||||
  script: [./scripts/release-playstore.sh]
 | 
			
		||||
  resource_group: playstore_release
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -66,8 +66,8 @@ class ContentBanner extends StatelessWidget {
 | 
			
		||||
              alignment: Alignment.bottomRight,
 | 
			
		||||
              child: FloatingActionButton(
 | 
			
		||||
                mini: true,
 | 
			
		||||
                child: Icon(Icons.camera_alt_outlined),
 | 
			
		||||
                onPressed: onEdit,
 | 
			
		||||
                child: Icon(Icons.camera_alt_outlined),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
        ],
 | 
			
		||||
 | 
			
		||||
@ -18,17 +18,17 @@ class AdaptiveFlatButton extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    if (PlatformInfos.isCupertinoStyle) {
 | 
			
		||||
      return CupertinoDialogAction(
 | 
			
		||||
        child: Text(label),
 | 
			
		||||
        onPressed: onPressed,
 | 
			
		||||
        textStyle: textColor != null ? TextStyle(color: textColor) : null,
 | 
			
		||||
        child: Text(label),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return TextButton(
 | 
			
		||||
      onPressed: onPressed,
 | 
			
		||||
      child: Text(
 | 
			
		||||
        label,
 | 
			
		||||
        style: TextStyle(color: textColor),
 | 
			
		||||
      ),
 | 
			
		||||
      onPressed: onPressed,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -105,6 +105,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
        body = Container(
 | 
			
		||||
          margin: EdgeInsets.only(left: 8.0, right: 8.0),
 | 
			
		||||
          child: Column(
 | 
			
		||||
            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
            children: <Widget>[
 | 
			
		||||
              Text(L10n.of(context).askSSSSSign,
 | 
			
		||||
                  style: TextStyle(fontSize: 20)),
 | 
			
		||||
@ -128,7 +129,6 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
        buttons.add(AdaptiveFlatButton(
 | 
			
		||||
@ -145,10 +145,10 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
        break;
 | 
			
		||||
      case KeyVerificationState.askAccept:
 | 
			
		||||
        body = Container(
 | 
			
		||||
          margin: EdgeInsets.only(left: 8.0, right: 8.0),
 | 
			
		||||
          child: Text(
 | 
			
		||||
              L10n.of(context).askVerificationRequest(widget.request.userId),
 | 
			
		||||
              style: TextStyle(fontSize: 20)),
 | 
			
		||||
          margin: EdgeInsets.only(left: 8.0, right: 8.0),
 | 
			
		||||
        );
 | 
			
		||||
        buttons.add(AdaptiveFlatButton(
 | 
			
		||||
          label: L10n.of(context).accept,
 | 
			
		||||
@ -165,6 +165,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
        break;
 | 
			
		||||
      case KeyVerificationState.waitingAccept:
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            PlatformInfos.isCupertinoStyle
 | 
			
		||||
                ? CupertinoActivityIndicator()
 | 
			
		||||
@ -175,7 +176,6 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        );
 | 
			
		||||
        final key = widget.request.client.userDeviceKeys[widget.request.userId]
 | 
			
		||||
            .deviceKeys[widget.request.deviceId];
 | 
			
		||||
@ -219,6 +219,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
              TextSpan(text: numbstr, style: TextStyle(fontSize: 40));
 | 
			
		||||
        }
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Center(
 | 
			
		||||
              child: Text(
 | 
			
		||||
@ -233,7 +234,6 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        );
 | 
			
		||||
        buttons.add(AdaptiveFlatButton(
 | 
			
		||||
          textColor: Colors.red,
 | 
			
		||||
@ -250,6 +250,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
            ? L10n.of(context).waitingPartnerEmoji
 | 
			
		||||
            : L10n.of(context).waitingPartnerNumbers;
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            PlatformInfos.isCupertinoStyle
 | 
			
		||||
                ? CupertinoActivityIndicator()
 | 
			
		||||
@ -260,11 +261,11 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
      case KeyVerificationState.done:
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Icon(Icons.check_circle_outlined, color: Colors.green, size: 200.0),
 | 
			
		||||
            SizedBox(height: 10),
 | 
			
		||||
@ -273,7 +274,6 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        );
 | 
			
		||||
        buttons.add(AdaptiveFlatButton(
 | 
			
		||||
          label: L10n.of(context).close,
 | 
			
		||||
@ -282,6 +282,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
        break;
 | 
			
		||||
      case KeyVerificationState.error:
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Icon(Icons.cancel, color: Colors.red, size: 200.0),
 | 
			
		||||
            SizedBox(height: 10),
 | 
			
		||||
@ -290,7 +291,6 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        );
 | 
			
		||||
        buttons.add(AdaptiveFlatButton(
 | 
			
		||||
          label: L10n.of(context).close,
 | 
			
		||||
@ -318,6 +318,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
    }
 | 
			
		||||
    final userNameTitle = Row(
 | 
			
		||||
      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
      crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        Text(
 | 
			
		||||
          otherName,
 | 
			
		||||
@ -336,7 +337,6 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
      ],
 | 
			
		||||
      crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
    );
 | 
			
		||||
    final title = Text(L10n.of(context).verifyTitle);
 | 
			
		||||
    final content = Scrollbar(
 | 
			
		||||
 | 
			
		||||
@ -108,15 +108,21 @@ class _RecordingDialogState extends State<RecordingDialog> {
 | 
			
		||||
      ),
 | 
			
		||||
      actions: <Widget>[
 | 
			
		||||
        TextButton(
 | 
			
		||||
          onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
 | 
			
		||||
          child: Text(
 | 
			
		||||
            L10n.of(context).cancel.toUpperCase(),
 | 
			
		||||
            style: TextStyle(
 | 
			
		||||
              color: Theme.of(context).textTheme.bodyText2.color.withAlpha(150),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
 | 
			
		||||
        ),
 | 
			
		||||
        TextButton(
 | 
			
		||||
          onPressed: () async {
 | 
			
		||||
            await _recorderSubscription?.cancel();
 | 
			
		||||
            await flutterSound.stopRecorder();
 | 
			
		||||
            Navigator.of(context, rootNavigator: false)
 | 
			
		||||
                .pop<String>(_recordedPath);
 | 
			
		||||
          },
 | 
			
		||||
          child: Row(
 | 
			
		||||
            children: <Widget>[
 | 
			
		||||
              Text(L10n.of(context).send.toUpperCase()),
 | 
			
		||||
@ -124,12 +130,6 @@ class _RecordingDialogState extends State<RecordingDialog> {
 | 
			
		||||
              Icon(Icons.send_outlined, size: 15),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          onPressed: () async {
 | 
			
		||||
            await _recorderSubscription?.cancel();
 | 
			
		||||
            await flutterSound.stopRecorder();
 | 
			
		||||
            Navigator.of(context, rootNavigator: false)
 | 
			
		||||
                .pop<String>(_recordedPath);
 | 
			
		||||
          },
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -78,14 +78,13 @@ class _SendFileDialogState extends State<SendFileDialog> {
 | 
			
		||||
      content: contentWidget,
 | 
			
		||||
      actions: <Widget>[
 | 
			
		||||
        TextButton(
 | 
			
		||||
          child: Text(L10n.of(context).cancel),
 | 
			
		||||
          onPressed: () {
 | 
			
		||||
            // just close the dialog
 | 
			
		||||
            Navigator.of(context, rootNavigator: false).pop();
 | 
			
		||||
          },
 | 
			
		||||
          child: Text(L10n.of(context).cancel),
 | 
			
		||||
        ),
 | 
			
		||||
        TextButton(
 | 
			
		||||
          child: Text(L10n.of(context).send),
 | 
			
		||||
          onPressed: _isSending
 | 
			
		||||
              ? null
 | 
			
		||||
              : () async {
 | 
			
		||||
@ -94,8 +93,9 @@ class _SendFileDialogState extends State<SendFileDialog> {
 | 
			
		||||
                  });
 | 
			
		||||
                  await showFutureLoadingDialog(
 | 
			
		||||
                      context: context, future: () => _send());
 | 
			
		||||
                  await Navigator.of(context, rootNavigator: false).pop();
 | 
			
		||||
                  Navigator.of(context, rootNavigator: false).pop();
 | 
			
		||||
                },
 | 
			
		||||
          child: Text(L10n.of(context).send),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -231,25 +231,25 @@ class InputBar extends StatelessWidget {
 | 
			
		||||
      }
 | 
			
		||||
      insertText = (isUnique
 | 
			
		||||
              ? insertEmote
 | 
			
		||||
              : ':${insertPack}~${insertEmote.substring(1)}') +
 | 
			
		||||
              : ':$insertPack~${insertEmote.substring(1)}') +
 | 
			
		||||
          ' ';
 | 
			
		||||
      startText = replaceText.replaceAllMapped(
 | 
			
		||||
        RegExp(r'(\s|^)(:(?:[-\w]+~)?[-\w]+)$'),
 | 
			
		||||
        (Match m) => '${m[1]}${insertText}',
 | 
			
		||||
        (Match m) => '${m[1]}$insertText',
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    if (suggestion['type'] == 'user') {
 | 
			
		||||
      insertText = suggestion['mxid'] + ' ';
 | 
			
		||||
      startText = replaceText.replaceAllMapped(
 | 
			
		||||
        RegExp(r'(\s|^)(@[-\w]+)$'),
 | 
			
		||||
        (Match m) => '${m[1]}${insertText}',
 | 
			
		||||
        (Match m) => '${m[1]}$insertText',
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    if (suggestion['type'] == 'room') {
 | 
			
		||||
      insertText = suggestion['mxid'] + ' ';
 | 
			
		||||
      startText = replaceText.replaceAllMapped(
 | 
			
		||||
        RegExp(r'(\s|^)(#[-\w]+)$'),
 | 
			
		||||
        (Match m) => '${m[1]}${insertText}',
 | 
			
		||||
        (Match m) => '${m[1]}$insertText',
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    if (insertText.isNotEmpty && startText.isNotEmpty) {
 | 
			
		||||
 | 
			
		||||
@ -109,6 +109,11 @@ class Message extends StatelessWidget {
 | 
			
		||||
                                  originServerTs: DateTime.now(),
 | 
			
		||||
                                );
 | 
			
		||||
                          return InkWell(
 | 
			
		||||
                            onTap: () {
 | 
			
		||||
                              if (scrollToEventId != null) {
 | 
			
		||||
                                scrollToEventId(replyEvent.eventId);
 | 
			
		||||
                              }
 | 
			
		||||
                            },
 | 
			
		||||
                            child: AbsorbPointer(
 | 
			
		||||
                              child: Container(
 | 
			
		||||
                                margin: EdgeInsets.symmetric(vertical: 4.0),
 | 
			
		||||
@ -116,11 +121,6 @@ class Message extends StatelessWidget {
 | 
			
		||||
                                    lightText: ownMessage, timeline: timeline),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                            onTap: () {
 | 
			
		||||
                              if (scrollToEventId != null) {
 | 
			
		||||
                                scrollToEventId(replyEvent.eventId);
 | 
			
		||||
                              }
 | 
			
		||||
                            },
 | 
			
		||||
                          );
 | 
			
		||||
                        },
 | 
			
		||||
                      ),
 | 
			
		||||
 | 
			
		||||
@ -123,42 +123,28 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
            continue textmessage;
 | 
			
		||||
          case MessageTypes.BadEncrypted:
 | 
			
		||||
          case EventTypes.Encrypted:
 | 
			
		||||
            return RaisedButton(
 | 
			
		||||
              elevation: 7,
 | 
			
		||||
              color: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
              shape: RoundedRectangleBorder(
 | 
			
		||||
                borderRadius: BorderRadius.circular(6),
 | 
			
		||||
              ),
 | 
			
		||||
              child: Row(
 | 
			
		||||
                mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                children: [
 | 
			
		||||
                  Icon(Icons.lock_outline),
 | 
			
		||||
                  SizedBox(width: 8),
 | 
			
		||||
                  Text(L10n.of(context).encrypted),
 | 
			
		||||
                ],
 | 
			
		||||
            return ElevatedButton.icon(
 | 
			
		||||
              style: ElevatedButton.styleFrom(
 | 
			
		||||
                primary: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                onPrimary: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
              ),
 | 
			
		||||
              onPressed: () => _verifyOrRequestKey(context),
 | 
			
		||||
              icon: Icon(Icons.lock_outline),
 | 
			
		||||
              label: Text(L10n.of(context).encrypted),
 | 
			
		||||
            );
 | 
			
		||||
          case MessageTypes.Location:
 | 
			
		||||
          case MessageTypes.None:
 | 
			
		||||
          textmessage:
 | 
			
		||||
          default:
 | 
			
		||||
            if (event.content['msgtype'] == Matrix.callNamespace) {
 | 
			
		||||
              return RaisedButton(
 | 
			
		||||
                elevation: 7,
 | 
			
		||||
                color: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                shape: RoundedRectangleBorder(
 | 
			
		||||
                  borderRadius: BorderRadius.circular(6),
 | 
			
		||||
                ),
 | 
			
		||||
                child: Row(
 | 
			
		||||
                  mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                  children: <Widget>[
 | 
			
		||||
                    Icon(Icons.phone_outlined, color: Colors.green),
 | 
			
		||||
                    SizedBox(width: 8),
 | 
			
		||||
                    Text(L10n.of(context).videoCall),
 | 
			
		||||
                  ],
 | 
			
		||||
              return ElevatedButton.icon(
 | 
			
		||||
                style: ElevatedButton.styleFrom(
 | 
			
		||||
                  primary: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                  onPrimary: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
                ),
 | 
			
		||||
                onPressed: () => launch(event.body),
 | 
			
		||||
                icon: Icon(Icons.phone_outlined, color: Colors.green),
 | 
			
		||||
                label: Text(L10n.of(context).videoCall),
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
            if (event.redacted) {
 | 
			
		||||
 | 
			
		||||
@ -19,12 +19,12 @@ class MessageDownloadContent extends StatelessWidget {
 | 
			
		||||
        crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
        mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          RaisedButton(
 | 
			
		||||
            elevation: 7,
 | 
			
		||||
            color: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
            shape: RoundedRectangleBorder(
 | 
			
		||||
              borderRadius: BorderRadius.circular(6),
 | 
			
		||||
          ElevatedButton(
 | 
			
		||||
            style: ElevatedButton.styleFrom(
 | 
			
		||||
              primary: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
              onPrimary: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
            ),
 | 
			
		||||
            onPressed: () => event.openFile(context),
 | 
			
		||||
            child: Row(
 | 
			
		||||
              mainAxisSize: MainAxisSize.min,
 | 
			
		||||
              children: [
 | 
			
		||||
@ -38,7 +38,6 @@ class MessageDownloadContent extends StatelessWidget {
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            onPressed: () => event.openFile(context),
 | 
			
		||||
          ),
 | 
			
		||||
          if (event.sizeString != null)
 | 
			
		||||
            Text(
 | 
			
		||||
 | 
			
		||||
@ -120,6 +120,7 @@ class _Reaction extends StatelessWidget {
 | 
			
		||||
          ));
 | 
			
		||||
    }
 | 
			
		||||
    return InkWell(
 | 
			
		||||
      onTap: () => onTap != null ? onTap() : null,
 | 
			
		||||
      child: Container(
 | 
			
		||||
        decoration: BoxDecoration(
 | 
			
		||||
          color: color,
 | 
			
		||||
@ -132,7 +133,6 @@ class _Reaction extends StatelessWidget {
 | 
			
		||||
        padding: EdgeInsets.all(padding),
 | 
			
		||||
        child: content,
 | 
			
		||||
      ),
 | 
			
		||||
      onTap: () => onTap != null ? onTap() : null,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,7 @@ class UserBottomSheet extends StatelessWidget {
 | 
			
		||||
      case 'message':
 | 
			
		||||
        final roomId = await user.startDirectChat();
 | 
			
		||||
        await AdaptivePageLayout.of(context)
 | 
			
		||||
            .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
 | 
			
		||||
            .pushNamedAndRemoveUntilIsFirst('/rooms/$roomId');
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -95,60 +95,66 @@ class UserBottomSheet extends StatelessWidget {
 | 
			
		||||
    if (onMention != null) {
 | 
			
		||||
      items.add(
 | 
			
		||||
        PopupMenuItem(
 | 
			
		||||
            child: _TextWithIcon(
 | 
			
		||||
              L10n.of(context).mention,
 | 
			
		||||
              Icons.alternate_email_outlined,
 | 
			
		||||
            ),
 | 
			
		||||
            value: 'mention'),
 | 
			
		||||
          value: 'mention',
 | 
			
		||||
          child: _TextWithIcon(
 | 
			
		||||
            L10n.of(context).mention,
 | 
			
		||||
            Icons.alternate_email_outlined,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    if (user.id != user.room.client.userID && !user.room.isDirectChat) {
 | 
			
		||||
      items.add(
 | 
			
		||||
        PopupMenuItem(
 | 
			
		||||
            child: _TextWithIcon(
 | 
			
		||||
              L10n.of(context).sendAMessage,
 | 
			
		||||
              Icons.send_outlined,
 | 
			
		||||
            ),
 | 
			
		||||
            value: 'message'),
 | 
			
		||||
          value: 'message',
 | 
			
		||||
          child: _TextWithIcon(
 | 
			
		||||
            L10n.of(context).sendAMessage,
 | 
			
		||||
            Icons.send_outlined,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    if (user.canChangePowerLevel) {
 | 
			
		||||
      items.add(
 | 
			
		||||
        PopupMenuItem(
 | 
			
		||||
            child: _TextWithIcon(
 | 
			
		||||
              L10n.of(context).setPermissionsLevel,
 | 
			
		||||
              Icons.edit_attributes_outlined,
 | 
			
		||||
            ),
 | 
			
		||||
            value: 'permission'),
 | 
			
		||||
          value: 'permission',
 | 
			
		||||
          child: _TextWithIcon(
 | 
			
		||||
            L10n.of(context).setPermissionsLevel,
 | 
			
		||||
            Icons.edit_attributes_outlined,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    if (user.canKick) {
 | 
			
		||||
      items.add(
 | 
			
		||||
        PopupMenuItem(
 | 
			
		||||
            child: _TextWithIcon(
 | 
			
		||||
              L10n.of(context).kickFromChat,
 | 
			
		||||
              Icons.exit_to_app_outlined,
 | 
			
		||||
            ),
 | 
			
		||||
            value: 'kick'),
 | 
			
		||||
          value: 'kick',
 | 
			
		||||
          child: _TextWithIcon(
 | 
			
		||||
            L10n.of(context).kickFromChat,
 | 
			
		||||
            Icons.exit_to_app_outlined,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    if (user.canBan && user.membership != Membership.ban) {
 | 
			
		||||
      items.add(
 | 
			
		||||
        PopupMenuItem(
 | 
			
		||||
            child: _TextWithIcon(
 | 
			
		||||
              L10n.of(context).banFromChat,
 | 
			
		||||
              Icons.warning_sharp,
 | 
			
		||||
            ),
 | 
			
		||||
            value: 'ban'),
 | 
			
		||||
          value: 'ban',
 | 
			
		||||
          child: _TextWithIcon(
 | 
			
		||||
            L10n.of(context).banFromChat,
 | 
			
		||||
            Icons.warning_sharp,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    } else if (user.canBan && user.membership == Membership.ban) {
 | 
			
		||||
      items.add(
 | 
			
		||||
        PopupMenuItem(
 | 
			
		||||
            child: _TextWithIcon(
 | 
			
		||||
              L10n.of(context).removeExile,
 | 
			
		||||
              Icons.warning_outlined,
 | 
			
		||||
            ),
 | 
			
		||||
            value: 'unban'),
 | 
			
		||||
          value: 'unban',
 | 
			
		||||
          child: _TextWithIcon(
 | 
			
		||||
            L10n.of(context).removeExile,
 | 
			
		||||
            Icons.warning_outlined,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return Center(
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ abstract class FluffyThemes {
 | 
			
		||||
      : TextTheme();
 | 
			
		||||
 | 
			
		||||
  static ThemeData light = ThemeData(
 | 
			
		||||
    visualDensity: VisualDensity.standard,
 | 
			
		||||
    primaryColorDark: Colors.white,
 | 
			
		||||
    primaryColorLight: Color(0xff121212),
 | 
			
		||||
    brightness: Brightness.light,
 | 
			
		||||
@ -54,6 +55,17 @@ abstract class FluffyThemes {
 | 
			
		||||
      backgroundColor: AppConfig.primaryColor,
 | 
			
		||||
      foregroundColor: Colors.white,
 | 
			
		||||
    ),
 | 
			
		||||
    elevatedButtonTheme: ElevatedButtonThemeData(
 | 
			
		||||
      style: ElevatedButton.styleFrom(
 | 
			
		||||
        primary: AppConfig.primaryColor,
 | 
			
		||||
        onPrimary: Colors.white,
 | 
			
		||||
        elevation: 7,
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: EdgeInsets.all(12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    inputDecorationTheme: InputDecorationTheme(
 | 
			
		||||
      border: OutlineInputBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(AppConfig.borderRadius)),
 | 
			
		||||
@ -80,6 +92,7 @@ abstract class FluffyThemes {
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  static ThemeData dark = ThemeData.dark().copyWith(
 | 
			
		||||
    visualDensity: VisualDensity.standard,
 | 
			
		||||
    primaryColorDark: Color(0xff121212),
 | 
			
		||||
    primaryColorLight: Colors.white,
 | 
			
		||||
    primaryColor: AppConfig.primaryColor,
 | 
			
		||||
 | 
			
		||||
@ -13,5 +13,5 @@ Future<Database> constructDb(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Future<String> getLocalstorage(String key) async {
 | 
			
		||||
  return await window.localStorage[key];
 | 
			
		||||
  return window.localStorage[key];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -84,7 +84,7 @@ class Store {
 | 
			
		||||
    if (!PlatformInfos.isMobile) {
 | 
			
		||||
      await _setupLocalStorage();
 | 
			
		||||
      try {
 | 
			
		||||
        return await storage.getItem(key)?.toString();
 | 
			
		||||
        return storage.getItem(key)?.toString();
 | 
			
		||||
      } catch (_) {
 | 
			
		||||
        return null;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -46,13 +46,13 @@ abstract class PlatformInfos {
 | 
			
		||||
      useRootNavigator: false,
 | 
			
		||||
      children: [
 | 
			
		||||
        Text('Version: $version'),
 | 
			
		||||
        RaisedButton(
 | 
			
		||||
          child: Text(L10n.of(context).sourceCode),
 | 
			
		||||
        OutlinedButton(
 | 
			
		||||
          onPressed: () => launch(AppConfig.sourceCodeUrl),
 | 
			
		||||
          child: Text(L10n.of(context).sourceCode),
 | 
			
		||||
        ),
 | 
			
		||||
        RaisedButton(
 | 
			
		||||
          child: Text(AppConfig.emojiFontName),
 | 
			
		||||
        OutlinedButton(
 | 
			
		||||
          onPressed: () => launch(AppConfig.emojiFontUrl),
 | 
			
		||||
          child: Text(AppConfig.emojiFontName),
 | 
			
		||||
        ),
 | 
			
		||||
        SentrySwitchListTile(label: L10n.of(context).sendBugReports),
 | 
			
		||||
      ],
 | 
			
		||||
 | 
			
		||||
@ -99,7 +99,7 @@ class UrlLauncher {
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        await AdaptivePageLayout.of(context)
 | 
			
		||||
            .pushNamedAndRemoveUntilIsFirst('/discover/${roomIdOrAlias}');
 | 
			
		||||
            .pushNamedAndRemoveUntilIsFirst('/discover/$roomIdOrAlias');
 | 
			
		||||
      }
 | 
			
		||||
    } else if (identityParts.primaryIdentifier.sigil == '@') {
 | 
			
		||||
      final user = User(
 | 
			
		||||
@ -109,7 +109,7 @@ class UrlLauncher {
 | 
			
		||||
      var roomId = matrix.client.getDirectChatFromUserId(user.id);
 | 
			
		||||
      if (roomId != null) {
 | 
			
		||||
        await AdaptivePageLayout.of(context)
 | 
			
		||||
            .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
 | 
			
		||||
            .pushNamedAndRemoveUntilIsFirst('/rooms/$roomId');
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
@ -128,7 +128,7 @@ class UrlLauncher {
 | 
			
		||||
 | 
			
		||||
        if (roomId != null) {
 | 
			
		||||
          await AdaptivePageLayout.of(context)
 | 
			
		||||
              .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
 | 
			
		||||
              .pushNamedAndRemoveUntilIsFirst('/rooms/$roomId');
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -636,24 +636,24 @@ class _ChatState extends State<Chat> {
 | 
			
		||||
                  },
 | 
			
		||||
                  itemBuilder: (_) => [
 | 
			
		||||
                    PopupMenuItem(
 | 
			
		||||
                      child: Text(L10n.of(context).copy),
 | 
			
		||||
                      value: 'copy',
 | 
			
		||||
                      child: Text(L10n.of(context).copy),
 | 
			
		||||
                    ),
 | 
			
		||||
                    if (canRedactSelectedEvents)
 | 
			
		||||
                      PopupMenuItem(
 | 
			
		||||
                        value: 'redact',
 | 
			
		||||
                        child: Text(
 | 
			
		||||
                          L10n.of(context).redactMessage,
 | 
			
		||||
                          style: TextStyle(color: Colors.orange),
 | 
			
		||||
                        ),
 | 
			
		||||
                        value: 'redact',
 | 
			
		||||
                      ),
 | 
			
		||||
                    if (selectedEvents.length == 1)
 | 
			
		||||
                      PopupMenuItem(
 | 
			
		||||
                        value: 'report',
 | 
			
		||||
                        child: Text(
 | 
			
		||||
                          L10n.of(context).reportMessage,
 | 
			
		||||
                          style: TextStyle(color: Colors.red),
 | 
			
		||||
                        ),
 | 
			
		||||
                        value: 'report',
 | 
			
		||||
                      ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
@ -672,12 +672,12 @@ class _ChatState extends State<Chat> {
 | 
			
		||||
          ? Padding(
 | 
			
		||||
              padding: const EdgeInsets.only(bottom: 56.0),
 | 
			
		||||
              child: FloatingActionButton(
 | 
			
		||||
                child: Icon(Icons.arrow_downward_outlined,
 | 
			
		||||
                    color: Theme.of(context).primaryColor),
 | 
			
		||||
                onPressed: () => _scrollController.jumpTo(0),
 | 
			
		||||
                foregroundColor: Theme.of(context).textTheme.bodyText2.color,
 | 
			
		||||
                backgroundColor: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                mini: true,
 | 
			
		||||
                child: Icon(Icons.arrow_downward_outlined,
 | 
			
		||||
                    color: Theme.of(context).primaryColor),
 | 
			
		||||
              ),
 | 
			
		||||
            )
 | 
			
		||||
          : null,
 | 
			
		||||
@ -756,6 +756,7 @@ class _ChatState extends State<Chat> {
 | 
			
		||||
                                      )
 | 
			
		||||
                                    : _canLoadMore
 | 
			
		||||
                                        ? TextButton(
 | 
			
		||||
                                            onPressed: requestHistory,
 | 
			
		||||
                                            child: Text(
 | 
			
		||||
                                              L10n.of(context).loadMore,
 | 
			
		||||
                                              style: TextStyle(
 | 
			
		||||
@ -766,7 +767,6 @@ class _ChatState extends State<Chat> {
 | 
			
		||||
                                                    TextDecoration.underline,
 | 
			
		||||
                                              ),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                            onPressed: requestHistory,
 | 
			
		||||
                                          )
 | 
			
		||||
                                        : Container()
 | 
			
		||||
                                : i == 0
 | 
			
		||||
@ -790,6 +790,11 @@ class _ChatState extends State<Chat> {
 | 
			
		||||
                                                        client.userID
 | 
			
		||||
                                                    ? Alignment.topRight
 | 
			
		||||
                                                    : Alignment.topLeft,
 | 
			
		||||
                                            padding: EdgeInsets.only(
 | 
			
		||||
                                              left: 8,
 | 
			
		||||
                                              right: 8,
 | 
			
		||||
                                              bottom: 8,
 | 
			
		||||
                                            ),
 | 
			
		||||
                                            child: Container(
 | 
			
		||||
                                              padding: EdgeInsets.symmetric(
 | 
			
		||||
                                                  horizontal: 4),
 | 
			
		||||
@ -810,11 +815,6 @@ class _ChatState extends State<Chat> {
 | 
			
		||||
                                                ),
 | 
			
		||||
                                              ),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                            padding: EdgeInsets.only(
 | 
			
		||||
                                              left: 8,
 | 
			
		||||
                                              right: 8,
 | 
			
		||||
                                              bottom: 8,
 | 
			
		||||
                                            ),
 | 
			
		||||
                                          );
 | 
			
		||||
                                        },
 | 
			
		||||
                                      )
 | 
			
		||||
@ -942,14 +942,14 @@ class _ChatState extends State<Chat> {
 | 
			
		||||
                        itemBuilder: (c, i) => i == emojis.length
 | 
			
		||||
                            ? InkWell(
 | 
			
		||||
                                borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                                onTap: () => _pickEmojiAction(
 | 
			
		||||
                                    context, allReactionEvents),
 | 
			
		||||
                                child: Container(
 | 
			
		||||
                                  width: 56,
 | 
			
		||||
                                  height: 56,
 | 
			
		||||
                                  alignment: Alignment.center,
 | 
			
		||||
                                  child: Icon(Icons.add_outlined),
 | 
			
		||||
                                ),
 | 
			
		||||
                                onTap: () => _pickEmojiAction(
 | 
			
		||||
                                    context, allReactionEvents),
 | 
			
		||||
                              )
 | 
			
		||||
                            : InkWell(
 | 
			
		||||
                                borderRadius: BorderRadius.circular(8),
 | 
			
		||||
 | 
			
		||||
@ -249,8 +249,8 @@ class _ChatDetailsState extends State<ChatDetails> {
 | 
			
		||||
                                    backgroundColor: Theme.of(context)
 | 
			
		||||
                                        .scaffoldBackgroundColor,
 | 
			
		||||
                                    foregroundColor: Colors.grey,
 | 
			
		||||
                                    child: Icon(Icons.edit_outlined),
 | 
			
		||||
                                    radius: Avatar.defaultSize / 2,
 | 
			
		||||
                                    child: Icon(Icons.edit_outlined),
 | 
			
		||||
                                  )
 | 
			
		||||
                                : null,
 | 
			
		||||
                            title: Text('${L10n.of(context).groupDescription}:',
 | 
			
		||||
@ -342,19 +342,6 @@ class _ChatDetailsState extends State<ChatDetails> {
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                          PopupMenuButton(
 | 
			
		||||
                            child: ListTile(
 | 
			
		||||
                              leading: CircleAvatar(
 | 
			
		||||
                                  backgroundColor:
 | 
			
		||||
                                      Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                  foregroundColor: Colors.grey,
 | 
			
		||||
                                  child: Icon(Icons.public_outlined)),
 | 
			
		||||
                              title: Text(
 | 
			
		||||
                                  L10n.of(context).whoIsAllowedToJoinThisGroup),
 | 
			
		||||
                              subtitle: Text(
 | 
			
		||||
                                room.joinRules.getLocalizedString(
 | 
			
		||||
                                    MatrixLocals(L10n.of(context))),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                            onSelected: (JoinRules joinRule) =>
 | 
			
		||||
                                showFutureLoadingDialog(
 | 
			
		||||
                              context: context,
 | 
			
		||||
@ -377,22 +364,21 @@ class _ChatDetailsState extends State<ChatDetails> {
 | 
			
		||||
                                          MatrixLocals(L10n.of(context)))),
 | 
			
		||||
                                ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                          PopupMenuButton(
 | 
			
		||||
                            child: ListTile(
 | 
			
		||||
                              leading: CircleAvatar(
 | 
			
		||||
                                backgroundColor:
 | 
			
		||||
                                    Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                foregroundColor: Colors.grey,
 | 
			
		||||
                                child: Icon(Icons.visibility_outlined),
 | 
			
		||||
                              ),
 | 
			
		||||
                                  backgroundColor:
 | 
			
		||||
                                      Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                  foregroundColor: Colors.grey,
 | 
			
		||||
                                  child: Icon(Icons.public_outlined)),
 | 
			
		||||
                              title: Text(
 | 
			
		||||
                                  L10n.of(context).visibilityOfTheChatHistory),
 | 
			
		||||
                                  L10n.of(context).whoIsAllowedToJoinThisGroup),
 | 
			
		||||
                              subtitle: Text(
 | 
			
		||||
                                room.historyVisibility.getLocalizedString(
 | 
			
		||||
                                room.joinRules.getLocalizedString(
 | 
			
		||||
                                    MatrixLocals(L10n.of(context))),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                          PopupMenuButton(
 | 
			
		||||
                            onSelected: (HistoryVisibility historyVisibility) =>
 | 
			
		||||
                                showFutureLoadingDialog(
 | 
			
		||||
                              context: context,
 | 
			
		||||
@ -430,23 +416,23 @@ class _ChatDetailsState extends State<ChatDetails> {
 | 
			
		||||
                                          MatrixLocals(L10n.of(context)))),
 | 
			
		||||
                                ),
 | 
			
		||||
                            ],
 | 
			
		||||
                            child: ListTile(
 | 
			
		||||
                              leading: CircleAvatar(
 | 
			
		||||
                                backgroundColor:
 | 
			
		||||
                                    Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                foregroundColor: Colors.grey,
 | 
			
		||||
                                child: Icon(Icons.visibility_outlined),
 | 
			
		||||
                              ),
 | 
			
		||||
                              title: Text(
 | 
			
		||||
                                  L10n.of(context).visibilityOfTheChatHistory),
 | 
			
		||||
                              subtitle: Text(
 | 
			
		||||
                                room.historyVisibility.getLocalizedString(
 | 
			
		||||
                                    MatrixLocals(L10n.of(context))),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                          if (room.joinRules == JoinRules.public)
 | 
			
		||||
                            PopupMenuButton(
 | 
			
		||||
                              child: ListTile(
 | 
			
		||||
                                leading: CircleAvatar(
 | 
			
		||||
                                  backgroundColor:
 | 
			
		||||
                                      Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                  foregroundColor: Colors.grey,
 | 
			
		||||
                                  child: Icon(Icons.info_outline),
 | 
			
		||||
                                ),
 | 
			
		||||
                                title: Text(
 | 
			
		||||
                                    L10n.of(context).areGuestsAllowedToJoin),
 | 
			
		||||
                                subtitle: Text(
 | 
			
		||||
                                  room.guestAccess.getLocalizedString(
 | 
			
		||||
                                      MatrixLocals(L10n.of(context))),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                              onSelected: (GuestAccess guestAccess) =>
 | 
			
		||||
                                  showFutureLoadingDialog(
 | 
			
		||||
                                context: context,
 | 
			
		||||
@ -471,6 +457,20 @@ class _ChatDetailsState extends State<ChatDetails> {
 | 
			
		||||
                                    ),
 | 
			
		||||
                                  ),
 | 
			
		||||
                              ],
 | 
			
		||||
                              child: ListTile(
 | 
			
		||||
                                leading: CircleAvatar(
 | 
			
		||||
                                  backgroundColor:
 | 
			
		||||
                                      Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                  foregroundColor: Colors.grey,
 | 
			
		||||
                                  child: Icon(Icons.info_outline),
 | 
			
		||||
                                ),
 | 
			
		||||
                                title: Text(
 | 
			
		||||
                                    L10n.of(context).areGuestsAllowedToJoin),
 | 
			
		||||
                                subtitle: Text(
 | 
			
		||||
                                  room.guestAccess.getLocalizedString(
 | 
			
		||||
                                      MatrixLocals(L10n.of(context))),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ListTile(
 | 
			
		||||
                            title: Text(L10n.of(context).editChatPermissions),
 | 
			
		||||
@ -502,11 +502,11 @@ class _ChatDetailsState extends State<ChatDetails> {
 | 
			
		||||
                              ? ListTile(
 | 
			
		||||
                                  title: Text(L10n.of(context).inviteContact),
 | 
			
		||||
                                  leading: CircleAvatar(
 | 
			
		||||
                                    child: Icon(Icons.add_outlined),
 | 
			
		||||
                                    backgroundColor:
 | 
			
		||||
                                        Theme.of(context).primaryColor,
 | 
			
		||||
                                    foregroundColor: Colors.white,
 | 
			
		||||
                                    radius: Avatar.defaultSize / 2,
 | 
			
		||||
                                    child: Icon(Icons.add_outlined),
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  onTap: () => AdaptivePageLayout.of(context)
 | 
			
		||||
                                      .pushNamed('/rooms/${room.id}/invite'),
 | 
			
		||||
 | 
			
		||||
@ -115,8 +115,8 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
 | 
			
		||||
                                    .verified ==
 | 
			
		||||
                                UserVerifiedStatus.unknown) {
 | 
			
		||||
                              items.add(PopupMenuItem(
 | 
			
		||||
                                child: Text(L10n.of(context).verifyUser),
 | 
			
		||||
                                value: 'verify_user',
 | 
			
		||||
                                child: Text(L10n.of(context).verifyUser),
 | 
			
		||||
                              ));
 | 
			
		||||
                            }
 | 
			
		||||
                            return items;
 | 
			
		||||
@ -162,22 +162,22 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
 | 
			
		||||
                          if (deviceKeys[i].blocked ||
 | 
			
		||||
                              !deviceKeys[i].verified) {
 | 
			
		||||
                            items.add(PopupMenuItem(
 | 
			
		||||
                              child: Text(L10n.of(context).verifyStart),
 | 
			
		||||
                              value: deviceKeys[i].userId == room.client.userID
 | 
			
		||||
                                  ? 'verify'
 | 
			
		||||
                                  : 'verify_user',
 | 
			
		||||
                              child: Text(L10n.of(context).verifyStart),
 | 
			
		||||
                            ));
 | 
			
		||||
                          }
 | 
			
		||||
                          if (deviceKeys[i].blocked) {
 | 
			
		||||
                            items.add(PopupMenuItem(
 | 
			
		||||
                              child: Text(L10n.of(context).unblockDevice),
 | 
			
		||||
                              value: 'unblock',
 | 
			
		||||
                              child: Text(L10n.of(context).unblockDevice),
 | 
			
		||||
                            ));
 | 
			
		||||
                          }
 | 
			
		||||
                          if (!deviceKeys[i].blocked) {
 | 
			
		||||
                            items.add(PopupMenuItem(
 | 
			
		||||
                              child: Text(L10n.of(context).blockDevice),
 | 
			
		||||
                              value: 'block',
 | 
			
		||||
                              child: Text(L10n.of(context).blockDevice),
 | 
			
		||||
                            ));
 | 
			
		||||
                          }
 | 
			
		||||
                          return items;
 | 
			
		||||
 | 
			
		||||
@ -192,6 +192,8 @@ class _ChatListState extends State<ChatList> {
 | 
			
		||||
                      ? Center(
 | 
			
		||||
                          child: InkWell(
 | 
			
		||||
                            borderRadius: BorderRadius.circular(32),
 | 
			
		||||
                            onTap: () => AdaptivePageLayout.of(context)
 | 
			
		||||
                                .pushNamedAndRemoveUntilIsFirst('/settings'),
 | 
			
		||||
                            child: FutureBuilder<Profile>(
 | 
			
		||||
                              future: Matrix.of(context).client.ownProfile,
 | 
			
		||||
                              builder: (_, snapshot) => Avatar(
 | 
			
		||||
@ -201,8 +203,6 @@ class _ChatListState extends State<ChatList> {
 | 
			
		||||
                                size: 32,
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                            onTap: () => AdaptivePageLayout.of(context)
 | 
			
		||||
                                .pushNamedAndRemoveUntilIsFirst('/settings'),
 | 
			
		||||
                          ),
 | 
			
		||||
                        )
 | 
			
		||||
                      : IconButton(
 | 
			
		||||
@ -378,9 +378,9 @@ class _ChatListState extends State<ChatList> {
 | 
			
		||||
            ]),
 | 
			
		||||
            floatingActionButton: selectMode == SelectMode.normal
 | 
			
		||||
                ? FloatingActionButton(
 | 
			
		||||
                    child: Icon(Icons.add_outlined),
 | 
			
		||||
                    onPressed: () => AdaptivePageLayout.of(context)
 | 
			
		||||
                        .pushNamedAndRemoveUntilIsFirst('/newprivatechat'),
 | 
			
		||||
                    child: Icon(Icons.add_outlined),
 | 
			
		||||
                  )
 | 
			
		||||
                : null,
 | 
			
		||||
            bottomNavigationBar: selectMode == SelectMode.normal
 | 
			
		||||
 | 
			
		||||
@ -119,9 +119,9 @@ class _ContactsState extends State<Contacts> {
 | 
			
		||||
                  ListTile(
 | 
			
		||||
                    leading: CircleAvatar(
 | 
			
		||||
                      radius: Avatar.defaultSize / 2,
 | 
			
		||||
                      child: Icon(Icons.person_add_outlined),
 | 
			
		||||
                      backgroundColor: Theme.of(context).primaryColor,
 | 
			
		||||
                      foregroundColor: Colors.white,
 | 
			
		||||
                      child: Icon(Icons.person_add_outlined),
 | 
			
		||||
                    ),
 | 
			
		||||
                    title: Text(L10n.of(context).addNewFriend),
 | 
			
		||||
                    onTap: () => AdaptivePageLayout.of(context)
 | 
			
		||||
@ -141,15 +141,15 @@ class _ContactsState extends State<Contacts> {
 | 
			
		||||
                        ),
 | 
			
		||||
                        Center(
 | 
			
		||||
                          child: OutlinedButton(
 | 
			
		||||
                            onPressed: () => FluffyShare.share(
 | 
			
		||||
                                L10n.of(context).inviteText(client.userID,
 | 
			
		||||
                                    'https://matrix.to/#/${client.userID}'),
 | 
			
		||||
                                context),
 | 
			
		||||
                            child: Text(
 | 
			
		||||
                              L10n.of(context).inviteContact,
 | 
			
		||||
                              style: TextStyle(
 | 
			
		||||
                                  color: Theme.of(context).accentColor),
 | 
			
		||||
                            ),
 | 
			
		||||
                            onPressed: () => FluffyShare.share(
 | 
			
		||||
                                L10n.of(context).inviteText(client.userID,
 | 
			
		||||
                                    'https://matrix.to/#/${client.userID}'),
 | 
			
		||||
                                context),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
 | 
			
		||||
@ -180,22 +180,16 @@ class _HomeserverPickerState extends State<HomeserverPicker> {
 | 
			
		||||
              tag: 'loginButton',
 | 
			
		||||
              child: Container(
 | 
			
		||||
                width: double.infinity,
 | 
			
		||||
                height: 56,
 | 
			
		||||
                padding: EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
                child: RaisedButton(
 | 
			
		||||
                  elevation: 7,
 | 
			
		||||
                  color: Theme.of(context).primaryColor,
 | 
			
		||||
                  shape: RoundedRectangleBorder(
 | 
			
		||||
                    borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
                  ),
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  onPressed:
 | 
			
		||||
                      _isLoading ? null : () => _checkHomeserverAction(context),
 | 
			
		||||
                  child: _isLoading
 | 
			
		||||
                      ? LinearProgressIndicator()
 | 
			
		||||
                      : Text(
 | 
			
		||||
                          L10n.of(context).connect.toUpperCase(),
 | 
			
		||||
                          style: TextStyle(color: Colors.white, fontSize: 16),
 | 
			
		||||
                        ),
 | 
			
		||||
                  onPressed:
 | 
			
		||||
                      _isLoading ? null : () => _checkHomeserverAction(context),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
@ -203,6 +197,7 @@ class _HomeserverPickerState extends State<HomeserverPicker> {
 | 
			
		||||
              alignment: WrapAlignment.center,
 | 
			
		||||
              children: [
 | 
			
		||||
                TextButton(
 | 
			
		||||
                  onPressed: () => launch(AppConfig.privacyUrl),
 | 
			
		||||
                  child: Text(
 | 
			
		||||
                    L10n.of(context).privacy,
 | 
			
		||||
                    style: TextStyle(
 | 
			
		||||
@ -210,9 +205,9 @@ class _HomeserverPickerState extends State<HomeserverPicker> {
 | 
			
		||||
                      color: Colors.blueGrey,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  onPressed: () => launch(AppConfig.privacyUrl),
 | 
			
		||||
                ),
 | 
			
		||||
                TextButton(
 | 
			
		||||
                  onPressed: () => PlatformInfos.showDialog(context),
 | 
			
		||||
                  child: Text(
 | 
			
		||||
                    L10n.of(context).about,
 | 
			
		||||
                    style: TextStyle(
 | 
			
		||||
@ -220,7 +215,6 @@ class _HomeserverPickerState extends State<HomeserverPicker> {
 | 
			
		||||
                      color: Colors.blueGrey,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  onPressed: () => PlatformInfos.showDialog(context),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,15 @@ class ImageView extends StatelessWidget {
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      body: InteractiveViewer(
 | 
			
		||||
        minScale: 1.0,
 | 
			
		||||
        maxScale: 10.0,
 | 
			
		||||
        onInteractionEnd: (ScaleEndDetails endDetails) {
 | 
			
		||||
          if (PlatformInfos.usesTouchscreen == false) {
 | 
			
		||||
            if (endDetails.velocity.pixelsPerSecond.dy > calcVelocity) {
 | 
			
		||||
              Navigator.of(context, rootNavigator: false).pop();
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        child: Center(
 | 
			
		||||
          child: ImageBubble(
 | 
			
		||||
            event,
 | 
			
		||||
@ -62,15 +71,6 @@ class ImageView extends StatelessWidget {
 | 
			
		||||
            thumbnailOnly: false,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        minScale: 1.0,
 | 
			
		||||
        maxScale: 10.0,
 | 
			
		||||
        onInteractionEnd: (ScaleEndDetails endDetails) {
 | 
			
		||||
          if (PlatformInfos.usesTouchscreen == false) {
 | 
			
		||||
            if (endDetails.velocity.pixelsPerSecond.dy > calcVelocity) {
 | 
			
		||||
              Navigator.of(context, rootNavigator: false).pop();
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -36,8 +36,8 @@ class _LogViewerState extends State<LogViewer> {
 | 
			
		||||
          PopupMenuButton<Level>(
 | 
			
		||||
            itemBuilder: (context) => Level.values
 | 
			
		||||
                .map((level) => PopupMenuItem(
 | 
			
		||||
                      child: Text(level.toString()),
 | 
			
		||||
                      value: level,
 | 
			
		||||
                      child: Text(level.toString()),
 | 
			
		||||
                    ))
 | 
			
		||||
                .toList(),
 | 
			
		||||
            onSelected: (Level level) => setState(() => logLevel = level),
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,6 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import '../utils/platform_infos.dart';
 | 
			
		||||
 | 
			
		||||
import '../app_config.dart';
 | 
			
		||||
 | 
			
		||||
class Login extends StatefulWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  _LoginState createState() => _LoginState();
 | 
			
		||||
@ -226,27 +224,22 @@ class _LoginState extends State<Login> {
 | 
			
		||||
            SizedBox(height: 12),
 | 
			
		||||
            Hero(
 | 
			
		||||
              tag: 'loginButton',
 | 
			
		||||
              child: Container(
 | 
			
		||||
                height: 56,
 | 
			
		||||
              child: Padding(
 | 
			
		||||
                padding: EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
                child: RaisedButton(
 | 
			
		||||
                  elevation: 7,
 | 
			
		||||
                  color: Theme.of(context).primaryColor,
 | 
			
		||||
                  shape: RoundedRectangleBorder(
 | 
			
		||||
                    borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
                  ),
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  onPressed: loading ? null : () => login(context),
 | 
			
		||||
                  child: loading
 | 
			
		||||
                      ? LinearProgressIndicator()
 | 
			
		||||
                      : Text(
 | 
			
		||||
                          L10n.of(context).login.toUpperCase(),
 | 
			
		||||
                          style: TextStyle(color: Colors.white, fontSize: 16),
 | 
			
		||||
                        ),
 | 
			
		||||
                  onPressed: loading ? null : () => login(context),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Center(
 | 
			
		||||
              child: TextButton(
 | 
			
		||||
                onPressed: () => _passwordForgotten(context),
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  L10n.of(context).passwordForgotten,
 | 
			
		||||
                  style: TextStyle(
 | 
			
		||||
@ -254,7 +247,6 @@ class _LoginState extends State<Login> {
 | 
			
		||||
                    decoration: TextDecoration.underline,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                onPressed: () => _passwordForgotten(context),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
 | 
			
		||||
@ -91,12 +91,12 @@ class _NewPrivateChatState extends State<NewPrivateChat> {
 | 
			
		||||
        elevation: 0,
 | 
			
		||||
        actions: [
 | 
			
		||||
          TextButton(
 | 
			
		||||
            onPressed: () => AdaptivePageLayout.of(context)
 | 
			
		||||
                .pushNamedAndRemoveUntilIsFirst('/newgroup'),
 | 
			
		||||
            child: Text(
 | 
			
		||||
              L10n.of(context).createNewGroup,
 | 
			
		||||
              style: TextStyle(color: Theme.of(context).accentColor),
 | 
			
		||||
            ),
 | 
			
		||||
            onPressed: () => AdaptivePageLayout.of(context)
 | 
			
		||||
                .pushNamedAndRemoveUntilIsFirst('/newgroup'),
 | 
			
		||||
          )
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
 | 
			
		||||
@ -174,13 +174,13 @@ class _EmotesSettingsState extends State<EmotesSettings> {
 | 
			
		||||
      ),
 | 
			
		||||
      floatingActionButton: showSave
 | 
			
		||||
          ? FloatingActionButton(
 | 
			
		||||
              child: Icon(Icons.save_outlined, color: Colors.white),
 | 
			
		||||
              onPressed: () async {
 | 
			
		||||
                await _save(context);
 | 
			
		||||
                setState(() {
 | 
			
		||||
                  showSave = false;
 | 
			
		||||
                });
 | 
			
		||||
              },
 | 
			
		||||
              child: Icon(Icons.save_outlined, color: Colors.white),
 | 
			
		||||
            )
 | 
			
		||||
          : null,
 | 
			
		||||
      body: StreamBuilder(
 | 
			
		||||
@ -190,6 +190,9 @@ class _EmotesSettingsState extends State<EmotesSettings> {
 | 
			
		||||
              children: <Widget>[
 | 
			
		||||
                if (!readonly)
 | 
			
		||||
                  Container(
 | 
			
		||||
                    padding: EdgeInsets.symmetric(
 | 
			
		||||
                      vertical: 8.0,
 | 
			
		||||
                    ),
 | 
			
		||||
                    child: ListTile(
 | 
			
		||||
                      leading: Container(
 | 
			
		||||
                        width: 180.0,
 | 
			
		||||
@ -222,11 +225,6 @@ class _EmotesSettingsState extends State<EmotesSettings> {
 | 
			
		||||
                      ),
 | 
			
		||||
                      title: _EmoteImagePicker(newMxcController),
 | 
			
		||||
                      trailing: InkWell(
 | 
			
		||||
                        child: Icon(
 | 
			
		||||
                          Icons.add_outlined,
 | 
			
		||||
                          color: Colors.green,
 | 
			
		||||
                          size: 32.0,
 | 
			
		||||
                        ),
 | 
			
		||||
                        onTap: () async {
 | 
			
		||||
                          if (newEmoteController.text == null ||
 | 
			
		||||
                              newEmoteController.text.isEmpty ||
 | 
			
		||||
@ -270,11 +268,13 @@ class _EmotesSettingsState extends State<EmotesSettings> {
 | 
			
		||||
                            showSave = false;
 | 
			
		||||
                          });
 | 
			
		||||
                        },
 | 
			
		||||
                        child: Icon(
 | 
			
		||||
                          Icons.add_outlined,
 | 
			
		||||
                          color: Colors.green,
 | 
			
		||||
                          size: 32.0,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    padding: EdgeInsets.symmetric(
 | 
			
		||||
                      vertical: 8.0,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                if (widget.room != null)
 | 
			
		||||
                  ListTile(
 | 
			
		||||
@ -346,7 +346,7 @@ class _EmotesSettingsState extends State<EmotesSettings> {
 | 
			
		||||
                                    border: InputBorder.none,
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  onSubmitted: (s) {
 | 
			
		||||
                                    final emoteCode = ':${s}:';
 | 
			
		||||
                                    final emoteCode = ':$s:';
 | 
			
		||||
                                    if (emotes.indexWhere((e) =>
 | 
			
		||||
                                            e.emote == emoteCode &&
 | 
			
		||||
                                            e.mxc != emote.mxc) !=
 | 
			
		||||
@ -382,16 +382,16 @@ class _EmotesSettingsState extends State<EmotesSettings> {
 | 
			
		||||
                              trailing: readonly
 | 
			
		||||
                                  ? null
 | 
			
		||||
                                  : InkWell(
 | 
			
		||||
                                      child: Icon(
 | 
			
		||||
                                        Icons.delete_forever_outlined,
 | 
			
		||||
                                        color: Colors.red,
 | 
			
		||||
                                        size: 32.0,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      onTap: () => setState(() {
 | 
			
		||||
                                        emotes.removeWhere(
 | 
			
		||||
                                            (e) => e.emote == emote.emote);
 | 
			
		||||
                                        showSave = true;
 | 
			
		||||
                                      }),
 | 
			
		||||
                                      child: Icon(
 | 
			
		||||
                                        Icons.delete_forever_outlined,
 | 
			
		||||
                                        color: Colors.red,
 | 
			
		||||
                                        size: 32.0,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ),
 | 
			
		||||
                            );
 | 
			
		||||
                          },
 | 
			
		||||
@ -440,14 +440,7 @@ class _EmoteImagePickerState extends State<_EmoteImagePicker> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    if (widget.controller.text == null || widget.controller.text.isEmpty) {
 | 
			
		||||
      return RaisedButton(
 | 
			
		||||
        color: Theme.of(context).primaryColor,
 | 
			
		||||
        elevation: 5,
 | 
			
		||||
        textColor: Colors.white,
 | 
			
		||||
        child: Text(L10n.of(context).pickImage),
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(10.0),
 | 
			
		||||
        ),
 | 
			
		||||
      return ElevatedButton(
 | 
			
		||||
        onPressed: () async {
 | 
			
		||||
          if (kIsWeb) {
 | 
			
		||||
            await FlushbarHelper.createError(
 | 
			
		||||
@ -487,6 +480,7 @@ class _EmoteImagePickerState extends State<_EmoteImagePicker> {
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        child: Text(L10n.of(context).pickImage),
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      return _EmoteImage(widget.controller.text);
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,6 @@ import 'package:flutter/cupertino.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
 | 
			
		||||
import '../app_config.dart';
 | 
			
		||||
 | 
			
		||||
class SignUp extends StatefulWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  _SignUpState createState() => _SignUpState();
 | 
			
		||||
@ -138,27 +136,23 @@ class _SignUpState extends State<SignUp> {
 | 
			
		||||
            SizedBox(height: 16),
 | 
			
		||||
            Hero(
 | 
			
		||||
              tag: 'loginButton',
 | 
			
		||||
              child: Container(
 | 
			
		||||
                height: 56,
 | 
			
		||||
              child: Padding(
 | 
			
		||||
                padding: EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
                child: RaisedButton(
 | 
			
		||||
                  elevation: 7,
 | 
			
		||||
                  color: Theme.of(context).primaryColor,
 | 
			
		||||
                  shape: RoundedRectangleBorder(
 | 
			
		||||
                    borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
                  ),
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  onPressed: loading ? null : () => signUpAction(context),
 | 
			
		||||
                  child: loading
 | 
			
		||||
                      ? LinearProgressIndicator()
 | 
			
		||||
                      : Text(
 | 
			
		||||
                          L10n.of(context).signUp.toUpperCase(),
 | 
			
		||||
                          style: TextStyle(color: Colors.white, fontSize: 16),
 | 
			
		||||
                        ),
 | 
			
		||||
                  onPressed: loading ? null : () => signUpAction(context),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Center(
 | 
			
		||||
              child: TextButton(
 | 
			
		||||
                onPressed: () =>
 | 
			
		||||
                    AdaptivePageLayout.of(context).pushNamed('/login'),
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  L10n.of(context).alreadyHaveAnAccount,
 | 
			
		||||
                  style: TextStyle(
 | 
			
		||||
@ -167,8 +161,6 @@ class _SignUpState extends State<SignUp> {
 | 
			
		||||
                    fontSize: 16,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                onPressed: () =>
 | 
			
		||||
                    AdaptivePageLayout.of(context).pushNamed('/login'),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ]),
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
import '../utils/platform_infos.dart';
 | 
			
		||||
 | 
			
		||||
import '../app_config.dart';
 | 
			
		||||
 | 
			
		||||
class SignUpPassword extends StatefulWidget {
 | 
			
		||||
  final MatrixFile avatar;
 | 
			
		||||
  final String username;
 | 
			
		||||
@ -174,22 +172,16 @@ class _SignUpPasswordState extends State<SignUpPassword> {
 | 
			
		||||
          SizedBox(height: 12),
 | 
			
		||||
          Hero(
 | 
			
		||||
            tag: 'loginButton',
 | 
			
		||||
            child: Container(
 | 
			
		||||
              height: 56,
 | 
			
		||||
            child: Padding(
 | 
			
		||||
              padding: EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
              child: RaisedButton(
 | 
			
		||||
                elevation: 7,
 | 
			
		||||
                color: Theme.of(context).primaryColor,
 | 
			
		||||
                shape: RoundedRectangleBorder(
 | 
			
		||||
                  borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
                ),
 | 
			
		||||
              child: ElevatedButton(
 | 
			
		||||
                onPressed: loading ? null : () => _signUpAction(context),
 | 
			
		||||
                child: loading
 | 
			
		||||
                    ? LinearProgressIndicator()
 | 
			
		||||
                    : Text(
 | 
			
		||||
                        L10n.of(context).createAccountNow.toUpperCase(),
 | 
			
		||||
                        style: TextStyle(color: Colors.white, fontSize: 16),
 | 
			
		||||
                      ),
 | 
			
		||||
                onPressed: loading ? null : () => _signUpAction(context),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										86
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								pubspec.lock
									
									
									
									
									
								
							@ -77,7 +77,7 @@ packages:
 | 
			
		||||
      name: async
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.5.0-nullsafety.1"
 | 
			
		||||
    version: "2.5.0"
 | 
			
		||||
  base58check:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -91,7 +91,7 @@ packages:
 | 
			
		||||
      name: boolean_selector
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.0-nullsafety.1"
 | 
			
		||||
    version: "2.1.0"
 | 
			
		||||
  cached_network_image:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -112,14 +112,14 @@ packages:
 | 
			
		||||
      name: characters
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.1.0-nullsafety.3"
 | 
			
		||||
    version: "1.1.0"
 | 
			
		||||
  charcode:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: charcode
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.2.0-nullsafety.1"
 | 
			
		||||
    version: "1.2.0"
 | 
			
		||||
  circular_check_box:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -140,14 +140,14 @@ packages:
 | 
			
		||||
      name: clock
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.1.0-nullsafety.1"
 | 
			
		||||
    version: "1.1.0"
 | 
			
		||||
  collection:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: collection
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.15.0-nullsafety.3"
 | 
			
		||||
    version: "1.15.0"
 | 
			
		||||
  convert:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -217,7 +217,7 @@ packages:
 | 
			
		||||
      name: fake_async
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.2.0-nullsafety.1"
 | 
			
		||||
    version: "1.2.0"
 | 
			
		||||
  famedlysdk:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -249,7 +249,7 @@ packages:
 | 
			
		||||
      name: file
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "5.2.1"
 | 
			
		||||
    version: "6.1.0"
 | 
			
		||||
  file_chooser:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -386,19 +386,14 @@ packages:
 | 
			
		||||
    description: flutter
 | 
			
		||||
    source: sdk
 | 
			
		||||
    version: "0.0.0"
 | 
			
		||||
  flutter_math:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_math
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.2.0+2"
 | 
			
		||||
  flutter_matrix_html:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_matrix_html
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
      path: "."
 | 
			
		||||
      ref: HEAD
 | 
			
		||||
      resolved-ref: b1505570660d2e4c212a67889fe260eca7fb136e
 | 
			
		||||
      url: "https://github.com/ChristianPauly/flutter_matrix_html.git"
 | 
			
		||||
    source: git
 | 
			
		||||
    version: "0.2.0"
 | 
			
		||||
  flutter_olm:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
@ -469,7 +464,7 @@ packages:
 | 
			
		||||
      name: flutter_svg
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.19.1"
 | 
			
		||||
    version: "0.19.3"
 | 
			
		||||
  flutter_test:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description: flutter
 | 
			
		||||
@ -570,7 +565,7 @@ packages:
 | 
			
		||||
      name: intl
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.16.1"
 | 
			
		||||
    version: "0.17.0"
 | 
			
		||||
  io:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -591,7 +586,7 @@ packages:
 | 
			
		||||
      name: js
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.6.3-nullsafety.2"
 | 
			
		||||
    version: "0.6.3"
 | 
			
		||||
  localstorage:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -626,7 +621,7 @@ packages:
 | 
			
		||||
      name: matcher
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.12.10-nullsafety.1"
 | 
			
		||||
    version: "0.12.10"
 | 
			
		||||
  matrix_api_lite:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -654,7 +649,7 @@ packages:
 | 
			
		||||
      name: meta
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.3.0-nullsafety.3"
 | 
			
		||||
    version: "1.3.0"
 | 
			
		||||
  mime:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -768,7 +763,7 @@ packages:
 | 
			
		||||
      name: path
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.8.0-nullsafety.1"
 | 
			
		||||
    version: "1.8.0"
 | 
			
		||||
  path_drawing:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -824,7 +819,7 @@ packages:
 | 
			
		||||
      name: pedantic
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.10.0-nullsafety.2"
 | 
			
		||||
    version: "1.11.0"
 | 
			
		||||
  permission_handler:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -852,7 +847,7 @@ packages:
 | 
			
		||||
      name: platform
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.2.1"
 | 
			
		||||
    version: "3.0.0"
 | 
			
		||||
  plugin_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -873,14 +868,14 @@ packages:
 | 
			
		||||
      name: pool
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.5.0-nullsafety.2"
 | 
			
		||||
    version: "1.5.0"
 | 
			
		||||
  process:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: process
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.0.13"
 | 
			
		||||
    version: "4.1.0"
 | 
			
		||||
  provider:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -1032,21 +1027,21 @@ packages:
 | 
			
		||||
      name: source_map_stack_trace
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.0-nullsafety.3"
 | 
			
		||||
    version: "2.1.0"
 | 
			
		||||
  source_maps:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: source_maps
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.10.10-nullsafety.2"
 | 
			
		||||
    version: "0.10.10"
 | 
			
		||||
  source_span:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: source_span
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.8.0-nullsafety.2"
 | 
			
		||||
    version: "1.8.0"
 | 
			
		||||
  sqflite:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -1081,21 +1076,21 @@ packages:
 | 
			
		||||
      name: stack_trace
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.10.0-nullsafety.1"
 | 
			
		||||
    version: "1.10.0"
 | 
			
		||||
  stream_channel:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: stream_channel
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.0-nullsafety.1"
 | 
			
		||||
    version: "2.1.0"
 | 
			
		||||
  string_scanner:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: string_scanner
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.1.0-nullsafety.1"
 | 
			
		||||
    version: "1.1.0"
 | 
			
		||||
  swipe_to_action:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -1116,28 +1111,28 @@ packages:
 | 
			
		||||
      name: term_glyph
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.2.0-nullsafety.1"
 | 
			
		||||
    version: "1.2.0"
 | 
			
		||||
  test:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: test
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.16.0-nullsafety.5"
 | 
			
		||||
    version: "1.16.5"
 | 
			
		||||
  test_api:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: test_api
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.2.19-nullsafety.2"
 | 
			
		||||
    version: "0.2.19"
 | 
			
		||||
  test_core:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: test_core
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.3.12-nullsafety.5"
 | 
			
		||||
    version: "0.3.15"
 | 
			
		||||
  timezone:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -1145,20 +1140,13 @@ packages:
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.5.9"
 | 
			
		||||
  tuple:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: tuple
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.3"
 | 
			
		||||
  typed_data:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: typed_data
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.3.0-nullsafety.3"
 | 
			
		||||
    version: "1.3.0"
 | 
			
		||||
  unifiedpush:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -1251,7 +1239,7 @@ packages:
 | 
			
		||||
      name: vector_math
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.0-nullsafety.3"
 | 
			
		||||
    version: "2.1.0"
 | 
			
		||||
  vm_service:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -1316,5 +1304,5 @@ packages:
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.1.2"
 | 
			
		||||
sdks:
 | 
			
		||||
  dart: ">=2.10.2 <2.11.0"
 | 
			
		||||
  flutter: ">=1.22.2 <2.0.0"
 | 
			
		||||
  dart: ">=2.12.0-0.0 <3.0.0"
 | 
			
		||||
  flutter: ">=1.24.0-10.1.pre"
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,9 @@ dependencies:
 | 
			
		||||
  mime_type: ^0.3.2
 | 
			
		||||
  flushbar: ^1.10.4
 | 
			
		||||
  adaptive_dialog: ^0.9.3
 | 
			
		||||
  flutter_matrix_html: ^0.2.0
 | 
			
		||||
  flutter_matrix_html:
 | 
			
		||||
    git:
 | 
			
		||||
      url: https://github.com/ChristianPauly/flutter_matrix_html.git
 | 
			
		||||
  moor: ^3.4.0
 | 
			
		||||
  sqlite3_flutter_libs: ^0.3.0
 | 
			
		||||
  sqlite3: ^0.1.8
 | 
			
		||||
@ -73,7 +75,7 @@ dependencies:
 | 
			
		||||
  sentry: ">=3.0.0 <4.0.0"
 | 
			
		||||
  scroll_to_index: ^1.0.6
 | 
			
		||||
  swipe_to_action: ^0.1.0
 | 
			
		||||
  flutter_svg: 0.19.1 # Because fluffychat depends on flutter_svg >=0.19.2 which requires Flutter SDK version >=1.24.0-6.0.pre <2.0.0, version solving failed.
 | 
			
		||||
  flutter_svg: ^0.19.3
 | 
			
		||||
  flutter_cache_manager: ^2.1.1
 | 
			
		||||
  open_noti_settings: ^0.0.4
 | 
			
		||||
  emoji_picker: ^0.1.0
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,4 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
flutter channel stable
 | 
			
		||||
flutter upgrade
 | 
			
		||||
flutter pub get
 | 
			
		||||
flutter build apk --release
 | 
			
		||||
mkdir -p build/android
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,2 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
flutter channel stable
 | 
			
		||||
flutter upgrade
 | 
			
		||||
flutter build apk --debug -v
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,4 @@
 | 
			
		||||
#!/bin/sh -ve
 | 
			
		||||
flutter channel stable
 | 
			
		||||
flutter upgrade
 | 
			
		||||
flutter clean
 | 
			
		||||
flutter pub get
 | 
			
		||||
cd ios
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
#!/bin/sh -ve
 | 
			
		||||
flutter channel master && flutter upgrade
 | 
			
		||||
flutter config --enable-linux-desktop
 | 
			
		||||
flutter clean
 | 
			
		||||
flutter pub get
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,4 @@
 | 
			
		||||
#!/bin/sh -ve
 | 
			
		||||
flutter channel dev
 | 
			
		||||
flutter upgrade
 | 
			
		||||
flutter config --enable-macos-desktop
 | 
			
		||||
flutter clean
 | 
			
		||||
flutter pub get
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,4 @@
 | 
			
		||||
#!/bin/sh -ve
 | 
			
		||||
#flutter channel beta
 | 
			
		||||
#flutter upgrade
 | 
			
		||||
flutter config --enable-web
 | 
			
		||||
flutter clean
 | 
			
		||||
flutter pub get
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,4 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
flutter channel stable
 | 
			
		||||
flutter upgrade
 | 
			
		||||
flutter pub get
 | 
			
		||||
flutter build appbundle --target-platform android-arm,android-arm64,android-x64
 | 
			
		||||
mkdir -p build/android
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user