commit
326f100520
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,6 +11,7 @@
|
||||||
.svn/
|
.svn/
|
||||||
lib/generated_plugin_registrant.dart
|
lib/generated_plugin_registrant.dart
|
||||||
google-services.json
|
google-services.json
|
||||||
|
prime
|
||||||
|
|
||||||
# libolm package
|
# libolm package
|
||||||
/assets/js/package/*
|
/assets/js/package/*
|
||||||
|
|
|
@ -158,8 +158,7 @@ upload_to_fdroid_repo:
|
||||||
- export UPDATE_VERSION=$(pcregrep -o1 'version:\\s([0-9]*\\.[0-9]*\\.[0-9]*)\\+[0-9]*' pubspec.yaml) && mv app-release.apk "${UPDATE_VERSION}.apk"
|
- export UPDATE_VERSION=$(pcregrep -o1 'version:\\s([0-9]*\\.[0-9]*\\.[0-9]*)\\+[0-9]*' pubspec.yaml) && mv app-release.apk "${UPDATE_VERSION}.apk"
|
||||||
- rsync -rav -e ssh ./ fluffy@fdroid.nordgedanken.dev:/fdroid/repo
|
- rsync -rav -e ssh ./ fluffy@fdroid.nordgedanken.dev:/fdroid/repo
|
||||||
- ssh fluffy@fdroid.nordgedanken.dev "cd fdroid && mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc && fdroid update"
|
- ssh fluffy@fdroid.nordgedanken.dev "cd fdroid && mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc && fdroid update"
|
||||||
dependencies:
|
needs: ["build_android_apk"]
|
||||||
- build_android_apk
|
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
|
|
||||||
|
@ -174,8 +173,7 @@ pages:
|
||||||
- cd build/web/ && bundle install && cd ../../
|
- cd build/web/ && bundle install && cd ../../
|
||||||
- cd build/web/ && bundle exec jekyll build -d public && cd ../../
|
- cd build/web/ && bundle exec jekyll build -d public && cd ../../
|
||||||
- mv build/web/public ./
|
- mv build/web/public ./
|
||||||
dependencies:
|
needs: ["build_web"]
|
||||||
- build_web
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
|
@ -215,3 +213,4 @@ snap:publish:
|
||||||
- './*.snap'
|
- './*.snap'
|
||||||
when: on_success
|
when: on_success
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
|
needs: []
|
||||||
|
|
13
README.md
13
README.md
|
@ -3,10 +3,12 @@
|
||||||
An experimental fork of FluffyChat.
|
An experimental fork of FluffyChat.
|
||||||
|
|
||||||
# Changes from FluffyChat
|
# Changes from FluffyChat
|
||||||
|
* Swipe to reply (or forward/edit)
|
||||||
* Reworked auth flow
|
* Reworked auth flow
|
||||||
* Removed Sentry
|
* Removed Sentry
|
||||||
* Double check of .well-known
|
* Double check of .well-known
|
||||||
* Get Jitsi instance from .well-known
|
* Get Jitsi instance from .well-known
|
||||||
|
* Redesigned settings
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
* Single and group chats
|
* Single and group chats
|
||||||
|
@ -35,8 +37,8 @@ An experimental fork of FluffyChat.
|
||||||
|
|
||||||
2. Clone the repo:
|
2. Clone the repo:
|
||||||
```
|
```
|
||||||
git clone --recurse-submodules https://gitlab.com/ChristianPauly/fluffychat-flutter
|
git clone --recurse-submodules https://github.com/innereq/FurryChat.git
|
||||||
cd fluffychat-flutter
|
cd FurryChat
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Choose your target platform below and enable support for it.
|
3. Choose your target platform below and enable support for it.
|
||||||
|
@ -80,13 +82,6 @@ flutter build windows --release
|
||||||
flutter build macos --release
|
flutter build macos --release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Docker
|
|
||||||
|
|
||||||
Don't even ask.
|
|
||||||
|
|
||||||
`docker run -ti --privileged -v /dev/bus/usb:/dev/bus/usb -v ${PWD}:/build -v /home/inex/.pub-cache:/home/inex/.pub-cache -v /home/inex/flutter:/home/inex/flutter -d flutter-fluffy:1.0`
|
|
||||||
|
|
||||||
## How to add translations for your language
|
## How to add translations for your language
|
||||||
|
|
||||||
You can use Weblate to translate the app to your language:
|
You can use Weblate to translate the app to your language:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
arb-dir: lib/l10n
|
arb-dir: lib/l10n
|
||||||
template-arb-file: intl_en.arb
|
template-arb-file: intl_en.arb
|
||||||
output-localization-file: l10n.dart
|
output-localization-file: l10n.dart
|
||||||
output-class: L10n
|
output-class: L10n
|
||||||
|
preferred-supported-locales: ["en"]
|
|
@ -11,7 +11,6 @@ import 'package:furrychat/utils/user_status.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:localstorage/localstorage.dart';
|
|
||||||
import 'package:universal_html/prefer_universal/html.dart' as html;
|
import 'package:universal_html/prefer_universal/html.dart' as html;
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
@ -72,13 +71,15 @@ class MatrixState extends State<Matrix> {
|
||||||
File wallpaper;
|
File wallpaper;
|
||||||
bool renderHtml = false;
|
bool renderHtml = false;
|
||||||
|
|
||||||
|
String swipeToEndAction;
|
||||||
|
String swipeToStartAction = 'reply';
|
||||||
|
|
||||||
String jitsiInstance = 'https://meet.jit.si/';
|
String jitsiInstance = 'https://meet.jit.si/';
|
||||||
|
|
||||||
void clean() async {
|
void clean() async {
|
||||||
if (!kIsWeb) return;
|
if (!kIsWeb) return;
|
||||||
|
|
||||||
final storage = LocalStorage('LocalStorage');
|
final storage = await getLocalStorage();
|
||||||
await storage.ready;
|
|
||||||
await storage.deleteItem(widget.clientName);
|
await storage.deleteItem(widget.clientName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +286,16 @@ class MatrixState extends State<Matrix> {
|
||||||
store.getItem('chat.fluffy.renderHtml').then((final render) async {
|
store.getItem('chat.fluffy.renderHtml').then((final render) async {
|
||||||
renderHtml = render == '1';
|
renderHtml = render == '1';
|
||||||
});
|
});
|
||||||
|
store
|
||||||
|
.getItem('dev.inex.furrychat.swipeToEndAction')
|
||||||
|
.then((final action) async {
|
||||||
|
swipeToEndAction = action ?? swipeToEndAction;
|
||||||
|
});
|
||||||
|
store
|
||||||
|
.getItem('dev.inex.furrychat.swipeToStartAction')
|
||||||
|
.then((final action) async {
|
||||||
|
swipeToStartAction = action ?? swipeToStartAction;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true);
|
onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true);
|
||||||
|
|
|
@ -189,7 +189,7 @@
|
||||||
"username": {}
|
"username": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"changedTheDisplaynameTo": "{username} غيّر اسمه الى {displayname}",
|
"changedTheDisplaynameTo": "{username} غيّر اسمه العلني الى {displayname}",
|
||||||
"@changedTheDisplaynameTo": {
|
"@changedTheDisplaynameTo": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
@ -375,7 +375,7 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"couldNotSetDisplayname": "تعذر تعيين الاسم",
|
"couldNotSetDisplayname": "تعذر تعيين الاسم العلني",
|
||||||
"@couldNotSetDisplayname": {
|
"@couldNotSetDisplayname": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
@ -489,7 +489,7 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"displaynameHasBeenChanged": "غُيِّر الاسم",
|
"displaynameHasBeenChanged": "غُيِّر الاسم العلني",
|
||||||
"@displaynameHasBeenChanged": {
|
"@displaynameHasBeenChanged": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
@ -499,7 +499,7 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"editDisplayname": "حرر الاسم",
|
"editDisplayname": "حرر الاسم العلني",
|
||||||
"@editDisplayname": {
|
"@editDisplayname": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
@ -514,7 +514,7 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"emoteWarnNeedToPick": "اختر صورة ورمزا للانفعالة",
|
"emoteWarnNeedToPick": "اختر صورة ورمزا للانفعالة!",
|
||||||
"@emoteWarnNeedToPick": {
|
"@emoteWarnNeedToPick": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
@ -1374,7 +1374,7 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"useAmoledTheme": "",
|
"useAmoledTheme": "هل تريد استخدم ألوان متوافقة مع Amoled؟",
|
||||||
"@useAmoledTheme": {
|
"@useAmoledTheme": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
@ -1507,14 +1507,14 @@
|
||||||
"unreadCount": {}
|
"unreadCount": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unreadMessages": "",
|
"unreadMessages": "{unreadEvents} رسالة غير مقروءة",
|
||||||
"@unreadMessages": {
|
"@unreadMessages": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"unreadEvents": {}
|
"unreadEvents": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unreadMessagesInChats": "",
|
"unreadMessagesInChats": "{unreadEvents} رسالة غير مقروءة من {unreadChats} محادثة",
|
||||||
"@unreadMessagesInChats": {
|
"@unreadMessagesInChats": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
@ -1702,5 +1702,10 @@
|
||||||
"@yourOwnUsername": {
|
"@yourOwnUsername": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"privacy": "الخصوصية",
|
||||||
|
"@privacy": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -514,6 +514,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"edit": "Edit",
|
||||||
|
"@edit": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"editDisplayname": "Edit displayname",
|
"editDisplayname": "Edit displayname",
|
||||||
"@editDisplayname": {
|
"@editDisplayname": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1439,6 +1444,16 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"swipeToEndAction": "Swipe to right action",
|
||||||
|
"@swipeToEndAction": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"swipeToStartAction": "Swipe to left action",
|
||||||
|
"@swipeToStartAction": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"donate": "Donate",
|
"donate": "Donate",
|
||||||
"@donate": {
|
"@donate": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
|
|
@ -194,5 +194,272 @@
|
||||||
"@about": {
|
"@about": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"deleteMessage": "Cancella messaggio",
|
||||||
|
"@deleteMessage": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"deleteAccount": "Elimina account",
|
||||||
|
"@deleteAccount": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"deactivateAccountWarning": "Disabiliterà il tuo account. Non puoi tornare indietro! Sei sicuro?",
|
||||||
|
"@deactivateAccountWarning": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"delete": "Cancella",
|
||||||
|
"@delete": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"dateWithYear": "{day}-{month}-{year}",
|
||||||
|
"@dateWithYear": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"year": {},
|
||||||
|
"month": {},
|
||||||
|
"day": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dateWithoutYear": "{month}-{day}",
|
||||||
|
"@dateWithoutYear": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"month": {},
|
||||||
|
"day": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dateAndTimeOfDay": "{date}, {timeOfDay}",
|
||||||
|
"@dateAndTimeOfDay": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"date": {},
|
||||||
|
"timeOfDay": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"currentlyActive": "Attualmente attivo",
|
||||||
|
"@currentlyActive": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"createNewGroup": "Crea un nuovo gruppo",
|
||||||
|
"@createNewGroup": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"createdTheChat": "{username} ha creato la chat",
|
||||||
|
"@createdTheChat": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"createAccountNow": "Crea ora un account",
|
||||||
|
"@createAccountNow": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"create": "Crea",
|
||||||
|
"@create": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"countParticipants": "{count} partecipanti",
|
||||||
|
"@countParticipants": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"couldNotSetDisplayname": "Impossibile impostare nome",
|
||||||
|
"@couldNotSetDisplayname": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"couldNotSetAvatar": "Impossibile impostare avatar",
|
||||||
|
"@couldNotSetAvatar": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"couldNotDecryptMessage": "Impossibile decriptare messaggio: {error}",
|
||||||
|
"@couldNotDecryptMessage": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"error": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"copy": "Copia",
|
||||||
|
"@copy": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"copiedToClipboard": "Copiato negli Appunti",
|
||||||
|
"@copiedToClipboard": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"contentViewer": "Visualizzatore contenuti",
|
||||||
|
"@contentViewer": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"contactHasBeenInvitedToTheGroup": "Il contatto è stato invitato nel gruppo",
|
||||||
|
"@contactHasBeenInvitedToTheGroup": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"connectionAttemptFailed": "Tentativo di connessione fallito",
|
||||||
|
"@connectionAttemptFailed": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"connect": "Connetti",
|
||||||
|
"@connect": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"confirm": "Conferma",
|
||||||
|
"@confirm": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"compareNumbersMatch": "Confronta e assicurati che le seguenti emoji corrispondano a quelle dell'altro dispositivo:",
|
||||||
|
"@compareNumbersMatch": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"compareEmojiMatch": "Confronta e assicurati che le seguenti emoji corrispondano a quelle dell'altro dispositivo:",
|
||||||
|
"@compareEmojiMatch": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"close": "Chiudi",
|
||||||
|
"@close": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"chooseAUsername": "Scegli un username",
|
||||||
|
"@chooseAUsername": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"chooseAStrongPassword": "Scegli una password complessa",
|
||||||
|
"@chooseAStrongPassword": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"chatDetails": "Dettagli chat",
|
||||||
|
"@chatDetails": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"chat": "Chat",
|
||||||
|
"@chat": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"channelCorruptedDecryptError": "La crittografia è corrotta",
|
||||||
|
"@channelCorruptedDecryptError": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"changeTheServer": "Cambia server",
|
||||||
|
"@changeTheServer": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"changeWallpaper": "Cambia sfondo",
|
||||||
|
"@changeWallpaper": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"changeTheNameOfTheGroup": "Cambia il nome del gruppo",
|
||||||
|
"@changeTheNameOfTheGroup": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"changelog": "Registro cambiamenti",
|
||||||
|
"@changelog": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"changedTheRoomInvitationLink": "{username} ha cambiato il link di invito",
|
||||||
|
"@changedTheRoomInvitationLink": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changedTheRoomAliases": "{username} ha cambiato il nome delle stanze",
|
||||||
|
"@changedTheRoomAliases": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changedTheProfileAvatar": "{username} ha cambiato il loro avatar",
|
||||||
|
"@changedTheProfileAvatar": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changedTheJoinRulesTo": "{username} ha cambiato le regole per unirsi in: {joinRules}",
|
||||||
|
"@changedTheJoinRulesTo": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {},
|
||||||
|
"joinRules": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changedTheJoinRules": "{username} ha cambiato le regole per unirsi",
|
||||||
|
"@changedTheJoinRules": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changedTheHistoryVisibilityTo": "{username} ha cambiato la visibilità della cronologia in: {rules}",
|
||||||
|
"@changedTheHistoryVisibilityTo": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {},
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changedTheHistoryVisibility": "{username} ha cambiato la visibilità della cronologia",
|
||||||
|
"@changedTheHistoryVisibility": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changedTheGuestAccessRulesTo": "{username} ha cambiato le regole di accesso per ospiti con: {rules}",
|
||||||
|
"@changedTheGuestAccessRulesTo": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {},
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changedTheChatAvatar": "{username} ha cambiato avatar",
|
||||||
|
"@changedTheChatAvatar": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"askSSSSSign": "Per entrare con l'altro utente, per favore inserisci la tua passphrase o recovery key.",
|
||||||
|
"@askSSSSSign": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"askSSSSCache": "Per favore inserisci la tua passphrase o recovery key per la cache delle chiavi.",
|
||||||
|
"@askSSSSCache": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"avatar": "Аватар",
|
||||||
|
"@avatar": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"avatarHasBeenChanged": "Аватар был изменён",
|
"avatarHasBeenChanged": "Аватар был изменён",
|
||||||
"@avatarHasBeenChanged": {
|
"@avatarHasBeenChanged": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -197,6 +202,11 @@
|
||||||
"displayname": {}
|
"displayname": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"changeThePassword": "Сменить пароль",
|
||||||
|
"@changeThePassword": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"changedTheGuestAccessRules": "{username} изменил(а) правила гостевого доступа",
|
"changedTheGuestAccessRules": "{username} изменил(а) правила гостевого доступа",
|
||||||
"@changedTheGuestAccessRules": {
|
"@changedTheGuestAccessRules": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -509,6 +519,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"edit": "Редактировать",
|
||||||
|
"@edit": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"editDisplayname": "Отображаемое имя",
|
"editDisplayname": "Отображаемое имя",
|
||||||
"@editDisplayname": {
|
"@editDisplayname": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -681,11 +696,21 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"homeserver": "Сервер Matrix",
|
||||||
|
"@homeserver": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"homeserverIsNotCompatible": "Несовместимый сервер Matrix",
|
"homeserverIsNotCompatible": "Несовместимый сервер Matrix",
|
||||||
"@homeserverIsNotCompatible": {
|
"@homeserverIsNotCompatible": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"homeserverOrMXID": "Сервер или полное имя пользователя",
|
||||||
|
"@homeserverOrMXID": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"@id": {
|
"@id": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1372,6 +1397,16 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"swipeToEndAction": "Действие по жесту вправо",
|
||||||
|
"@swipeToEndAction": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"swipeToStartAction": "Действие по жесту влево",
|
||||||
|
"@swipeToStartAction": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"systemTheme": "Системная",
|
"systemTheme": "Системная",
|
||||||
"@systemTheme": {
|
"@systemTheme": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:localstorage/localstorage.dart';
|
|
||||||
import 'package:universal_html/prefer_universal/html.dart' as html;
|
import 'package:universal_html/prefer_universal/html.dart' as html;
|
||||||
|
|
||||||
import 'components/matrix.dart';
|
import 'components/matrix.dart';
|
||||||
|
@ -21,8 +20,6 @@ void main() {
|
||||||
runZonedGuarded(
|
runZonedGuarded(
|
||||||
() => runApp(App()),
|
() => runApp(App()),
|
||||||
(error, stackTrace) async {
|
(error, stackTrace) async {
|
||||||
final storage = LocalStorage('LocalStorage');
|
|
||||||
await storage.ready;
|
|
||||||
debugPrint(error.toString());
|
debugPrint(error.toString());
|
||||||
debugPrint(stackTrace.toString());
|
debugPrint(stackTrace.toString());
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,12 +6,22 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:localstorage/localstorage.dart';
|
import 'package:localstorage/localstorage.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
import './database/shared.dart';
|
import './database/shared.dart';
|
||||||
import 'package:olm/olm.dart' as olm; // needed for migration
|
import 'package:olm/olm.dart' as olm; // needed for migration
|
||||||
import 'package:random_string/random_string.dart';
|
import 'package:random_string/random_string.dart';
|
||||||
|
|
||||||
|
Future<LocalStorage> getLocalStorage() async {
|
||||||
|
final directory = PlatformInfos.isBetaDesktop
|
||||||
|
? await getApplicationSupportDirectory()
|
||||||
|
: await getApplicationDocumentsDirectory();
|
||||||
|
final localStorage = LocalStorage('LocalStorage', directory.path);
|
||||||
|
await localStorage.ready;
|
||||||
|
return localStorage;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Database> getDatabase(Client client) async {
|
Future<Database> getDatabase(Client client) async {
|
||||||
while (_generateDatabaseLock) {
|
while (_generateDatabaseLock) {
|
||||||
await Future.delayed(Duration(milliseconds: 50));
|
await Future.delayed(Duration(milliseconds: 50));
|
||||||
|
|
|
@ -18,8 +18,8 @@ Future<MatrixImageFile> resizeImage(MatrixImageFile file,
|
||||||
try {
|
try {
|
||||||
final nativeImg = native.Image();
|
final nativeImg = native.Image();
|
||||||
await nativeImg.loadEncoded(file.bytes);
|
await nativeImg.loadEncoded(file.bytes);
|
||||||
file.width = nativeImg.width();
|
file.width = nativeImg.width;
|
||||||
file.height = nativeImg.height();
|
file.height = nativeImg.height;
|
||||||
args = _IsolateArgs(
|
args = _IsolateArgs(
|
||||||
width: file.width, height: file.height, bytes: file.bytes, max: max);
|
width: file.width, height: file.height, bytes: file.bytes, max: max);
|
||||||
nativeImg.free();
|
nativeImg.free();
|
||||||
|
@ -96,8 +96,8 @@ Future<_IsolateResponse> _isolateFunction(_IsolateArgs args) async {
|
||||||
final ret = _IsolateResponse(
|
final ret = _IsolateResponse(
|
||||||
blurhash: blurhash,
|
blurhash: blurhash,
|
||||||
jpegBytes: jpegBytes,
|
jpegBytes: jpegBytes,
|
||||||
width: nativeImg.width(),
|
width: nativeImg.width,
|
||||||
height: nativeImg.height());
|
height: nativeImg.height);
|
||||||
|
|
||||||
nativeImg.free();
|
nativeImg.free();
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:pedantic/pedantic.dart';
|
import 'package:pedantic/pedantic.dart';
|
||||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||||
|
import 'package:swipe_to_action/swipe_to_action.dart';
|
||||||
|
|
||||||
import '../components/dialogs/send_file_dialog.dart';
|
import '../components/dialogs/send_file_dialog.dart';
|
||||||
import '../components/input_bar.dart';
|
import '../components/input_bar.dart';
|
||||||
|
@ -316,8 +317,10 @@ class _ChatState extends State<_Chat> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void forwardEventsAction(BuildContext context) async {
|
void forwardEventsAction(BuildContext context, {Event event}) async {
|
||||||
if (selectedEvents.length == 1) {
|
if (event != null) {
|
||||||
|
Matrix.of(context).shareContent = event.content;
|
||||||
|
} else if (selectedEvents.length == 1) {
|
||||||
Matrix.of(context).shareContent = selectedEvents.first.content;
|
Matrix.of(context).shareContent = selectedEvents.first.content;
|
||||||
} else {
|
} else {
|
||||||
Matrix.of(context).shareContent = {
|
Matrix.of(context).shareContent = {
|
||||||
|
@ -343,9 +346,9 @@ class _ChatState extends State<_Chat> {
|
||||||
setState(() => selectedEvents.clear());
|
setState(() => selectedEvents.clear());
|
||||||
}
|
}
|
||||||
|
|
||||||
void replyAction() {
|
void replyAction({Event replyTo}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
replyEvent = selectedEvents.first;
|
replyEvent = replyTo ?? selectedEvents.first;
|
||||||
selectedEvents.clear();
|
selectedEvents.clear();
|
||||||
});
|
});
|
||||||
inputFocus.requestFocus();
|
inputFocus.requestFocus();
|
||||||
|
@ -411,6 +414,128 @@ class _ChatState extends State<_Chat> {
|
||||||
e.type != 'm.reaction')
|
e.type != 'm.reaction')
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
SwipeDirection _getSwipeDirection(Event event) {
|
||||||
|
var swipeToEndAction = Matrix.of(context).swipeToEndAction;
|
||||||
|
var swipeToStartAction = Matrix.of(context).swipeToStartAction;
|
||||||
|
var client = Matrix.of(context).client;
|
||||||
|
if (event.senderId != client.userID && swipeToEndAction == 'edit') {
|
||||||
|
swipeToEndAction = null;
|
||||||
|
}
|
||||||
|
if (event.senderId != client.userID && swipeToStartAction == 'edit') {
|
||||||
|
swipeToStartAction = null;
|
||||||
|
}
|
||||||
|
if (swipeToEndAction != null && swipeToStartAction != null) {
|
||||||
|
return SwipeDirection.horizontal;
|
||||||
|
}
|
||||||
|
if (swipeToEndAction != null) {
|
||||||
|
return SwipeDirection.startToEnd;
|
||||||
|
}
|
||||||
|
if (swipeToStartAction != null) {
|
||||||
|
return SwipeDirection.endToStart;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _getSwipeBackground(Event event, {bool isSecondary = false}) {
|
||||||
|
var alignToRight, action;
|
||||||
|
if (_getSwipeDirection(event) == SwipeDirection.horizontal) {
|
||||||
|
if (isSecondary) {
|
||||||
|
alignToRight = true;
|
||||||
|
action = Matrix.of(context).swipeToStartAction;
|
||||||
|
} else {
|
||||||
|
alignToRight = false;
|
||||||
|
action = Matrix.of(context).swipeToEndAction;
|
||||||
|
}
|
||||||
|
} else if (isSecondary) {
|
||||||
|
return null;
|
||||||
|
} else if (_getSwipeDirection(event) == SwipeDirection.endToStart) {
|
||||||
|
alignToRight = true;
|
||||||
|
action = Matrix.of(context).swipeToStartAction;
|
||||||
|
} else {
|
||||||
|
alignToRight = false;
|
||||||
|
action = Matrix.of(context).swipeToStartAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'reply':
|
||||||
|
return Container(
|
||||||
|
color: Theme.of(context).primaryColor.withAlpha(100),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
alignToRight ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.reply_outlined),
|
||||||
|
SizedBox(width: 2.0),
|
||||||
|
Text(L10n.of(context).reply)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 'forward':
|
||||||
|
return Container(
|
||||||
|
color: Theme.of(context).primaryColor.withAlpha(100),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
alignToRight ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.forward_outlined),
|
||||||
|
SizedBox(width: 2.0),
|
||||||
|
Text(L10n.of(context).forward)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 'edit':
|
||||||
|
return Container(
|
||||||
|
color: Theme.of(context).primaryColor.withAlpha(100),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
alignToRight ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.edit_outlined),
|
||||||
|
SizedBox(width: 2.0),
|
||||||
|
Text(L10n.of(context).edit)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return Container(
|
||||||
|
color: Theme.of(context).primaryColor.withAlpha(100),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleSwipe(SwipeDirection direction, Event event) {
|
||||||
|
var action;
|
||||||
|
if (direction == SwipeDirection.endToStart) {
|
||||||
|
action = Matrix.of(context).swipeToStartAction;
|
||||||
|
} else {
|
||||||
|
action = Matrix.of(context).swipeToEndAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'reply':
|
||||||
|
replyAction(replyTo: event);
|
||||||
|
break;
|
||||||
|
case 'forward':
|
||||||
|
forwardEventsAction(context, event: event);
|
||||||
|
break;
|
||||||
|
case 'edit':
|
||||||
|
setState(() {
|
||||||
|
editEvent = event;
|
||||||
|
sendController.text = editEvent
|
||||||
|
.getDisplayEvent(timeline)
|
||||||
|
.getLocalizedBody(MatrixLocals(L10n.of(context)),
|
||||||
|
withSenderNamePrefix: false, hideReply: true);
|
||||||
|
selectedEvents.clear();
|
||||||
|
});
|
||||||
|
inputFocus.requestFocus();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
matrix = Matrix.of(context);
|
matrix = Matrix.of(context);
|
||||||
|
@ -668,43 +793,57 @@ class _ChatState extends State<_Chat> {
|
||||||
key: ValueKey(i - 1),
|
key: ValueKey(i - 1),
|
||||||
index: i - 1,
|
index: i - 1,
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
child: Message(filteredEvents[i - 1],
|
child: Swipeable(
|
||||||
onAvatarTab: (Event event) {
|
key: ValueKey(
|
||||||
sendController.text +=
|
filteredEvents[i - 1].eventId),
|
||||||
' ${event.senderId}';
|
background: _getSwipeBackground(
|
||||||
},
|
filteredEvents[i - 1]),
|
||||||
onSelect: (Event event) {
|
secondaryBackground:
|
||||||
if (!event.redacted) {
|
_getSwipeBackground(
|
||||||
if (selectedEvents
|
filteredEvents[i - 1],
|
||||||
.contains(event)) {
|
isSecondary: true),
|
||||||
setState(
|
direction: _getSwipeDirection(
|
||||||
() => selectedEvents
|
filteredEvents[i - 1]),
|
||||||
.remove(event),
|
onSwipe: (direction) => _handleSwipe(
|
||||||
);
|
direction, filteredEvents[i - 1]),
|
||||||
} else {
|
child: Message(filteredEvents[i - 1],
|
||||||
setState(
|
onAvatarTab: (Event event) {
|
||||||
() =>
|
sendController.text +=
|
||||||
selectedEvents.add(event),
|
' ${event.senderId}';
|
||||||
|
},
|
||||||
|
onSelect: (Event event) {
|
||||||
|
if (!event.redacted) {
|
||||||
|
if (selectedEvents
|
||||||
|
.contains(event)) {
|
||||||
|
setState(
|
||||||
|
() => selectedEvents
|
||||||
|
.remove(event),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setState(
|
||||||
|
() => selectedEvents
|
||||||
|
.add(event),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
selectedEvents.sort(
|
||||||
|
(a, b) => a.originServerTs
|
||||||
|
.compareTo(
|
||||||
|
b.originServerTs),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
selectedEvents.sort(
|
},
|
||||||
(a, b) => a.originServerTs
|
scrollToEventId: (String eventId) =>
|
||||||
.compareTo(
|
_scrollToEventId(eventId,
|
||||||
b.originServerTs),
|
context: context),
|
||||||
);
|
longPressSelect:
|
||||||
}
|
selectedEvents.isEmpty,
|
||||||
},
|
selected: selectedEvents.contains(
|
||||||
scrollToEventId: (String eventId) =>
|
filteredEvents[i - 1]),
|
||||||
_scrollToEventId(eventId,
|
timeline: timeline,
|
||||||
context: context),
|
nextEvent: i >= 2
|
||||||
longPressSelect:
|
? filteredEvents[i - 2]
|
||||||
selectedEvents.isEmpty,
|
: null),
|
||||||
selected: selectedEvents
|
),
|
||||||
.contains(filteredEvents[i - 1]),
|
|
||||||
timeline: timeline,
|
|
||||||
nextEvent: i >= 2
|
|
||||||
? filteredEvents[i - 2]
|
|
||||||
: null),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -171,7 +171,6 @@ class _LoginState extends State<Login> {
|
||||||
readOnly: loading,
|
readOnly: loading,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
keyboardType: TextInputType.emailAddress,
|
|
||||||
onChanged: (t) => _checkWellKnownWithCoolDown(t, context),
|
onChanged: (t) => _checkWellKnownWithCoolDown(t, context),
|
||||||
controller: usernameController,
|
controller: usernameController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
|
|
@ -22,6 +22,74 @@ class ChatSettings extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChatSettingsState extends State<ChatSettings> {
|
class _ChatSettingsState extends State<ChatSettings> {
|
||||||
|
String _getActionDescription(String action) {
|
||||||
|
switch (action) {
|
||||||
|
case 'reply':
|
||||||
|
return L10n.of(context).reply;
|
||||||
|
case 'forward':
|
||||||
|
return L10n.of(context).forward;
|
||||||
|
case 'edit':
|
||||||
|
return L10n.of(context).edit;
|
||||||
|
default:
|
||||||
|
return L10n.of(context).none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _changeSwipeAction(bool isToEnd, String action) async {
|
||||||
|
if (isToEnd) {
|
||||||
|
Matrix.of(context).swipeToEndAction = action;
|
||||||
|
await Matrix.of(context)
|
||||||
|
.store
|
||||||
|
.setItem('chat.fluffy.swipeToEndAction', action);
|
||||||
|
setState(() => null);
|
||||||
|
} else {
|
||||||
|
Matrix.of(context).swipeToStartAction = action;
|
||||||
|
await Matrix.of(context)
|
||||||
|
.store
|
||||||
|
.setItem('chat.fluffy.swipeToStartAction', action);
|
||||||
|
setState(() => null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _swipeActionChooser(BuildContext context, bool isToEnd) {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context).none),
|
||||||
|
leading: Icon(Icons.clear_outlined),
|
||||||
|
onTap: () {
|
||||||
|
_changeSwipeAction(isToEnd, null);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context).reply),
|
||||||
|
leading: Icon(Icons.reply_outlined),
|
||||||
|
onTap: () {
|
||||||
|
_changeSwipeAction(isToEnd, 'reply');
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context).forward),
|
||||||
|
leading: Icon(Icons.forward_outlined),
|
||||||
|
onTap: () {
|
||||||
|
_changeSwipeAction(isToEnd, 'forward');
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context).edit),
|
||||||
|
leading: Icon(Icons.edit_outlined),
|
||||||
|
onTap: () {
|
||||||
|
_changeSwipeAction(isToEnd, 'edit');
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -42,6 +110,27 @@ class _ChatSettingsState extends State<ChatSettings> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Divider(thickness: 1),
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context).swipeToEndAction),
|
||||||
|
onTap: () => showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
_swipeActionChooser(context, true),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
_getActionDescription(Matrix.of(context).swipeToEndAction)),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context).swipeToStartAction),
|
||||||
|
onTap: () => showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
_swipeActionChooser(context, false),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
_getActionDescription(Matrix.of(context).swipeToStartAction)),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -553,7 +553,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: master
|
ref: master
|
||||||
resolved-ref: bd24832f96537447174aa34ba78eaed7ff05bb8e
|
resolved-ref: c8eb59c25c4e3a568bd64e4722108ec45259e157
|
||||||
url: "https://gitlab.com/famedly/libraries/native_imaging.git"
|
url: "https://gitlab.com/famedly/libraries/native_imaging.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
@ -870,6 +870,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.1"
|
version: "1.1.0-nullsafety.1"
|
||||||
|
swipe_to_action:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: swipe_to_action
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0"
|
||||||
synchronized:
|
synchronized:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -69,6 +69,7 @@ dependencies:
|
||||||
ref: master
|
ref: master
|
||||||
flutter_blurhash: ^0.5.0
|
flutter_blurhash: ^0.5.0
|
||||||
scroll_to_index: ^1.0.6
|
scroll_to_index: ^1.0.6
|
||||||
|
swipe_to_action: ^0.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
59
snap/snapcraft.yaml
Normal file
59
snap/snapcraft.yaml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
name: fluffychat
|
||||||
|
base: core18 # the base snap is the execution environment for this snap
|
||||||
|
version: git # just for humans, typically '1.2+git' or '1.3.2'
|
||||||
|
summary: Open. Nonprofit. Cute ♥
|
||||||
|
description: |
|
||||||
|
FluffyChat - Chat with your friends
|
||||||
|
|
||||||
|
9 greatest FluffyChat features:
|
||||||
|
1. Opensource and open development where everyone can join.
|
||||||
|
2. Nonprofit - FluffyChat is donation funded.
|
||||||
|
3. Cute design and many theme settings including a dark mode.
|
||||||
|
4. Unlimited groups and direct chats.
|
||||||
|
5. FluffyChat is made as simple to use as possible.
|
||||||
|
6. Free to use for everyone without ads.
|
||||||
|
7. FluffyChat can use your addressbook to find your friends or you can use
|
||||||
|
usernames.
|
||||||
|
8. There is no "FluffyChat server" you are forced to use. Use the server
|
||||||
|
you find trustworthy or host your own.
|
||||||
|
9. Compatible with Riot, Fractal, Nekho and all matrix messengers.
|
||||||
|
|
||||||
|
Join the community: fluffychat://+ubports_community:matrix.org
|
||||||
|
Website: http://fluffy.chat
|
||||||
|
Microblog: https://metalhead.club/@krille
|
||||||
|
|
||||||
|
grade: devel # must be 'stable' to release into candidate/stable channels
|
||||||
|
confinement: strict # use 'strict' once you have the right plugs and slots
|
||||||
|
|
||||||
|
parts:
|
||||||
|
olm:
|
||||||
|
plugin: cmake
|
||||||
|
source: https://gitlab.matrix.org/matrix-org/olm.git
|
||||||
|
source-type: git
|
||||||
|
source-tag: 3.2.1
|
||||||
|
fluffychat:
|
||||||
|
plugin: flutter
|
||||||
|
source: .
|
||||||
|
flutter-target: lib/main.dart
|
||||||
|
stage-packages:
|
||||||
|
- libsqlite3-0
|
||||||
|
override-prime: |
|
||||||
|
snapcraftctl prime
|
||||||
|
ln -sf libsqlite3.so.0 ${SNAPCRAFT_PRIME}/usr/lib/x86_64-linux-gnu/libsqlite3.so
|
||||||
|
|
||||||
|
slots:
|
||||||
|
dbus-svc:
|
||||||
|
interface: dbus
|
||||||
|
bus: session
|
||||||
|
name: chat.fluffy.fluffychat
|
||||||
|
|
||||||
|
apps:
|
||||||
|
fluffychat:
|
||||||
|
command: fluffychat
|
||||||
|
extensions:
|
||||||
|
- flutter-dev
|
||||||
|
plugs:
|
||||||
|
- network
|
||||||
|
- home
|
||||||
|
slots:
|
||||||
|
- dbus-svc
|
Loading…
Reference in a new issue