Merge branch 'krille-chan:main' into scanqr
This commit is contained in:
commit
154ff5ee7b
19 changed files with 313 additions and 297 deletions
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
|
|
@ -191,7 +191,7 @@ jobs:
|
||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Snapcraft
|
- name: Install Snapcraft
|
||||||
uses: samuelmeuli/action-snapcraft@v2
|
uses: samuelmeuli/action-snapcraft@v3
|
||||||
- name: Get Tag Name
|
- name: Get Tag Name
|
||||||
id: tag_name
|
id: tag_name
|
||||||
run: echo "::set-output name=tag::$(echo ${GITHUB_REF#refs/tags/})"
|
run: echo "::set-output name=tag::$(echo ${GITHUB_REF#refs/tags/})"
|
||||||
|
|
|
||||||
2
.github/workflows/versions.env
vendored
2
.github/workflows/versions.env
vendored
|
|
@ -1,2 +1,2 @@
|
||||||
FLUTTER_VERSION=3.24.3
|
FLUTTER_VERSION=3.24.5
|
||||||
JAVA_VERSION=17
|
JAVA_VERSION=17
|
||||||
|
|
|
||||||
|
|
@ -2806,5 +2806,11 @@
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"website": "Website",
|
"website": "Website",
|
||||||
"compressBeforeSending": "Compress before sending"
|
"sendUncompressed": "Send uncompressed",
|
||||||
|
"boldText": "Bold text",
|
||||||
|
"italicText": "Italic text",
|
||||||
|
"strikeThrough": "Strikethrough",
|
||||||
|
"pleaseFillOut": "Please fill out",
|
||||||
|
"invalidUrl": "Invalid url",
|
||||||
|
"addLink": "Add link"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"chatBackupDescription": "La copia de respaldo del chat está protegida por una clave de seguridad. Procure no perderla.",
|
"chatBackupDescription": "La copia de respaldo del chat está protegida por una llave de seguridad. Procure no perderla.",
|
||||||
"@chatBackupDescription": {
|
"@chatBackupDescription": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
|
@ -662,7 +662,7 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"inviteText": "{username} te invitó a FluffyChat.\n1. Instale FluffyChat: https://fluffychat.im\n2. Regístrate o inicia sesión \n3. Abra el enlace de invitación: {link}",
|
"inviteText": "{username} te invitó a FluffyChat.\n1.Visita fluffychat.im e instala la app\n2. Regístrate o inicia sesión\n3. Abre el enlace de invitación:\n{link}",
|
||||||
"@inviteText": {
|
"@inviteText": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|
@ -1667,7 +1667,7 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"defaultPermissionLevel": "Nivel de permiso predeterminado",
|
"defaultPermissionLevel": "Nivel de permiso predeterminado para nuevo usuarios",
|
||||||
"@defaultPermissionLevel": {
|
"@defaultPermissionLevel": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
|
@ -2315,5 +2315,66 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commandHint_googly": "Enviar unos ojos saltones",
|
"commandHint_googly": "Enviar unos ojos saltones",
|
||||||
"@commandHint_googly": {}
|
"@commandHint_googly": {},
|
||||||
|
"noChatsFoundHere": "No se han encontrado chats. Inicia un nuevo chat usando el botón de abajo. ⤵️",
|
||||||
|
"@noChatsFoundHere": {},
|
||||||
|
"joinedChats": "Chats Unidos",
|
||||||
|
"@joinedChats": {},
|
||||||
|
"space": "Espacio",
|
||||||
|
"@space": {},
|
||||||
|
"spaces": "Espacios",
|
||||||
|
"@spaces": {},
|
||||||
|
"block": "Bloquear",
|
||||||
|
"@block": {},
|
||||||
|
"blockListDescription": "Puedes bloquear usuarios que te estén molestando. No podrás recibir mensajes ni invitaciones de chat de los usuarios de tu lista de bloqueo.",
|
||||||
|
"@blockListDescription": {},
|
||||||
|
"aboutHomeserver": "Acerca de {homeserver}",
|
||||||
|
"@aboutHomeserver": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"homeserver": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unread": "No leídos",
|
||||||
|
"@unread": {},
|
||||||
|
"swipeRightToLeftToReply": "Desliza a la izquierda para responder",
|
||||||
|
"@swipeRightToLeftToReply": {},
|
||||||
|
"hideRedactedMessagesBody": "Si alguien elimina un mensaje, este mensaje ya no será visible en el chat.",
|
||||||
|
"@hideRedactedMessagesBody": {},
|
||||||
|
"hideInvalidOrUnknownMessageFormats": "Esconde formatos de mensajes inválidos o desconocidos",
|
||||||
|
"@hideInvalidOrUnknownMessageFormats": {},
|
||||||
|
"hideRedactedMessages": "Esconde mensajes eliminados",
|
||||||
|
"@hideRedactedMessages": {},
|
||||||
|
"appLockDescription": "Bloquear la aplicación cuando no se use con código pin",
|
||||||
|
"@appLockDescription": {},
|
||||||
|
"alwaysUse24HourFormat": "Falso",
|
||||||
|
"@alwaysUse24HourFormat": {
|
||||||
|
"description": "Set to true to always display time of day in 24 hour format."
|
||||||
|
},
|
||||||
|
"accessAndVisibility": "Acceso y visibilidad",
|
||||||
|
"@accessAndVisibility": {},
|
||||||
|
"globalChatId": "ID de chat Global",
|
||||||
|
"@globalChatId": {},
|
||||||
|
"accessAndVisibilityDescription": "A quién se le permite unirse a este chat y cómo se puede descubrir el chat.",
|
||||||
|
"@accessAndVisibilityDescription": {},
|
||||||
|
"calls": "Llamadas",
|
||||||
|
"@calls": {},
|
||||||
|
"customEmojisAndStickers": "Emojis y stickers personalizados",
|
||||||
|
"@customEmojisAndStickers": {},
|
||||||
|
"customEmojisAndStickersBody": "Agrega o comparte emojis y stickers personalizados que se pueden utilizar en cualquier chat.",
|
||||||
|
"@customEmojisAndStickersBody": {},
|
||||||
|
"blockedUsers": "Usuarios bloqueados",
|
||||||
|
"@blockedUsers": {},
|
||||||
|
"blockUsername": "Ignorar nombre de usuario",
|
||||||
|
"@blockUsername": {},
|
||||||
|
"noMoreChatsFound": "No se encontraron más chats...",
|
||||||
|
"@noMoreChatsFound": {},
|
||||||
|
"countChatsAndCountParticipants": "{chats} chats y {participants} participantes",
|
||||||
|
"@countChatsAndCountParticipants": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"chats": {},
|
||||||
|
"participants": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'package:animations/animations.dart';
|
import 'package:animations/animations.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
|
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
|
|
@ -96,15 +94,7 @@ class ChatInputRow extends StatelessWidget {
|
||||||
]
|
]
|
||||||
: <Widget>[
|
: <Widget>[
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
KeyBoardShortcuts(
|
AnimatedContainer(
|
||||||
keysToPress: {
|
|
||||||
LogicalKeyboardKey.altLeft,
|
|
||||||
LogicalKeyboardKey.keyA,
|
|
||||||
},
|
|
||||||
onKeysPressed: () =>
|
|
||||||
controller.onAddPopupMenuButtonSelected('file'),
|
|
||||||
helpLabel: L10n.of(context).sendFile,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: FluffyThemes.animationDuration,
|
duration: FluffyThemes.animationDuration,
|
||||||
curve: FluffyThemes.animationCurve,
|
curve: FluffyThemes.animationCurve,
|
||||||
height: height,
|
height: height,
|
||||||
|
|
@ -183,18 +173,10 @@ class ChatInputRow extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Container(
|
Container(
|
||||||
height: height,
|
height: height,
|
||||||
width: height,
|
width: height,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: KeyBoardShortcuts(
|
|
||||||
keysToPress: {
|
|
||||||
LogicalKeyboardKey.altLeft,
|
|
||||||
LogicalKeyboardKey.keyE,
|
|
||||||
},
|
|
||||||
onKeysPressed: controller.emojiPickerAction,
|
|
||||||
helpLabel: L10n.of(context).emojis,
|
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: L10n.of(context).emojis,
|
tooltip: L10n.of(context).emojis,
|
||||||
icon: PageTransitionSwitcher(
|
icon: PageTransitionSwitcher(
|
||||||
|
|
@ -221,7 +203,6 @@ class ChatInputRow extends StatelessWidget {
|
||||||
onPressed: controller.emojiPickerAction,
|
onPressed: controller.emojiPickerAction,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (Matrix.of(context).isMultiAccount &&
|
if (Matrix.of(context).isMultiAccount &&
|
||||||
Matrix.of(context).hasComplexBundles &&
|
Matrix.of(context).hasComplexBundles &&
|
||||||
Matrix.of(context).currentBundle!.length > 1)
|
Matrix.of(context).currentBundle!.length > 1)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import 'package:pasteboard/pasteboard.dart';
|
||||||
import 'package:slugify/slugify.dart';
|
import 'package:slugify/slugify.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
|
import 'package:fluffychat/utils/markdown_context_builder.dart';
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
import 'package:fluffychat/utils/platform_infos.dart';
|
||||||
import 'package:fluffychat/widgets/mxc_image.dart';
|
import 'package:fluffychat/widgets/mxc_image.dart';
|
||||||
import '../../widgets/avatar.dart';
|
import '../../widgets/avatar.dart';
|
||||||
|
|
@ -456,6 +457,8 @@ class InputBar extends StatelessWidget {
|
||||||
builder: (context, controller, focusNode) => TextField(
|
builder: (context, controller, focusNode) => TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
|
contextMenuBuilder: (c, e) =>
|
||||||
|
markdownContextBuilder(c, e, controller),
|
||||||
contentInsertionConfiguration: ContentInsertionConfiguration(
|
contentInsertionConfiguration: ContentInsertionConfiguration(
|
||||||
onContentInserted: (KeyboardInsertedContent content) {
|
onContentInserted: (KeyboardInsertedContent content) {
|
||||||
final data = content.data;
|
final data = content.data;
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ class SendFileDialogState extends State<SendFileDialog> {
|
||||||
|
|
||||||
var sendStr = L10n.of(context).sendFile;
|
var sendStr = L10n.of(context).sendFile;
|
||||||
final uniqueMimeType = widget.files
|
final uniqueMimeType = widget.files
|
||||||
.map((file) => file.mimeType ?? lookupMimeType(file.path))
|
.map((file) => file.mimeType ?? lookupMimeType(file.name))
|
||||||
.toSet()
|
.toSet()
|
||||||
.singleOrNull;
|
.singleOrNull;
|
||||||
|
|
||||||
|
|
@ -250,26 +250,45 @@ class SendFileDialogState extends State<SendFileDialog> {
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
if ({TargetPlatform.iOS, TargetPlatform.macOS}
|
||||||
|
.contains(theme.platform))
|
||||||
CupertinoSwitch(
|
CupertinoSwitch(
|
||||||
value: compress,
|
value: !compress,
|
||||||
onChanged: uniqueMimeType.startsWith('video') &&
|
onChanged: uniqueMimeType.startsWith('video') &&
|
||||||
!PlatformInfos.isMobile
|
!PlatformInfos.isMobile
|
||||||
? null
|
? null
|
||||||
: (v) => setState(() => compress = v),
|
: (v) => setState(() => compress = !v),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Switch.adaptive(
|
||||||
|
value: !compress,
|
||||||
|
onChanged: uniqueMimeType.startsWith('video') &&
|
||||||
|
!PlatformInfos.isMobile
|
||||||
|
? null
|
||||||
|
: (v) => setState(() => compress = !v),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).compressBeforeSending,
|
L10n.of(context).sendUncompressed,
|
||||||
style: theme.textTheme.labelMedium,
|
style: theme.textTheme.titleMedium,
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Text(
|
||||||
|
' ($sizeString)',
|
||||||
|
style: theme.textTheme.labelSmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
|
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
|
|
@ -138,14 +136,7 @@ class ChatListView extends StatelessWidget {
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: ChatListViewBody(controller),
|
body: ChatListViewBody(controller),
|
||||||
floatingActionButton: KeyBoardShortcuts(
|
floatingActionButton: selectMode == SelectMode.normal &&
|
||||||
keysToPress: {
|
|
||||||
LogicalKeyboardKey.controlLeft,
|
|
||||||
LogicalKeyboardKey.keyN,
|
|
||||||
},
|
|
||||||
onKeysPressed: () => context.go('/rooms/newprivatechat'),
|
|
||||||
helpLabel: L10n.of(context).newChat,
|
|
||||||
child: selectMode == SelectMode.normal &&
|
|
||||||
!controller.isSearchMode &&
|
!controller.isSearchMode &&
|
||||||
controller.activeSpaceId == null
|
controller.activeSpaceId == null
|
||||||
? FloatingActionButton.extended(
|
? FloatingActionButton.extended(
|
||||||
|
|
@ -161,7 +152,6 @@ class ChatListView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
|
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/widgets/avatar.dart';
|
import 'package:fluffychat/widgets/avatar.dart';
|
||||||
|
|
@ -166,36 +164,10 @@ class ClientChooserButton extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
...List.generate(
|
...List.generate(
|
||||||
clientCount,
|
clientCount,
|
||||||
(index) => KeyBoardShortcuts(
|
(index) => const SizedBox.shrink(),
|
||||||
keysToPress: _buildKeyboardShortcut(index + 1),
|
|
||||||
helpLabel: L10n.of(context).switchToAccount(index + 1),
|
|
||||||
onKeysPressed: () => _handleKeyboardShortcut(
|
|
||||||
matrix,
|
|
||||||
index,
|
|
||||||
context,
|
|
||||||
),
|
|
||||||
child: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
KeyBoardShortcuts(
|
|
||||||
keysToPress: {
|
|
||||||
LogicalKeyboardKey.controlLeft,
|
|
||||||
LogicalKeyboardKey.tab,
|
|
||||||
},
|
|
||||||
helpLabel: L10n.of(context).nextAccount,
|
|
||||||
onKeysPressed: () => _nextAccount(matrix, context),
|
|
||||||
child: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
KeyBoardShortcuts(
|
|
||||||
keysToPress: {
|
|
||||||
LogicalKeyboardKey.controlLeft,
|
|
||||||
LogicalKeyboardKey.shiftLeft,
|
|
||||||
LogicalKeyboardKey.tab,
|
|
||||||
},
|
|
||||||
helpLabel: L10n.of(context).previousAccount,
|
|
||||||
onKeysPressed: () => _previousAccount(matrix, context),
|
|
||||||
child: const SizedBox.shrink(),
|
|
||||||
),
|
),
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
const SizedBox.shrink(),
|
||||||
PopupMenuButton<Object>(
|
PopupMenuButton<Object>(
|
||||||
onSelected: (o) => _clientSelected(o, context),
|
onSelected: (o) => _clientSelected(o, context),
|
||||||
itemBuilder: _bundleMenuItems,
|
itemBuilder: _bundleMenuItems,
|
||||||
|
|
@ -215,17 +187,6 @@ class ClientChooserButton extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<LogicalKeyboardKey>? _buildKeyboardShortcut(int index) {
|
|
||||||
if (index > 0 && index < 10) {
|
|
||||||
return {
|
|
||||||
LogicalKeyboardKey.altLeft,
|
|
||||||
LogicalKeyboardKey(0x00000000030 + index),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _clientSelected(
|
void _clientSelected(
|
||||||
Object object,
|
Object object,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
|
|
@ -265,75 +226,6 @@ class ClientChooserButton extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleKeyboardShortcut(
|
|
||||||
MatrixState matrix,
|
|
||||||
int index,
|
|
||||||
BuildContext context,
|
|
||||||
) {
|
|
||||||
final bundles = matrix.accountBundles.keys.toList()
|
|
||||||
..sort(
|
|
||||||
(a, b) => a!.isValidMatrixId == b!.isValidMatrixId
|
|
||||||
? 0
|
|
||||||
: a.isValidMatrixId && !b.isValidMatrixId
|
|
||||||
? -1
|
|
||||||
: 1,
|
|
||||||
);
|
|
||||||
// beginning from end if negative
|
|
||||||
if (index < 0) {
|
|
||||||
var clientCount = 0;
|
|
||||||
matrix.accountBundles
|
|
||||||
.forEach((key, value) => clientCount += value.length);
|
|
||||||
_handleKeyboardShortcut(matrix, clientCount, context);
|
|
||||||
}
|
|
||||||
for (final bundleName in bundles) {
|
|
||||||
final bundle = matrix.accountBundles[bundleName];
|
|
||||||
if (bundle != null) {
|
|
||||||
if (index < bundle.length) {
|
|
||||||
return _clientSelected(bundle[index]!, context);
|
|
||||||
} else {
|
|
||||||
index -= bundle.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if index too high, restarting from 0
|
|
||||||
_handleKeyboardShortcut(matrix, 0, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
int? _shortcutIndexOfClient(MatrixState matrix, Client client) {
|
|
||||||
var index = 0;
|
|
||||||
|
|
||||||
final bundles = matrix.accountBundles.keys.toList()
|
|
||||||
..sort(
|
|
||||||
(a, b) => a!.isValidMatrixId == b!.isValidMatrixId
|
|
||||||
? 0
|
|
||||||
: a.isValidMatrixId && !b.isValidMatrixId
|
|
||||||
? -1
|
|
||||||
: 1,
|
|
||||||
);
|
|
||||||
for (final bundleName in bundles) {
|
|
||||||
final bundle = matrix.accountBundles[bundleName];
|
|
||||||
if (bundle == null) return null;
|
|
||||||
if (bundle.contains(client)) {
|
|
||||||
return index + bundle.indexOf(client);
|
|
||||||
} else {
|
|
||||||
index += bundle.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _nextAccount(MatrixState matrix, BuildContext context) {
|
|
||||||
final client = matrix.client;
|
|
||||||
final lastIndex = _shortcutIndexOfClient(matrix, client);
|
|
||||||
_handleKeyboardShortcut(matrix, lastIndex! + 1, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _previousAccount(MatrixState matrix, BuildContext context) {
|
|
||||||
final client = matrix.client;
|
|
||||||
final lastIndex = _shortcutIndexOfClient(matrix, client);
|
|
||||||
_handleKeyboardShortcut(matrix, lastIndex! - 1, context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SettingsAction {
|
enum SettingsAction {
|
||||||
|
|
|
||||||
102
lib/utils/markdown_context_builder.dart
Normal file
102
lib/utils/markdown_context_builder.dart
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
|
||||||
|
Widget markdownContextBuilder(
|
||||||
|
BuildContext context,
|
||||||
|
EditableTextState editableTextState,
|
||||||
|
TextEditingController controller,
|
||||||
|
) {
|
||||||
|
final value = editableTextState.textEditingValue;
|
||||||
|
final selectedText = value.selection.textInside(value.text);
|
||||||
|
final buttonItems = editableTextState.contextMenuButtonItems;
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
|
||||||
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
||||||
|
anchors: editableTextState.contextMenuAnchors,
|
||||||
|
buttonItems: [
|
||||||
|
...buttonItems,
|
||||||
|
if (selectedText.isNotEmpty) ...[
|
||||||
|
ContextMenuButtonItem(
|
||||||
|
label: l10n.link,
|
||||||
|
onPressed: () async {
|
||||||
|
final input = await showTextInputDialog(
|
||||||
|
context: context,
|
||||||
|
title: l10n.addLink,
|
||||||
|
okLabel: l10n.ok,
|
||||||
|
cancelLabel: l10n.cancel,
|
||||||
|
textFields: [
|
||||||
|
DialogTextField(
|
||||||
|
validator: (text) {
|
||||||
|
if (text == null || text.isEmpty) {
|
||||||
|
return l10n.pleaseFillOut;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
text.startsWith('http')
|
||||||
|
? Uri.parse(text)
|
||||||
|
: Uri.https(text);
|
||||||
|
} catch (_) {
|
||||||
|
return l10n.invalidUrl;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
hintText: 'www...',
|
||||||
|
keyboardType: TextInputType.url,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
final urlString = input?.singleOrNull;
|
||||||
|
if (urlString == null) return;
|
||||||
|
final url = urlString.startsWith('http')
|
||||||
|
? Uri.parse(urlString)
|
||||||
|
: Uri.https(urlString);
|
||||||
|
final selection = controller.selection;
|
||||||
|
controller.text = controller.text.replaceRange(
|
||||||
|
selection.start,
|
||||||
|
selection.end,
|
||||||
|
'[$selectedText](${url.toString()})',
|
||||||
|
);
|
||||||
|
ContextMenuController.removeAny();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ContextMenuButtonItem(
|
||||||
|
label: l10n.boldText,
|
||||||
|
onPressed: () {
|
||||||
|
final selection = controller.selection;
|
||||||
|
controller.text = controller.text.replaceRange(
|
||||||
|
selection.start,
|
||||||
|
selection.end,
|
||||||
|
'**$selectedText**',
|
||||||
|
);
|
||||||
|
ContextMenuController.removeAny();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ContextMenuButtonItem(
|
||||||
|
label: l10n.italicText,
|
||||||
|
onPressed: () {
|
||||||
|
final selection = controller.selection;
|
||||||
|
controller.text = controller.text.replaceRange(
|
||||||
|
selection.start,
|
||||||
|
selection.end,
|
||||||
|
'*$selectedText*',
|
||||||
|
);
|
||||||
|
ContextMenuController.removeAny();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ContextMenuButtonItem(
|
||||||
|
label: l10n.strikeThrough,
|
||||||
|
onPressed: () {
|
||||||
|
final selection = controller.selection;
|
||||||
|
controller.text = controller.text.replaceRange(
|
||||||
|
selection.start,
|
||||||
|
selection.end,
|
||||||
|
'~~$selectedText~~',
|
||||||
|
);
|
||||||
|
ContextMenuController.removeAny();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
|
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||||
|
|
@ -52,15 +50,7 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
||||||
return Stack(
|
return Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
KeyBoardShortcuts(
|
const SizedBox.shrink(),
|
||||||
keysToPress: {
|
|
||||||
LogicalKeyboardKey.controlLeft,
|
|
||||||
LogicalKeyboardKey.keyI,
|
|
||||||
},
|
|
||||||
helpLabel: L10n.of(context).chatDetails,
|
|
||||||
onKeysPressed: _showChatDetails,
|
|
||||||
child: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
PopupMenuButton<ChatPopupMenuActions>(
|
PopupMenuButton<ChatPopupMenuActions>(
|
||||||
onSelected: (choice) async {
|
onSelected: (choice) async {
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0920;
|
LastSwiftUpdateCheck = 0920;
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1510;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
33CC10EC2044A3C60003C045 = {
|
33CC10EC2044A3C60003C045 = {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1510"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
|
|
||||||
@NSApplicationMain
|
@main
|
||||||
class AppDelegate: FlutterAppDelegate {
|
class AppDelegate: FlutterAppDelegate {
|
||||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
21
pubspec.lock
21
pubspec.lock
|
|
@ -1098,15 +1098,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.13"
|
version: "0.4.13"
|
||||||
keyboard_shortcuts:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
path: "."
|
|
||||||
ref: null-safety
|
|
||||||
resolved-ref: a3d4020911860ff091d90638ab708604b71d2c5a
|
|
||||||
url: "https://github.com/TheOneWithTheBraid/keyboard_shortcuts.git"
|
|
||||||
source: git
|
|
||||||
version: "0.1.4"
|
|
||||||
latlong2:
|
latlong2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1247,10 +1238,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: matrix
|
name: matrix
|
||||||
sha256: e06783394db3a49dbcd98a45803cac7d735a2fb1e3aafef65ddf01e72e16fc83
|
sha256: "94a66e563b89fabbeb67f24428f05f4547c6ee98878ec20f647530cbfb6f04db"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.34.0"
|
version: "0.35.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -2320,14 +2311,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.3.2"
|
||||||
visibility_detector:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: visibility_detector
|
|
||||||
sha256: "15c54a459ec2c17b4705450483f3d5a2858e733aee893dcee9d75fd04814940d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.3.3"
|
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,9 @@ dependencies:
|
||||||
image_picker: ^1.1.0
|
image_picker: ^1.1.0
|
||||||
intl: any
|
intl: any
|
||||||
just_audio: ^0.9.39
|
just_audio: ^0.9.39
|
||||||
keyboard_shortcuts: ^0.1.4
|
|
||||||
latlong2: ^0.9.1
|
latlong2: ^0.9.1
|
||||||
linkify: ^5.0.0
|
linkify: ^5.0.0
|
||||||
matrix: ^0.34.0
|
matrix: ^0.35.0
|
||||||
mime: ^1.0.6
|
mime: ^1.0.6
|
||||||
native_imaging: ^0.1.1
|
native_imaging: ^0.1.1
|
||||||
opus_caf_converter_dart: ^1.0.1
|
opus_caf_converter_dart: ^1.0.1
|
||||||
|
|
@ -153,10 +152,4 @@ msix_config:
|
||||||
install_certificate: false
|
install_certificate: false
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
# waiting for null safety
|
|
||||||
# Upstream pull request: https://github.com/AntoineMarcel/keyboard_shortcuts/pull/13
|
|
||||||
keyboard_shortcuts:
|
|
||||||
git:
|
|
||||||
url: https://github.com/TheOneWithTheBraid/keyboard_shortcuts.git
|
|
||||||
ref: null-safety
|
|
||||||
win32: 5.5.3
|
win32: 5.5.3
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,6 @@ parts:
|
||||||
fluffychat:
|
fluffychat:
|
||||||
plugin: flutter
|
plugin: flutter
|
||||||
source: .
|
source: .
|
||||||
override-build: |
|
|
||||||
# Workaround for Flutter build error:
|
|
||||||
rm -rf build
|
|
||||||
craftctl default
|
|
||||||
build-packages:
|
build-packages:
|
||||||
- libjsoncpp-dev
|
- libjsoncpp-dev
|
||||||
- curl
|
- curl
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 49 KiB |
Loading…
Add table
Reference in a new issue