mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-04 06:17:26 +01:00 
			
		
		
		
	Merge branch 'krille/use-hive-collections' into 'main'
refactor: Switch to Hive Collections DB See merge request famedly/fluffychat!896
This commit is contained in:
		
						commit
						8e2acb2819
					
				@ -461,11 +461,11 @@ class ChatController extends State<Chat> {
 | 
			
		||||
    if (selectedEvents.length == 1) {
 | 
			
		||||
      return selectedEvents.first
 | 
			
		||||
          .getDisplayEvent(timeline!)
 | 
			
		||||
          .getLocalizedBody(MatrixLocals(L10n.of(context)!));
 | 
			
		||||
          .calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)!));
 | 
			
		||||
    }
 | 
			
		||||
    for (final event in selectedEvents) {
 | 
			
		||||
      if (copyString.isNotEmpty) copyString += '\n\n';
 | 
			
		||||
      copyString += event.getDisplayEvent(timeline!).getLocalizedBody(
 | 
			
		||||
      copyString += event.getDisplayEvent(timeline!).calcLocalizedBodyFallback(
 | 
			
		||||
          MatrixLocals(L10n.of(context)!),
 | 
			
		||||
          withSenderNamePrefix: true);
 | 
			
		||||
    }
 | 
			
		||||
@ -773,7 +773,7 @@ class ChatController extends State<Chat> {
 | 
			
		||||
      editEvent = selectedEvents.first;
 | 
			
		||||
      inputText = sendController.text = editEvent!
 | 
			
		||||
          .getDisplayEvent(timeline!)
 | 
			
		||||
          .getLocalizedBody(MatrixLocals(L10n.of(context)!),
 | 
			
		||||
          .calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)!),
 | 
			
		||||
              withSenderNamePrefix: false, hideReply: true);
 | 
			
		||||
      selectedEvents.clear();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -29,10 +29,11 @@ class ChatAppBarTitle extends StatelessWidget {
 | 
			
		||||
          ? () => showModalBottomSheet(
 | 
			
		||||
                context: context,
 | 
			
		||||
                builder: (c) => UserBottomSheet(
 | 
			
		||||
                  user: room.getUserByMXIDSync(directChatMatrixID),
 | 
			
		||||
                  user: room
 | 
			
		||||
                      .unsafeGetUserFromMemoryOrFallback(directChatMatrixID),
 | 
			
		||||
                  outerContext: context,
 | 
			
		||||
                  onMention: () => controller.sendController.text +=
 | 
			
		||||
                      '${room.getUserByMXIDSync(directChatMatrixID).mention} ',
 | 
			
		||||
                      '${room.unsafeGetUserFromMemoryOrFallback(directChatMatrixID).mention} ',
 | 
			
		||||
                ),
 | 
			
		||||
              )
 | 
			
		||||
          : () => VRouter.of(context).toSegments(['rooms', room.id, 'details']),
 | 
			
		||||
 | 
			
		||||
@ -293,14 +293,14 @@ class _ChatAccountPicker extends StatelessWidget {
 | 
			
		||||
    return Padding(
 | 
			
		||||
      padding: const EdgeInsets.all(8.0),
 | 
			
		||||
      child: FutureBuilder<Profile>(
 | 
			
		||||
        future: controller.sendingClient!.ownProfile,
 | 
			
		||||
        future: controller.sendingClient!.fetchOwnProfile(),
 | 
			
		||||
        builder: (context, snapshot) => PopupMenuButton<String>(
 | 
			
		||||
          onSelected: _popupMenuButtonSelected,
 | 
			
		||||
          itemBuilder: (BuildContext context) => clients
 | 
			
		||||
              .map((client) => PopupMenuItem<String>(
 | 
			
		||||
                    value: client!.userID,
 | 
			
		||||
                    child: FutureBuilder<Profile>(
 | 
			
		||||
                      future: client.ownProfile,
 | 
			
		||||
                      future: client.fetchOwnProfile(),
 | 
			
		||||
                      builder: (context, snapshot) => ListTile(
 | 
			
		||||
                        leading: Avatar(
 | 
			
		||||
                          mxContent: snapshot.data?.avatarUrl,
 | 
			
		||||
 | 
			
		||||
@ -350,12 +350,12 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                                                    builder: (c) =>
 | 
			
		||||
                                                                        UserBottomSheet(
 | 
			
		||||
                                                                      user: event
 | 
			
		||||
                                                                          .sender,
 | 
			
		||||
                                                                          .senderFromMemoryOrFallback,
 | 
			
		||||
                                                                      outerContext:
 | 
			
		||||
                                                                          context,
 | 
			
		||||
                                                                      onMention: () => controller
 | 
			
		||||
                                                                          .sendController
 | 
			
		||||
                                                                          .text += '${event.sender.mention} ',
 | 
			
		||||
                                                                          .text += '${event.senderFromMemoryOrFallback.mention} ',
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                                                  ),
 | 
			
		||||
                                                              unfold: controller
 | 
			
		||||
 | 
			
		||||
@ -48,12 +48,12 @@ class EventInfoDialog extends StatelessWidget {
 | 
			
		||||
        children: [
 | 
			
		||||
          ListTile(
 | 
			
		||||
            leading: Avatar(
 | 
			
		||||
              mxContent: event.sender.avatarUrl,
 | 
			
		||||
              name: event.sender.calcDisplayname(),
 | 
			
		||||
              mxContent: event.senderFromMemoryOrFallback.avatarUrl,
 | 
			
		||||
              name: event.senderFromMemoryOrFallback.calcDisplayname(),
 | 
			
		||||
            ),
 | 
			
		||||
            title: Text(L10n.of(context)!.sender),
 | 
			
		||||
            subtitle:
 | 
			
		||||
                Text('${event.sender.calcDisplayname()} [${event.senderId}]'),
 | 
			
		||||
            subtitle: Text(
 | 
			
		||||
                '${event.senderFromMemoryOrFallback.calcDisplayname()} [${event.senderId}]'),
 | 
			
		||||
          ),
 | 
			
		||||
          ListTile(
 | 
			
		||||
            title: Text(L10n.of(context)!.time),
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,7 @@ class Message extends StatelessWidget {
 | 
			
		||||
              EventTypes.Sticker,
 | 
			
		||||
              EventTypes.Encrypted,
 | 
			
		||||
            ].contains(nextEvent!.type)
 | 
			
		||||
        ? nextEvent!.sender.id == event.sender.id && !displayTime
 | 
			
		||||
        ? nextEvent!.senderId == event.senderId && !displayTime
 | 
			
		||||
        : false;
 | 
			
		||||
    final textColor = ownMessage
 | 
			
		||||
        ? Theme.of(context).colorScheme.onPrimary
 | 
			
		||||
@ -125,11 +125,16 @@ class Message extends StatelessWidget {
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ))
 | 
			
		||||
          : Avatar(
 | 
			
		||||
              mxContent: event.sender.avatarUrl,
 | 
			
		||||
              name: event.sender.calcDisplayname(),
 | 
			
		||||
              onTap: () => onAvatarTab!(event),
 | 
			
		||||
            ),
 | 
			
		||||
          : FutureBuilder<User?>(
 | 
			
		||||
              future: event.fetchSenderUser(),
 | 
			
		||||
              builder: (context, snapshot) {
 | 
			
		||||
                final user = snapshot.data ?? event.senderFromMemoryOrFallback;
 | 
			
		||||
                return Avatar(
 | 
			
		||||
                  mxContent: user.avatarUrl,
 | 
			
		||||
                  name: user.calcDisplayname(),
 | 
			
		||||
                  onTap: () => onAvatarTab!(event),
 | 
			
		||||
                );
 | 
			
		||||
              }),
 | 
			
		||||
      Expanded(
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
@ -140,14 +145,22 @@ class Message extends StatelessWidget {
 | 
			
		||||
                padding: const EdgeInsets.only(left: 8.0, bottom: 4),
 | 
			
		||||
                child: ownMessage || event.room.isDirectChat
 | 
			
		||||
                    ? const SizedBox(height: 12)
 | 
			
		||||
                    : Text(
 | 
			
		||||
                        event.sender.calcDisplayname(),
 | 
			
		||||
                        style: TextStyle(
 | 
			
		||||
                          fontSize: 12,
 | 
			
		||||
                          fontWeight: FontWeight.bold,
 | 
			
		||||
                          color: event.sender.calcDisplayname().color,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    : FutureBuilder<User?>(
 | 
			
		||||
                        future: event.fetchSenderUser(),
 | 
			
		||||
                        builder: (context, snapshot) {
 | 
			
		||||
                          final displayname =
 | 
			
		||||
                              snapshot.data?.calcDisplayname() ??
 | 
			
		||||
                                  event.senderFromMemoryOrFallback
 | 
			
		||||
                                      .calcDisplayname();
 | 
			
		||||
                          return Text(
 | 
			
		||||
                            displayname,
 | 
			
		||||
                            style: TextStyle(
 | 
			
		||||
                              fontSize: 12,
 | 
			
		||||
                              fontWeight: FontWeight.bold,
 | 
			
		||||
                              color: displayname.color,
 | 
			
		||||
                            ),
 | 
			
		||||
                          );
 | 
			
		||||
                        }),
 | 
			
		||||
              ),
 | 
			
		||||
            Container(
 | 
			
		||||
              alignment: alignment,
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
          content: Text(
 | 
			
		||||
        event.type == EventTypes.Encrypted
 | 
			
		||||
            ? L10n.of(context)!.needPantalaimonWarning
 | 
			
		||||
            : event.getLocalizedBody(
 | 
			
		||||
            : event.calcLocalizedBodyFallback(
 | 
			
		||||
                MatrixLocals(L10n.of(context)!),
 | 
			
		||||
              ),
 | 
			
		||||
      )));
 | 
			
		||||
@ -172,48 +172,73 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
          textmessage:
 | 
			
		||||
          default:
 | 
			
		||||
            if (event.redacted) {
 | 
			
		||||
              return _ButtonContent(
 | 
			
		||||
                label: L10n.of(context)!
 | 
			
		||||
                    .redactedAnEvent(event.sender.calcDisplayname()),
 | 
			
		||||
                icon: const Icon(Icons.delete_outlined),
 | 
			
		||||
                textColor: buttonTextColor,
 | 
			
		||||
                onPressed: () => onInfoTab!(event),
 | 
			
		||||
              );
 | 
			
		||||
              return FutureBuilder<User?>(
 | 
			
		||||
                  future: event.fetchSenderUser(),
 | 
			
		||||
                  builder: (context, snapshot) {
 | 
			
		||||
                    return _ButtonContent(
 | 
			
		||||
                      label: L10n.of(context)!.redactedAnEvent(snapshot.data
 | 
			
		||||
                              ?.calcDisplayname() ??
 | 
			
		||||
                          event.senderFromMemoryOrFallback.calcDisplayname()),
 | 
			
		||||
                      icon: const Icon(Icons.delete_outlined),
 | 
			
		||||
                      textColor: buttonTextColor,
 | 
			
		||||
                      onPressed: () => onInfoTab!(event),
 | 
			
		||||
                    );
 | 
			
		||||
                  });
 | 
			
		||||
            }
 | 
			
		||||
            final bigEmotes = event.onlyEmotes &&
 | 
			
		||||
                event.numberEmotes > 0 &&
 | 
			
		||||
                event.numberEmotes <= 10;
 | 
			
		||||
            return LinkText(
 | 
			
		||||
              text: event.getLocalizedBody(MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                  hideReply: true),
 | 
			
		||||
              textStyle: TextStyle(
 | 
			
		||||
                color: textColor,
 | 
			
		||||
                fontSize: bigEmotes ? fontSize * 3 : fontSize,
 | 
			
		||||
                decoration: event.redacted ? TextDecoration.lineThrough : null,
 | 
			
		||||
              ),
 | 
			
		||||
              linkStyle: TextStyle(
 | 
			
		||||
                color: textColor.withAlpha(150),
 | 
			
		||||
                fontSize: bigEmotes ? fontSize * 3 : fontSize,
 | 
			
		||||
                decoration: TextDecoration.underline,
 | 
			
		||||
              ),
 | 
			
		||||
              onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
 | 
			
		||||
            );
 | 
			
		||||
            return FutureBuilder<String>(
 | 
			
		||||
                future: event.calcLocalizedBody(MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                    hideReply: true),
 | 
			
		||||
                builder: (context, snapshot) {
 | 
			
		||||
                  return LinkText(
 | 
			
		||||
                    text: snapshot.data ??
 | 
			
		||||
                        event.calcLocalizedBodyFallback(
 | 
			
		||||
                            MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                            hideReply: true),
 | 
			
		||||
                    textStyle: TextStyle(
 | 
			
		||||
                      color: textColor,
 | 
			
		||||
                      fontSize: bigEmotes ? fontSize * 3 : fontSize,
 | 
			
		||||
                      decoration:
 | 
			
		||||
                          event.redacted ? TextDecoration.lineThrough : null,
 | 
			
		||||
                    ),
 | 
			
		||||
                    linkStyle: TextStyle(
 | 
			
		||||
                      color: textColor.withAlpha(150),
 | 
			
		||||
                      fontSize: bigEmotes ? fontSize * 3 : fontSize,
 | 
			
		||||
                      decoration: TextDecoration.underline,
 | 
			
		||||
                    ),
 | 
			
		||||
                    onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
 | 
			
		||||
                  );
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
      case EventTypes.CallInvite:
 | 
			
		||||
        return _ButtonContent(
 | 
			
		||||
          label: L10n.of(context)!.startedACall(event.sender.calcDisplayname()),
 | 
			
		||||
          icon: const Icon(Icons.phone_outlined),
 | 
			
		||||
          textColor: buttonTextColor,
 | 
			
		||||
          onPressed: () => onInfoTab!(event),
 | 
			
		||||
        );
 | 
			
		||||
        return FutureBuilder<User?>(
 | 
			
		||||
            future: event.fetchSenderUser(),
 | 
			
		||||
            builder: (context, snapshot) {
 | 
			
		||||
              return _ButtonContent(
 | 
			
		||||
                label: L10n.of(context)!.startedACall(
 | 
			
		||||
                    snapshot.data?.calcDisplayname() ??
 | 
			
		||||
                        event.senderFromMemoryOrFallback.calcDisplayname()),
 | 
			
		||||
                icon: const Icon(Icons.phone_outlined),
 | 
			
		||||
                textColor: buttonTextColor,
 | 
			
		||||
                onPressed: () => onInfoTab!(event),
 | 
			
		||||
              );
 | 
			
		||||
            });
 | 
			
		||||
      default:
 | 
			
		||||
        return _ButtonContent(
 | 
			
		||||
          label: L10n.of(context)!
 | 
			
		||||
              .userSentUnknownEvent(event.sender.calcDisplayname(), event.type),
 | 
			
		||||
          icon: const Icon(Icons.info_outlined),
 | 
			
		||||
          textColor: buttonTextColor,
 | 
			
		||||
          onPressed: () => onInfoTab!(event),
 | 
			
		||||
        );
 | 
			
		||||
        return FutureBuilder<User?>(
 | 
			
		||||
            future: event.fetchSenderUser(),
 | 
			
		||||
            builder: (context, snapshot) {
 | 
			
		||||
              return _ButtonContent(
 | 
			
		||||
                label: L10n.of(context)!.userSentUnknownEvent(
 | 
			
		||||
                    snapshot.data?.calcDisplayname() ??
 | 
			
		||||
                        event.senderFromMemoryOrFallback.calcDisplayname(),
 | 
			
		||||
                    event.type),
 | 
			
		||||
                icon: const Icon(Icons.info_outlined),
 | 
			
		||||
                textColor: buttonTextColor,
 | 
			
		||||
                onPressed: () => onInfoTab!(event),
 | 
			
		||||
              );
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ class MessageReactions extends StatelessWidget {
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
        reactionMap[key]!.count++;
 | 
			
		||||
        reactionMap[key]!.reactors!.add(e.sender);
 | 
			
		||||
        reactionMap[key]!.reactors!.add(e.senderFromMemoryOrFallback);
 | 
			
		||||
        reactionMap[key]!.reacted |= e.senderId == e.room.client.userID;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,7 @@ class ReplyContent extends StatelessWidget {
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      replyBody = Text(
 | 
			
		||||
        displayEvent.getLocalizedBody(
 | 
			
		||||
        displayEvent.calcLocalizedBodyFallback(
 | 
			
		||||
          MatrixLocals(L10n.of(context)!),
 | 
			
		||||
          withSenderNamePrefix: false,
 | 
			
		||||
          hideReply: true,
 | 
			
		||||
@ -83,18 +83,25 @@ class ReplyContent extends StatelessWidget {
 | 
			
		||||
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
            mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
            children: <Widget>[
 | 
			
		||||
              Text(
 | 
			
		||||
                displayEvent.sender.calcDisplayname() + ':',
 | 
			
		||||
                maxLines: 1,
 | 
			
		||||
                overflow: TextOverflow.ellipsis,
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
                  fontWeight: FontWeight.bold,
 | 
			
		||||
                  color: ownMessage
 | 
			
		||||
                      ? Theme.of(context).colorScheme.onPrimary
 | 
			
		||||
                      : Theme.of(context).colorScheme.onBackground,
 | 
			
		||||
                  fontSize: fontSize,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              FutureBuilder<User?>(
 | 
			
		||||
                  future: displayEvent.fetchSenderUser(),
 | 
			
		||||
                  builder: (context, snapshot) {
 | 
			
		||||
                    return Text(
 | 
			
		||||
                      (snapshot.data?.calcDisplayname() ??
 | 
			
		||||
                              displayEvent.senderFromMemoryOrFallback
 | 
			
		||||
                                  .calcDisplayname()) +
 | 
			
		||||
                          ':',
 | 
			
		||||
                      maxLines: 1,
 | 
			
		||||
                      overflow: TextOverflow.ellipsis,
 | 
			
		||||
                      style: TextStyle(
 | 
			
		||||
                        fontWeight: FontWeight.bold,
 | 
			
		||||
                        color: ownMessage
 | 
			
		||||
                            ? Theme.of(context).colorScheme.onPrimary
 | 
			
		||||
                            : Theme.of(context).colorScheme.onBackground,
 | 
			
		||||
                        fontSize: fontSize,
 | 
			
		||||
                      ),
 | 
			
		||||
                    );
 | 
			
		||||
                  }),
 | 
			
		||||
              replyBody,
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
@ -39,16 +39,24 @@ class StateMessage extends StatelessWidget {
 | 
			
		||||
            child: Column(
 | 
			
		||||
              mainAxisSize: MainAxisSize.min,
 | 
			
		||||
              children: [
 | 
			
		||||
                Text(
 | 
			
		||||
                  event.getLocalizedBody(MatrixLocals(L10n.of(context)!)),
 | 
			
		||||
                  textAlign: TextAlign.center,
 | 
			
		||||
                  style: TextStyle(
 | 
			
		||||
                    fontSize: 14 * AppConfig.fontSizeFactor,
 | 
			
		||||
                    color: Theme.of(context).textTheme.bodyText2!.color,
 | 
			
		||||
                    decoration:
 | 
			
		||||
                        event.redacted ? TextDecoration.lineThrough : null,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                FutureBuilder<String>(
 | 
			
		||||
                    future: event
 | 
			
		||||
                        .calcLocalizedBody(MatrixLocals(L10n.of(context)!)),
 | 
			
		||||
                    builder: (context, snapshot) {
 | 
			
		||||
                      return Text(
 | 
			
		||||
                        snapshot.data ??
 | 
			
		||||
                            event.calcLocalizedBodyFallback(
 | 
			
		||||
                                MatrixLocals(L10n.of(context)!)),
 | 
			
		||||
                        textAlign: TextAlign.center,
 | 
			
		||||
                        style: TextStyle(
 | 
			
		||||
                          fontSize: 14 * AppConfig.fontSizeFactor,
 | 
			
		||||
                          color: Theme.of(context).textTheme.bodyText2!.color,
 | 
			
		||||
                          decoration: event.redacted
 | 
			
		||||
                              ? TextDecoration.lineThrough
 | 
			
		||||
                              : null,
 | 
			
		||||
                        ),
 | 
			
		||||
                      );
 | 
			
		||||
                    }),
 | 
			
		||||
                if (counter != 0)
 | 
			
		||||
                  Text(
 | 
			
		||||
                    L10n.of(context)!.moreEvents(counter),
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ class PinnedEvents extends StatelessWidget {
 | 
			
		||||
            actions: events
 | 
			
		||||
                .map((event) => SheetAction(
 | 
			
		||||
                      key: event?.eventId ?? '',
 | 
			
		||||
                      label: event?.getLocalizedBody(
 | 
			
		||||
                      label: event?.calcLocalizedBodyFallback(
 | 
			
		||||
                            MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                            withSenderNamePrefix: true,
 | 
			
		||||
                            hideReply: true,
 | 
			
		||||
@ -90,32 +90,41 @@ class PinnedEvents extends StatelessWidget {
 | 
			
		||||
                  Expanded(
 | 
			
		||||
                    child: Padding(
 | 
			
		||||
                      padding: const EdgeInsets.symmetric(horizontal: 4.0),
 | 
			
		||||
                      child: LinkText(
 | 
			
		||||
                        text: event.getLocalizedBody(
 | 
			
		||||
                          MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                          withSenderNamePrefix: true,
 | 
			
		||||
                          hideReply: true,
 | 
			
		||||
                        ),
 | 
			
		||||
                        maxLines: 2,
 | 
			
		||||
                        textStyle: TextStyle(
 | 
			
		||||
                          overflow: TextOverflow.ellipsis,
 | 
			
		||||
                          fontSize: fontSize,
 | 
			
		||||
                          decoration: event.redacted
 | 
			
		||||
                              ? TextDecoration.lineThrough
 | 
			
		||||
                              : null,
 | 
			
		||||
                        ),
 | 
			
		||||
                        linkStyle: TextStyle(
 | 
			
		||||
                          color: Theme.of(context)
 | 
			
		||||
                              .textTheme
 | 
			
		||||
                              .bodyText1
 | 
			
		||||
                              ?.color
 | 
			
		||||
                              ?.withAlpha(150),
 | 
			
		||||
                          fontSize: fontSize,
 | 
			
		||||
                          decoration: TextDecoration.underline,
 | 
			
		||||
                        ),
 | 
			
		||||
                        onLinkTap: (url) =>
 | 
			
		||||
                            UrlLauncher(context, url).launchUrl(),
 | 
			
		||||
                      ),
 | 
			
		||||
                      child: FutureBuilder<String>(
 | 
			
		||||
                          future: event.calcLocalizedBody(
 | 
			
		||||
                            MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                            withSenderNamePrefix: true,
 | 
			
		||||
                            hideReply: true,
 | 
			
		||||
                          ),
 | 
			
		||||
                          builder: (context, snapshot) {
 | 
			
		||||
                            return LinkText(
 | 
			
		||||
                              text: snapshot.data ??
 | 
			
		||||
                                  event.calcLocalizedBodyFallback(
 | 
			
		||||
                                    MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                                    withSenderNamePrefix: true,
 | 
			
		||||
                                    hideReply: true,
 | 
			
		||||
                                  ),
 | 
			
		||||
                              maxLines: 2,
 | 
			
		||||
                              textStyle: TextStyle(
 | 
			
		||||
                                overflow: TextOverflow.ellipsis,
 | 
			
		||||
                                fontSize: fontSize,
 | 
			
		||||
                                decoration: event.redacted
 | 
			
		||||
                                    ? TextDecoration.lineThrough
 | 
			
		||||
                                    : null,
 | 
			
		||||
                              ),
 | 
			
		||||
                              linkStyle: TextStyle(
 | 
			
		||||
                                color: Theme.of(context)
 | 
			
		||||
                                    .textTheme
 | 
			
		||||
                                    .bodyText1
 | 
			
		||||
                                    ?.color
 | 
			
		||||
                                    ?.withAlpha(150),
 | 
			
		||||
                                fontSize: fontSize,
 | 
			
		||||
                                decoration: TextDecoration.underline,
 | 
			
		||||
                              ),
 | 
			
		||||
                              onLinkTap: (url) =>
 | 
			
		||||
                                  UrlLauncher(context, url).launchUrl(),
 | 
			
		||||
                            );
 | 
			
		||||
                          }),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,7 @@ class _EditContent extends StatelessWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final event = this.event;
 | 
			
		||||
    if (event == null) {
 | 
			
		||||
      return Container();
 | 
			
		||||
    }
 | 
			
		||||
@ -60,19 +61,27 @@ class _EditContent extends StatelessWidget {
 | 
			
		||||
          color: Theme.of(context).primaryColor,
 | 
			
		||||
        ),
 | 
			
		||||
        Container(width: 15.0),
 | 
			
		||||
        Text(
 | 
			
		||||
          event?.getLocalizedBody(
 | 
			
		||||
                MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                withSenderNamePrefix: false,
 | 
			
		||||
                hideReply: true,
 | 
			
		||||
              ) ??
 | 
			
		||||
              '',
 | 
			
		||||
          overflow: TextOverflow.ellipsis,
 | 
			
		||||
          maxLines: 1,
 | 
			
		||||
          style: TextStyle(
 | 
			
		||||
            color: Theme.of(context).textTheme.bodyText2!.color,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        FutureBuilder<String>(
 | 
			
		||||
            future: event.calcLocalizedBody(
 | 
			
		||||
              MatrixLocals(L10n.of(context)!),
 | 
			
		||||
              withSenderNamePrefix: false,
 | 
			
		||||
              hideReply: true,
 | 
			
		||||
            ),
 | 
			
		||||
            builder: (context, snapshot) {
 | 
			
		||||
              return Text(
 | 
			
		||||
                snapshot.data ??
 | 
			
		||||
                    event.calcLocalizedBodyFallback(
 | 
			
		||||
                      MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                      withSenderNamePrefix: false,
 | 
			
		||||
                      hideReply: true,
 | 
			
		||||
                    ),
 | 
			
		||||
                overflow: TextOverflow.ellipsis,
 | 
			
		||||
                maxLines: 1,
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
                  color: Theme.of(context).textTheme.bodyText2!.color,
 | 
			
		||||
                ),
 | 
			
		||||
              );
 | 
			
		||||
            }),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -94,15 +94,18 @@ class ChatEncryptionSettingsView extends StatelessWidget {
 | 
			
		||||
                                child: ListTile(
 | 
			
		||||
                                  leading: Avatar(
 | 
			
		||||
                                    mxContent: room
 | 
			
		||||
                                        .getUserByMXIDSync(deviceKeys[i].userId)
 | 
			
		||||
                                        .unsafeGetUserFromMemoryOrFallback(
 | 
			
		||||
                                            deviceKeys[i].userId)
 | 
			
		||||
                                        .avatarUrl,
 | 
			
		||||
                                    name: room
 | 
			
		||||
                                        .getUserByMXIDSync(deviceKeys[i].userId)
 | 
			
		||||
                                        .unsafeGetUserFromMemoryOrFallback(
 | 
			
		||||
                                            deviceKeys[i].userId)
 | 
			
		||||
                                        .calcDisplayname(),
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  title: Text(
 | 
			
		||||
                                    room
 | 
			
		||||
                                        .getUserByMXIDSync(deviceKeys[i].userId)
 | 
			
		||||
                                        .unsafeGetUserFromMemoryOrFallback(
 | 
			
		||||
                                            deviceKeys[i].userId)
 | 
			
		||||
                                        .calcDisplayname(),
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  subtitle: Text(
 | 
			
		||||
 | 
			
		||||
@ -262,32 +262,47 @@ class ChatListItem extends StatelessWidget {
 | 
			
		||||
                      ),
 | 
			
		||||
                      softWrap: false,
 | 
			
		||||
                    )
 | 
			
		||||
                  : Text(
 | 
			
		||||
                      room.membership == Membership.invite
 | 
			
		||||
                          ? L10n.of(context)!.youAreInvitedToThisChat
 | 
			
		||||
                          : room.lastEvent?.getLocalizedBody(
 | 
			
		||||
                                MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                                hideReply: true,
 | 
			
		||||
                                hideEdit: true,
 | 
			
		||||
                                plaintextBody: true,
 | 
			
		||||
                                removeMarkdown: true,
 | 
			
		||||
                                withSenderNamePrefix: !room.isDirectChat ||
 | 
			
		||||
                                    room.directChatMatrixID !=
 | 
			
		||||
                                        room.lastEvent?.senderId,
 | 
			
		||||
                              ) ??
 | 
			
		||||
                              L10n.of(context)!.emptyChat,
 | 
			
		||||
                      softWrap: false,
 | 
			
		||||
                      maxLines: 1,
 | 
			
		||||
                      overflow: TextOverflow.ellipsis,
 | 
			
		||||
                      style: TextStyle(
 | 
			
		||||
                        color: unread
 | 
			
		||||
                            ? Theme.of(context).colorScheme.secondary
 | 
			
		||||
                            : Theme.of(context).textTheme.bodyText2!.color,
 | 
			
		||||
                        decoration: room.lastEvent?.redacted == true
 | 
			
		||||
                            ? TextDecoration.lineThrough
 | 
			
		||||
                            : null,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  : FutureBuilder<String>(
 | 
			
		||||
                      future: room.lastEvent?.calcLocalizedBody(
 | 
			
		||||
                            MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                            hideReply: true,
 | 
			
		||||
                            hideEdit: true,
 | 
			
		||||
                            plaintextBody: true,
 | 
			
		||||
                            removeMarkdown: true,
 | 
			
		||||
                            withSenderNamePrefix: !room.isDirectChat ||
 | 
			
		||||
                                room.directChatMatrixID !=
 | 
			
		||||
                                    room.lastEvent?.senderId,
 | 
			
		||||
                          ) ??
 | 
			
		||||
                          Future.value(L10n.of(context)!.emptyChat),
 | 
			
		||||
                      builder: (context, snapshot) {
 | 
			
		||||
                        return Text(
 | 
			
		||||
                          room.membership == Membership.invite
 | 
			
		||||
                              ? L10n.of(context)!.youAreInvitedToThisChat
 | 
			
		||||
                              : snapshot.data ??
 | 
			
		||||
                                  room.lastEvent?.calcLocalizedBodyFallback(
 | 
			
		||||
                                    MatrixLocals(L10n.of(context)!),
 | 
			
		||||
                                    hideReply: true,
 | 
			
		||||
                                    hideEdit: true,
 | 
			
		||||
                                    plaintextBody: true,
 | 
			
		||||
                                    removeMarkdown: true,
 | 
			
		||||
                                    withSenderNamePrefix: !room.isDirectChat ||
 | 
			
		||||
                                        room.directChatMatrixID !=
 | 
			
		||||
                                            room.lastEvent?.senderId,
 | 
			
		||||
                                  ) ??
 | 
			
		||||
                                  L10n.of(context)!.emptyChat,
 | 
			
		||||
                          softWrap: false,
 | 
			
		||||
                          maxLines: 1,
 | 
			
		||||
                          overflow: TextOverflow.ellipsis,
 | 
			
		||||
                          style: TextStyle(
 | 
			
		||||
                            color: unread
 | 
			
		||||
                                ? Theme.of(context).colorScheme.secondary
 | 
			
		||||
                                : Theme.of(context).textTheme.bodyText2!.color,
 | 
			
		||||
                            decoration: room.lastEvent?.redacted == true
 | 
			
		||||
                                ? TextDecoration.lineThrough
 | 
			
		||||
                                : null,
 | 
			
		||||
                          ),
 | 
			
		||||
                        );
 | 
			
		||||
                      }),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 8),
 | 
			
		||||
            AnimatedContainer(
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ class ClientChooserButton extends StatelessWidget {
 | 
			
		||||
              (client) => PopupMenuItem(
 | 
			
		||||
                value: client,
 | 
			
		||||
                child: FutureBuilder<Profile>(
 | 
			
		||||
                  future: client!.ownProfile,
 | 
			
		||||
                  future: client!.fetchOwnProfile(),
 | 
			
		||||
                  builder: (context, snapshot) => Row(
 | 
			
		||||
                    children: [
 | 
			
		||||
                      Avatar(
 | 
			
		||||
@ -90,7 +90,7 @@ class ClientChooserButton extends StatelessWidget {
 | 
			
		||||
    matrix.accountBundles.forEach((key, value) => clientCount += value.length);
 | 
			
		||||
    return Center(
 | 
			
		||||
      child: FutureBuilder<Profile>(
 | 
			
		||||
        future: matrix.client.ownProfile,
 | 
			
		||||
        future: matrix.client.fetchOwnProfile(),
 | 
			
		||||
        builder: (context, snapshot) => Stack(
 | 
			
		||||
          alignment: Alignment.center,
 | 
			
		||||
          children: [
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,7 @@ class InvitationSelectionController extends State<InvitationSelection> {
 | 
			
		||||
    final participantsIds = participants.map((p) => p.stateKey).toList();
 | 
			
		||||
    final contacts = client.rooms
 | 
			
		||||
        .where((r) => r.isDirectChat)
 | 
			
		||||
        .map((r) => r.getUserByMXIDSync(r.directChatMatrixID!))
 | 
			
		||||
        .map((r) => r.unsafeGetUserFromMemoryOrFallback(r.directChatMatrixID!))
 | 
			
		||||
        .toList()
 | 
			
		||||
      ..removeWhere((u) => participantsIds.contains(u.stateKey));
 | 
			
		||||
    contacts.sort(
 | 
			
		||||
 | 
			
		||||
@ -111,7 +111,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
    if (directChatId != null) {
 | 
			
		||||
      user = widget.request.client
 | 
			
		||||
          .getRoomById(directChatId)!
 | 
			
		||||
          .getUserByMXIDSync(widget.request.userId);
 | 
			
		||||
          .unsafeGetUserFromMemoryOrFallback(widget.request.userId);
 | 
			
		||||
    }
 | 
			
		||||
    final displayName =
 | 
			
		||||
        user?.calcDisplayname() ?? widget.request.userId.localpart!;
 | 
			
		||||
 | 
			
		||||
@ -46,13 +46,11 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
    }).then((QueryPublicRoomsResponse res) {
 | 
			
		||||
      final genericSearchTerm = controller.genericSearchTerm;
 | 
			
		||||
      if (genericSearchTerm != null &&
 | 
			
		||||
          !res.chunk.any((room) =>
 | 
			
		||||
              (room.aliases?.contains(controller.genericSearchTerm) ?? false) ||
 | 
			
		||||
              room.canonicalAlias == controller.genericSearchTerm)) {
 | 
			
		||||
          !res.chunk.any(
 | 
			
		||||
              (room) => room.canonicalAlias == controller.genericSearchTerm)) {
 | 
			
		||||
        // we have to tack on the original alias
 | 
			
		||||
        res.chunk.add(
 | 
			
		||||
          PublicRoomsChunk(
 | 
			
		||||
            aliases: [genericSearchTerm],
 | 
			
		||||
            name: genericSearchTerm,
 | 
			
		||||
            numJoinedMembers: 0,
 | 
			
		||||
            roomId: '!unknown',
 | 
			
		||||
 | 
			
		||||
@ -370,7 +370,7 @@ class StoryPageController extends State<StoryPage> {
 | 
			
		||||
      .client
 | 
			
		||||
      .getRoomById(roomId)
 | 
			
		||||
      ?.getState(EventTypes.RoomCreate)
 | 
			
		||||
      ?.sender
 | 
			
		||||
      ?.senderFromMemoryOrFallback
 | 
			
		||||
      .avatarUrl;
 | 
			
		||||
 | 
			
		||||
  String get title =>
 | 
			
		||||
@ -378,7 +378,7 @@ class StoryPageController extends State<StoryPage> {
 | 
			
		||||
          .client
 | 
			
		||||
          .getRoomById(roomId)
 | 
			
		||||
          ?.getState(EventTypes.RoomCreate)
 | 
			
		||||
          ?.sender
 | 
			
		||||
          ?.senderFromMemoryOrFallback
 | 
			
		||||
          .calcDisplayname() ??
 | 
			
		||||
      'Story not found';
 | 
			
		||||
 | 
			
		||||
@ -485,7 +485,8 @@ class StoryPageController extends State<StoryPage> {
 | 
			
		||||
      case PopupStoryAction.message:
 | 
			
		||||
        final roomIdResult = await showFutureLoadingDialog(
 | 
			
		||||
          context: context,
 | 
			
		||||
          future: () => currentEvent!.sender.startDirectChat(),
 | 
			
		||||
          future: () =>
 | 
			
		||||
              currentEvent!.senderFromMemoryOrFallback.startDirectChat(),
 | 
			
		||||
        );
 | 
			
		||||
        if (roomIdResult.error != null) return;
 | 
			
		||||
        VRouter.of(context).toSegments(['rooms', roomIdResult.result!]);
 | 
			
		||||
 | 
			
		||||
@ -8,10 +8,10 @@ import 'package:matrix/matrix.dart';
 | 
			
		||||
import 'package:path_provider/path_provider.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:fluffychat/utils/custom_image_resizer.dart';
 | 
			
		||||
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart';
 | 
			
		||||
import 'package:fluffychat/utils/platform_infos.dart';
 | 
			
		||||
import 'famedlysdk_store.dart';
 | 
			
		||||
import 'matrix_sdk_extensions.dart/fluffybox_database.dart';
 | 
			
		||||
import 'matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart';
 | 
			
		||||
 | 
			
		||||
abstract class ClientManager {
 | 
			
		||||
  static const String clientNamespace = 'im.fluffychat.store.clients';
 | 
			
		||||
@ -95,8 +95,8 @@ abstract class ClientManager {
 | 
			
		||||
          // To check which story room we can post in
 | 
			
		||||
          EventTypes.RoomPowerLevels,
 | 
			
		||||
        },
 | 
			
		||||
        databaseBuilder: FlutterFluffyBoxDatabase.databaseBuilder,
 | 
			
		||||
        legacyDatabaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder,
 | 
			
		||||
        databaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder,
 | 
			
		||||
        legacyDatabaseBuilder: FlutterFluffyBoxDatabase.databaseBuilder,
 | 
			
		||||
        supportedLoginTypes: {
 | 
			
		||||
          AuthenticationTypes.password,
 | 
			
		||||
          if (PlatformInfos.isMobile ||
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,8 @@ extension ClientStoriesExtension on Client {
 | 
			
		||||
 | 
			
		||||
  List<User> get contacts => rooms
 | 
			
		||||
      .where((room) => room.isDirectChat)
 | 
			
		||||
      .map((room) => room.getUserByMXIDSync(room.directChatMatrixID!))
 | 
			
		||||
      .map((room) =>
 | 
			
		||||
          room.unsafeGetUserFromMemoryOrFallback(room.directChatMatrixID!))
 | 
			
		||||
      .toList();
 | 
			
		||||
 | 
			
		||||
  List<Room> get storiesRooms => rooms
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ import 'package:path_provider/path_provider.dart';
 | 
			
		||||
import '../client_manager.dart';
 | 
			
		||||
import '../famedlysdk_store.dart';
 | 
			
		||||
 | 
			
		||||
// ignore: deprecated_member_use
 | 
			
		||||
class FlutterFluffyBoxDatabase extends FluffyBoxDatabase {
 | 
			
		||||
  FlutterFluffyBoxDatabase(
 | 
			
		||||
    String name,
 | 
			
		||||
@ -27,6 +28,7 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase {
 | 
			
		||||
 | 
			
		||||
  static const String _cipherStorageKey = 'database_encryption_key';
 | 
			
		||||
 | 
			
		||||
  // ignore: deprecated_member_use
 | 
			
		||||
  static Future<FluffyBoxDatabase> databaseBuilder(Client client) async {
 | 
			
		||||
    Logs().d('Open FluffyBox...');
 | 
			
		||||
    fluffybox.HiveAesCipher? hiverCipher;
 | 
			
		||||
@ -59,6 +61,7 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase {
 | 
			
		||||
      rethrow;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ignore: deprecated_member_use
 | 
			
		||||
    final db = FluffyBoxDatabase(
 | 
			
		||||
      'fluffybox_${client.clientName.replaceAll(' ', '_').toLowerCase()}',
 | 
			
		||||
      await _findDatabasePath(client),
 | 
			
		||||
 | 
			
		||||
@ -2,78 +2,108 @@ import 'dart:convert';
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
import 'dart:typed_data';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter/foundation.dart' hide Key;
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
 | 
			
		||||
import 'package:hive/hive.dart';
 | 
			
		||||
import 'package:hive_flutter/hive_flutter.dart';
 | 
			
		||||
import 'package:matrix/matrix.dart';
 | 
			
		||||
import 'package:path_provider/path_provider.dart';
 | 
			
		||||
 | 
			
		||||
import '../platform_infos.dart';
 | 
			
		||||
 | 
			
		||||
class FlutterMatrixHiveStore extends FamedlySdkHiveDatabase {
 | 
			
		||||
  FlutterMatrixHiveStore(String name, {HiveCipher? encryptionCipher})
 | 
			
		||||
      : super(
 | 
			
		||||
class FlutterHiveCollectionsDatabase extends HiveCollectionsDatabase {
 | 
			
		||||
  FlutterHiveCollectionsDatabase(
 | 
			
		||||
    String name,
 | 
			
		||||
    String path, {
 | 
			
		||||
    HiveCipher? key,
 | 
			
		||||
  }) : super(
 | 
			
		||||
          name,
 | 
			
		||||
          encryptionCipher: encryptionCipher,
 | 
			
		||||
          path,
 | 
			
		||||
          key: key,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
  static bool _hiveInitialized = false;
 | 
			
		||||
  static const String _hiveCipherStorageKey = 'hive_encryption_key';
 | 
			
		||||
  static const String _cipherStorageKey = 'database_encryption_key';
 | 
			
		||||
 | 
			
		||||
  static Future<FamedlySdkHiveDatabase> hiveDatabaseBuilder(
 | 
			
		||||
  static Future<FlutterHiveCollectionsDatabase> databaseBuilder(
 | 
			
		||||
      Client client) async {
 | 
			
		||||
    if (!kIsWeb && !_hiveInitialized) {
 | 
			
		||||
      _hiveInitialized = true;
 | 
			
		||||
    }
 | 
			
		||||
    HiveCipher? hiverCipher;
 | 
			
		||||
    Logs().d('Open Hive...');
 | 
			
		||||
    HiveAesCipher? hiverCipher;
 | 
			
		||||
    try {
 | 
			
		||||
      // Workaround for secure storage is calling Platform.operatingSystem on web
 | 
			
		||||
      if (kIsWeb || Platform.isLinux) throw MissingPluginException();
 | 
			
		||||
      if (kIsWeb) throw MissingPluginException();
 | 
			
		||||
 | 
			
		||||
      const secureStorage = FlutterSecureStorage();
 | 
			
		||||
      final containsEncryptionKey =
 | 
			
		||||
          await secureStorage.containsKey(key: _hiveCipherStorageKey);
 | 
			
		||||
          await secureStorage.containsKey(key: _cipherStorageKey);
 | 
			
		||||
      if (!containsEncryptionKey) {
 | 
			
		||||
        // do not try to create a buggy secure storage for new Linux users
 | 
			
		||||
        if (Platform.isLinux) throw MissingPluginException();
 | 
			
		||||
        final key = Hive.generateSecureKey();
 | 
			
		||||
        await secureStorage.write(
 | 
			
		||||
          key: _hiveCipherStorageKey,
 | 
			
		||||
          key: _cipherStorageKey,
 | 
			
		||||
          value: base64UrlEncode(key),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // workaround for if we just wrote to the key and it still doesn't exist
 | 
			
		||||
      final rawEncryptionKey =
 | 
			
		||||
          await secureStorage.read(key: _hiveCipherStorageKey);
 | 
			
		||||
      final rawEncryptionKey = await secureStorage.read(key: _cipherStorageKey);
 | 
			
		||||
      if (rawEncryptionKey == null) throw MissingPluginException();
 | 
			
		||||
 | 
			
		||||
      final encryptionKey = base64Url.decode(rawEncryptionKey);
 | 
			
		||||
      hiverCipher = HiveAesCipher(encryptionKey);
 | 
			
		||||
      hiverCipher = HiveAesCipher(base64Url.decode(rawEncryptionKey));
 | 
			
		||||
    } on MissingPluginException catch (_) {
 | 
			
		||||
      Logs().i('Hive encryption is not supported on this platform');
 | 
			
		||||
    } catch (_) {
 | 
			
		||||
      const FlutterSecureStorage().delete(key: _cipherStorageKey);
 | 
			
		||||
      rethrow;
 | 
			
		||||
    }
 | 
			
		||||
    final db = FlutterMatrixHiveStore(
 | 
			
		||||
      client.clientName,
 | 
			
		||||
      encryptionCipher: hiverCipher,
 | 
			
		||||
 | 
			
		||||
    final db = FlutterHiveCollectionsDatabase(
 | 
			
		||||
      'hive_collections_${client.clientName.replaceAll(' ', '_').toLowerCase()}',
 | 
			
		||||
      await _findDatabasePath(client),
 | 
			
		||||
      key: hiverCipher,
 | 
			
		||||
    );
 | 
			
		||||
    try {
 | 
			
		||||
      await db.open();
 | 
			
		||||
    } catch (e, s) {
 | 
			
		||||
      Logs().e('Unable to open Hive. Delete and try again...', e, s);
 | 
			
		||||
    } catch (_) {
 | 
			
		||||
      Logs().w('Unable to open Hive. Delete database and storage key...');
 | 
			
		||||
      const FlutterSecureStorage().delete(key: _cipherStorageKey);
 | 
			
		||||
      await db.clear();
 | 
			
		||||
      await db.open();
 | 
			
		||||
      rethrow;
 | 
			
		||||
    }
 | 
			
		||||
    Logs().d('Hive is ready');
 | 
			
		||||
    return db;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static Future<String> _findDatabasePath(Client client) async {
 | 
			
		||||
    String path = client.clientName;
 | 
			
		||||
    if (!kIsWeb) {
 | 
			
		||||
      Directory directory;
 | 
			
		||||
      try {
 | 
			
		||||
        if (Platform.isLinux) {
 | 
			
		||||
          directory = await getApplicationSupportDirectory();
 | 
			
		||||
        } else {
 | 
			
		||||
          directory = await getApplicationDocumentsDirectory();
 | 
			
		||||
        }
 | 
			
		||||
      } catch (_) {
 | 
			
		||||
        try {
 | 
			
		||||
          directory = await getLibraryDirectory();
 | 
			
		||||
        } catch (_) {
 | 
			
		||||
          directory = Directory.current;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      // do not destroy your stable FluffyChat in debug mode
 | 
			
		||||
      if (kDebugMode) {
 | 
			
		||||
        directory = Directory(directory.uri.resolve('debug').toFilePath());
 | 
			
		||||
        directory.create(recursive: true);
 | 
			
		||||
      }
 | 
			
		||||
      path = directory.path;
 | 
			
		||||
    }
 | 
			
		||||
    return path;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get maxFileSize => supportsFileStoring ? 100 * 1024 * 1024 : 0;
 | 
			
		||||
  @override
 | 
			
		||||
  bool get supportsFileStoring => (PlatformInfos.isIOS ||
 | 
			
		||||
      PlatformInfos.isAndroid ||
 | 
			
		||||
      PlatformInfos.isDesktop);
 | 
			
		||||
  bool get supportsFileStoring => !kIsWeb;
 | 
			
		||||
 | 
			
		||||
  Future<String> _getFileStoreDirectory() async {
 | 
			
		||||
    try {
 | 
			
		||||
@ -62,7 +62,7 @@ Future<void> pushHelper(
 | 
			
		||||
  final matrixLocals = MatrixLocals(l10n);
 | 
			
		||||
 | 
			
		||||
  // Calculate the body
 | 
			
		||||
  final body = event.getLocalizedBody(
 | 
			
		||||
  final body = await event.calcLocalizedBody(
 | 
			
		||||
    matrixLocals,
 | 
			
		||||
    plaintextBody: true,
 | 
			
		||||
    withSenderNamePrefix: !event.room.isDirectChat,
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ extension LocalNotificationsExtension on MatrixState {
 | 
			
		||||
    final event = Event.fromJson(eventUpdate.content, room);
 | 
			
		||||
    final title =
 | 
			
		||||
        room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)!));
 | 
			
		||||
    final body = event.getLocalizedBody(
 | 
			
		||||
    final body = await event.calcLocalizedBody(
 | 
			
		||||
      MatrixLocals(L10n.of(widget.context)!),
 | 
			
		||||
      withSenderNamePrefix:
 | 
			
		||||
          !room.isDirectChat || room.lastEvent?.senderId == client.userID,
 | 
			
		||||
@ -40,8 +40,11 @@ extension LocalNotificationsExtension on MatrixState {
 | 
			
		||||
      hideEdit: true,
 | 
			
		||||
      removeMarkdown: true,
 | 
			
		||||
    );
 | 
			
		||||
    final icon = event.sender.avatarUrl?.getThumbnail(client,
 | 
			
		||||
            width: 64, height: 64, method: ThumbnailMethod.crop) ??
 | 
			
		||||
    final icon = event.senderFromMemoryOrFallback.avatarUrl?.getThumbnail(
 | 
			
		||||
            client,
 | 
			
		||||
            width: 64,
 | 
			
		||||
            height: 64,
 | 
			
		||||
            method: ThumbnailMethod.crop) ??
 | 
			
		||||
        room.avatar?.getThumbnail(client,
 | 
			
		||||
            width: 64, height: 64, method: ThumbnailMethod.crop);
 | 
			
		||||
    if (kIsWeb) {
 | 
			
		||||
 | 
			
		||||
@ -41,9 +41,7 @@ class PublicRoomBottomSheet extends StatelessWidget {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool _testRoom(PublicRoomsChunk r) =>
 | 
			
		||||
      r.canonicalAlias == roomAlias ||
 | 
			
		||||
      (r.aliases?.contains(roomAlias) ?? false);
 | 
			
		||||
  bool _testRoom(PublicRoomsChunk r) => r.canonicalAlias == roomAlias;
 | 
			
		||||
 | 
			
		||||
  Future<PublicRoomsChunk> _search(BuildContext context) async {
 | 
			
		||||
    final chunk = this.chunk;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								pubspec.lock
									
									
									
									
									
								
							@ -395,6 +395,13 @@ packages:
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "5.0.1"
 | 
			
		||||
  enhanced_enum:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: enhanced_enum
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.1.2"
 | 
			
		||||
  fake_async:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -788,7 +795,7 @@ packages:
 | 
			
		||||
      name: hive
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.0"
 | 
			
		||||
    version: "2.2.1"
 | 
			
		||||
  hive_flutter:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -1012,21 +1019,21 @@ packages:
 | 
			
		||||
      name: matrix
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.9.6"
 | 
			
		||||
    version: "0.9.12"
 | 
			
		||||
  matrix_api_lite:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: matrix_api_lite
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.5.3"
 | 
			
		||||
    version: "1.1.1"
 | 
			
		||||
  matrix_homeserver_recommendations:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: matrix_homeserver_recommendations
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.2.0"
 | 
			
		||||
    version: "0.2.1"
 | 
			
		||||
  matrix_link_text:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,7 @@ dependencies:
 | 
			
		||||
  keyboard_shortcuts: ^0.1.4
 | 
			
		||||
  localstorage: ^4.0.0+1
 | 
			
		||||
  lottie: ^1.2.2
 | 
			
		||||
  matrix: ^0.9.4
 | 
			
		||||
  matrix: ^0.9.12
 | 
			
		||||
  matrix_homeserver_recommendations: ^0.2.0
 | 
			
		||||
  matrix_link_text: ^1.0.2
 | 
			
		||||
  native_imaging:
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import 'package:matrix/encryption/utils/key_verification.dart';
 | 
			
		||||
import 'package:matrix/matrix.dart';
 | 
			
		||||
import 'package:matrix_api_lite/fake_matrix_api.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart';
 | 
			
		||||
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart';
 | 
			
		||||
 | 
			
		||||
Future<Client> prepareTestClient({
 | 
			
		||||
  bool loggedIn = false,
 | 
			
		||||
@ -20,7 +20,7 @@ Future<Client> prepareTestClient({
 | 
			
		||||
    importantStateEvents: <String>{
 | 
			
		||||
      'im.ponies.room_emotes', // we want emotes to work properly
 | 
			
		||||
    },
 | 
			
		||||
    databaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder,
 | 
			
		||||
    databaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder,
 | 
			
		||||
    supportedLoginTypes: {
 | 
			
		||||
      AuthenticationTypes.password,
 | 
			
		||||
      AuthenticationTypes.sso
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user