Merge pull request #678 from pangeachat/merge-analytics
Merge analytics
This commit is contained in:
commit
ee70ccb1f2
224 changed files with 30304 additions and 32854 deletions
27
.github/workflows/issue_pr_management.yaml
vendored
27
.github/workflows/issue_pr_management.yaml
vendored
|
|
@ -1,27 +0,0 @@
|
|||
name: Close Inactive Issues And PRs
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-issue-stale: 120
|
||||
days-before-issue-close: 14
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 120 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
stale-pr-message: "This pull request is stale because it has been open for 120 days with no activity."
|
||||
close-pr-message: "This pull request was closed because it has been inactive for 14 days since being marked as stale."
|
||||
days-before-pr-stale: 120
|
||||
days-before-pr-close: 14
|
||||
exempt-milestones: true
|
||||
exempt-assignees: krille-chan
|
||||
operations-per-run: 500
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
|
|
@ -103,7 +103,7 @@ jobs:
|
|||
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: 3.19.6 # Workaround for not working on 3.22
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 libssl-dev -y
|
||||
|
|
|
|||
2
.github/workflows/versions.env
vendored
2
.github/workflows/versions.env
vendored
|
|
@ -1,2 +1,2 @@
|
|||
FLUTTER_VERSION=3.22.2
|
||||
FLUTTER_VERSION=3.24.1
|
||||
JAVA_VERSION=17
|
||||
|
|
|
|||
111
CHANGELOG.md
111
CHANGELOG.md
|
|
@ -1,3 +1,114 @@
|
|||
## v1.22.0
|
||||
|
||||
FluffyChat v1.22.0 brings a new design for spaces, replaces the bottom navigation bar with filter chips and makes it finally possible to play ogg audio messages on iOS. A lot of other fixes and improvements have also been added to this release.
|
||||
|
||||
FluffyChat also now uses the new authenticated media endpoints if the server supports Matrix v1.11 or
|
||||
mentions the msc with the key `org.matrix.msc3916.stable` in the `unstable_features`.
|
||||
|
||||
- build: (deps): bump docker/build-push-action from 5 to 6 (dependabot[bot])
|
||||
- build(deps): bump rexml from 3.2.8 to 3.3.3 in /ios (dependabot[bot])
|
||||
- build: Remove permissions for screensharing until it is fixed (Krille)
|
||||
- build: Update android target sdk to 34 (Krille)
|
||||
- build: Update dependencies after release (krille-chan)
|
||||
- build: Update to Flutter 3.22.3 (krille-chan)
|
||||
- build: Update to Matrix SDK 0.32.0 (Krille)
|
||||
- chore: Bring back add to space feature (Krille)
|
||||
- chore: Bring back navrail (krille-chan)
|
||||
- chore: Bring back separate chat types (krille-chan)
|
||||
- chore: Chat permissions page follow up (krille-chan)
|
||||
- chore: Do not hide error on file sending (Krille)
|
||||
- chore: Improved create group and space design (Krille)
|
||||
- chore: Make VOIP plugin less noisy in logs (krille-chan)
|
||||
- chore: Move default PR template to correct dir (krille-chan)
|
||||
- chore: nicer bottom sheets (krille-chan)
|
||||
- chore: Nicer empty chat list placeholder (krille-chan)
|
||||
- chore: Polish public room bottom sheet (krille-chan)
|
||||
- chore: Show short forms of months and week days in UI (krille-chan)
|
||||
- chore: Sligthly improve chat permissions page design (krille-chan)
|
||||
- design: Add snackbar with link to changelog on new version (Krille)
|
||||
- docs: Update privacy policy (krille-chan)
|
||||
- feat: Support for matrix auth media endpoints
|
||||
- feat: Convert opus to aac on iOS before playing (Krille)
|
||||
- feat: New spaces and chat list design (krille-chan)
|
||||
- feat: Record voice message with opus/ogg if supported (Krille)
|
||||
- feat: Send voice messages from web (Krille)
|
||||
- fix: Display only available join rules (Krille)
|
||||
- fix: Path correct userId to ignore list (krille-chan)
|
||||
- fix: Scroll to event missing the position (Krille)
|
||||
- Fix web base url and privacy url configuration processing (dlyrsk)
|
||||
- refactor: Clean up some widths (krille-chan)
|
||||
- refactor: Design polishment and better user viewer (Krille)
|
||||
- refactor: Migrate android gradle plugin (Krille)
|
||||
- refactor: Only initialize FlutterLocalNotificationsPlugin once (krille-chan)
|
||||
- refactor: Recording dialog (Krille)
|
||||
- Refactor: Reduce .of(context) calls theme (Thomas Klein Langenhorst)
|
||||
- refactor: Use cached network image for mxc image uris (Krille)
|
||||
- Translated using Weblate (Arabic) (kdh8219)
|
||||
- Translated using Weblate (Arabic) (Rex_sa)
|
||||
- Translated using Weblate (Basque) (kdh8219)
|
||||
- Translated using Weblate (Basque) (xabirequejo)
|
||||
- Translated using Weblate (Chinese (Simplified)) (kdh8219)
|
||||
- Translated using Weblate (Chinese (Simplified)) (大王叫我来巡山)
|
||||
- Translated using Weblate (Chinese (Traditional)) (kdh8219)
|
||||
- Translated using Weblate (Chinese (Traditional)) (Lukas)
|
||||
- Translated using Weblate (Chinese (Traditional)) (Ricky From Hong Kong)
|
||||
- Translated using Weblate (Chinese (Traditional)) (不知火 Shiranui)
|
||||
- Translated using Weblate (Croatian) (Milo Ivir)
|
||||
- Translated using Weblate (Czech) (Anonymous)
|
||||
- Translated using Weblate (Czech) (Michal Bedáň)
|
||||
- Translated using Weblate (Dutch) (Guacamolie)
|
||||
- Translated using Weblate (Dutch) (Jelv)
|
||||
- Translated using Weblate (Dutch) (Thomas Klein Langenhorst)
|
||||
- Translated using Weblate (Esperanto) (Anonymous)
|
||||
- Translated using Weblate (Estonian) (kdh8219)
|
||||
- Translated using Weblate (Estonian) (Priit Jõerüüt)
|
||||
- Translated using Weblate (Finnish) (Anonymous)
|
||||
- Translated using Weblate (French) (Sovkipyk)
|
||||
- Translated using Weblate (Galician) (josé m)
|
||||
- Translated using Weblate (German) (Christian)
|
||||
- Translated using Weblate (German) (Pixelcode)
|
||||
- Translated using Weblate (German) (tct123)
|
||||
- Translated using Weblate (Hebrew) (Anonymous)
|
||||
- Translated using Weblate (Indonesian) (Linerly)
|
||||
- Translated using Weblate (Irish) (Anonymous)
|
||||
- Translated using Weblate (Japanese) (Anonymous)
|
||||
- Translated using Weblate (Korean) (kdh8219)
|
||||
- Translated using Weblate (Lithuanian) (Anonymous)
|
||||
- Translated using Weblate (Norwegian Bokmål) (Anonymous)
|
||||
- Translated using Weblate (Persian) (Anonymous)
|
||||
- Translated using Weblate (Portuguese (Portugal)) (Anonymous)
|
||||
- Translated using Weblate (Romanian) (Anonymous)
|
||||
- Translated using Weblate (Russian) (-)
|
||||
- Translated using Weblate (Serbian) (Anonymous)
|
||||
- Translated using Weblate (Slovenian) (Anonymous)
|
||||
- Translated using Weblate (Spanish) (Anonymous)
|
||||
- Translated using Weblate (Turkish) (kdh8219)
|
||||
- Translated using Weblate (Turkish) (Oğuz Ersen)
|
||||
- Translated using Weblate (Ukrainian) (Bezruchenko Simon)
|
||||
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
|
||||
|
||||
## v1.21.2
|
||||
Updates the Matrix Dart SDK to fix some minor bugs.
|
||||
|
||||
- Added translation using Weblate (Lojban) (Zig-Rust-Odin)
|
||||
- build: Update matrix dart sdk (Krille)
|
||||
- chore: Update last event after decryption (Krille)
|
||||
- fix: Correctly localize time of date (Krille)
|
||||
- refactor: Omit local types (Krille)
|
||||
- Translated using Weblate (Arabic) (Rex_sa)
|
||||
- Translated using Weblate (Basque) (xabirequejo)
|
||||
- Translated using Weblate (Chinese (Simplified)) (大王叫我来巡山)
|
||||
- Translated using Weblate (Croatian) (Milo Ivir)
|
||||
- Translated using Weblate (Estonian) (Priit Jõerüüt)
|
||||
- Translated using Weblate (Galician) (josé m)
|
||||
- Translated using Weblate (Hungarian) (H Tamás)
|
||||
- Translated using Weblate (Korean) (kdh8219)
|
||||
- Translated using Weblate (Latvian) (Edgars Andersons)
|
||||
- Translated using Weblate (Polish) (Eryk Michalak)
|
||||
- Translated using Weblate (Portuguese (Brazil)) (lucasmz-dev)
|
||||
- Translated using Weblate (Russian) (Nicholas Winterhalter)
|
||||
- Translated using Weblate (Turkish) (Oğuz Ersen)
|
||||
|
||||
## v1.21.1
|
||||
- build: Update Matrix Dart SDK (Krille)
|
||||
- build: Update to Flutter 3.22.2 (krille-chan)
|
||||
|
|
|
|||
30
PRIVACY.md
30
PRIVACY.md
|
|
@ -3,31 +3,29 @@
|
|||
FluffyChat is available on Android, iOS and as a web version. Desktop versions for Windows, Linux and macOS may follow.
|
||||
|
||||
* [Matrix](#matrix)
|
||||
* Sentry
|
||||
* [Database](#database)
|
||||
* [Encryption](#encryption)
|
||||
* [App Permissions](#app-permissions)
|
||||
* [Push Notifications](#push-notifications)
|
||||
* [Stories](#stories)
|
||||
|
||||
## Matrix<a id="matrix"/>
|
||||
FluffyChat uses the Matrix protocol. This means that FluffyChat is just a client that can be connected to any compatible matrix server. The respective data protection agreement of the server selected by the user then applies.
|
||||
|
||||
For convenience, one or more servers are set as default that the FluffyChat developers consider trustworthy. The developers of FluffyChat do not guarantee their trustworthiness. Before the first communication, users are informed which server they are connecting to.
|
||||
|
||||
FluffyChat only communicates with the selected server, with sentry.io if enabled and with [OpenStreetMap](https://openstreetmap.org) to display maps.
|
||||
FluffyChat only communicates with the selected server and with [OpenStreetMap](https://openstreetmap.org) to display maps.
|
||||
|
||||
More information is available at: [https://matrix.org](https://matrix.org)
|
||||
|
||||
## Database<a id="database"/>
|
||||
FluffyChat caches some data received from the server in a local database on the device of the user.
|
||||
FluffyChat caches some data received from the server in a local sqflite database on the device of the user. On web indexedDB is used. FluffyChat always tries to encrypt the database by using SQLCipher and stores the encryption key in the [Secure Storage](https://pub.dev/packages/flutter_secure_storage) of the device.
|
||||
|
||||
More information is available at: [https://pub.dev/packages/hive](https://pub.dev/packages/hive)
|
||||
More information is available at: [https://pub.dev/packages/sqflite](https://pub.dev/packages/sqflite) and [https://pub.dev/packages/sqlcipher_flutter_libs](https://pub.dev/packages/sqlcipher_flutter_libs)
|
||||
|
||||
## Encryption<a id="encryption"/>
|
||||
All communication of substantive content between Fluffychat and any server is done in secure way, using transport encryption to protect it.
|
||||
|
||||
FluffyChat is able to use End-To-End-Encryption as a tech preview.
|
||||
FluffyChat also uses End-To-End-Encryption by using [libolm](https://gitlab.matrix.org/matrix-org/olm) and enables it by default for private chats.
|
||||
|
||||
## App Permissions<a id="app-permissions"/>
|
||||
|
||||
|
|
@ -94,23 +92,3 @@ A typical push notification could look like this:
|
|||
```
|
||||
|
||||
FluffyChat sets the `event_id_only` flag at the Matrix Server. This server is then responsible to send the correct data.
|
||||
|
||||
## Stories<a id="stories"/>
|
||||
|
||||
FluffyChat supports stories which is a feature similar to WhatsApp status or Instagram stories. However it is just a different GUI for the same room-related communication. More information about the feature can be found here:
|
||||
|
||||
https://github.com/krillefear/matrix-doc/blob/main/proposals/3588-stories-as-rooms.md
|
||||
|
||||
Stories are basically:
|
||||
|
||||
- End to end encrypted rooms
|
||||
- Read-only rooms with only one admin who can post stuff (while there is no technical limitation to have multiple admins)
|
||||
|
||||
By default:
|
||||
|
||||
- The user has to invite all contacts manually to a story room
|
||||
- The user can only invite contacts (matrix users the user shares a DM room with) to the story room
|
||||
- The story room is created when the first story is posted
|
||||
- User can mute and leave story rooms
|
||||
|
||||
The user is informed in the app that in theory all contacts can see each other in the story room. The user must give consent here. However the user is at any time able to create a group chat and invite all of their contacts to this chat in any matrix client which has the same result.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
//id "com.google.gms.google-services"
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
|
|
@ -6,11 +13,6 @@ if (localPropertiesFile.exists()) {
|
|||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
|
|
@ -21,11 +23,6 @@ if (flutterVersionName == null) {
|
|||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
|
|
@ -44,7 +41,10 @@ android {
|
|||
}
|
||||
|
||||
defaultConfig {
|
||||
// #Pangea
|
||||
// applicationId "chat.fluffy.fluffychat"
|
||||
applicationId "com.talktolearn.chat"
|
||||
// Pangea#
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
|
|
@ -66,8 +66,7 @@ android {
|
|||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
applicationIdSuffix ""
|
||||
versionNameSuffix ""
|
||||
versionNameSuffix "-debug"
|
||||
}
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
|
|
@ -87,9 +86,6 @@ flutter {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
//implementation 'com.google.firebase:firebase-messaging:19.0.1' // Workaround for https://github.com/microg/android_packages_apps_GmsCore/issues/313#issuecomment-617651698
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
}
|
||||
|
||||
//apply plugin: 'com.google.gms.google-services'
|
||||
}
|
||||
|
|
@ -18,14 +18,12 @@
|
|||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"
|
||||
android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
|
||||
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<!-- #Pangea -->
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
|
|
|
|||
|
|
@ -1,17 +1,3 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.8.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.google.gms:google-services:4.3.13'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
|
|
@ -29,4 +15,4 @@ subprojects {
|
|||
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,26 @@
|
|||
include ':app'
|
||||
pluginManagement {
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.1.2" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.0" apply false
|
||||
// id "com.google.gms.google-services" version "4.3.8" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
|
@ -736,7 +736,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"oopsSomethingWentWrong": "عذراً، هناك خطأ ما…",
|
||||
"oopsSomethingWentWrong": "عفوًا، حدث خطأ ما…",
|
||||
"@oopsSomethingWentWrong": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1821,7 +1821,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "مستوى الأذونات الإفتراضي",
|
||||
"defaultPermissionLevel": "مستوى الأذونات الافتراضية للمستخدمين الجدد",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2260,7 +2260,7 @@
|
|||
},
|
||||
"jump": "قفز",
|
||||
"@jump": {},
|
||||
"report": "التقرير",
|
||||
"report": "تقرير",
|
||||
"@report": {},
|
||||
"noKeyForThisMessage": "يمكن أن يحدث هذا إذا تم إرسال الرسالة قبل تسجيل الدخول إلى حسابك على هذا الجهاز.\n\nمن الممكن أيضا أن يكون المرسل قد حظر جهازك أو حدث خطأ ما في الاتصال بالإنترنت.\n\nهل يمكنك قراءة الرسالة في جلسة أخرى؟ ثم يمكنك نقل الرسالة منه! انتقل إلى الإعدادات > الأجهزة وتأكد من أن أجهزتك قد تحققت من بعضها البعض. عندما تفتح الغرفة في المرة التالية وتكون كلتا الجلستين في المقدمة ، سيتم إرسال المفاتيح تلقائيا.\n\nألا تريد أن تفقد المفاتيح عند تسجيل الخروج أو تبديل الأجهزة؟ تأكد من تمكين النسخ الاحتياطي للدردشة في الإعدادات.",
|
||||
"@noKeyForThisMessage": {},
|
||||
|
|
@ -2706,5 +2706,102 @@
|
|||
"searchMore": "ابحث أكثر...",
|
||||
"@searchMore": {},
|
||||
"gallery": "المعرض",
|
||||
"@gallery": {}
|
||||
"@gallery": {},
|
||||
"swipeRightToLeftToReply": "اسحب من اليمين إلى اليسار للرد",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"countChatsAndCountParticipants": "{chats} دردشات و {participants} مشاركين",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"noMoreChatsFound": "لم يتم العثور على دردشات...",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "انضم إلى الدردشة",
|
||||
"@joinedChats": {},
|
||||
"unread": "غير المقروءة",
|
||||
"@unread": {},
|
||||
"space": "المساحة",
|
||||
"@space": {},
|
||||
"spaces": "المساحات",
|
||||
"@spaces": {},
|
||||
"markAsUnread": "تحديد كغير مقروء",
|
||||
"@markAsUnread": {},
|
||||
"goToSpace": "انتقل إلى المساحة: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"userLevel": "{level} - مستخدم",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - مشرف",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - مدير",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeGeneralChatSettings": "تغيير إعدادات الدردشة العامة",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"inviteOtherUsers": "دعوة مستخدمين آخرين إلى هذه الدردشة",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheChatPermissions": "تغيير أذونات الدردشة",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheVisibilityOfChatHistory": "تغيير رؤية سجل الدردشة",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"changeTheCanonicalRoomAlias": "تغيير عنوان الدردشة العامة الرئيسي",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"sendRoomNotifications": "إرسال إشعارات @room",
|
||||
"@sendRoomNotifications": {},
|
||||
"changeTheDescriptionOfTheGroup": "تغيير وصف الدردشة",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"invitedBy": "📩 تمت دعوته من قبل {user}",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"chatPermissionsDescription": "حدد مستوى الصلاحية الضروري لإجراءات معينة في هذه الدردشة. عادة ما تمثل مستويات الصلاحية 0 و 50 و 100 المستخدمين والمشرفين ولكن أي تدرج ممكن.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"changelog": "سجل التغييرات",
|
||||
"@changelog": {},
|
||||
"updateInstalled": "تم تثبيت🎉 تحديث {version}!",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"sendCanceled": "تم إلغاء الإرسال",
|
||||
"@sendCanceled": {},
|
||||
"noChatsFoundHere": "لم يتم العثور على دردشات هنا حتى الآن. ابدأ محادثة جديدة مع شخص ما باستخدام الزر أدناه. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"loginWithMatrixId": "تسجيل الدخول باستخدام معرف ماتريكس",
|
||||
"@loginWithMatrixId": {},
|
||||
"discoverHomeservers": "اكتشف الخوادم المنزلية",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "ما هو خادم المنزل ؟",
|
||||
"@whatIsAHomeserver": {},
|
||||
"homeserverDescription": "يتم تخزين جميع بياناتك على خادم المنزل، تمامًا مثل مزود خدمة البريد الإلكتروني. يمكنك اختيار خادم البيت الذي تريد استخدامه، بينما لا يزال بإمكانك التواصل مع الجميع. اعرف المزيد على https://matrix.org.",
|
||||
"@homeserverDescription": {},
|
||||
"doesNotSeemToBeAValidHomeserver": "لا يبدو أنه خادم منزلي متوافق. عنوان URL غير صحيح ؟",
|
||||
"@doesNotSeemToBeAValidHomeserver": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2349,48 +2349,11 @@
|
|||
"count": {}
|
||||
}
|
||||
},
|
||||
"@reportErrorDescription": {},
|
||||
"@banUserDescription": {},
|
||||
"@removeDevicesDescription": {},
|
||||
"@unbanUserDescription": {},
|
||||
"@pushNotificationsNotAvailable": {},
|
||||
"@makeAdminDescription": {},
|
||||
"@archiveRoomDescription": {},
|
||||
"@invalidInput": {},
|
||||
"@report": {},
|
||||
"@hasKnocked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@wrongPinEntered": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"@inviteGroupChat": {},
|
||||
"@invitePrivateChat": {},
|
||||
"@learnMore": {},
|
||||
"@roomUpgradeDescription": {},
|
||||
"@pleaseEnterANumber": {},
|
||||
"emoteKeyboardNoRecents": "Naposledy použité emoce se zobrazí zde...",
|
||||
"@emoteKeyboardNoRecents": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@kickUserDescription": {},
|
||||
"@invite": {},
|
||||
"@indexedDbErrorLong": {},
|
||||
"@callingAccount": {},
|
||||
"@enterSpace": {},
|
||||
"@noKeyForThisMessage": {},
|
||||
"@readUpToHere": {},
|
||||
"@appearOnTopDetails": {},
|
||||
"@enterRoom": {},
|
||||
"@hideUnimportantStateEvents": {},
|
||||
"@noBackupWarning": {},
|
||||
"@indexedDbErrorTitle": {},
|
||||
"appLockDescription": "Zamknout aplikaci pomocí PIN kódu když není používána",
|
||||
"@appLockDescription": {},
|
||||
"globalChatId": "Globální ID chatu",
|
||||
|
|
@ -2404,5 +2367,17 @@
|
|||
"accessAndVisibilityDescription": "Kdo se může připojit a najít tuto konverzaci.",
|
||||
"@accessAndVisibilityDescription": {},
|
||||
"customEmojisAndStickersBody": "Přidat nebo sdílet vlastní emoji nebo nálepky, které mohou být použité v konverzaci.",
|
||||
"@customEmojisAndStickersBody": {}
|
||||
"@customEmojisAndStickersBody": {},
|
||||
"swipeRightToLeftToReply": "Potáhněte z prava do leva pro odpověď",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"countChatsAndCountParticipants": "{chats} konverzaci a {participants} účastníci",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"noMoreChatsFound": "Žádné další konverzace nalezeny...",
|
||||
"@noMoreChatsFound": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
{
|
||||
"@@locale": "de",
|
||||
"@@last_modified": "2021-08-14 12:41:10.119255",
|
||||
"alwaysUse24HourFormat": "true",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"about": "Über",
|
||||
"@about": {
|
||||
"type": "text",
|
||||
|
|
@ -356,7 +360,7 @@
|
|||
"type": "text",
|
||||
"description": "Usage hint for the command /invite"
|
||||
},
|
||||
"commandHint_join": "Betrete den ausgewählten Raum",
|
||||
"commandHint_join": "Betritt den ausgewählten Raum",
|
||||
"@commandHint_join": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /join"
|
||||
|
|
@ -554,7 +558,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "Standardberechtigungsstufe",
|
||||
"defaultPermissionLevel": "Standardberechtigungsstufe für neue Benutzer",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1080,7 +1084,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"noGoogleServicesWarning": "Firebase Cloud Messaging scheint auf deinem Gerät nicht verfügbar zu sein. Um trotzdem Push-Benachrichtigungen zu erhalten, empfehlen wir die Installation von ntfy. Mit ntfy oder einem anderen Unified Push Anbieter kannst du Push-Benachrichtigungen datensicher empfangen. Du kannst ntfy im PlayStore oder bei F-Droid herunterladen.",
|
||||
"noGoogleServicesWarning": "Firebase Cloud Messaging scheint auf deinem Gerät nicht verfügbar zu sein. Um trotzdem Push-Benachrichtigungen zu erhalten, empfehlen wir die Installation von ntfy. Mit ntfy oder einem anderen Unified-Push-Anbieter kannst du Push-Benachrichtigungen datensicher empfangen. Du kannst ntfy im PlayStore oder bei F-Droid herunterladen.",
|
||||
"@noGoogleServicesWarning": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1663,7 +1667,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"tryToSendAgain": "Nochmal versuchen zu senden",
|
||||
"tryToSendAgain": "Noch mal versuchen zu senden",
|
||||
"@tryToSendAgain": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1708,7 +1712,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"unpin": "Abpinnen",
|
||||
"unpin": "Nicht mehr anpinnen",
|
||||
"@unpin": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1961,7 +1965,7 @@
|
|||
"@start": {},
|
||||
"repeatPassword": "Passwort wiederholen",
|
||||
"@repeatPassword": {},
|
||||
"commandHint_dm": "Starte einen direkten Chat\nBenutze --no-encryption um die Verschlüsselung auszuschalten",
|
||||
"commandHint_dm": "Starte einen direkten Chat\nBenutze --no-encryption, um die Verschlüsselung auszuschalten",
|
||||
"@commandHint_dm": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /dm"
|
||||
|
|
@ -1976,7 +1980,7 @@
|
|||
"type": "text",
|
||||
"description": "Usage hint for the command /clearcache"
|
||||
},
|
||||
"commandHint_create": "Erstelle ein leeren Gruppenchat\nBenutze --no-encryption um die Verschlüsselung auszuschalten",
|
||||
"commandHint_create": "Erstelle ein leeren Gruppenchat\nBenutze --no-encryption, um die Verschlüsselung auszuschalten",
|
||||
"@commandHint_create": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /create"
|
||||
|
|
@ -2009,7 +2013,7 @@
|
|||
"@videoCallsBetaWarning": {},
|
||||
"emailOrUsername": "E-Mail oder Benutzername",
|
||||
"@emailOrUsername": {},
|
||||
"unsupportedAndroidVersionLong": "Diese Funktion erfordert eine neuere Android-Version. Bitte suche nach Updates oder Lineage OS-Unterstützung.",
|
||||
"unsupportedAndroidVersionLong": "Diese Funktion erfordert eine neuere Android-Version. Bitte suche nach Updates oder prüfe die Lineage-OS-Unterstützung.",
|
||||
"@unsupportedAndroidVersionLong": {},
|
||||
"experimentalVideoCalls": "Experimentelle Videoanrufe",
|
||||
"@experimentalVideoCalls": {},
|
||||
|
|
@ -2147,7 +2151,7 @@
|
|||
"@saveKeyManuallyDescription": {},
|
||||
"hydrateTorLong": "Hast du deine Sitzung das letzte Mal auf TOR exportiert? Importiere sie schnell und chatte weiter.",
|
||||
"@hydrateTorLong": {},
|
||||
"pleaseEnterRecoveryKey": "Bitte gebe deinen Wiederherstellungsschlüssel ein:",
|
||||
"pleaseEnterRecoveryKey": "Bitte gib deinen Wiederherstellungsschlüssel ein:",
|
||||
"@pleaseEnterRecoveryKey": {},
|
||||
"countFiles": "{count} Dateien",
|
||||
"@countFiles": {
|
||||
|
|
@ -2261,7 +2265,7 @@
|
|||
"senderName": {}
|
||||
}
|
||||
},
|
||||
"commandHint_googly": "Googly Eyes senden",
|
||||
"commandHint_googly": "Glupschaugen senden",
|
||||
"@commandHint_googly": {},
|
||||
"disableEncryptionWarning": "Aus Sicherheitsgründen können Sie die Verschlüsselung in einem Chat nicht deaktivieren, wo sie zuvor aktiviert wurde.",
|
||||
"@disableEncryptionWarning": {},
|
||||
|
|
@ -2269,7 +2273,7 @@
|
|||
"@reopenChat": {},
|
||||
"fileIsTooBigForServer": "Der Server meldet, dass die Datei zu groß ist für eine Übermittlung ist.",
|
||||
"@fileIsTooBigForServer": {},
|
||||
"noBackupWarning": "Achtung! Ohne Aktivierung des Chat-Backups verlierst du den Zugriff auf deine verschlüsselten Nachrichten. Vor dem Ausloggen wird dringend empfohlen den Chat-Backup zu aktivieren.",
|
||||
"noBackupWarning": "Achtung! Ohne Aktivierung des Chat-Backups verlierst du den Zugriff auf deine verschlüsselten Nachrichten. Vor dem Ausloggen wird dringend empfohlen, das Chat-Backup zu aktivieren.",
|
||||
"@noBackupWarning": {},
|
||||
"noOtherDevicesFound": "Keine anderen Geräte anwesend",
|
||||
"@noOtherDevicesFound": {},
|
||||
|
|
@ -2585,7 +2589,7 @@
|
|||
"sender": {}
|
||||
}
|
||||
},
|
||||
"verifyOtherDeviceDescription": "Wenn Sie ein anderes Gerät verifizieren, können diese Geräteschlüssel austauschen, was Ihre Sicherheit insgesamt erhöht. 💪 Wenn Sie eine Verifizierung starten, erscheint ein Pop-up in der App auf beiden Geräten. Dort sehen Sie dann eine Reihe von Emojis oder Zahlen, die Sie miteinander vergleichen müssen. Am besten hältst du beide Geräte bereit, bevor du die Verifizierung startest. 🤳",
|
||||
"verifyOtherDeviceDescription": "Wenn Sie ein anderes Gerät verifizieren, können diese Geräteschlüssel austauschen, was Ihre Sicherheit insgesamt erhöht. 💪 Wenn Sie eine Verifizierung starten, erscheint ein Pop-up in der App auf beiden Geräten. Dort sehen Sie dann eine Reihe von Emojis oder Zahlen, die Sie miteinander vergleichen müssen. Am besten halten Sie beide Geräte bereit, bevor Sie die Verifizierung starten. 🤳",
|
||||
"@verifyOtherDeviceDescription": {},
|
||||
"presenceStyle": "Statusmeldungen:",
|
||||
"@presenceStyle": {
|
||||
|
|
@ -2706,5 +2710,88 @@
|
|||
}
|
||||
},
|
||||
"searchMore": "Weiter suchen ...",
|
||||
"@searchMore": {}
|
||||
"@searchMore": {},
|
||||
"unread": "Ungelesen",
|
||||
"@unread": {},
|
||||
"noMoreChatsFound": "Keine weiteren Chats gefunden ...",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "Beigetretene Chats",
|
||||
"@joinedChats": {},
|
||||
"space": "Space",
|
||||
"@space": {},
|
||||
"spaces": "Spaces",
|
||||
"@spaces": {},
|
||||
"goToSpace": "Geh zum Space: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"markAsUnread": "Als ungelesen markieren",
|
||||
"@markAsUnread": {},
|
||||
"swipeRightToLeftToReply": "Wische von rechts nach links zum Antworten",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"countChatsAndCountParticipants": "{chats} Chats und {participants} Teilnehmer",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"changeGeneralChatSettings": "Allgemeine Chat-Einstellungen ändern",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"userLevel": "{level} - Benutzer",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - Moderator",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeTheChatPermissions": "Ändere die Chat-Berechtigungen",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheVisibilityOfChatHistory": "Wechsele die Sichtbarkeit der Chat-Historie",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"chatPermissionsDescription": "Definieren Sie, welche Befugnisstufe für bestimmte Aktionen in diesem Chat erforderlich ist. Die Befugnisstufen 0, 50 und 100 stehen üblicherweise für Benutzer, Moderatoren und Admins, aber jede Abstufung ist möglich.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"invitedBy": "📩 Eingeladen von {user}",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - Administrator",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"inviteOtherUsers": "Lade andere Benutzer in diesen Chat ein",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheCanonicalRoomAlias": "Ändern der Hauptadresse für den öffentlichen Chat",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"sendRoomNotifications": "Senden Sie eine @room-Benachrichtigung",
|
||||
"@sendRoomNotifications": {},
|
||||
"changeTheDescriptionOfTheGroup": "Ändern Sie die Beschreibung des Chats",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"updateInstalled": "🎉 Update {version} installiert!",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"changelog": "Änderungsprotokoll",
|
||||
"@changelog": {},
|
||||
"sendCanceled": "Senden abgebrochen",
|
||||
"@sendCanceled": {},
|
||||
"noChatsFoundHere": "Hier wurden noch keine Chats gefunden. Starten Sie einen neuen Chat mit jemandem, indem Sie die Schaltfläche unten verwenden. ⤵️",
|
||||
"@noChatsFoundHere": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
{
|
||||
"@@locale": "en",
|
||||
"@@last_modified": "2021-08-14 12:38:37.885451",
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"repeatPassword": "Repeat password",
|
||||
"@repeatPassword": {},
|
||||
"notAnImage": "Not an image file.",
|
||||
|
|
@ -197,6 +201,20 @@
|
|||
"supportedVersions": {}
|
||||
}
|
||||
},
|
||||
"countChatsAndCountParticipants": "{chats} chats and {participants} participants",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"noMoreChatsFound": "No more chats found...",
|
||||
"noChatsFoundHere": "No chats found here yet. Start a new chat with someone by using the button below. ⤵️",
|
||||
"joinedChats": "Joined chats",
|
||||
"unread": "Unread",
|
||||
"space": "Space",
|
||||
"spaces": "Spaces",
|
||||
"banFromChat": "Ban from chat",
|
||||
"@banFromChat": {
|
||||
"type": "text",
|
||||
|
|
@ -660,7 +678,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "Default permission level",
|
||||
"defaultPermissionLevel": "Default permission level for new users",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -3885,7 +3903,7 @@
|
|||
"define": "Define",
|
||||
"listen": "Listen",
|
||||
"addConversationBot": "Enable Conversation Bot",
|
||||
"addConversationBotDesc": "Add a bot to this group chat",
|
||||
"addConversationBotDesc": "Add a bot to this chat",
|
||||
"convoBotSettingsTitle": "Conversation Bot Settings",
|
||||
"convoBotSettingsDescription": "Edit conversation topic and difficulty",
|
||||
"enterAConversationTopic": "Enter a conversation topic",
|
||||
|
|
@ -3950,7 +3968,7 @@
|
|||
"seeOptions": "See options",
|
||||
"continuedWithoutSubscription": "Continue without subscribing",
|
||||
"trialPeriodExpired": "Your trial period has expired",
|
||||
"selectToDefine": "Double click a word to see its definition!",
|
||||
"selectToDefine": "Highlight a word or phrase to see its definition!",
|
||||
"translations": "translations",
|
||||
"messageAudio": "message audio",
|
||||
"definitions": "definitions",
|
||||
|
|
@ -4095,11 +4113,11 @@
|
|||
"placeholders": {}
|
||||
},
|
||||
"addChatToSpaceDesc": "Adding a chat to a space will make the chat appear within the space for students and give them access.",
|
||||
"addSpaceToSpaceDesc": "Adding a space to another space will make the child space appear within the parent space for students and give them access.",
|
||||
"addSpaceToSpaceDesc": "Adding a sub space to space will make the sub space appear in the main space's chat list.",
|
||||
"spaceAnalytics": "Space Analytics",
|
||||
"changeAnalyticsLanguage": "Change Analytics Language",
|
||||
"suggestToSpace": "Suggest this space",
|
||||
"suggestToSpaceDesc": "Suggested spaces will appear in the chat lists for their parent spaces",
|
||||
"suggestToSpaceDesc": "Suggested sub spaces will appear in their main space's chat list",
|
||||
"practice": "Practice",
|
||||
"noLanguagesSet": "No languages set",
|
||||
"noActivitiesFound": "No practice activities found for this message",
|
||||
|
|
@ -4152,7 +4170,63 @@
|
|||
"createChat": "Create chat",
|
||||
"error520Title": "Please try again.",
|
||||
"error520Desc": "Sorry, we could not understand your message...",
|
||||
"wordsUsed": "Words Used",
|
||||
"errorTypes": "Error Types",
|
||||
"level": "Level",
|
||||
"canceledSend": "Canceled send",
|
||||
"morphsUsed": "Morphs Used",
|
||||
"translationChoicesBody": "Click and hold an option for a hint.",
|
||||
"sendCanceled": "Sending canceled",
|
||||
"space": "Space"
|
||||
"goToSpace": "Go to space: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"markAsUnread": "Mark as unread",
|
||||
"userLevel": "{level} - User",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - Moderator",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - Admin",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeGeneralChatSettings": "Change general chat settings",
|
||||
"inviteOtherUsers": "Invite other users to this chat",
|
||||
"changeTheChatPermissions": "Change the chat permissions",
|
||||
"changeTheVisibilityOfChatHistory": "Change the visibility of the chat history",
|
||||
"changeTheCanonicalRoomAlias": "Change the main public chat address",
|
||||
"sendRoomNotifications": "Send a @room notifications",
|
||||
"changeTheDescriptionOfTheGroup": "Change the description of the chat",
|
||||
"chatPermissionsDescription": "Define which power level is necessary for certain actions in this chat. The power levels 0, 50 and 100 are usually representing users, moderators and admins, but any gradation is possible.",
|
||||
"updateInstalled": "🎉 Update {version} installed!",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"changelog": "Changelog",
|
||||
"loginWithMatrixId": "Login with Matrix-ID",
|
||||
"discoverHomeservers": "Discover homeservers",
|
||||
"whatIsAHomeserver": "What is a homeserver?",
|
||||
"homeserverDescription": "All your data is stored on the homeserver, just like an email provider. You can choose which homeserver you want to use, while you can still communicate with everyone. Learn more at at https://matrix.org.",
|
||||
"doesNotSeemToBeAValidHomeserver": "Doesn't seem to be a compatible homeserver. Wrong URL?",
|
||||
"grammar": "Grammar",
|
||||
"contactHasBeenInvitedToTheChat": "Contact has been invited to the chat",
|
||||
"inviteChat": "📨 Invite chat",
|
||||
"chatName": "Chat name"
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -559,7 +559,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "Vaikimisi õigused",
|
||||
"defaultPermissionLevel": "Vaikimisi õigused uutele kasutajatele",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1085,7 +1085,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"noGoogleServicesWarning": "Tundub, et sinu nutiseadmes pole Firebase Cloud Messaging teenuseid. Sinu privaatsuse mõttes on see kindlasti hea otsus! Kui sa soovid FluffyChat'is näha tõuketeavitusi, siis soovitame, et selle jaoks kasutad ntfy liidestust. Kasutades ntfy'd või mõnda muud Unified Push standardil põhinevat liidestust saad tõuketeavitusi turvalisel moel. Ntfy rakendus on saadaval nii PlayStore kui F-Droid'i rakendusepoodides.",
|
||||
"noGoogleServicesWarning": "Tundub, et sinu nutiseadmes pole Firebase Cloud Messaging teenuseid. Sinu privaatsuse mõttes on see kindlasti hea otsus! Kui sa soovid FluffyChatis näha tõuketeavitusi, siis soovitame, et selle jaoks kasutad ntfy liidestust. Kasutades ntfyd või mõnda muud Unified Push standardil põhinevat liidestust saad tõuketeavitusi turvalisel moel. Ntfy rakendus on saadaval nii PlayStore kui F-Droidi rakendusepoodides.",
|
||||
"@noGoogleServicesWarning": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1150,7 +1150,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"ok": "sobib",
|
||||
"ok": "Sobib",
|
||||
"@ok": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2020,7 +2020,7 @@
|
|||
"@emailOrUsername": {},
|
||||
"experimentalVideoCalls": "Katselised videokõned",
|
||||
"@experimentalVideoCalls": {},
|
||||
"unsupportedAndroidVersionLong": "See funktsionaalsus eeldab uuemat Androidi versiooni. Palun kontrolli, kas sinu nutiseadmele leidub süsteemiuuendusi või saaks seal Lineage OS'i kasutada.",
|
||||
"unsupportedAndroidVersionLong": "See funktsionaalsus eeldab uuemat Androidi versiooni. Palun kontrolli, kas sinu nutiseadmele leidub süsteemiuuendusi või saaks seal Lineage OSi kasutada.",
|
||||
"@unsupportedAndroidVersionLong": {},
|
||||
"nextAccount": "Järgmine kasutajakonto",
|
||||
"@nextAccount": {},
|
||||
|
|
@ -2206,7 +2206,7 @@
|
|||
"@foregroundServiceRunning": {},
|
||||
"allSpaces": "Kõik kogukonnad",
|
||||
"@allSpaces": {},
|
||||
"screenSharingDetail": "Sa jagad oma ekraani FuffyChat'i vahendusel",
|
||||
"screenSharingDetail": "Sa jagad oma ekraani FuffyChati vahendusel",
|
||||
"@screenSharingDetail": {},
|
||||
"numChats": "{number} vestlust",
|
||||
"@numChats": {
|
||||
|
|
@ -2706,5 +2706,98 @@
|
|||
"gallery": "Galerii",
|
||||
"@gallery": {},
|
||||
"files": "Failid",
|
||||
"@files": {}
|
||||
"@files": {},
|
||||
"swipeRightToLeftToReply": "Vastamiseks viipa paremalt vasakule",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"noMoreChatsFound": "Rohkem vestlusi ei leidu...",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "Vestlusi, millega oled liitunud",
|
||||
"@joinedChats": {},
|
||||
"unread": "Lugemata",
|
||||
"@unread": {},
|
||||
"space": "Kogukond",
|
||||
"@space": {},
|
||||
"spaces": "Kogukonnad",
|
||||
"@spaces": {},
|
||||
"goToSpace": "Ava kogukond: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"markAsUnread": "Märgi mitteloetuks",
|
||||
"@markAsUnread": {},
|
||||
"countChatsAndCountParticipants": "{chats} vestlust ja {participants} osalejat",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"userLevel": "{level} - kasutaja",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - moderaator",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - peakasutaja",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeTheVisibilityOfChatHistory": "Muuda vestluse ajaloo nähtavust",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"sendRoomNotifications": "Saada @jututuba teavitusi",
|
||||
"@sendRoomNotifications": {},
|
||||
"changeTheCanonicalRoomAlias": "Muuda vestluse põhilist avalikult nähtavat aadressi",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"changeGeneralChatSettings": "Muuda vestluse üldiseid seadistusi",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"inviteOtherUsers": "Kutsu teisi osalejaid sellesse vestlusesse",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheChatPermissions": "Muuda vestluse õigusi",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheDescriptionOfTheGroup": "Muuda vestluse kirjeldust",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"chatPermissionsDescription": "Määra erinevatele kasutajatele selles vestluses vajalikud õigused. Tüüpiliselt on need 0, 50 ja 100 (vastavalt kasutajad, moderaatorid ja peakasutajad), kuid igasugused vahepealsed variatsioonid on ka võimalikud.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"invitedBy": "📩 Kutsujaks {user}",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"updateInstalled": "🎉 Versiooniuuendus {version} on paigaldatud!",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"changelog": "Muudatuste logi",
|
||||
"@changelog": {},
|
||||
"sendCanceled": "Saatmine on katkestatud",
|
||||
"@sendCanceled": {},
|
||||
"noChatsFoundHere": "Siin ei leidu veel ühtegi vestlust. Alusta uut vestlust klõpsides allpool asuvat nuppu. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"loginWithMatrixId": "Logi sisse Matrix-ID alusel",
|
||||
"@loginWithMatrixId": {},
|
||||
"discoverHomeservers": "Leia koduservereid",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "Mis on koduserver?",
|
||||
"@whatIsAHomeserver": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"activatedEndToEndEncryption": "🔐 {username}(e)k ertzetik ertzerako zifraketa gaitu du",
|
||||
"activatedEndToEndEncryption": "🔐 {username}(e)k ertzetik ertzerako zifratzea gaitu du",
|
||||
"@activatedEndToEndEncryption": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -226,7 +226,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"channelCorruptedDecryptError": "Zifraketa hondatu egin da",
|
||||
"channelCorruptedDecryptError": "Zifratzea hondatu egin da",
|
||||
"@channelCorruptedDecryptError": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -412,17 +412,17 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"enableEncryptionWarning": "Ezingo duzu zifraketa ezgaitu. Ziur zaude?",
|
||||
"enableEncryptionWarning": "Ezingo duzu zifratzea ezgaitu. Ziur zaude?",
|
||||
"@enableEncryptionWarning": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"encryption": "Zifraketa",
|
||||
"encryption": "Zifratzea",
|
||||
"@encryption": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"encryptionNotEnabled": "Zifraketa ez dago gaituta",
|
||||
"encryptionNotEnabled": "Zifratzea ez dago gaituta",
|
||||
"@encryptionNotEnabled": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -666,7 +666,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"needPantalaimonWarning": "Kontuan izan oraingoz Pantalaimon behar duzula puntuz puntuko zifraketarako.",
|
||||
"needPantalaimonWarning": "Kontuan izan oraingoz Pantalaimon behar duzula ertzetik ertzerako zifratzerako.",
|
||||
"@needPantalaimonWarning": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1036,7 +1036,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"unknownEncryptionAlgorithm": "Zifraketa-algoritmo ezezaguna",
|
||||
"unknownEncryptionAlgorithm": "Zifratze-algoritmo ezezaguna",
|
||||
"@unknownEncryptionAlgorithm": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1560,7 +1560,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"contentHasBeenReported": "Edukia zerbitzariko administrariei jakinarazi zaie",
|
||||
"contentHasBeenReported": "Edukia zerbitzariko administratzaileei jakinarazi zaie",
|
||||
"@contentHasBeenReported": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1649,7 +1649,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"enableEncryption": "Gaitu zifraketa",
|
||||
"enableEncryption": "Gaitu zifratzea",
|
||||
"@enableEncryption": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1781,7 +1781,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "Defektuzko botere-maila",
|
||||
"defaultPermissionLevel": "Erabiltzaile berrien defektuzko botere-maila",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1841,7 +1841,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"noEncryptionForPublicRooms": "Zifraketa aktiba dezakezu soilik gelak publikoa izateari utzi badio.",
|
||||
"noEncryptionForPublicRooms": "Zifratzea aktiba dezakezu soilik gelak publikoa izateari utzi badio.",
|
||||
"@noEncryptionForPublicRooms": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2161,7 +2161,7 @@
|
|||
"@hydrateTor": {},
|
||||
"saveKeyManuallyDescription": "Gorde eskuz gako hau gailuko partekatze-menua edo arbela erabiliz.",
|
||||
"@saveKeyManuallyDescription": {},
|
||||
"indexedDbErrorTitle": "Arazoak modu pribatuan",
|
||||
"indexedDbErrorTitle": "Arazoak modu pribatuarekin",
|
||||
"@indexedDbErrorTitle": {},
|
||||
"confirmMatrixId": "Baieztatu zure Matrix IDa kontua ezabatu ahal izateko.",
|
||||
"@confirmMatrixId": {},
|
||||
|
|
@ -2202,7 +2202,7 @@
|
|||
},
|
||||
"hideUnimportantStateEvents": "Ezkutatu garrantzirik gabeko gertaerak",
|
||||
"@hideUnimportantStateEvents": {},
|
||||
"noKeyForThisMessage": "Mezua gailu honetan saioa hasi baino lehen bidali bazen gertatu daiteke.\n\nBeste aukera bat igorleak zure gailua blokeatu izana da, edo zerbaitek huts egin izana interneteko konexioan.\n\nMezua beste saio batean irakur dezakezu? Hala bada, mezua transferitu dezakezu! Zoaz Ezrpenetara > Gailuak eta baieztatu zure gailuek bata bestea egiaztatu dutela. Gela irakiko duzun hurrengo aldian eta bi saioak aurreko planoan irekita daudenean, gakoak automatikoki partekatuko dira.\n\nEz duzu gakorik galdu nahi saioa amaitu edo gailuak aldatzen dituzunean? Baieztatu ezarpenetan txaten babeskopiak gaituta dituzula.",
|
||||
"noKeyForThisMessage": "Mezua gailu honetan saioa hasi baino lehen bidali bazen gertatu daiteke.\n\nBeste aukera bat igorleak zure gailua blokeatu izana da, edo zerbaitek huts egin izana interneteko konexioan.\n\nMezua beste saio batean irakur dezakezu? Hala bada, mezua transferitu dezakezu! Zoaz Ezrpenetara > Gailuak eta baieztatu zure gailuek bata bestea egiaztatu dutela. Gela irekiko duzun hurrengo aldian eta bi saioak aurreko planoan irekita daudenean, gakoak automatikoki partekatuko dira.\n\nEz duzu gakorik galdu nahi saioa amaitu edo gailuak aldatzen dituzunean? Baieztatu ezarpenetan txaten babeskopiak gaituta dituzula.",
|
||||
"@noKeyForThisMessage": {},
|
||||
"supposedMxid": "Hau {mxid} izan behar da",
|
||||
"@supposedMxid": {
|
||||
|
|
@ -2238,7 +2238,7 @@
|
|||
"@startFirstChat": {},
|
||||
"newSpaceDescription": "Guneek txatak taldekatzea ahalbidetzen dute eta komunitate pribatu edo publikoak osatzea.",
|
||||
"@newSpaceDescription": {},
|
||||
"disableEncryptionWarning": "Segurtasun arrazoiak direla-eta, ezin duzu lehendik zifratuta zegoen txat bateko zifraketa ezgaitu.",
|
||||
"disableEncryptionWarning": "Segurtasun arrazoiak direla-eta, ezin duzu lehendik zifratuta zegoen txat bateko zifratzea ezgaitu.",
|
||||
"@disableEncryptionWarning": {},
|
||||
"encryptThisChat": "Zifratu txata",
|
||||
"@encryptThisChat": {},
|
||||
|
|
@ -2357,7 +2357,7 @@
|
|||
},
|
||||
"redactMessageDescription": "Mezua elkarrizketa honetako partaide guztientzat botako da atzera. Ezin da desegin.",
|
||||
"@redactMessageDescription": {},
|
||||
"addChatDescription": "Gehitu txat honen deskribapena…",
|
||||
"addChatDescription": "Gehitu txataren deskribapena…",
|
||||
"@addChatDescription": {},
|
||||
"directChat": "Banakako txata",
|
||||
"@directChat": {},
|
||||
|
|
@ -2486,7 +2486,7 @@
|
|||
"@thisDevice": {},
|
||||
"decline": "Baztertu",
|
||||
"@decline": {},
|
||||
"databaseBuildErrorBody": "Ezin izan da SQlite datu-basea eraiki. Aplikazioa aurreko datu-basea erabiltzen saiatuko da oraingoz. Jakinarazi errorea garatzaileei {url} helbidean. Errorearen mezua ondorengoa da: {error}",
|
||||
"databaseBuildErrorBody": "Ezin da SQlite datu-basea eraiki. Aplikazioa aurreko datu-basea erabiltzen saiatuko da oraingoz. Jakinarazi errorea garatzaileei {url} helbidean. Errorearen mezua ondorengoa da: {error}",
|
||||
"@databaseBuildErrorBody": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -2520,7 +2520,7 @@
|
|||
},
|
||||
"transparent": "Gardena",
|
||||
"@transparent": {},
|
||||
"sendReadReceipts": "Bidali irakurri izanaren adierazlea",
|
||||
"sendReadReceipts": "Bidali irakurri izanaren agiria",
|
||||
"@sendReadReceipts": {},
|
||||
"formattedMessages": "Formatua duten mezuak",
|
||||
"@formattedMessages": {},
|
||||
|
|
@ -2547,7 +2547,7 @@
|
|||
"sender": {}
|
||||
}
|
||||
},
|
||||
"sendReadReceiptsDescription": "Txateko beste kideek mezu bat irakurri duzula ikus dezakete.",
|
||||
"sendReadReceiptsDescription": "Txateko beste partaideek mezu bat irakurri duzula ikus dezakete.",
|
||||
"@sendReadReceiptsDescription": {},
|
||||
"forwardMessageTo": "Birbidali mezua {roomName}(e)ra?",
|
||||
"@forwardMessageTo": {
|
||||
|
|
@ -2576,7 +2576,7 @@
|
|||
"@verifyOtherUserDescription": {},
|
||||
"formattedMessagesDescription": "Erakutsi mezu aberatsen edukia markdown erabiliz, testu lodia esaterako.",
|
||||
"@formattedMessagesDescription": {},
|
||||
"sendTypingNotificationsDescription": "Txateko beste kideek mezu berri bat idazten ari zarela ikus dezakete.",
|
||||
"sendTypingNotificationsDescription": "Txateko beste partaideek mezu berri bat idazten ari zarela ikus dezakete.",
|
||||
"@sendTypingNotificationsDescription": {},
|
||||
"verifyOtherUser": "🔐 Egiaztatu beste erabiltzaile bat",
|
||||
"@verifyOtherUser": {},
|
||||
|
|
@ -2679,7 +2679,7 @@
|
|||
"@noPublicLinkHasBeenCreatedYet": {},
|
||||
"userRole": "Erabiltzailearen rola",
|
||||
"@userRole": {},
|
||||
"minimumPowerLevel": "{level} da gutxieneko botere maila.",
|
||||
"minimumPowerLevel": "{level} da gutxieneko botere-maila.",
|
||||
"@minimumPowerLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -2694,7 +2694,7 @@
|
|||
"@files": {},
|
||||
"gallery": "Galeria",
|
||||
"@gallery": {},
|
||||
"searchIn": "Bilatu {chat}ean...",
|
||||
"searchIn": "Bilatu {chat} txatean...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -2702,5 +2702,106 @@
|
|||
}
|
||||
},
|
||||
"searchMore": "Bilatu gehiago...",
|
||||
"@searchMore": {}
|
||||
"@searchMore": {},
|
||||
"restricted": "Mugatuta",
|
||||
"@restricted": {},
|
||||
"knockRestricted": "Eskatu baimena sarrera mugatua duen txatean",
|
||||
"@knockRestricted": {},
|
||||
"swipeRightToLeftToReply": "Herrestatu eskuin-ezker erantzuteko",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"noMoreChatsFound": "Ez da beste txatik aurkitu...",
|
||||
"@noMoreChatsFound": {},
|
||||
"unread": "Irakurri gabe",
|
||||
"@unread": {},
|
||||
"space": "Gunea",
|
||||
"@space": {},
|
||||
"joinedChats": "Batu zaren txatak",
|
||||
"@joinedChats": {},
|
||||
"goToSpace": "Joan {space} gunera",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"markAsUnread": "Markatu irakurri gabetzat",
|
||||
"@markAsUnread": {},
|
||||
"countChatsAndCountParticipants": "{chats} txat eta {participants} partaide",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"spaces": "Guneak",
|
||||
"@spaces": {},
|
||||
"adminLevel": "{level} - Administratzailea",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeTheChatPermissions": "Aldatu txataren baimenak",
|
||||
"@changeTheChatPermissions": {},
|
||||
"inviteOtherUsers": "Gonbidatu beste erabiltzaileak txat honetara",
|
||||
"@inviteOtherUsers": {},
|
||||
"userLevel": "{level} - Erabiltzailea",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - Moderatzailea",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"sendRoomNotifications": "Bidali @gela jakinarazpena",
|
||||
"@sendRoomNotifications": {},
|
||||
"changeTheDescriptionOfTheGroup": "Aldatu txataren deskribapena",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"changeGeneralChatSettings": "Aldatu txataren ezarpen orokorrak",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"changeTheVisibilityOfChatHistory": "Aldatu txataren historiaren ikusgaitasuna",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"changeTheCanonicalRoomAlias": "Aldatu txataren helbide publiko nagusia",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"invitedBy": "📩 {user}(e)k gonbidatua",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"updateInstalled": "🎉 {version} bertsioa instalatu da!",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"changelog": "Aldaketak",
|
||||
"@changelog": {},
|
||||
"chatPermissionsDescription": "Definitu zer botere-maila behar den txat honetako ekintza jakinetarako. 0, 50 eta 100 botere-mailek erabiltzaileak, moderatzaileak eta administratzaileak ordezkatzen dituzte, baina edozein graduazio posible da.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"sendCanceled": "Bidalketa bertan behera utzi da",
|
||||
"@sendCanceled": {},
|
||||
"noChatsFoundHere": "Ez da txatik aurkitu. Hasi norbaitekin txateatzen beheko botoia erabiliz. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"homeserverDescription": "Zerbitzariak datuak gordetzen ditu, ePosta hornitzaileek mezuak gordetzen dituzten bezala. Nahi duzun zerbitzaria aukeratu dezakezu eta, hala ere, besteetako edonorekin hitz egin. Ikasi gehiago https://matrix.org webgunean.",
|
||||
"@homeserverDescription": {},
|
||||
"loginWithMatrixId": "Hasi saioa Matrix IDarekin",
|
||||
"@loginWithMatrixId": {},
|
||||
"discoverHomeservers": "Arakatu zerbitzariak",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "Zer da zerbitzari bat?",
|
||||
"@whatIsAHomeserver": {},
|
||||
"doesNotSeemToBeAValidHomeserver": "Ez dirudi zerbitzaria bateragarria denik. Zuzena da URLa?",
|
||||
"@doesNotSeemToBeAValidHomeserver": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2300,78 +2300,6 @@
|
|||
"@signInWithPassword": {},
|
||||
"pleaseTryAgainLaterOrChooseDifferentServer": "لطفا بعدا تلاش کنید یا سرور دیگری انتخاب کنید.",
|
||||
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
|
||||
"@setColorTheme": {},
|
||||
"@banUserDescription": {},
|
||||
"@removeDevicesDescription": {},
|
||||
"@tryAgain": {},
|
||||
"@unbanUserDescription": {},
|
||||
"@messagesStyle": {},
|
||||
"@chatDescription": {},
|
||||
"@pushNotificationsNotAvailable": {},
|
||||
"@invalidServerName": {},
|
||||
"@chatPermissions": {},
|
||||
"@makeAdminDescription": {},
|
||||
"@setChatDescription": {},
|
||||
"@importFromZipFile": {},
|
||||
"@redactedBy": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"username": {}
|
||||
}
|
||||
},
|
||||
"@signInWith": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"provider": {}
|
||||
}
|
||||
},
|
||||
"@optionalRedactReason": {},
|
||||
"@archiveRoomDescription": {},
|
||||
"@exportEmotePack": {},
|
||||
"@inviteContactToGroupQuestion": {},
|
||||
"@redactedByBecause": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"username": {},
|
||||
"reason": {}
|
||||
}
|
||||
},
|
||||
"@redactMessageDescription": {},
|
||||
"@invalidInput": {},
|
||||
"@addChatDescription": {},
|
||||
"@hasKnocked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@directChat": {},
|
||||
"@wrongPinEntered": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"@sendTypingNotifications": {},
|
||||
"@inviteGroupChat": {},
|
||||
"@invitePrivateChat": {},
|
||||
"@importEmojis": {},
|
||||
"@noChatDescriptionYet": {},
|
||||
"@learnMore": {},
|
||||
"notAnImage": "یک فایل تصویری نیست.",
|
||||
"@notAnImage": {},
|
||||
"@chatDescriptionHasBeenChanged": {},
|
||||
"@roomUpgradeDescription": {},
|
||||
"@pleaseEnterANumber": {},
|
||||
"@profileNotFound": {},
|
||||
"@shareInviteLink": {},
|
||||
"@emoteKeyboardNoRecents": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@setTheme": {},
|
||||
"@replace": {},
|
||||
"@createGroup": {},
|
||||
"@kickUserDescription": {},
|
||||
"@importNow": {},
|
||||
"@invite": {}
|
||||
"@notAnImage": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2402,12 +2402,5 @@
|
|||
"importNow": "Tuo nyt",
|
||||
"@importNow": {},
|
||||
"invite": "Kutsu",
|
||||
"@invite": {},
|
||||
"@banUserDescription": {},
|
||||
"@removeDevicesDescription": {},
|
||||
"@unbanUserDescription": {},
|
||||
"@pushNotificationsNotAvailable": {},
|
||||
"@makeAdminDescription": {},
|
||||
"@learnMore": {},
|
||||
"@kickUserDescription": {}
|
||||
"@invite": {}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -190,7 +190,7 @@
|
|||
"description": {}
|
||||
}
|
||||
},
|
||||
"changedTheChatNameTo": "{username} mudou o nome da conversa a: '{chatname}'",
|
||||
"changedTheChatNameTo": "{username} mudou o nome da charla a: '{chatname}'",
|
||||
"@changedTheChatNameTo": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -559,7 +559,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "Nivel de permisos por omisión",
|
||||
"defaultPermissionLevel": "Nivel de permisos por defecto para novas usuarias",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1931,7 +1931,7 @@
|
|||
"@oneClientLoggedOut": {},
|
||||
"link": "Ligazón",
|
||||
"@link": {},
|
||||
"yourChatBackupHasBeenSetUp": "Configurouse a copia de apoio do chat.",
|
||||
"yourChatBackupHasBeenSetUp": "Configurouse a copia de apoio da charla.",
|
||||
"@yourChatBackupHasBeenSetUp": {},
|
||||
"unverified": "Sen verificar",
|
||||
"@unverified": {},
|
||||
|
|
@ -1968,7 +1968,7 @@
|
|||
"type": "text",
|
||||
"description": "Usage hint for the command /clearcache"
|
||||
},
|
||||
"commandHint_dm": "Iniciar un chat directo\nUsa --no-encryption para desactivar a cifraxe",
|
||||
"commandHint_dm": "Iniciar unha charla directa\nUsa --no-encryption para desactivar a cifraxe",
|
||||
"@commandHint_dm": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /dm"
|
||||
|
|
@ -2141,7 +2141,7 @@
|
|||
},
|
||||
"unlockOldMessages": "Desbloquear mensaxes antigas",
|
||||
"@unlockOldMessages": {},
|
||||
"dehydrateTorLong": "Para usuarias de TOR, é recomendable exportar a sesión antes de pechar a ventál.",
|
||||
"dehydrateTorLong": "Para usuarias de TOR, é recomendable exportar a sesión antes de pechar a xanela.",
|
||||
"@dehydrateTorLong": {},
|
||||
"hydrateTor": "Usuarias TOR: Importar a sesión exportada",
|
||||
"@hydrateTor": {},
|
||||
|
|
@ -2198,7 +2198,7 @@
|
|||
"@callingAccount": {},
|
||||
"callingAccountDetails": "Permítelle a FluffyChat usar a app marcador nativa de android.",
|
||||
"@callingAccountDetails": {},
|
||||
"appearOnTopDetails": "Permítelle á app aparecer por enrriba (non é preciso se xa configuraches FluffyChat como unha conta para chamadas)",
|
||||
"appearOnTopDetails": "Permítelle á app aparecer por enriba (non é preciso se xa configuraches FluffyChat como unha conta para chamadas)",
|
||||
"@appearOnTopDetails": {},
|
||||
"enterSpace": "Entrar no espazo",
|
||||
"@enterSpace": {},
|
||||
|
|
@ -2206,7 +2206,7 @@
|
|||
"@enterRoom": {},
|
||||
"allSpaces": "Todos os espazos",
|
||||
"@allSpaces": {},
|
||||
"screenSharingDetail": "Estás compartindo a túa pantalla en FluffyChat",
|
||||
"screenSharingDetail": "Estás a compartir a túa pantalla en FluffyChat",
|
||||
"@screenSharingDetail": {},
|
||||
"numChats": "{number} conversas",
|
||||
"@numChats": {
|
||||
|
|
@ -2394,7 +2394,7 @@
|
|||
"@pushNotificationsNotAvailable": {},
|
||||
"makeAdminDescription": "Cando convirtas a esta usuaria en admin non poderás desfacer a acción xa que terá os mesmos permisos ca ti.",
|
||||
"@makeAdminDescription": {},
|
||||
"archiveRoomDescription": "Vaise mover o chat ao arquivo. Outras usuarias poderán ver que saíches da conversa.",
|
||||
"archiveRoomDescription": "Vaise mover a charla ao arquivo. Outras usuarias poderán ver que saíches da conversa.",
|
||||
"@archiveRoomDescription": {},
|
||||
"invalidInput": "Contido non válido!",
|
||||
"@invalidInput": {},
|
||||
|
|
@ -2413,7 +2413,7 @@
|
|||
},
|
||||
"learnMore": "Saber máis",
|
||||
"@learnMore": {},
|
||||
"roomUpgradeDescription": "Vaise recrear o chat coa nova versión da sala. Todas as participantes recibirán unha notificación para que cambien ao novo chat. Podes ler máis información acerca das versións das salas en https://spec.matrix.org/latest/rooms/",
|
||||
"roomUpgradeDescription": "Vaise recrear a charla coa nova versión da sala. Todas as participantes recibirán unha notificación para que cambien á nova charla. Podes ler máis información acerca das versións das salas en https://spec.matrix.org/latest/rooms/",
|
||||
"@roomUpgradeDescription": {},
|
||||
"pleaseEnterANumber": "Escribe un número maior de cero",
|
||||
"@pleaseEnterANumber": {},
|
||||
|
|
@ -2474,7 +2474,7 @@
|
|||
"@select": {},
|
||||
"pleaseChooseAStrongPassword": "Elixe un contrasinal forte",
|
||||
"@pleaseChooseAStrongPassword": {},
|
||||
"addChatOrSubSpace": "Engadir chat ou sub espazo",
|
||||
"addChatOrSubSpace": "Engadir charla ou sub espazo",
|
||||
"@addChatOrSubSpace": {},
|
||||
"leaveEmptyToClearStatus": "Deixa baleiro para limpar o teu estado.",
|
||||
"@leaveEmptyToClearStatus": {},
|
||||
|
|
@ -2530,7 +2530,7 @@
|
|||
"@verifyOtherDevice": {},
|
||||
"verifyOtherUser": "🔐 Verificar outra usuaria",
|
||||
"@verifyOtherUser": {},
|
||||
"verifyOtherDeviceDescription": "Ao verificar outro dispositivo estás compartindo as chaves, aumentando a túa seguridade 💪. Ao iniciar a verificación aparecerá unha ventá emerxente nos dous dispositivos. Nesa ventá verás varios emojis ou números que tes que comparar entre eles. O mellor xeito de facelo é ter os dous dispositivos contigo cando inicias o proceso de verificación. 🤳",
|
||||
"verifyOtherDeviceDescription": "Ao verificar outro dispositivo estás compartindo as chaves, aumentando a túa seguridade 💪. Ao iniciar a verificación aparecerá unha xanela emerxente nos dous dispositivos. Nesa xanela verás varios emojis ou números que tes que comparar entre eles. O mellor xeito de facelo é ter os dous dispositivos contigo cando inicias o proceso de verificación. 🤳",
|
||||
"@verifyOtherDeviceDescription": {},
|
||||
"canceledKeyVerification": "{sender} desbotou a verificación da chave",
|
||||
"@canceledKeyVerification": {
|
||||
|
|
@ -2557,7 +2557,7 @@
|
|||
"@sendTypingNotificationsDescription": {},
|
||||
"formattedMessagesDescription": "Mostrar texto enriquecido nas mensaxes como letra grosa usando markdown.",
|
||||
"@formattedMessagesDescription": {},
|
||||
"verifyOtherUserDescription": "Se verificas a outra usuaria, podes ter a certeza de que sabes con quen estás a conversar. 💪\n\nAo iniciar a verificación, ti mais a outra usuaria veredes unha ventá emerxente na app onde aparecerán varios emojis ou números que teredes que comparar entre vós.\n\nO mellor xeito de facelo é en persoa o cunha chamada de vídeo. 👭",
|
||||
"verifyOtherUserDescription": "Se verificas a outra usuaria, podes ter a certeza de que sabes con quen estás a conversar. 💪\n\nAo iniciar a verificación, ti mais a outra usuaria veredes unha xanela emerxente na app onde aparecerán varios emojis ou números que teredes que comparar entre vós.\n\nO mellor xeito de facelo é en persoa o cunha chamada de vídeo. 👭",
|
||||
"@verifyOtherUserDescription": {},
|
||||
"requestedKeyVerification": "{sender} solicitou verificar a chave",
|
||||
"@requestedKeyVerification": {
|
||||
|
|
@ -2609,7 +2609,7 @@
|
|||
"@commandHint_ignore": {},
|
||||
"commandHint_unignore": "Non ignorar o ID matrix indicado",
|
||||
"@commandHint_unignore": {},
|
||||
"unreadChatsInApp": "{appname}: {unread} chats sen ler",
|
||||
"unreadChatsInApp": "{appname}: {unread} charlas sen ler",
|
||||
"@unreadChatsInApp": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -2621,7 +2621,7 @@
|
|||
"@noDatabaseEncryption": {},
|
||||
"accessAndVisibility": "Acceso e Visibilidade",
|
||||
"@accessAndVisibility": {},
|
||||
"accessAndVisibilityDescription": "Quen pode unirse a este chat de que xeito pode ser atopado.",
|
||||
"accessAndVisibilityDescription": "Quen pode unirse a esta charla e de que xeito e como poden atopala.",
|
||||
"@accessAndVisibilityDescription": {},
|
||||
"customEmojisAndStickers": "Emojis personais e adhesivos",
|
||||
"@customEmojisAndStickers": {},
|
||||
|
|
@ -2629,19 +2629,19 @@
|
|||
"@calls": {},
|
||||
"hideRedactedMessages": "Agochar mensaxes editadas",
|
||||
"@hideRedactedMessages": {},
|
||||
"hideRedactedMessagesBody": "Se alguén corrixe unha mensaxe entón esta xa non será visible no chat.",
|
||||
"hideRedactedMessagesBody": "Se alguén corrixe unha mensaxe entón esta xa non será visible na charla.",
|
||||
"@hideRedactedMessagesBody": {},
|
||||
"hideInvalidOrUnknownMessageFormats": "Agochar formatos de mensaxe non válidos ou descoñecidos",
|
||||
"@hideInvalidOrUnknownMessageFormats": {},
|
||||
"hideMemberChangesInPublicChats": "Agochar cambios dos membros nos chats públicos",
|
||||
"hideMemberChangesInPublicChats": "Agochar cambios dos membros nas charlas públicas",
|
||||
"@hideMemberChangesInPublicChats": {},
|
||||
"notifyMeFor": "Notificarme sobre",
|
||||
"@notifyMeFor": {},
|
||||
"hideMemberChangesInPublicChatsBody": "Non mostrar na cronoloxía se alguén se une ou deixa un chat público, para mellorar a lexibilidade.",
|
||||
"hideMemberChangesInPublicChatsBody": "Non mostrar na cronoloxía se alguén se une ou deixa unha conversa pública, para mellorar a lexibilidade.",
|
||||
"@hideMemberChangesInPublicChatsBody": {},
|
||||
"usersMustKnock": "As usuarias teñen que pedir entrar",
|
||||
"@usersMustKnock": {},
|
||||
"userWouldLikeToChangeTheChat": "{user} quere unirse ao chat.",
|
||||
"userWouldLikeToChangeTheChat": "{user} quere unirse á charla.",
|
||||
"@userWouldLikeToChangeTheChat": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2649,7 +2649,7 @@
|
|||
},
|
||||
"knocking": "A solicitar",
|
||||
"@knocking": {},
|
||||
"chatCanBeDiscoveredViaSearchOnServer": "O chat pode ser atopado ao buscar en {server}",
|
||||
"chatCanBeDiscoveredViaSearchOnServer": "A charla pode ser atopada ao buscar en {server}",
|
||||
"@chatCanBeDiscoveredViaSearchOnServer": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -2660,9 +2660,9 @@
|
|||
"@createNewAddress": {},
|
||||
"appLockDescription": "Bloquear a app cun código PIN cando non a uses",
|
||||
"@appLockDescription": {},
|
||||
"globalChatId": "ID Global do chat",
|
||||
"globalChatId": "ID Global da charla",
|
||||
"@globalChatId": {},
|
||||
"customEmojisAndStickersBody": "Engade ou comparte emojis personais e adhesivos que poden usarse nos chats.",
|
||||
"customEmojisAndStickersBody": "Engade ou comparte emojis personais e adhesivos que poden usarse nas charlas.",
|
||||
"@customEmojisAndStickersBody": {},
|
||||
"overview": "Vista xeral",
|
||||
"@overview": {},
|
||||
|
|
@ -2679,7 +2679,7 @@
|
|||
"type": "text",
|
||||
"count": {}
|
||||
},
|
||||
"publicChatAddresses": "Enderezos públicos do chat",
|
||||
"publicChatAddresses": "Enderezos públicos da charla",
|
||||
"@publicChatAddresses": {},
|
||||
"userRole": "Rol da usuaria",
|
||||
"@userRole": {},
|
||||
|
|
@ -2690,7 +2690,7 @@
|
|||
"level": {}
|
||||
}
|
||||
},
|
||||
"searchIn": "Buscar no chat \"{chat}\"...",
|
||||
"searchIn": "Buscar na charla \"{chat}\"...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -2706,5 +2706,102 @@
|
|||
"knockRestricted": "Peta á porta",
|
||||
"@knockRestricted": {},
|
||||
"restricted": "Non accesible",
|
||||
"@restricted": {}
|
||||
"@restricted": {},
|
||||
"swipeRightToLeftToReply": "Despraza hacia a esquerda para responder",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "falso",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"noMoreChatsFound": "Non se atopan máis charlas…",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "Charlas nas que participas",
|
||||
"@joinedChats": {},
|
||||
"countChatsAndCountParticipants": "{chats} charlas e {participants} participantes",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"unread": "Sen ler",
|
||||
"@unread": {},
|
||||
"space": "Espazo",
|
||||
"@space": {},
|
||||
"spaces": "Espazos",
|
||||
"@spaces": {},
|
||||
"goToSpace": "Ir ao espazo: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"markAsUnread": "Marcar como non lido",
|
||||
"@markAsUnread": {},
|
||||
"userLevel": "{level} - Usuaria",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - Moderadora",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - Administradora",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeGeneralChatSettings": "Cambiar os axustes xerais da charla",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"inviteOtherUsers": "Convidar a outras usuarias a esta charla",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheChatPermissions": "Cambiar os permisos na charla",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheVisibilityOfChatHistory": "Cambiar a visibilidade do historial da charla",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"changeTheCanonicalRoomAlias": "Cambiar o enderezo público principal da charla",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"sendRoomNotifications": "Enviar notificacións a @room",
|
||||
"@sendRoomNotifications": {},
|
||||
"changeTheDescriptionOfTheGroup": "Cambiar a descrición da charla",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"invitedBy": "📩 Convidada por {user}",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"changelog": "Novidades na versión",
|
||||
"@changelog": {},
|
||||
"chatPermissionsDescription": "Define que nivel de permisos son necesarios para realizar certas accións nesta charla. Os niveis de permiso 0, 50 e 100 normalmente representan, usuarias, moderadoras e administradoras, pero son posibles outras escalas.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"updateInstalled": "🎉 Instalouse a actualización a {version}!",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"sendCanceled": "Cancelouse o envío",
|
||||
"@sendCanceled": {},
|
||||
"noChatsFoundHere": "Sen charlas por aquí. Comeza unha nova conversa con alguén premendo no botón de abaixo. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"discoverHomeservers": "Atopar servidores",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "Que é un servidor de inicio?",
|
||||
"@whatIsAHomeserver": {},
|
||||
"loginWithMatrixId": "Acceder co ID-Matrix",
|
||||
"@loginWithMatrixId": {},
|
||||
"homeserverDescription": "Todos os teus datos quedan gardados no servidor de inicio, igual que co teu provedor de correo electrónico. Podes elexir o servidor que queres usar e poderás comunicarte con todos os demais. Aprende máis en https://matrix.org.",
|
||||
"@homeserverDescription": {},
|
||||
"doesNotSeemToBeAValidHomeserver": "Non semella ser un servidor de inicio compatible. É o URL correcto?",
|
||||
"@doesNotSeemToBeAValidHomeserver": {}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -914,7 +914,7 @@
|
|||
"targetName": {}
|
||||
}
|
||||
},
|
||||
"kickedAndBanned": "🙅 {username} je izbacio/la i isključio/la {targetName}",
|
||||
"kickedAndBanned": "🙅 {username} je izbacio/la i blokirao/la {targetName}",
|
||||
"@kickedAndBanned": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -949,7 +949,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"lightTheme": "Svjetla",
|
||||
"lightTheme": "Svijetla",
|
||||
"@lightTheme": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2029,7 +2029,7 @@
|
|||
"@recoveryKey": {},
|
||||
"recoveryKeyLost": "Izgubio/la si ključ za obnavljanje?",
|
||||
"@recoveryKeyLost": {},
|
||||
"youKickedAndBanned": "🙅 Izbacio/la si i isključio/la korisnika {user}",
|
||||
"youKickedAndBanned": "🙅 Izbacio/la si i blokirao/la korisnika {user}",
|
||||
"@youKickedAndBanned": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2449,5 +2449,268 @@
|
|||
"discover": "Otkrij",
|
||||
"@discover": {},
|
||||
"formattedMessagesDescription": "Prikaži formatirani sadržaj poruke poput podebljanog teksta koristeći markdown.",
|
||||
"@formattedMessagesDescription": {}
|
||||
"@formattedMessagesDescription": {},
|
||||
"nothingFound": "Ništa nije pronađeno...",
|
||||
"@nothingFound": {},
|
||||
"select": "Odaberi",
|
||||
"@select": {},
|
||||
"newPassword": "Nova lozinka",
|
||||
"@newPassword": {},
|
||||
"unbanUserDescription": "Korisnik će se ponovo moći pridružiti razgovoru ako pokuša.",
|
||||
"@unbanUserDescription": {},
|
||||
"publicSpaces": "Javni prostori",
|
||||
"@publicSpaces": {},
|
||||
"subspace": "Podprostori",
|
||||
"@subspace": {},
|
||||
"decline": "Odbij",
|
||||
"@decline": {},
|
||||
"thisDevice": "Ovaj uređaj:",
|
||||
"@thisDevice": {},
|
||||
"presenceStyle": "Prisutnost:",
|
||||
"@presenceStyle": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"presencesToggle": "Prikaži poruke stanja od drugih korisnika",
|
||||
"@presencesToggle": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"noPublicLinkHasBeenCreatedYet": "Još nije stvorena nijedna javna poveznica",
|
||||
"@noPublicLinkHasBeenCreatedYet": {},
|
||||
"hidePresences": "Sakriti popis stanja?",
|
||||
"@hidePresences": {},
|
||||
"pleaseEnterYourCurrentPassword": "Upiši svoju trenutačnu lozinku",
|
||||
"@pleaseEnterYourCurrentPassword": {},
|
||||
"publicLink": "Javna poveznica",
|
||||
"@publicLink": {},
|
||||
"passwordIsWrong": "Tvoja upisana lozinka je kriva",
|
||||
"@passwordIsWrong": {},
|
||||
"initAppError": "Dogodila se greška prilikom inicijaliziranja aplikacije",
|
||||
"@initAppError": {},
|
||||
"hideRedactedMessagesBody": "Ako netko redigira poruku, ta poruka više neće biti vidljiva u razgovoru.",
|
||||
"@hideRedactedMessagesBody": {},
|
||||
"kickUserDescription": "Korisnik je izbačen iz razgovora, ali nije blokiran. U javnim razgovorima se korisnik može ponovo pridružiti u bilo kojem trenutku.",
|
||||
"@kickUserDescription": {},
|
||||
"addChatOrSubSpace": "Dodaj razgovor ili podpodručje",
|
||||
"@addChatOrSubSpace": {},
|
||||
"appLockDescription": "Zaključaj aplikaciju kada je ne koristiš s PIN kodom",
|
||||
"@appLockDescription": {},
|
||||
"globalChatId": "Globalni ID razgovora",
|
||||
"@globalChatId": {},
|
||||
"hideRedactedMessages": "Sakrij redigirane poruke",
|
||||
"@hideRedactedMessages": {},
|
||||
"hideInvalidOrUnknownMessageFormats": "Sakrij nevažeće ili nepoznate formate poruka",
|
||||
"@hideInvalidOrUnknownMessageFormats": {},
|
||||
"overview": "Pregled",
|
||||
"@overview": {},
|
||||
"notifyMeFor": "Obavijesit me za",
|
||||
"@notifyMeFor": {},
|
||||
"passwordRecoverySettings": "Postavke za obnavljanje lozinke",
|
||||
"@passwordRecoverySettings": {},
|
||||
"hideMemberChangesInPublicChats": "Sakrij promjene članova u javnim razgovorima",
|
||||
"@hideMemberChangesInPublicChats": {},
|
||||
"youInvitedToBy": "📩 Pozvan/a si putem poveznice na:\n{alias}",
|
||||
"@youInvitedToBy": {
|
||||
"placeholders": {
|
||||
"alias": {}
|
||||
}
|
||||
},
|
||||
"usersMustKnock": "Korisnici moraju pokucati",
|
||||
"@usersMustKnock": {},
|
||||
"noOneCanJoin": "Nitko se ne može pridružiti",
|
||||
"@noOneCanJoin": {},
|
||||
"userWouldLikeToChangeTheChat": "{user} se želi pridružiti razgovoru.",
|
||||
"@userWouldLikeToChangeTheChat": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"knock": "Pokucaj",
|
||||
"@knock": {},
|
||||
"knocking": "Kucanje",
|
||||
"@knocking": {},
|
||||
"chatCanBeDiscoveredViaSearchOnServer": "Razgovor se može otkriti pretraživanjem servera {server}",
|
||||
"@chatCanBeDiscoveredViaSearchOnServer": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"server": {}
|
||||
}
|
||||
},
|
||||
"searchForUsers": "Traži @users...",
|
||||
"@searchForUsers": {},
|
||||
"pleaseChooseAStrongPassword": "Odaberi snažnu lozinku",
|
||||
"@pleaseChooseAStrongPassword": {},
|
||||
"joinSpace": "Pridruži se prostoru",
|
||||
"@joinSpace": {},
|
||||
"publicChatAddresses": "Adrese javnih razgovora",
|
||||
"@publicChatAddresses": {},
|
||||
"createNewAddress": "Stvori novu adresu",
|
||||
"@createNewAddress": {},
|
||||
"userRole": "Korisnička uloga",
|
||||
"@userRole": {},
|
||||
"verifyOtherUser": "🔐 Potvrdi drugog korisnika",
|
||||
"@verifyOtherUser": {},
|
||||
"sendTypingNotificationsDescription": "Drugi sudionici u razgovoru mogu vidjeti kada pišeš novu poruku.",
|
||||
"@sendTypingNotificationsDescription": {},
|
||||
"sendReadReceiptsDescription": "Drugi sudionici u raygovoru mogu vidjeti kada pročitaš poruku.",
|
||||
"@sendReadReceiptsDescription": {},
|
||||
"searchIn": "Traži u razgovoru „{chat}”...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chat": {}
|
||||
}
|
||||
},
|
||||
"searchMore": "Traži više...",
|
||||
"@searchMore": {},
|
||||
"gallery": "Galerija",
|
||||
"@gallery": {},
|
||||
"files": "Datoteke",
|
||||
"@files": {},
|
||||
"verifyOtherDevice": "🔐 Potvrdi drugi uređaj",
|
||||
"@verifyOtherDevice": {},
|
||||
"unreadChatsInApp": "{appname}: Nroj nepročitanih razgovora: {unread}",
|
||||
"@unreadChatsInApp": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"appname": {},
|
||||
"unread": {}
|
||||
}
|
||||
},
|
||||
"commandHint_ignore": "Zanemari navedeni matrix ID",
|
||||
"@commandHint_ignore": {},
|
||||
"blockListDescription": "Možeš blokirati korisnike koji te ometaju. Nećeš moći primati poruke ili pozivnice za sobe od korisnika koji se nalaze u tvom osobnom popisu blokiranih.",
|
||||
"@blockListDescription": {},
|
||||
"isReadyForKeyVerification": "{sender} je spreman/na za potvrđivanje ključa",
|
||||
"@isReadyForKeyVerification": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"sender": {}
|
||||
}
|
||||
},
|
||||
"banUserDescription": "Korisnik će biti isključen iz razgovora i moći će ponovo prisustvovati razgovoru kad ga se deblokira.",
|
||||
"@banUserDescription": {},
|
||||
"sessionLostBody": "Tvoja je sesija izgubljena. Prijavi ovu grešku programerima na {url}. Poruka o grešci glasi: {error}",
|
||||
"@sessionLostBody": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"url": {},
|
||||
"error": {}
|
||||
}
|
||||
},
|
||||
"completedKeyVerification": "{sender} je dovršio/la potvrđivanje ključa",
|
||||
"@completedKeyVerification": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"sender": {}
|
||||
}
|
||||
},
|
||||
"archiveRoomDescription": "Razgovor će se premjestiti u arhivu. Drugi korisnici će moći vidjeti da si napustio/la razgovor.",
|
||||
"@archiveRoomDescription": {},
|
||||
"removeDevicesDescription": "Bit ćeš odjavljen/a s ovog uređaja i više nećeš moći primati poruke.",
|
||||
"@removeDevicesDescription": {},
|
||||
"noUsersFoundWithQuery": "Nažalost nije pronađen nijedan korisnik s „{query}”. Provjeri točnost upisa.",
|
||||
"@noUsersFoundWithQuery": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"query": {}
|
||||
}
|
||||
},
|
||||
"restoreSessionBody": "Aplikacija sada pokušava obnoviti tvoju sesiju iz sigurnosne kopije. Prijavi ovu grešku programerima na {url}. Poruka o grešci glasi: {error}",
|
||||
"@restoreSessionBody": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"url": {},
|
||||
"error": {}
|
||||
}
|
||||
},
|
||||
"requestedKeyVerification": "{sender} je zatražio/la potvrđivanje ključa",
|
||||
"@requestedKeyVerification": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"sender": {}
|
||||
}
|
||||
},
|
||||
"restricted": "Ograničeni",
|
||||
"@restricted": {},
|
||||
"roomUpgradeDescription": "Razgovor će se tada ponovo stvoriti s novom verzijom sobe. Svi sudionici će biti obaviješteni da se moraju prebaciti na novi razgovor. Više o verzijama soba možeš saznati na https://spec.matrix.org/latest/rooms/",
|
||||
"@roomUpgradeDescription": {},
|
||||
"noGoogleServicesWarning": "Čini se da Firebase Cloud Messaging nije dostupan na tvom uređaju. Za daljnje primanje push obavijesti, preporučujemo da instaliraš ntfy. S ntfy ili drugim pružateljem usluge Unified Push možeš primati push obavijesti na podatkovno siguran način. Ntfy možeš preuzeti s PlayStorea ili s F-Droida.",
|
||||
"@noGoogleServicesWarning": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"verifyOtherDeviceDescription": "Kada potvrdiš jedan drugi uređaj, ti uređaji mogu razmjenjivati ključeve, povećavajući tvoju ukupnu sigurnost. 💪 Kada pokreneš provjeru, pojavit će se skočni prozor u aplikaciji na oba uređaja. Tamo ćeš tada vidjeti niz emojija ili brojeve koje moraš međusobno usporediti. Najbolje je imati oba uređaja pri ruci prije nego što započneš provjeru. 🤳",
|
||||
"@verifyOtherDeviceDescription": {},
|
||||
"verifyOtherUserDescription": "Ako potvrdiš jednog drugog korisnika, možeš biti siguran/na da znaš kome zapravo pišeš. 💪\n\nKada pokreneš provjeru, vi i drugi korisnik vidjet ćete skočni prozor u aplikaciji. Tamo ćeš tada vidjeti niz emojija ili brojeve koje morate međusobno usporediti.\n\nNajbolji način za to je da se nađete zajedno ili započnete videopoziv. 👭",
|
||||
"@verifyOtherUserDescription": {},
|
||||
"knockRestricted": "Pokucaj na ograničene sobe",
|
||||
"@knockRestricted": {},
|
||||
"hideMemberChangesInPublicChatsBody": "Za bolju čitljivosti, na vremenskoj traci razgovora nemoj prikazivati kad se netko pridruži ili napusti javni razgovor.",
|
||||
"@hideMemberChangesInPublicChatsBody": {},
|
||||
"makeAdminDescription": "Nakon postavljanja ovog korisnika kao administratora, to možda nećeš moći poništiti jer će on tada imati iste dozvole kao i ti.",
|
||||
"@makeAdminDescription": {},
|
||||
"leaveEmptyToClearStatus": "Ostavi prazno za brisanje tvog stanja.",
|
||||
"@leaveEmptyToClearStatus": {},
|
||||
"forwardMessageTo": "Proslijediti poruku u sobu {roomName}?",
|
||||
"@forwardMessageTo": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"roomName": {}
|
||||
}
|
||||
},
|
||||
"minimumPowerLevel": "{level} je najmanja razina prava.",
|
||||
"@minimumPowerLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"databaseBuildErrorBody": "Nije moguće izgraditi SQlite bazu podataka. Aplikacija za sada pokušava koristiti staru bazu podataka. Prijavi ovu grešku programerima na {url}. Poruka o grešci glasi: {error}",
|
||||
"@databaseBuildErrorBody": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"url": {},
|
||||
"error": {}
|
||||
}
|
||||
},
|
||||
"sendReadReceipts": "Šalji potvrde o čitanju",
|
||||
"@sendReadReceipts": {},
|
||||
"acceptedKeyVerification": "{sender} je prihvatio/la potvrđivanje ključa",
|
||||
"@acceptedKeyVerification": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"sender": {}
|
||||
}
|
||||
},
|
||||
"canceledKeyVerification": "{sender} je prekinuo/la potvrđivanje ključa",
|
||||
"@canceledKeyVerification": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"sender": {}
|
||||
}
|
||||
},
|
||||
"startedKeyVerification": "{sender} je pokrenuo/la potvrđivanje ključa",
|
||||
"@startedKeyVerification": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"sender": {}
|
||||
}
|
||||
},
|
||||
"commandHint_unignore": "Poništi zanemarivanje navedenog matrix ID-a",
|
||||
"@commandHint_unignore": {},
|
||||
"noDatabaseEncryption": "Šifriranje baze podataka nije podržano na ovoj platformi",
|
||||
"@noDatabaseEncryption": {},
|
||||
"thereAreCountUsersBlocked": "Broj trenutačno blokiranih korisnika: {count}.",
|
||||
"@thereAreCountUsersBlocked": {
|
||||
"type": "text",
|
||||
"count": {}
|
||||
},
|
||||
"swipeRightToLeftToReply": "Za odgovaranje povuci prstom zdesna ulijevo",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "true",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2245,7 +2245,7 @@
|
|||
},
|
||||
"addChatDescription": "Chat leírás hozzáadása...",
|
||||
"@addChatDescription": {},
|
||||
"hasKnocked": "{user} kopogott",
|
||||
"hasKnocked": "🚪 {user} bekopogott",
|
||||
"@hasKnocked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2419,7 +2419,7 @@
|
|||
"@screenSharingDetail": {},
|
||||
"placeCall": "Tér hívás",
|
||||
"@placeCall": {},
|
||||
"block": "blokkolás",
|
||||
"block": "Blokkolás",
|
||||
"@block": {},
|
||||
"blockedUsers": "Blokkolt felhasználók",
|
||||
"@blockedUsers": {},
|
||||
|
|
@ -2664,5 +2664,47 @@
|
|||
"appname": {},
|
||||
"unread": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"searchIn": "Keresés a csevegésben \"{chat}\"...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chat": {}
|
||||
}
|
||||
},
|
||||
"files": "Fájlok",
|
||||
"@files": {},
|
||||
"commandHint_unignore": "Adott matrix ID figyelembe vétele",
|
||||
"@commandHint_unignore": {},
|
||||
"restricted": "Korlátozott",
|
||||
"@restricted": {},
|
||||
"knockRestricted": "Kopogás korlátozva",
|
||||
"@knockRestricted": {},
|
||||
"globalChatId": "Globális csevegő azonosító",
|
||||
"@globalChatId": {},
|
||||
"hideRedactedMessages": "Szerkesztett üzenetek elrejtése",
|
||||
"@hideRedactedMessages": {},
|
||||
"hideRedactedMessagesBody": "Ha valaki szerkeszti az üzenetét, ez az üzenet nem jelenik meg a csevegés során.",
|
||||
"@hideRedactedMessagesBody": {},
|
||||
"hideMemberChangesInPublicChats": "Tag változások elrejtése a publikus csevegésben",
|
||||
"@hideMemberChangesInPublicChats": {},
|
||||
"knocking": "Bekopogás",
|
||||
"@knocking": {},
|
||||
"usersMustKnock": "A felhasználóknak be kell kopogniuk",
|
||||
"@usersMustKnock": {},
|
||||
"knock": "Kopogás",
|
||||
"@knock": {},
|
||||
"minimumPowerLevel": "{level} a minimum szint.",
|
||||
"@minimumPowerLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"searchMore": "További keresés...",
|
||||
"@searchMore": {},
|
||||
"gallery": "Galéria",
|
||||
"@gallery": {},
|
||||
"commandHint_ignore": "Adott matrix ID figyelmen kívül hagyása",
|
||||
"@commandHint_ignore": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"edit": "Edit",
|
||||
"edit": "Sunting",
|
||||
"@edit": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2323,7 +2323,7 @@
|
|||
"@sendTypingNotifications": {},
|
||||
"createGroup": "Buat grup",
|
||||
"@createGroup": {},
|
||||
"inviteContactToGroupQuestion": "Apakah kamu ingin mengundang {contact} ke chat \"{groupName}\"?",
|
||||
"inviteContactToGroupQuestion": "Apakah kamu ingin mengundang {contact} ke obrolan \"{groupName}\"?",
|
||||
"@inviteContactToGroupQuestion": {},
|
||||
"tryAgain": "Coba ulang",
|
||||
"@tryAgain": {},
|
||||
|
|
@ -2335,15 +2335,15 @@
|
|||
"@setTheme": {},
|
||||
"invalidServerName": "Nama server tidak valid",
|
||||
"@invalidServerName": {},
|
||||
"addChatDescription": "Tambahkan deskripsi chat...",
|
||||
"addChatDescription": "Tambahkan deskripsi obrolan...",
|
||||
"@addChatDescription": {},
|
||||
"chatPermissions": "Perizinan chat",
|
||||
"chatPermissions": "Perizinan obrolan",
|
||||
"@chatPermissions": {},
|
||||
"chatDescription": "Deskripsi chat",
|
||||
"chatDescription": "Deskripsi obrolan",
|
||||
"@chatDescription": {},
|
||||
"chatDescriptionHasBeenChanged": "Deskripsi chat diubah",
|
||||
"chatDescriptionHasBeenChanged": "Deskripsi obrolan diubah",
|
||||
"@chatDescriptionHasBeenChanged": {},
|
||||
"noChatDescriptionYet": "Deskripsi chat belum dibuat.",
|
||||
"noChatDescriptionYet": "Deskripsi obrolan belum dibuat.",
|
||||
"@noChatDescriptionYet": {},
|
||||
"redactMessageDescription": "Pesan akan dihilangkan untuk semua anggota dalam percakapan ini. Ini tidak dapat diurungkan.",
|
||||
"@redactMessageDescription": {},
|
||||
|
|
@ -2366,7 +2366,7 @@
|
|||
"reason": {}
|
||||
}
|
||||
},
|
||||
"setChatDescription": "Lihat deskripsi chat",
|
||||
"setChatDescription": "Lihat deskripsi obrolan",
|
||||
"@setChatDescription": {},
|
||||
"profileNotFound": "Pengguna ini tidak dapat ditemukan di server. Mungkin ada masalah koneksi atau penggunanya tidak ada.",
|
||||
"@profileNotFound": {},
|
||||
|
|
@ -2688,5 +2688,28 @@
|
|||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"swipeRightToLeftToReply": "Usap dari kanan ke kiri untuk membalas",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"searchMore": "Cari lebih banyak...",
|
||||
"@searchMore": {},
|
||||
"gallery": "Galeri",
|
||||
"@gallery": {},
|
||||
"searchIn": "Cari dalam obrolan \"{chat}\"...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chat": {}
|
||||
}
|
||||
},
|
||||
"files": "Berkas",
|
||||
"@files": {},
|
||||
"restricted": "Dibatasi",
|
||||
"@restricted": {},
|
||||
"knockRestricted": "Ketukan dibatasi",
|
||||
"@knockRestricted": {},
|
||||
"alwaysUse24HourFormat": "tidak",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -99,17 +99,17 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"groups": "그룹",
|
||||
"groups": "그룹 채팅",
|
||||
"@groups": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"groupIsPublic": "그룹 공개됨",
|
||||
"groupIsPublic": "그룹 채팅 공개됨",
|
||||
"@groupIsPublic": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"group": "그룹",
|
||||
"group": "그룹 채팅",
|
||||
"@group": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -369,17 +369,17 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"containsUserName": "유저 이름 포함",
|
||||
"containsUserName": "내 아이디 포함",
|
||||
"@containsUserName": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"containsDisplayName": "표시 이름 포함",
|
||||
"containsDisplayName": "내 닉네임 포함",
|
||||
"@containsDisplayName": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"contactHasBeenInvitedToTheGroup": "연락처가 그룹에 초대되었습니다",
|
||||
"contactHasBeenInvitedToTheGroup": "연락처가 채팅에 초대되었습니다",
|
||||
"@contactHasBeenInvitedToTheGroup": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -495,7 +495,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"changeTheNameOfTheGroup": "그룹의 이름 바꾸기",
|
||||
"changeTheNameOfTheGroup": "채팅의 이름 바꾸기",
|
||||
"@changeTheNameOfTheGroup": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -727,7 +727,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"answeredTheCall": "{senderName} 가 전화에 응답했습니다",
|
||||
"answeredTheCall": "{senderName}님이 전화에 응답했습니다",
|
||||
"@answeredTheCall": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -808,12 +808,12 @@
|
|||
"targetName": {}
|
||||
}
|
||||
},
|
||||
"guestsAreForbidden": "게스트는 금지되어 있습니다",
|
||||
"guestsAreForbidden": "게스트가 들어올 수 없음",
|
||||
"@guestsAreForbidden": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"groupWith": "{displayname} 과의 그룹",
|
||||
"groupWith": "{displayname}님과의 그룹",
|
||||
"@groupWith": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -892,7 +892,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"whoIsAllowedToJoinThisGroup": "누가 이 그룹에 들어오도록 허용할지",
|
||||
"whoIsAllowedToJoinThisGroup": "누가 이 그룹 채팅에 들어오도록 허용할지",
|
||||
"@whoIsAllowedToJoinThisGroup": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1723,7 +1723,7 @@
|
|||
"username": {}
|
||||
}
|
||||
},
|
||||
"isTyping": "가 입력 중…",
|
||||
"isTyping": "입력 중…",
|
||||
"@isTyping": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1736,7 +1736,7 @@
|
|||
"link": {}
|
||||
}
|
||||
},
|
||||
"inviteForMe": "나를 위해 초대",
|
||||
"inviteForMe": "초대됨",
|
||||
"@inviteForMe": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1759,7 +1759,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"inviteContactToGroup": "연락처 {groupName} 에 초대",
|
||||
"inviteContactToGroup": "연락처를 {groupName}에 초대",
|
||||
"@inviteContactToGroup": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
|
|
@ -1975,7 +1975,7 @@
|
|||
"type": "text",
|
||||
"description": "Usage hint for the command /clearcache"
|
||||
},
|
||||
"commandHint_create": "빈 그룹 채팅을 생성\t\n--no-encryption을 사용해 암호화를 비활성화",
|
||||
"commandHint_create": "빈 그룹 채팅을 생성\n--no-encryption을 사용해 암호화를 비활성화",
|
||||
"@commandHint_create": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /create"
|
||||
|
|
@ -2041,7 +2041,7 @@
|
|||
"senderName": {}
|
||||
}
|
||||
},
|
||||
"commandHint_markasgroup": "그룹으로 만들기",
|
||||
"commandHint_markasgroup": "그룹 채팅으로 만들기",
|
||||
"@commandHint_markasgroup": {},
|
||||
"dehydrate": "세션을 내보내고 기기 초기화 하기",
|
||||
"@dehydrate": {},
|
||||
|
|
@ -2092,7 +2092,7 @@
|
|||
"@user": {},
|
||||
"youAcceptedTheInvitation": "👍 초대를 수락했습니다",
|
||||
"@youAcceptedTheInvitation": {},
|
||||
"youInvitedBy": "📩 {user}에 의해 초대되었습니다",
|
||||
"youInvitedBy": "📩 {user}님에 의해 초대되었습니다",
|
||||
"@youInvitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2104,14 +2104,14 @@
|
|||
"@widgetEtherpad": {},
|
||||
"removeDevicesDescription": "이 기기에서 로그아웃되며 더 이상 메시지를 받을 수 없습니다.",
|
||||
"@removeDevicesDescription": {},
|
||||
"separateChatTypes": "다이렉트 채팅 및 그룹 분리",
|
||||
"separateChatTypes": "다이렉트 채팅과 그룹 채팅 분리",
|
||||
"@separateChatTypes": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"tryAgain": "다시 시도하기",
|
||||
"@tryAgain": {},
|
||||
"youKickedAndBanned": "🙅 {user}를 영구 추방했습니다",
|
||||
"youKickedAndBanned": "🙅 {user}님을 영구 추방했습니다",
|
||||
"@youKickedAndBanned": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2237,7 +2237,7 @@
|
|||
"reason": {}
|
||||
}
|
||||
},
|
||||
"youHaveWithdrawnTheInvitationFor": "{user}에 대한 초대를 철회함",
|
||||
"youHaveWithdrawnTheInvitationFor": "{user}님에 대한 초대를 철회함",
|
||||
"@youHaveWithdrawnTheInvitationFor": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2247,7 +2247,7 @@
|
|||
"@appearOnTopDetails": {},
|
||||
"enterRoom": "방에 입장",
|
||||
"@enterRoom": {},
|
||||
"youInvitedUser": "📩 {user}를 초대했습니다",
|
||||
"youInvitedUser": "📩 {user}님을 초대했습니다",
|
||||
"@youInvitedUser": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2278,7 +2278,7 @@
|
|||
"@screenSharingTitle": {},
|
||||
"widgetCustom": "사용자 정의",
|
||||
"@widgetCustom": {},
|
||||
"youBannedUser": "{user}을 영구 추방함",
|
||||
"youBannedUser": "{user}님을 영구 추방함",
|
||||
"@youBannedUser": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2286,7 +2286,7 @@
|
|||
},
|
||||
"addChatDescription": "채팅 설명 추가하기...",
|
||||
"@addChatDescription": {},
|
||||
"hasKnocked": "🚪 {user}가 참가를 요청했습니다",
|
||||
"hasKnocked": "🚪 {user}님이 참가를 요청했습니다",
|
||||
"@hasKnocked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2334,7 +2334,7 @@
|
|||
"@users": {},
|
||||
"chatDescriptionHasBeenChanged": "채팅 설명 변경됨",
|
||||
"@chatDescriptionHasBeenChanged": {},
|
||||
"newGroup": "새 그룹",
|
||||
"newGroup": "새 그룹 채팅",
|
||||
"@newGroup": {},
|
||||
"dehydrateTor": "TOR 사용자: 세션 내보내기",
|
||||
"@dehydrateTor": {},
|
||||
|
|
@ -2342,7 +2342,7 @@
|
|||
"@roomUpgradeDescription": {},
|
||||
"pleaseEnterANumber": "0보다 큰 숫자를 입력하세요",
|
||||
"@pleaseEnterANumber": {},
|
||||
"youKicked": "👞 {user}를 추방했습니다",
|
||||
"youKicked": "👞 {user}님을 추방했습니다",
|
||||
"@youKicked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2386,7 +2386,7 @@
|
|||
"@commandHint_hug": {},
|
||||
"replace": "대체",
|
||||
"@replace": {},
|
||||
"youUnbannedUser": "{user}의 영구 추방을 해제했습니다",
|
||||
"youUnbannedUser": "{user}님의 영구 추방을 해제했습니다",
|
||||
"@youUnbannedUser": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2396,11 +2396,11 @@
|
|||
"@newSpace": {},
|
||||
"pleaseTryAgainLaterOrChooseDifferentServer": "나중에 다시 시도하거나 다른 서버를 선택하십시오.",
|
||||
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
|
||||
"createGroup": "그룹 만들기",
|
||||
"createGroup": "새 그룹 채팅",
|
||||
"@createGroup": {},
|
||||
"hydrateTorLong": "지난 TOR 이용에서 세션을 내보내셨나요? 빠르게 불러오고 채팅을 계속하세요.",
|
||||
"@hydrateTorLong": {},
|
||||
"custom": "개인화",
|
||||
"custom": "커스텀",
|
||||
"@custom": {},
|
||||
"noBackupWarning": "경고! 채팅 백업을 켜지 않을경우, 당신은 암호화된 메시지에대한 접근권한을 잃을것입니다. 로그아웃 하기 전에 채팅을 백업하는것이 강력히 권장됩니다.",
|
||||
"@noBackupWarning": {},
|
||||
|
|
@ -2424,7 +2424,7 @@
|
|||
"@block": {},
|
||||
"blockedUsers": "차단된 유저",
|
||||
"@blockedUsers": {},
|
||||
"groupName": "그룹 이름",
|
||||
"groupName": "그룹 채팅 이름",
|
||||
"@groupName": {},
|
||||
"commandHint_sendraw": "raw json 전송",
|
||||
"@commandHint_sendraw": {},
|
||||
|
|
@ -2464,7 +2464,7 @@
|
|||
"sender": {}
|
||||
}
|
||||
},
|
||||
"createGroupAndInviteUsers": "그룹을 생성하고 유저를 초대",
|
||||
"createGroupAndInviteUsers": "그룹 채팅을 생성하고 유저를 초대",
|
||||
"@createGroupAndInviteUsers": {},
|
||||
"passwordsDoNotMatch": "비밀번호가 일치하지 않습니다",
|
||||
"@passwordsDoNotMatch": {},
|
||||
|
|
@ -2484,7 +2484,7 @@
|
|||
"@hidePresences": {},
|
||||
"searchChatsRooms": "#chats, @users 검색...",
|
||||
"@searchChatsRooms": {},
|
||||
"groupCanBeFoundViaSearch": "검색으로 그룹을 찾을 수 있음",
|
||||
"groupCanBeFoundViaSearch": "검색으로 그룹 채팅을 찾을 수 있음",
|
||||
"@groupCanBeFoundViaSearch": {},
|
||||
"restoreSessionBody": "앱이 백업에서 세션을 복원하려 시도중입니다. {url} 에서 개발자에게 오류를 신고하세요. 오류 메시지는 다음과 같습니다: {error}",
|
||||
"@restoreSessionBody": {
|
||||
|
|
@ -2622,13 +2622,13 @@
|
|||
"@calls": {},
|
||||
"globalChatId": "글로벌 채팅 ID",
|
||||
"@globalChatId": {},
|
||||
"customEmojisAndStickers": "개인 이모지와 스티커",
|
||||
"customEmojisAndStickers": "커스텀 이모지와 스티커",
|
||||
"@customEmojisAndStickers": {},
|
||||
"accessAndVisibilityDescription": "채팅에 참여하거나 찾을 수 있는 사람을 설정합니다.",
|
||||
"accessAndVisibilityDescription": "채팅에 참여 할 수 있는 사람과 채팅을 볼 수 있는 범위",
|
||||
"@accessAndVisibilityDescription": {},
|
||||
"accessAndVisibility": "방 가입과 대화 기록",
|
||||
"accessAndVisibility": "채팅 가입과 대화 기록",
|
||||
"@accessAndVisibility": {},
|
||||
"customEmojisAndStickersBody": "모든 채팅에서 사용할 수있는 개인 이모지와 스티커를 추가하거나 공유합니다.",
|
||||
"customEmojisAndStickersBody": "모든 채팅에서 사용할 수있는 커스텀 이모지와 스티커를 추가하거나 공유합니다.",
|
||||
"@customEmojisAndStickersBody": {},
|
||||
"hideRedactedMessages": "삭제된 메시지 숨기기",
|
||||
"@hideRedactedMessages": {},
|
||||
|
|
@ -2644,7 +2644,7 @@
|
|||
"@passwordRecoverySettings": {},
|
||||
"hideMemberChangesInPublicChats": "공개 채팅에서의 참가자 변화 숨김",
|
||||
"@hideMemberChangesInPublicChats": {},
|
||||
"userWouldLikeToChangeTheChat": "{user}가 참가를 희망합니다.",
|
||||
"userWouldLikeToChangeTheChat": "{user}님이 참가를 희망합니다.",
|
||||
"@userWouldLikeToChangeTheChat": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2667,7 +2667,7 @@
|
|||
},
|
||||
"noOneCanJoin": "아무도 들어올 수 없음",
|
||||
"@noOneCanJoin": {},
|
||||
"thereAreCountUsersBlocked": "여기 {count}명의 차단된 유저가 있습니다.",
|
||||
"thereAreCountUsersBlocked": "{count}명의 차단된 유저가 있습니다.",
|
||||
"@thereAreCountUsersBlocked": {
|
||||
"type": "text",
|
||||
"count": {}
|
||||
|
|
@ -2701,5 +2701,40 @@
|
|||
}
|
||||
},
|
||||
"gallery": "갤러리",
|
||||
"@gallery": {}
|
||||
"@gallery": {},
|
||||
"restricted": "스페이스 멤버로 제한",
|
||||
"@restricted": {},
|
||||
"knockRestricted": "스페이스 멤버만 참가 요청 가능",
|
||||
"@knockRestricted": {},
|
||||
"swipeRightToLeftToReply": "오른쪽에서 왼쪽으로 스와이프해서 답장",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"unread": "읽지 않은",
|
||||
"@unread": {},
|
||||
"space": "스페이스",
|
||||
"@space": {},
|
||||
"spaces": "스페이스",
|
||||
"@spaces": {},
|
||||
"goToSpace": "스페이스로: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"markAsUnread": "읽지 않음으로 표시",
|
||||
"@markAsUnread": {},
|
||||
"countChatsAndCountParticipants": "{chats} 채팅과 {participants} 참여자",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"joinedChats": "참여한 채팅",
|
||||
"@joinedChats": {},
|
||||
"noMoreChatsFound": "채팅을 찾을 수 없습니다...",
|
||||
"@noMoreChatsFound": {}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -740,10 +740,6 @@
|
|||
},
|
||||
"signInWithPassword": "Pieteikties ar paroli",
|
||||
"@signInWithPassword": {},
|
||||
"@ignoredUsers": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"lastActiveAgo": "Pēdējoreiz redzēts: {localizedTimeShort}",
|
||||
"@lastActiveAgo": {
|
||||
"type": "text",
|
||||
|
|
@ -840,10 +836,6 @@
|
|||
},
|
||||
"editBundlesForAccount": "Labot šī konta komplektus",
|
||||
"@editBundlesForAccount": {},
|
||||
"@renderRichContent": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"enableEncryption": "Iespējot šifrēšanu",
|
||||
"@enableEncryption": {
|
||||
"type": "text",
|
||||
|
|
@ -1114,10 +1106,6 @@
|
|||
"joinRules": {}
|
||||
}
|
||||
},
|
||||
"@ignore": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"recording": "Ieraksta",
|
||||
"@recording": {
|
||||
"type": "text",
|
||||
|
|
@ -1537,10 +1525,6 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@hideUnknownEvents": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"dehydrateTorLong": "TOR lietotājiem ir ieteicams izgūt sesiju pirms loga aizvēršanas.",
|
||||
"@dehydrateTorLong": {},
|
||||
"yourPublicKey": "Tava publiskā atslēga",
|
||||
|
|
@ -2683,5 +2667,24 @@
|
|||
"count": {}
|
||||
},
|
||||
"verifyOtherDeviceDescription": "Kad apliecini citu ierīci, šīs ierīces var apmainīt atslēgas, palielinot vispārējo drošību. 💪 Kad uzsāc apliecināšanu, abās ierīcēs lietotnē parādīsies uznirstošais logs. Tajā būs redzamas dažādas emocijzīmes vai skaitļi, kas jāsalīdzina abās ierīcēs. Vislabāk, ja abas ierīces ir pieejams, pirms tiek uzsākta apliecināšana. 🤳",
|
||||
"@verifyOtherDeviceDescription": {}
|
||||
"@verifyOtherDeviceDescription": {},
|
||||
"swipeRightToLeftToReply": "Pavilkt pa labi, lai atbildētu",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"searchIn": "Meklēt tērzēšanā \"{chat}\"...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chat": {}
|
||||
}
|
||||
},
|
||||
"searchMore": "Meklēt vairāk...",
|
||||
"@searchMore": {},
|
||||
"gallery": "Galerija",
|
||||
"@gallery": {},
|
||||
"files": "Datnes",
|
||||
"@files": {},
|
||||
"restricted": "Ierobežots",
|
||||
"@restricted": {},
|
||||
"knockRestricted": "Pieklauvēt ierobežotajiem",
|
||||
"@knockRestricted": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1648,537 +1648,6 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@showPassword": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@hugContent": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"senderName": {}
|
||||
}
|
||||
},
|
||||
"@jumpToLastReadMessage": {},
|
||||
"@allRooms": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@obtainingLocation": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@commandHint_cuddle": {},
|
||||
"@chats": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@widgetVideo": {},
|
||||
"@dismiss": {},
|
||||
"@noEncryptionForPublicRooms": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@reportErrorDescription": {},
|
||||
"@addAccount": {},
|
||||
"@chatHasBeenAddedToThisSpace": {},
|
||||
"@removeYourAvatar": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@unsupportedAndroidVersion": {},
|
||||
"@commandHint_html": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /html"
|
||||
},
|
||||
"@widgetJitsi": {},
|
||||
"@messageType": {},
|
||||
"@indexedDbErrorLong": {},
|
||||
"@oneClientLoggedOut": {},
|
||||
"@startFirstChat": {},
|
||||
"@callingAccount": {},
|
||||
"@setColorTheme": {},
|
||||
"@nextAccount": {},
|
||||
"@commandHint_create": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /create"
|
||||
},
|
||||
"@singlesignon": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@allSpaces": {},
|
||||
"@supposedMxid": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"mxid": {}
|
||||
}
|
||||
},
|
||||
"@user": {},
|
||||
"@roomVersion": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@youAcceptedTheInvitation": {},
|
||||
"@noMatrixServer": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"server1": {},
|
||||
"server2": {}
|
||||
}
|
||||
},
|
||||
"@youInvitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@banUserDescription": {},
|
||||
"@widgetEtherpad": {},
|
||||
"@removeDevicesDescription": {},
|
||||
"@separateChatTypes": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@tryAgain": {},
|
||||
"@youKickedAndBanned": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@unbanUserDescription": {},
|
||||
"@saveFile": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@youRejectedTheInvitation": {},
|
||||
"@otherCallingPermissions": {},
|
||||
"@messagesStyle": {},
|
||||
"@link": {},
|
||||
"@widgetUrlError": {},
|
||||
"@emailOrUsername": {},
|
||||
"@newSpaceDescription": {},
|
||||
"@chatDescription": {},
|
||||
"@callingAccountDetails": {},
|
||||
"@editRoomAliases": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@enterSpace": {},
|
||||
"@encryptThisChat": {},
|
||||
"@previousAccount": {},
|
||||
"@reopenChat": {},
|
||||
"@pleaseEnterRecoveryKey": {},
|
||||
"@widgetNameError": {},
|
||||
"@addToBundle": {},
|
||||
"@spaceIsPublic": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@addWidget": {},
|
||||
"@countFiles": {
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"@noKeyForThisMessage": {},
|
||||
"@shareLocation": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@commandHint_markasgroup": {},
|
||||
"@errorObtainingLocation": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"error": {}
|
||||
}
|
||||
},
|
||||
"@hydrateTor": {},
|
||||
"@pushNotificationsNotAvailable": {},
|
||||
"@storeInAppleKeyChain": {},
|
||||
"@hydrate": {},
|
||||
"@invalidServerName": {},
|
||||
"@chatPermissions": {},
|
||||
"@wipeChatBackup": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@sender": {},
|
||||
"@storeInAndroidKeystore": {},
|
||||
"@signInWithPassword": {},
|
||||
"@makeAdminDescription": {},
|
||||
"@synchronizingPleaseWait": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@goToTheNewRoom": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@commandHint_clearcache": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /clearcache"
|
||||
},
|
||||
"@saveKeyManuallyDescription": {},
|
||||
"@editBundlesForAccount": {},
|
||||
"@whyIsThisMessageEncrypted": {},
|
||||
"@setChatDescription": {},
|
||||
"@spaceName": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@importFromZipFile": {},
|
||||
"@or": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@dehydrateWarning": {},
|
||||
"@noOtherDevicesFound": {},
|
||||
"@yourChatBackupHasBeenSetUp": {},
|
||||
"@redactedBy": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"username": {}
|
||||
}
|
||||
},
|
||||
"@videoCallsBetaWarning": {},
|
||||
"@signInWith": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"provider": {}
|
||||
}
|
||||
},
|
||||
"@fileIsTooBigForServer": {},
|
||||
"@homeserver": {},
|
||||
"@people": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@verified": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@callingPermissions": {},
|
||||
"@readUpToHere": {},
|
||||
"@start": {},
|
||||
"@register": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@unlockOldMessages": {},
|
||||
"@numChats": {
|
||||
"type": "number",
|
||||
"placeholders": {
|
||||
"number": {}
|
||||
}
|
||||
},
|
||||
"@optionalRedactReason": {},
|
||||
"@dehydrate": {},
|
||||
"@locationPermissionDeniedNotice": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@sendAsText": {
|
||||
"type": "text"
|
||||
},
|
||||
"@archiveRoomDescription": {},
|
||||
"@exportEmotePack": {},
|
||||
"@sendSticker": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@switchToAccount": {
|
||||
"type": "number",
|
||||
"placeholders": {
|
||||
"number": {}
|
||||
}
|
||||
},
|
||||
"@commandInvalid": {
|
||||
"type": "text"
|
||||
},
|
||||
"@setAsCanonicalAlias": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@locationDisabledNotice": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@commandHint_plain": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /plain"
|
||||
},
|
||||
"@experimentalVideoCalls": {},
|
||||
"@pleaseEnterRecoveryKeyDescription": {},
|
||||
"@openInMaps": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@inviteContactToGroupQuestion": {},
|
||||
"@redactedByBecause": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"username": {},
|
||||
"reason": {}
|
||||
}
|
||||
},
|
||||
"@youHaveWithdrawnTheInvitationFor": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@appearOnTopDetails": {},
|
||||
"@enterRoom": {},
|
||||
"@pleaseChooseAPasscode": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@reportUser": {},
|
||||
"@commandHint_send": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /send"
|
||||
},
|
||||
"@confirmEventUnpin": {},
|
||||
"@youInvitedUser": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@fileHasBeenSavedAt": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"path": {}
|
||||
}
|
||||
},
|
||||
"@commandMissing": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"command": {}
|
||||
},
|
||||
"description": "State that {command} is not a valid /command."
|
||||
},
|
||||
"@redactMessageDescription": {},
|
||||
"@recoveryKey": {},
|
||||
"@redactMessage": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@commandHint_discardsession": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /discardsession"
|
||||
},
|
||||
"@invalidInput": {},
|
||||
"@dehydrateTorLong": {},
|
||||
"@commandHint_myroomnick": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /myroomnick"
|
||||
},
|
||||
"@doNotShowAgain": {},
|
||||
"@report": {},
|
||||
"@unverified": {},
|
||||
"@serverRequiresEmail": {},
|
||||
"@hideUnimportantStateEvents": {},
|
||||
"@screenSharingTitle": {},
|
||||
"@widgetCustom": {},
|
||||
"@addToSpaceDescription": {},
|
||||
"@googlyEyesContent": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"senderName": {}
|
||||
}
|
||||
},
|
||||
"@youBannedUser": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@addChatDescription": {},
|
||||
"@commandHint_leave": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /leave"
|
||||
},
|
||||
"@commandHint_myroomavatar": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /myroomavatar"
|
||||
},
|
||||
"@hasKnocked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@publish": {},
|
||||
"@openLinkInBrowser": {},
|
||||
"@clearArchive": {},
|
||||
"@commandHint_react": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /react"
|
||||
},
|
||||
"@commandHint_me": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /me"
|
||||
},
|
||||
"@messageInfo": {},
|
||||
"@disableEncryptionWarning": {},
|
||||
"@directChat": {},
|
||||
"@wrongPinEntered": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"@sendTypingNotifications": {},
|
||||
"@inviteGroupChat": {},
|
||||
"@appearOnTop": {},
|
||||
"@invitePrivateChat": {},
|
||||
"@foregroundServiceRunning": {},
|
||||
"@voiceCall": {},
|
||||
"@commandHint_kick": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /kick"
|
||||
},
|
||||
"@createNewSpace": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@commandHint_unban": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /unban"
|
||||
},
|
||||
"@commandHint_ban": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /ban"
|
||||
},
|
||||
"@importEmojis": {},
|
||||
"@wasDirectChatDisplayName": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"oldDisplayName": {}
|
||||
}
|
||||
},
|
||||
"@noChatDescriptionYet": {},
|
||||
"@removeFromBundle": {},
|
||||
"@confirmMatrixId": {},
|
||||
"@learnMore": {},
|
||||
"notAnImage": "Ikke en bildefil.",
|
||||
"@notAnImage": {},
|
||||
"@users": {},
|
||||
"@openGallery": {},
|
||||
"@chatDescriptionHasBeenChanged": {},
|
||||
"@newGroup": {},
|
||||
"@bundleName": {},
|
||||
"@dehydrateTor": {},
|
||||
"@removeFromSpace": {},
|
||||
"@commandHint_op": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /op"
|
||||
},
|
||||
"@commandHint_join": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /join"
|
||||
},
|
||||
"@roomUpgradeDescription": {},
|
||||
"@commandHint_invite": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /invite"
|
||||
},
|
||||
"@scanQrCode": {},
|
||||
"@pleaseEnterANumber": {},
|
||||
"@youKicked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@profileNotFound": {},
|
||||
"@jump": {},
|
||||
"@reactedWith": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"sender": {},
|
||||
"reaction": {}
|
||||
}
|
||||
},
|
||||
"@sorryThatsNotPossible": {},
|
||||
"@videoWithSize": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"size": {}
|
||||
}
|
||||
},
|
||||
"@shareInviteLink": {},
|
||||
"@commandHint_markasdm": {},
|
||||
"@recoveryKeyLost": {},
|
||||
"@cuddleContent": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"senderName": {}
|
||||
}
|
||||
},
|
||||
"@deviceKeys": {},
|
||||
"@emoteKeyboardNoRecents": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@setTheme": {},
|
||||
"@youJoinedTheChat": {},
|
||||
"@openVideoCamera": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@markAsRead": {},
|
||||
"@widgetName": {},
|
||||
"@errorAddingWidget": {},
|
||||
"@commandHint_dm": {
|
||||
"type": "text",
|
||||
"description": "Usage hint for the command /dm"
|
||||
},
|
||||
"@commandHint_hug": {},
|
||||
"@replace": {},
|
||||
"@oopsPushError": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@youUnbannedUser": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@pleaseEnter4Digits": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@newSpace": {},
|
||||
"@emojis": {},
|
||||
"@pleaseEnterYourPin": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@pleaseChoose": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@commandHint_googly": {},
|
||||
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
|
||||
"@createGroup": {},
|
||||
"@hydrateTorLong": {},
|
||||
"@time": {},
|
||||
"@custom": {},
|
||||
"@noBackupWarning": {},
|
||||
"@storeInSecureStorageDescription": {},
|
||||
"@openChat": {},
|
||||
"@kickUserDescription": {},
|
||||
"@importNow": {},
|
||||
"@pinMessage": {},
|
||||
"@invite": {},
|
||||
"@enableMultiAccounts": {},
|
||||
"@indexedDbErrorTitle": {},
|
||||
"@unsupportedAndroidVersionLong": {},
|
||||
"@storeSecurlyOnThisDevice": {},
|
||||
"@screenSharingDetail": {},
|
||||
"@waitingPartnerAcceptRequest": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@waitingPartnerEmoji": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"@placeCall": {}
|
||||
"@notAnImage": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -562,7 +562,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "Standaardmachtigingsniveau",
|
||||
"defaultPermissionLevel": "Standaard machtigingsniveau voor nieuwe personen",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2313,7 +2313,7 @@
|
|||
"@replace": {},
|
||||
"report": "rapporteer",
|
||||
"@report": {},
|
||||
"reportErrorDescription": "Oh nee. Er is iets misgegaan. Probeer het later nog eens. Als je wilt, kun je de bug rapporteren aan de ontwikkelaars.",
|
||||
"reportErrorDescription": "😭 Oh nee. Er is iets misgegaan. Probeer het later nog eens. Als je wilt, kun je de bug rapporteren aan de ontwikkelaars.",
|
||||
"@reportErrorDescription": {},
|
||||
"sendTypingNotifications": "Typemeldingen verzenden",
|
||||
"@sendTypingNotifications": {},
|
||||
|
|
@ -2349,7 +2349,7 @@
|
|||
"@inviteContactToGroupQuestion": {},
|
||||
"optionalRedactReason": "(Optioneel) Reden voor aanpassing van dit bericht...",
|
||||
"@optionalRedactReason": {},
|
||||
"addChatDescription": "Voeg een chatbeschrijving toe",
|
||||
"addChatDescription": "Voeg een chatbeschrijving toe...",
|
||||
"@addChatDescription": {},
|
||||
"invalidServerName": "Foute servernaam",
|
||||
"@invalidServerName": {},
|
||||
|
|
@ -2404,7 +2404,7 @@
|
|||
"@makeAdminDescription": {},
|
||||
"archiveRoomDescription": "De chat zal naar het archief worden verplaatst. Andere personen zullen in staat zijn te zien dat je de chat hebt verlaten.",
|
||||
"@archiveRoomDescription": {},
|
||||
"hasKnocked": "{user} heeft geklopt",
|
||||
"hasKnocked": "🚪 {user} heeft geklopt",
|
||||
"@hasKnocked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2417,5 +2417,179 @@
|
|||
"pleaseEnterANumber": "Vul een getal in groter dan 0",
|
||||
"@pleaseEnterANumber": {},
|
||||
"kickUserDescription": "De persoon is verwijderd uit de chat, maar is niet verbannen. In publieke chats kan de persoon op elk moment opnieuw deelnemen.",
|
||||
"@kickUserDescription": {}
|
||||
"@kickUserDescription": {},
|
||||
"alwaysUse24HourFormat": "true",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"joinSpace": "Deelname aan space",
|
||||
"@joinSpace": {},
|
||||
"block": "Blokkeren",
|
||||
"@block": {},
|
||||
"blockedUsers": "Geblokkeerde personen",
|
||||
"@blockedUsers": {},
|
||||
"presenceStyle": "Aanwezigheid:",
|
||||
"@presenceStyle": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"searchChatsRooms": "Zoek naar #chats, @personen...",
|
||||
"@searchChatsRooms": {},
|
||||
"swipeRightToLeftToReply": "Veeg van rechts naar links om te reageren",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"calls": "Gesprekken",
|
||||
"@calls": {},
|
||||
"customEmojisAndStickers": "Aangepaste emojis and stickers",
|
||||
"@customEmojisAndStickers": {},
|
||||
"accessAndVisibilityDescription": "Wie mag meedoen in deze chat en hoe de chat ontdekt kan worden.",
|
||||
"@accessAndVisibilityDescription": {},
|
||||
"customEmojisAndStickersBody": "Voeg toe of deel aangepaste emojis of stickers die gebruikt kunnen worden in elke chat.",
|
||||
"@customEmojisAndStickersBody": {},
|
||||
"hideRedactedMessages": "Verberg verwijderde berichten",
|
||||
"@hideRedactedMessages": {},
|
||||
"hideRedactedMessagesBody": "Als iemand een bericht verwijdert, is dit bericht niet meer zichtbaar in de chat.",
|
||||
"@hideRedactedMessagesBody": {},
|
||||
"hideInvalidOrUnknownMessageFormats": "Verberg ongeldige of onbekende berichtformaten",
|
||||
"@hideInvalidOrUnknownMessageFormats": {},
|
||||
"passwordRecoverySettings": "Wachtwoord herstel instellingen",
|
||||
"@passwordRecoverySettings": {},
|
||||
"youInvitedToBy": "📩 Je bent uitgenodigd via een link voor:\n{alias}",
|
||||
"@youInvitedToBy": {
|
||||
"placeholders": {
|
||||
"alias": {}
|
||||
}
|
||||
},
|
||||
"knock": "Kloppen",
|
||||
"@knock": {},
|
||||
"overview": "Overzicht",
|
||||
"@overview": {},
|
||||
"hidePresences": "Verberg Status Lijst?",
|
||||
"@hidePresences": {},
|
||||
"noOneCanJoin": "Niemand kan deelnemen",
|
||||
"@noOneCanJoin": {},
|
||||
"yourGlobalUserIdIs": "Je globale gebruikers-ID is: ",
|
||||
"@yourGlobalUserIdIs": {},
|
||||
"appLockDescription": "Vergendel de app wanneer het niet gebruikt wordt met een pincode",
|
||||
"@appLockDescription": {},
|
||||
"globalChatId": "Globale chat ID",
|
||||
"@globalChatId": {},
|
||||
"accessAndVisibility": "Toegang en zichtbaarheid",
|
||||
"@accessAndVisibility": {},
|
||||
"invitedBy": "📩 Uitgenodigd door: {user}",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"publicSpaces": "Publieke spaces",
|
||||
"@publicSpaces": {},
|
||||
"blockUsername": "Negeer gebruikersnaam",
|
||||
"@blockUsername": {},
|
||||
"publicChatAddresses": "Publieke chat adressen",
|
||||
"@publicChatAddresses": {},
|
||||
"createNewAddress": "Creëer nieuw adres",
|
||||
"@createNewAddress": {},
|
||||
"countChatsAndCountParticipants": "{chats} chats en {participants} deelnemers",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"noMoreChatsFound": "Geen chats gevonden...",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "Deelnemende chats",
|
||||
"@joinedChats": {},
|
||||
"knocking": "Kloppen",
|
||||
"@knocking": {},
|
||||
"space": "Space",
|
||||
"@space": {},
|
||||
"spaces": "Spaces",
|
||||
"@spaces": {},
|
||||
"unread": "Zet als ongelezen",
|
||||
"@unread": {},
|
||||
"databaseBuildErrorBody": "Het aanmaken van de SQlite database is mislukt. De app probeert nu een traditionele database te gebruiken. Meldt alsjeblieft deze fout aan de ontwikkelaars via deze {url}. De foutmelding is: {error}",
|
||||
"@databaseBuildErrorBody": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"url": {},
|
||||
"error": {}
|
||||
}
|
||||
},
|
||||
"groupName": "Groepsnaam",
|
||||
"@groupName": {},
|
||||
"changeGeneralChatSettings": "Wijzig algemene chat instellingen",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"restricted": "Beperkt",
|
||||
"@restricted": {},
|
||||
"searchForUsers": "Zoek naar @personen...",
|
||||
"@searchForUsers": {},
|
||||
"searchMore": "Zoek meer...",
|
||||
"@searchMore": {},
|
||||
"noPublicLinkHasBeenCreatedYet": "Publieke link is nog niet gecreëerd",
|
||||
"@noPublicLinkHasBeenCreatedYet": {},
|
||||
"groupCanBeFoundViaSearch": "Groep kan gevonden worden via zoeken",
|
||||
"@groupCanBeFoundViaSearch": {},
|
||||
"searchIn": "Zoek in chat \"{chat}\"...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chat": {}
|
||||
}
|
||||
},
|
||||
"files": "Bestanden",
|
||||
"@files": {},
|
||||
"unreadChatsInApp": "{appname}: {unread} ongelezen chats",
|
||||
"@unreadChatsInApp": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"appname": {},
|
||||
"unread": {}
|
||||
}
|
||||
},
|
||||
"noDatabaseEncryption": "Database versleuteling is niet ondersteund op dit platform",
|
||||
"@noDatabaseEncryption": {},
|
||||
"thereAreCountUsersBlocked": "Nu zijn er {count} personen geblokkeerd.",
|
||||
"@thereAreCountUsersBlocked": {
|
||||
"type": "text",
|
||||
"count": {}
|
||||
},
|
||||
"markAsUnread": "Markeer als ongelezen",
|
||||
"@markAsUnread": {},
|
||||
"userLevel": "{level} - Persoon",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - Moderator",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - Administrator",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"stickers": "Stickers",
|
||||
"@stickers": {},
|
||||
"nothingFound": "Niets gevonden...",
|
||||
"@nothingFound": {},
|
||||
"gallery": "Gallerij",
|
||||
"@gallery": {},
|
||||
"transparent": "Transparant",
|
||||
"@transparent": {},
|
||||
"incomingMessages": "Inkomende berichten",
|
||||
"@incomingMessages": {},
|
||||
"discover": "Ontdek",
|
||||
"@discover": {},
|
||||
"commandHint_ignore": "Negeer de gegeven matrix ID",
|
||||
"@commandHint_ignore": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2393,23 +2393,44 @@
|
|||
"@importNow": {},
|
||||
"invite": "Zaproszenie",
|
||||
"@invite": {},
|
||||
"@banUserDescription": {},
|
||||
"@removeDevicesDescription": {},
|
||||
"@unbanUserDescription": {},
|
||||
"@pushNotificationsNotAvailable": {},
|
||||
"@makeAdminDescription": {},
|
||||
"@archiveRoomDescription": {},
|
||||
"@hasKnocked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"@learnMore": {},
|
||||
"@roomUpgradeDescription": {},
|
||||
"@pleaseEnterANumber": {},
|
||||
"@kickUserDescription": {},
|
||||
"block": "zablokuj",
|
||||
"block": "Zablokuj",
|
||||
"@block": {},
|
||||
"blockedUsers": "Zablokowani użytkownicy",
|
||||
"@blockedUsers": {}
|
||||
"@blockedUsers": {},
|
||||
"blockUsername": "Ignoruj użytkownika",
|
||||
"@blockUsername": {},
|
||||
"publicLink": "Link publiczny",
|
||||
"@publicLink": {},
|
||||
"transparent": "Przezroczystość",
|
||||
"@transparent": {},
|
||||
"select": "Zaznacz",
|
||||
"@select": {},
|
||||
"calls": "Połączenia",
|
||||
"@calls": {},
|
||||
"overview": "Podsumowanie",
|
||||
"@overview": {},
|
||||
"learnMore": "Dowiedz się więcej",
|
||||
"@learnMore": {},
|
||||
"groupName": "Nazwa grupy",
|
||||
"@groupName": {},
|
||||
"startConversation": "Rozpocznij rozmowę",
|
||||
"@startConversation": {},
|
||||
"newPassword": "Nowe hasło",
|
||||
"@newPassword": {},
|
||||
"thisDevice": "To urządzenie:",
|
||||
"@thisDevice": {},
|
||||
"gallery": "Galeria",
|
||||
"@gallery": {},
|
||||
"files": "Pliki",
|
||||
"@files": {},
|
||||
"discover": "Odkrywaj",
|
||||
"@discover": {},
|
||||
"restricted": "Ograniczone",
|
||||
"@restricted": {},
|
||||
"decline": "Odmów",
|
||||
"@decline": {},
|
||||
"nothingFound": "Nic nie odnaleziono...",
|
||||
"@nothingFound": {},
|
||||
"stickers": "Naklejki",
|
||||
"@stickers": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2688,5 +2688,22 @@
|
|||
"createNewAddress": "Criar um novo endereço",
|
||||
"@createNewAddress": {},
|
||||
"knock": "Bater na porta",
|
||||
"@knock": {}
|
||||
"@knock": {},
|
||||
"searchIn": "Pesquisar em {chat}...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chat": {}
|
||||
}
|
||||
},
|
||||
"restricted": "Restrito",
|
||||
"@restricted": {},
|
||||
"knockRestricted": "Bater na porta restrito",
|
||||
"@knockRestricted": {},
|
||||
"searchMore": "Pesquisar mais...",
|
||||
"@searchMore": {},
|
||||
"gallery": "Galeria",
|
||||
"@gallery": {},
|
||||
"files": "Arquivos",
|
||||
"@files": {}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -2232,7 +2232,7 @@
|
|||
},
|
||||
"commandHint_cuddle": "Отправить улыбку",
|
||||
"@commandHint_cuddle": {},
|
||||
"readUpToHere": "Дочитать до сюда",
|
||||
"readUpToHere": "Непрочитанное",
|
||||
"@readUpToHere": {},
|
||||
"commandHint_hug": "Отправить обнимашки",
|
||||
"@commandHint_hug": {},
|
||||
|
|
@ -2369,7 +2369,7 @@
|
|||
"reason": {}
|
||||
}
|
||||
},
|
||||
"setChatDescription": "Изменить описание чата",
|
||||
"setChatDescription": "Установить описание чата",
|
||||
"@setChatDescription": {},
|
||||
"setColorTheme": "Цветовая тема:",
|
||||
"@setColorTheme": {},
|
||||
|
|
@ -2689,5 +2689,76 @@
|
|||
"@thereAreCountUsersBlocked": {
|
||||
"type": "text",
|
||||
"count": {}
|
||||
},
|
||||
"restricted": "Запрещено",
|
||||
"@restricted": {},
|
||||
"knockRestricted": "Стук запрещен",
|
||||
"@knockRestricted": {},
|
||||
"searchIn": "Поиск в чате \"{chat}\"...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chat": {}
|
||||
}
|
||||
},
|
||||
"searchMore": "Найти еще...",
|
||||
"@searchMore": {},
|
||||
"gallery": "Галерея",
|
||||
"@gallery": {},
|
||||
"files": "Файлы",
|
||||
"@files": {},
|
||||
"swipeRightToLeftToReply": "Для ответа проведите с права на лево",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"userLevel": "{level} - Пользователь",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - Модератор",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - Администратор",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeGeneralChatSettings": "Изменить общие настройки чата",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"changeTheChatPermissions": "Изменить права доступа к чату",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheDescriptionOfTheGroup": "Изменить описание чата",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"inviteOtherUsers": "Пригласить других пользователей в этот чат",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheVisibilityOfChatHistory": "Изменить видимость истории чата",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"countChatsAndCountParticipants": "{chats} чатов и {participants} участников",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"unread": "Непрочитанные",
|
||||
"@unread": {},
|
||||
"space": "Пространство",
|
||||
"@space": {},
|
||||
"spaces": "Пространства",
|
||||
"@spaces": {},
|
||||
"markAsUnread": "Отметить как непрочитанное",
|
||||
"@markAsUnread": {},
|
||||
"goToSpace": "Перейти к пространству: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -563,7 +563,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "Öntanımlı izin seviyesi",
|
||||
"defaultPermissionLevel": "Yeni kullanıcılar içi öntanımlı izin seviyesi",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2706,5 +2706,102 @@
|
|||
"knockRestricted": "Tıklatma kısıtlı",
|
||||
"@knockRestricted": {},
|
||||
"restricted": "Kısıtlı",
|
||||
"@restricted": {}
|
||||
"@restricted": {},
|
||||
"swipeRightToLeftToReply": "Yanıtlamak için sağdan sola kaydır",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"countChatsAndCountParticipants": "{chats} sohbet ve {participants} katılımcı",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"noMoreChatsFound": "Başka sohbet bulunamadı...",
|
||||
"@noMoreChatsFound": {},
|
||||
"goToSpace": "Alana git: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"joinedChats": "Katılınan sohbetler",
|
||||
"@joinedChats": {},
|
||||
"unread": "Okunmadı",
|
||||
"@unread": {},
|
||||
"markAsUnread": "Okunmadı olarak işaretle",
|
||||
"@markAsUnread": {},
|
||||
"space": "Alan",
|
||||
"@space": {},
|
||||
"spaces": "Alanlar",
|
||||
"@spaces": {},
|
||||
"inviteOtherUsers": "Diğer kullanıcıları bu sohbete davet et",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheChatPermissions": "Sohbet izinlerini değiştir",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheCanonicalRoomAlias": "Ana herkese açık sohbet adresini değiştir",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"sendRoomNotifications": "@oda bildirimleri gönder",
|
||||
"@sendRoomNotifications": {},
|
||||
"changeTheDescriptionOfTheGroup": "Sohbetin açıklamasını değiştir",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"userLevel": "{level} - Kullanıcı",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - Moderatör",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - Yönetici",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeGeneralChatSettings": "Genel sohbet ayarlarını değiştir",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"changeTheVisibilityOfChatHistory": "Sohbet geçmişinin görünürlüğünü değiştir",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"invitedBy": "📩 {user} davet etti",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"chatPermissionsDescription": "Bu sohbette belirli eylemler için hangi güç düzeyinin gerekli olduğunu tanımlayın. 0, 50 ve 100 güç düzeyleri genellikle kullanıcıları, moderatörleri ve yöneticileri temsil eder, ancak herhangi bir derecelendirme mümkündür.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"changelog": "Değişiklik günlüğü",
|
||||
"@changelog": {},
|
||||
"updateInstalled": "🎉 Güncelleme {version} kuruldu!",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"sendCanceled": "Gönderme iptal edildi",
|
||||
"@sendCanceled": {},
|
||||
"noChatsFoundHere": "Burada henüz sohbet bulunamadı. Aşağıdaki düğmeyi kullanarak biriyle yeni bir sohbet başlatın. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"loginWithMatrixId": "Matrix kimliği ile oturum aç",
|
||||
"@loginWithMatrixId": {},
|
||||
"discoverHomeservers": "Ana sunucuları keşfet",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "Ana sunucu nedir?",
|
||||
"@whatIsAHomeserver": {},
|
||||
"homeserverDescription": "Tüm verileriniz tıpkı bir e-posta sağlayıcısı gibi ana sunucuda saklanır. Hangi ana sunucuyu kullanmak istediğinizi seçebilir ve herkesle iletişim kurmaya devam edebilirsiniz. https://matrix.org adresinden daha fazla bilgi edinin.",
|
||||
"@homeserverDescription": {},
|
||||
"doesNotSeemToBeAValidHomeserver": "Uyumlu bir ana sunucu gibi görünmüyor. Yanlış URL mi?",
|
||||
"@doesNotSeemToBeAValidHomeserver": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1557,7 +1557,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "Типовий рівень дозволів",
|
||||
"defaultPermissionLevel": "Типовий рівень дозволів для нових користувачів",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -1913,12 +1913,12 @@
|
|||
},
|
||||
"unverified": "Неперевірений",
|
||||
"@unverified": {},
|
||||
"locationDisabledNotice": "Служби визначення місцеположення вимкнені. Увімкніть їх, щоб могти надавати доступ до вашого місцеположення.",
|
||||
"locationDisabledNotice": "Служби визначення розташування вимкнені. Увімкніть їх, щоб мати змогу ділитися своїм розташуванням.",
|
||||
"@locationDisabledNotice": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"locationPermissionDeniedNotice": "Дозвіл на розташування відхилено. Надайте можливість ділитися своїм місцеперебуванням.",
|
||||
"locationPermissionDeniedNotice": "Дозвіл на розташування відхилено. Надайте можливість ділитися своїм розташуванням.",
|
||||
"@locationPermissionDeniedNotice": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2405,7 +2405,7 @@
|
|||
"@makeAdminDescription": {},
|
||||
"archiveRoomDescription": "Бесіду буде переміщено до архіву. Інші користувачі зможуть побачити, що ви вийшли з неї.",
|
||||
"@archiveRoomDescription": {},
|
||||
"hasKnocked": "{user} стукає до вас",
|
||||
"hasKnocked": "🚪{user} стукає до вас",
|
||||
"@hasKnocked": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
|
|
@ -2436,7 +2436,7 @@
|
|||
"query": {}
|
||||
}
|
||||
},
|
||||
"block": "блокування",
|
||||
"block": "Заблокувати",
|
||||
"@block": {},
|
||||
"yourGlobalUserIdIs": "Ваш глобальний ID користувача: ",
|
||||
"@yourGlobalUserIdIs": {},
|
||||
|
|
@ -2604,5 +2604,204 @@
|
|||
"discover": "Огляд",
|
||||
"@discover": {},
|
||||
"stickers": "Наліпки",
|
||||
"@stickers": {}
|
||||
"@stickers": {},
|
||||
"searchIn": "Пошук у бесіді \"{chat}\"...",
|
||||
"@searchIn": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chat": {}
|
||||
}
|
||||
},
|
||||
"commandHint_ignore": "Ігнорувати цей Matrix ID",
|
||||
"@commandHint_ignore": {},
|
||||
"restricted": "Обмежено",
|
||||
"@restricted": {},
|
||||
"swipeRightToLeftToReply": "Посунути праворуч або ліворуч, щоб відповісти",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"globalChatId": "Глобальний ID бесіди",
|
||||
"@globalChatId": {},
|
||||
"accessAndVisibility": "Доступ і видимість",
|
||||
"@accessAndVisibility": {},
|
||||
"accessAndVisibilityDescription": "Хто може приєднатися до цієї бесіди і як її можна знайти.",
|
||||
"@accessAndVisibilityDescription": {},
|
||||
"calls": "Виклики",
|
||||
"@calls": {},
|
||||
"customEmojisAndStickers": "Власні емоджі та наліпки",
|
||||
"@customEmojisAndStickers": {},
|
||||
"customEmojisAndStickersBody": "Додавайте або діліться власними емоджі або наліпками, які можна використовувати в будь-якій бесіді.",
|
||||
"@customEmojisAndStickersBody": {},
|
||||
"createNewAddress": "Створити нову адресу",
|
||||
"@createNewAddress": {},
|
||||
"userRole": "Роль користувача",
|
||||
"@userRole": {},
|
||||
"minimumPowerLevel": "{level} — це найнижчий рівень повноважень.",
|
||||
"@minimumPowerLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"commandHint_unignore": "Не ігнорувати цей Matrix ID",
|
||||
"@commandHint_unignore": {},
|
||||
"knockRestricted": "Стук обмежено",
|
||||
"@knockRestricted": {},
|
||||
"appLockDescription": "Блокувати застосунок, коли не використовується ПІН-код",
|
||||
"@appLockDescription": {},
|
||||
"hideRedactedMessages": "Сховати змінені повідомлення",
|
||||
"@hideRedactedMessages": {},
|
||||
"hideRedactedMessagesBody": "Якщо хтось змінить повідомлення, його більше не буде видно в бесіді.",
|
||||
"@hideRedactedMessagesBody": {},
|
||||
"hideInvalidOrUnknownMessageFormats": "Сховати недійсні або невідомі формати повідомлень",
|
||||
"@hideInvalidOrUnknownMessageFormats": {},
|
||||
"hideMemberChangesInPublicChats": "Сховати зміни користувачів у загальнодоступних бесідах",
|
||||
"@hideMemberChangesInPublicChats": {},
|
||||
"hideMemberChangesInPublicChatsBody": "Не показувати в хронології бесіди, якщо хтось приєднується до загальнодоступної бесіди або виходить з неї, щоб покращити її читабельність.",
|
||||
"@hideMemberChangesInPublicChatsBody": {},
|
||||
"overview": "Огляд",
|
||||
"@overview": {},
|
||||
"notifyMeFor": "Сповіщати мене про",
|
||||
"@notifyMeFor": {},
|
||||
"passwordRecoverySettings": "Налаштування відновлення пароля",
|
||||
"@passwordRecoverySettings": {},
|
||||
"userWouldLikeToChangeTheChat": "{user} хоче приєднатися до бесіди.",
|
||||
"@userWouldLikeToChangeTheChat": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"noPublicLinkHasBeenCreatedYet": "Загальнодоступне посилання ще не створено",
|
||||
"@noPublicLinkHasBeenCreatedYet": {},
|
||||
"knock": "Постукатись",
|
||||
"@knock": {},
|
||||
"knocking": "Стукаються",
|
||||
"@knocking": {},
|
||||
"noDatabaseEncryption": "Шифрування бази даних не підтримується на цій платформі",
|
||||
"@noDatabaseEncryption": {},
|
||||
"usersMustKnock": "Користувачі повинні постукатись",
|
||||
"@usersMustKnock": {},
|
||||
"noOneCanJoin": "Ніхто не може приєднатись",
|
||||
"@noOneCanJoin": {},
|
||||
"chatCanBeDiscoveredViaSearchOnServer": "Бесіду можна знайти за допомогою пошуку на {server}",
|
||||
"@chatCanBeDiscoveredViaSearchOnServer": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"server": {}
|
||||
}
|
||||
},
|
||||
"publicChatAddresses": "Адреси загальнодоступної бесіди",
|
||||
"@publicChatAddresses": {},
|
||||
"searchMore": "Шукати ще...",
|
||||
"@searchMore": {},
|
||||
"gallery": "Галерея",
|
||||
"@gallery": {},
|
||||
"files": "Файли",
|
||||
"@files": {},
|
||||
"unreadChatsInApp": "{appname}: {unread} непрочитаних бесід",
|
||||
"@unreadChatsInApp": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"appname": {},
|
||||
"unread": {}
|
||||
}
|
||||
},
|
||||
"thereAreCountUsersBlocked": "Наразі заблоковано {count} користувачів.",
|
||||
"@thereAreCountUsersBlocked": {
|
||||
"type": "text",
|
||||
"count": {}
|
||||
},
|
||||
"moderatorLevel": "{level} - Модератор",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - Адміністратор",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"userLevel": "{level} - Користувач",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"changeGeneralChatSettings": "Змінити загальні налаштування чату",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"inviteOtherUsers": "Запросити інших користувачів до цього чату",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheChatPermissions": "Змінити права доступу до чату",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheVisibilityOfChatHistory": "Змінити видимість історії чату",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"changeTheCanonicalRoomAlias": "Змінити основну адресу загальнодоступного чату",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"sendRoomNotifications": "Надсилати сповіщення @room",
|
||||
"@sendRoomNotifications": {},
|
||||
"space": "Простір",
|
||||
"@space": {},
|
||||
"spaces": "Простори",
|
||||
"@spaces": {},
|
||||
"goToSpace": "Перейти до простору: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"markAsUnread": "Позначити непрочитаним",
|
||||
"@markAsUnread": {},
|
||||
"alwaysUse24HourFormat": "ні",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"invitedBy": "📩 Запрошений {user}",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"changeTheDescriptionOfTheGroup": "Змінити опис чату",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"updateInstalled": "🎉 Оновлення {version} встановлено!",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"changelog": "Зміни",
|
||||
"@changelog": {},
|
||||
"chatPermissionsDescription": "Визначте, який рівень повноважень необхідний для певних дій у цьому чаті. Рівні повноважень 0, 50 і 100 зазвичай представляють користувачів, модераторів та адміністраторів, але можливі будь-які градації.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"countChatsAndCountParticipants": "{chats} чати та {participants} учасників",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"noMoreChatsFound": "Більше чатів не знайдено...",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "Приєднані чати",
|
||||
"@joinedChats": {},
|
||||
"unread": "Непрочитані",
|
||||
"@unread": {},
|
||||
"sendCanceled": "Надсилання скасовано",
|
||||
"@sendCanceled": {},
|
||||
"noChatsFoundHere": "Бесід ще немає. Розпочніть спілкування натиснувши кнопку нижче. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"loginWithMatrixId": "Увійти за допомогою Matrix-ID",
|
||||
"@loginWithMatrixId": {},
|
||||
"discoverHomeservers": "Знайти домашні сервери",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "Що таке домашній сервер?",
|
||||
"@whatIsAHomeserver": {},
|
||||
"homeserverDescription": "Усі ваші дані зберігаються на домашньому сервері, так само як у постачальника послуг електронної пошти. Ви можете вибрати, який домашній сервер ви хочете використовувати, водночас ви можете спілкуватися з усіма. Докладніше на https://matrix.org.",
|
||||
"@homeserverDescription": {},
|
||||
"doesNotSeemToBeAValidHomeserver": "Здається, це несумісний домашній сервер. Неправильна URL-адреса?",
|
||||
"@doesNotSeemToBeAValidHomeserver": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -537,7 +537,7 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"defaultPermissionLevel": "默认权限级别",
|
||||
"defaultPermissionLevel": "新用户默认权限级别",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
|
|
@ -2706,5 +2706,102 @@
|
|||
"knockRestricted": "“请求加入”请求受限",
|
||||
"@knockRestricted": {},
|
||||
"restricted": "受限",
|
||||
"@restricted": {}
|
||||
"@restricted": {},
|
||||
"swipeRightToLeftToReply": "从右向左滑动进行回复",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"noMoreChatsFound": "找不到更多聊天…",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "已加入的聊天",
|
||||
"@joinedChats": {},
|
||||
"space": "空间",
|
||||
"@space": {},
|
||||
"spaces": "空间",
|
||||
"@spaces": {},
|
||||
"goToSpace": "转到空间:{space}",
|
||||
"@goToSpace": {
|
||||
"type": "text",
|
||||
"space": {}
|
||||
},
|
||||
"markAsUnread": "标为未读",
|
||||
"@markAsUnread": {},
|
||||
"countChatsAndCountParticipants": "{chats} 个聊天和 {participants} 名参与者",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"unread": "未读",
|
||||
"@unread": {},
|
||||
"userLevel": "{level} - 用户",
|
||||
"@userLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"moderatorLevel": "{level} - 主持人",
|
||||
"@moderatorLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - 管理员",
|
||||
"@adminLevel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"level": {}
|
||||
}
|
||||
},
|
||||
"inviteOtherUsers": "邀请其他用户到这个聊天",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheChatPermissions": "更改聊天权限",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheVisibilityOfChatHistory": "更改聊天历史的可见性",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"changeTheCanonicalRoomAlias": "更改主公共聊天地址",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"sendRoomNotifications": "发送通知聊天室所有人的通知",
|
||||
"@sendRoomNotifications": {},
|
||||
"changeTheDescriptionOfTheGroup": "更改聊天描述",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"changeGeneralChatSettings": "更改常规聊天设置",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"invitedBy": "📩 邀请人 {user}",
|
||||
"@invitedBy": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"chatPermissionsDescription": "定义此聊天中哪个权限等级对特定操作是必需的。权限等级 0、50 和 100 通常代表用户、主持人和管理员,但你可以自定义任何等级。",
|
||||
"@chatPermissionsDescription": {},
|
||||
"changelog": "更新记录",
|
||||
"@changelog": {},
|
||||
"updateInstalled": "🎉 已安装更新 {version} !",
|
||||
"@updateInstalled": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"version": {}
|
||||
}
|
||||
},
|
||||
"sendCanceled": "发送被取消",
|
||||
"@sendCanceled": {},
|
||||
"noChatsFoundHere": "此处尚未找到聊天。使用下方按钮 ⤵️ 开始和某人的新聊天",
|
||||
"@noChatsFoundHere": {},
|
||||
"loginWithMatrixId": "使用 Matrix-ID 登录",
|
||||
"@loginWithMatrixId": {},
|
||||
"discoverHomeservers": "发现主服务器",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "什么是主服务器?",
|
||||
"@whatIsAHomeserver": {},
|
||||
"homeserverDescription": "主服务器上就像电子邮件提供商,你的所有数据都存储在上面。你可以选择你想使用哪个主服务器。在 https://matrix.org 上了解更多信息。",
|
||||
"@homeserverDescription": {},
|
||||
"doesNotSeemToBeAValidHomeserver": "似乎不是兼容的主服务器。URL 不正确?",
|
||||
"@doesNotSeemToBeAValidHomeserver": {}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -38,7 +38,7 @@
|
|||
<a href='https://ko-fi.com/C1C86VN53' target='_blank' class="m-2 hover:scale-110 transition-transform "><img
|
||||
class="h-7" src='https://storage.ko-fi.com/cdn/kofi2.png?v=3' border='0'
|
||||
alt='Buy Me a Coffee at ko-fi.com' /></a>
|
||||
<a href="https://mastodon.art/@krille" rel="me" class="m-2 hover:scale-110 transition-transform "><img
|
||||
<a href="https://troet.cafe/@krille" rel="me" class="m-2 hover:scale-110 transition-transform "><img
|
||||
src="mastodon.svg" class="h-7" /></a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -156,8 +156,8 @@ GEM
|
|||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.8)
|
||||
strscan (>= 3.0.9)
|
||||
rexml (3.3.6)
|
||||
strscan
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.4)
|
||||
rubyzip (2.3.0)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import UIKit
|
||||
import Flutter
|
||||
|
||||
@UIApplicationMain
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ abstract class AppConfig {
|
|||
static double fontSizeFactor = 1;
|
||||
static const Color chatColor = primaryColor;
|
||||
static Color? colorSchemeSeed = primaryColor;
|
||||
static const double messageFontSize = 15.75;
|
||||
static const double messageFontSize = 16.0;
|
||||
static const bool allowOtherHomeservers = true;
|
||||
static const bool enableRegistration = true;
|
||||
static const double toolbarMaxHeight = 315.0;
|
||||
|
|
@ -57,6 +57,8 @@ abstract class AppConfig {
|
|||
static const String supportUrl = 'https://www.pangeachat.com/faqs';
|
||||
static const String termsOfServiceUrl =
|
||||
'https://www.pangeachat.com/terms-of-service';
|
||||
// static const String changelogUrl =
|
||||
// 'https://github.com/krille-chan/fluffychat/blob/main/CHANGELOG.md';
|
||||
//Pangea#
|
||||
static final Uri newIssueUrl = Uri(
|
||||
scheme: 'https',
|
||||
|
|
@ -153,10 +155,10 @@ abstract class AppConfig {
|
|||
_defaultHomeserver = json['default_homeserver'];
|
||||
}
|
||||
if (json['privacy_url'] is String) {
|
||||
_webBaseUrl = json['privacy_url'];
|
||||
_privacyUrl = json['privacy_url'];
|
||||
}
|
||||
if (json['web_base_url'] is String) {
|
||||
_privacyUrl = json['web_base_url'];
|
||||
_webBaseUrl = json['web_base_url'];
|
||||
}
|
||||
if (json['render_html'] is bool) {
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ import 'package:fluffychat/pages/settings_notifications/settings_notifications.d
|
|||
import 'package:fluffychat/pages/settings_password/settings_password.dart';
|
||||
import 'package:fluffychat/pages/settings_security/settings_security.dart';
|
||||
import 'package:fluffychat/pages/settings_style/settings_style.dart';
|
||||
import 'package:fluffychat/pangea/enum/bar_chart_view_enum.dart';
|
||||
import 'package:fluffychat/pangea/guard/p_vguard.dart';
|
||||
import 'package:fluffychat/pangea/pages/analytics/student_analytics/student_analytics.dart';
|
||||
import 'package:fluffychat/pangea/pages/find_partner/find_partner.dart';
|
||||
import 'package:fluffychat/pangea/pages/p_user_age/p_user_age.dart';
|
||||
import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart';
|
||||
|
|
@ -76,7 +74,7 @@ abstract class AppRoutes {
|
|||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const HomeserverPicker(),
|
||||
const HomeserverPicker(addMultiAccount: false),
|
||||
),
|
||||
redirect: loggedInRedirect,
|
||||
routes: [
|
||||
|
|
@ -162,17 +160,17 @@ abstract class AppRoutes {
|
|||
),
|
||||
routes: [
|
||||
// #Pangea
|
||||
GoRoute(
|
||||
path: 'mylearning',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const StudentAnalyticsPage(
|
||||
selectedView: BarChartViewSelection.messages,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
// GoRoute(
|
||||
// path: 'mylearning',
|
||||
// pageBuilder: (context, state) => defaultPageBuilder(
|
||||
// context,
|
||||
// state,
|
||||
// const StudentAnalyticsPage(
|
||||
// selectedView: BarChartViewSelection.messages,
|
||||
// ),
|
||||
// ),
|
||||
// redirect: loggedOutRedirect,
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: 'analytics',
|
||||
// pageBuilder: (context, state) => defaultPageBuilder(
|
||||
|
|
@ -351,13 +349,15 @@ abstract class AppRoutes {
|
|||
// redirect: loggedOutRedirect,
|
||||
// pageBuilder: (context, state) => defaultPageBuilder(
|
||||
// context,
|
||||
// const HomeserverPicker(),
|
||||
// state,
|
||||
// const HomeserverPicker(addMultiAccount: true),
|
||||
// ),
|
||||
// routes: [
|
||||
// GoRoute(
|
||||
// path: 'login',
|
||||
// pageBuilder: (context, state) => defaultPageBuilder(
|
||||
// context,
|
||||
// state,
|
||||
// const Login(),
|
||||
// ),
|
||||
// redirect: loggedOutRedirect,
|
||||
|
|
|
|||
|
|
@ -78,9 +78,6 @@ abstract class FluffyThemes {
|
|||
? Typography.material2018().black.merge(fallbackTextTheme)
|
||||
: Typography.material2018().white.merge(fallbackTextTheme)
|
||||
: null,
|
||||
snackBarTheme: const SnackBarThemeData(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
dividerColor: brightness == Brightness.light
|
||||
? Colors.blueGrey.shade50
|
||||
: Colors.blueGrey.shade900,
|
||||
|
|
@ -95,11 +92,10 @@ abstract class FluffyThemes {
|
|||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
filled: true,
|
||||
filled: false,
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
toolbarHeight: FluffyThemes.isColumnMode(context) ? 72 : 56,
|
||||
|
|
@ -118,13 +114,6 @@ abstract class FluffyThemes {
|
|||
systemNavigationBarColor: colorScheme.surface,
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(
|
||||
|
|
@ -149,9 +138,6 @@ abstract class FluffyThemes {
|
|||
elevation: 0,
|
||||
padding: const EdgeInsets.all(16),
|
||||
textStyle: const TextStyle(fontSize: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
_wipe ??= widget.wipe;
|
||||
final buttons = <Widget>[];
|
||||
Widget body = const CircularProgressIndicator.adaptive();
|
||||
|
|
@ -119,7 +120,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
backgroundColor: Colors.transparent,
|
||||
child: Icon(
|
||||
Icons.info_outlined,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
subtitle: Text(L10n.of(context)!.chatBackupDescription),
|
||||
|
|
@ -144,7 +145,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
CheckboxListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
value: _storeInSecureStorage,
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
activeColor: theme.colorScheme.primary,
|
||||
onChanged: (b) {
|
||||
setState(() {
|
||||
_storeInSecureStorage = b;
|
||||
|
|
@ -158,7 +159,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
CheckboxListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
value: _recoveryKeyCopied,
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
activeColor: theme.colorScheme.primary,
|
||||
onChanged: (b) {
|
||||
FluffyShare.share(key!, context);
|
||||
setState(() => _recoveryKeyCopied = true);
|
||||
|
|
@ -241,7 +242,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
trailing: Icon(
|
||||
Icons.info_outlined,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
subtitle: Text(
|
||||
L10n.of(context)!.pleaseEnterRecoveryKeyDescription,
|
||||
|
|
@ -261,10 +262,11 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
hintStyle: TextStyle(
|
||||
fontFamily:
|
||||
Theme.of(context).textTheme.bodyLarge?.fontFamily,
|
||||
fontFamily: theme.textTheme.bodyLarge?.fontFamily,
|
||||
),
|
||||
hintText: L10n.of(context)!.recoveryKey,
|
||||
prefixIcon: const Icon(Icons.key_outlined),
|
||||
labelText: L10n.of(context)!.recoveryKey,
|
||||
hintText: 'Es** **** **** ****',
|
||||
errorText: _recoveryKeyInputError,
|
||||
errorMaxLines: 2,
|
||||
),
|
||||
|
|
@ -272,9 +274,8 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimary,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: theme.colorScheme.onPrimary,
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
),
|
||||
icon: _recoveryKeyInputLoading
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
|
|
@ -386,10 +387,8 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.errorContainer,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onErrorContainer,
|
||||
backgroundColor: theme.colorScheme.errorContainer,
|
||||
foregroundColor: theme.colorScheme.onErrorContainer,
|
||||
),
|
||||
icon: const Icon(Icons.delete_outlined),
|
||||
label: Text(L10n.of(context)!.recoveryKeyLost),
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class AddWidgetTileView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
ButtonBar(
|
||||
OverflowBar(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: controller.addWidget,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:developer';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
|
|
@ -124,7 +125,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
|
||||
Timeline? timeline;
|
||||
|
||||
String? readMarkerEventId;
|
||||
late final String readMarkerEventId;
|
||||
|
||||
String get roomId => widget.room.id;
|
||||
|
||||
|
|
@ -254,23 +255,22 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
// void requestHistory([_]) async {
|
||||
Future<void> requestHistory() async {
|
||||
if (timeline == null) return;
|
||||
// Pangea#
|
||||
if (!timeline!.canRequestHistory) return;
|
||||
// Pangea#
|
||||
Logs().v('Requesting history...');
|
||||
await timeline!.requestHistory(historyCount: _loadHistoryCount);
|
||||
await timeline?.requestHistory(historyCount: _loadHistoryCount);
|
||||
}
|
||||
|
||||
void requestFuture() async {
|
||||
final timeline = this.timeline;
|
||||
if (timeline == null) return;
|
||||
if (!timeline.canRequestFuture) return;
|
||||
Logs().v('Requesting future...');
|
||||
final mostRecentEventId = timeline.events.first.eventId;
|
||||
await timeline.requestFuture(historyCount: _loadHistoryCount);
|
||||
setReadMarker(eventId: mostRecentEventId);
|
||||
}
|
||||
|
||||
void _updateScrollController() {
|
||||
void updateScrollController() {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -289,7 +289,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}
|
||||
}
|
||||
|
||||
void _loadDraft() async {
|
||||
void loadDraft() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final draft = widget.shareText ?? prefs.getString('draft_$roomId');
|
||||
if (draft != null && draft.isNotEmpty) {
|
||||
|
|
@ -299,17 +299,18 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
scrollController.addListener(_updateScrollController);
|
||||
inputFocus.addListener(_inputFocusListener);
|
||||
scrollController.addListener(updateScrollController);
|
||||
inputFocus.addListener(inputFocusListener);
|
||||
|
||||
_loadDraft();
|
||||
loadDraft();
|
||||
super.initState();
|
||||
_displayChatDetailsColumn = ValueNotifier(
|
||||
displayChatDetailsColumn = ValueNotifier(
|
||||
Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ??
|
||||
false,
|
||||
);
|
||||
|
||||
sendingClient = Matrix.of(context).client;
|
||||
readMarkerEventId = room.hasNewMessages ? room.fullyRead : '';
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
// #Pangea
|
||||
if (!mounted) return;
|
||||
|
|
@ -333,32 +334,48 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
await Matrix.of(context).client.roomsLoading;
|
||||
});
|
||||
// Pangea#
|
||||
_tryLoadTimeline();
|
||||
tryLoadTimeline();
|
||||
if (kIsWeb) {
|
||||
onFocusSub = html.window.onFocus.listen((_) => setReadMarker());
|
||||
}
|
||||
}
|
||||
|
||||
void _tryLoadTimeline() async {
|
||||
void tryLoadTimeline() async {
|
||||
final initialEventId = widget.eventId;
|
||||
loadTimelineFuture = _getTimeline();
|
||||
try {
|
||||
await loadTimelineFuture;
|
||||
if (initialEventId != null) scrollToEventId(initialEventId);
|
||||
|
||||
final fullyRead = room.fullyRead;
|
||||
if (fullyRead.isEmpty) {
|
||||
setReadMarker();
|
||||
return;
|
||||
var readMarkerEventIndex = readMarkerEventId.isEmpty
|
||||
? -1
|
||||
: timeline!.events
|
||||
.where((e) => e.isVisibleInGui || e.eventId == readMarkerEventId)
|
||||
.toList()
|
||||
.indexWhere((e) => e.eventId == readMarkerEventId);
|
||||
|
||||
// Read marker is existing but not found in first events. Try a single
|
||||
// requestHistory call before opening timeline on event context:
|
||||
if (readMarkerEventId.isNotEmpty && readMarkerEventIndex == -1) {
|
||||
await timeline?.requestHistory(historyCount: _loadHistoryCount);
|
||||
readMarkerEventIndex = timeline!.events
|
||||
.where((e) => e.isVisibleInGui || e.eventId == readMarkerEventId)
|
||||
.toList()
|
||||
.indexWhere((e) => e.eventId == readMarkerEventId);
|
||||
}
|
||||
if (timeline?.events.any((event) => event.eventId == fullyRead) ??
|
||||
false) {
|
||||
Logs().v('Scroll up to visible event', fullyRead);
|
||||
setReadMarker();
|
||||
|
||||
if (readMarkerEventIndex > 1) {
|
||||
Logs().v('Scroll up to visible event', readMarkerEventId);
|
||||
scrollToEventId(readMarkerEventId, highlightEvent: false);
|
||||
return;
|
||||
} else if (readMarkerEventId.isNotEmpty && readMarkerEventIndex == -1) {
|
||||
showScrollUpMaterialBanner(readMarkerEventId);
|
||||
}
|
||||
|
||||
// Mark room as read on first visit if requirements are fulfilled
|
||||
setReadMarker();
|
||||
|
||||
if (!mounted) return;
|
||||
_showScrollUpMaterialBanner(fullyRead);
|
||||
} catch (e, s) {
|
||||
ErrorReporter(context, 'Unable to load timeline').onErrorCallback(e, s);
|
||||
rethrow;
|
||||
|
|
@ -371,12 +388,13 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
scrollUpBannerEventId = null;
|
||||
});
|
||||
|
||||
void _showScrollUpMaterialBanner(String eventId) => setState(() {
|
||||
void showScrollUpMaterialBanner(String eventId) => setState(() {
|
||||
scrollUpBannerEventId = eventId;
|
||||
});
|
||||
|
||||
void updateView() {
|
||||
if (!mounted) return;
|
||||
setReadMarker();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
|
@ -385,11 +403,6 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
int? animateInEventIndex;
|
||||
|
||||
void onInsert(int i) {
|
||||
if (timeline?.events[i].status == EventStatus.synced) {
|
||||
final index = timeline!.events.firstIndexWhereNotError;
|
||||
if (i == index) setReadMarker(eventId: timeline?.events[i].eventId);
|
||||
}
|
||||
|
||||
// setState will be called by updateView() anyway
|
||||
animateInEventIndex = i;
|
||||
}
|
||||
|
|
@ -414,6 +427,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
eventContextId = null;
|
||||
}
|
||||
try {
|
||||
timeline?.cancelSubscriptions();
|
||||
timeline = await room.getTimeline(
|
||||
onUpdate: updateView,
|
||||
eventContextId: eventContextId,
|
||||
|
|
@ -421,9 +435,9 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
// #Pangea
|
||||
if (visibleEvents.length < 10 && timeline != null) {
|
||||
int prevNumEvents = timeline!.events.length;
|
||||
var prevNumEvents = timeline!.events.length;
|
||||
await requestHistory();
|
||||
int numRequests = 0;
|
||||
var numRequests = 0;
|
||||
while (timeline!.events.length > prevNumEvents &&
|
||||
visibleEvents.length < 10 &&
|
||||
numRequests <= 5) {
|
||||
|
|
@ -442,7 +456,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
if (!mounted) return;
|
||||
if (e is TimeoutException || e is IOException) {
|
||||
_showScrollUpMaterialBanner(eventContextId!);
|
||||
showScrollUpMaterialBanner(eventContextId!);
|
||||
}
|
||||
}
|
||||
timeline!.requestKeys(onlineKeyBackupOnly: false);
|
||||
|
|
@ -459,12 +473,13 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
setReadMarker();
|
||||
}
|
||||
|
||||
Future<void>? _setReadMarkerFuture;
|
||||
Future<void>? setReadMarkerFuture;
|
||||
|
||||
void setReadMarker({String? eventId}) {
|
||||
if (_setReadMarkerFuture != null) return;
|
||||
if (setReadMarkerFuture != null) return;
|
||||
if (_scrolledUp) return;
|
||||
if (scrollUpBannerEventId != null) return;
|
||||
|
||||
if (eventId == null &&
|
||||
!room.hasNewMessages &&
|
||||
room.notificationCount == 0) {
|
||||
|
|
@ -477,7 +492,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
// Pangea#
|
||||
if (kIsWeb && !Matrix.of(context).webHasFocus) return;
|
||||
// #Pangea
|
||||
} catch (err, s) {
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
// Pangea#
|
||||
|
|
@ -493,13 +508,13 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
|
||||
Logs().d('Set read marker...', eventId);
|
||||
// ignore: unawaited_futures
|
||||
_setReadMarkerFuture = timeline
|
||||
setReadMarkerFuture = timeline
|
||||
.setReadMarker(
|
||||
eventId: eventId,
|
||||
public: AppConfig.sendPublicReadReceipts,
|
||||
)
|
||||
.then((_) {
|
||||
_setReadMarkerFuture = null;
|
||||
setReadMarkerFuture = null;
|
||||
})
|
||||
// #Pangea
|
||||
.catchError((e, s) {
|
||||
|
|
@ -531,7 +546,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
void dispose() {
|
||||
timeline?.cancelSubscriptions();
|
||||
timeline = null;
|
||||
inputFocus.removeListener(_inputFocusListener);
|
||||
inputFocus.removeListener(inputFocusListener);
|
||||
onFocusSub?.cancel();
|
||||
//#Pangea
|
||||
choreographer.stateListener.close();
|
||||
|
|
@ -590,7 +605,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}) async {
|
||||
// Pangea#
|
||||
if (sendController.text.trim().isEmpty) return;
|
||||
_storeInputTimeoutTimer?.cancel();
|
||||
storeInputTimeoutTimer?.cancel();
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.remove('draft_$roomId');
|
||||
var parseCommands = true;
|
||||
|
|
@ -634,6 +649,22 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
.then(
|
||||
(String? msgEventId) async {
|
||||
// #Pangea
|
||||
// There's a listen in my_analytics_controller that decides when to auto-update
|
||||
// analytics based on when / how many messages the logged in user send. This
|
||||
// stream sends the data for newly sent messages.
|
||||
if (msgEventId != null) {
|
||||
pangeaController.myAnalytics.setState(
|
||||
data: {
|
||||
'eventID': msgEventId,
|
||||
'eventType': EventTypes.Message,
|
||||
'roomID': room.id,
|
||||
'originalSent': originalSent,
|
||||
'tokensSent': tokensSent,
|
||||
'choreo': choreo,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (previousEdit != null) {
|
||||
pangeaEditingEvent = previousEdit;
|
||||
}
|
||||
|
|
@ -661,7 +692,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
|
||||
setState(() {
|
||||
sendController.text = pendingText;
|
||||
_inputTextIsEmpty = pendingText.isEmpty;
|
||||
inputTextIsEmpty = pendingText.isEmpty;
|
||||
replyEvent = null;
|
||||
editEvent = null;
|
||||
pendingText = '';
|
||||
|
|
@ -800,18 +831,11 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
builder: (c) => const RecordingDialog(),
|
||||
);
|
||||
if (result == null) return;
|
||||
// #Pangea
|
||||
// enable web recording
|
||||
// final audioFile = File(result.path);
|
||||
// final file = MatrixAudioFile(
|
||||
// bytes: audioFile.readAsBytesSync(),
|
||||
// name: audioFile.path,
|
||||
// );
|
||||
final audioFile = XFile(result.path);
|
||||
final file = MatrixAudioFile(
|
||||
bytes: result.bytes,
|
||||
name: result.path,
|
||||
bytes: await audioFile.readAsBytes(),
|
||||
name: result.fileName ?? audioFile.path,
|
||||
);
|
||||
// Pangea#
|
||||
await room.sendFileEvent(
|
||||
file,
|
||||
inReplyTo: replyEvent,
|
||||
|
|
@ -841,12 +865,9 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
});
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
// void hideEmojiPicker() {
|
||||
void hideEmojiPicker({bool closeOverlay = false}) {
|
||||
if (closeOverlay) {
|
||||
MatrixState.pAnyState.closeOverlay();
|
||||
}
|
||||
void hideEmojiPicker() {
|
||||
// #Pangea
|
||||
clearSelectedEvents();
|
||||
// Pangea#
|
||||
setState(() => showEmojiPicker = false);
|
||||
}
|
||||
|
|
@ -868,7 +889,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
setState(() => showEmojiPicker = !showEmojiPicker);
|
||||
}
|
||||
|
||||
void _inputFocusListener() {
|
||||
void inputFocusListener() {
|
||||
if (showEmojiPicker && inputFocus.hasFocus) {
|
||||
emojiPickerType = EmojiPickerType.keyboard;
|
||||
setState(() => showEmojiPicker = false);
|
||||
|
|
@ -882,7 +903,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
|
||||
String _getSelectedEventString() {
|
||||
String getSelectedEventString() {
|
||||
var copyString = '';
|
||||
if (selectedEvents.length == 1) {
|
||||
return selectedEvents.first
|
||||
|
|
@ -900,7 +921,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}
|
||||
|
||||
void copyEventsAction() {
|
||||
Clipboard.setData(ClipboardData(text: _getSelectedEventString()));
|
||||
Clipboard.setData(ClipboardData(text: getSelectedEventString()));
|
||||
setState(() {
|
||||
showEmojiPicker = false;
|
||||
// #Pangea
|
||||
|
|
@ -1108,7 +1129,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
} else {
|
||||
Matrix.of(context).shareContent = {
|
||||
'msgtype': 'm.text',
|
||||
'body': _getSelectedEventString(),
|
||||
'body': getSelectedEventString(),
|
||||
};
|
||||
}
|
||||
setState(() => selectedEvents.clear());
|
||||
|
|
@ -1144,10 +1165,16 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
String eventId, {
|
||||
bool highlightEvent = true,
|
||||
}) async {
|
||||
final eventIndex = timeline!.events
|
||||
.where((event) => event.isVisibleInGui)
|
||||
.toList()
|
||||
.indexWhere((e) => e.eventId == eventId);
|
||||
final foundEvent =
|
||||
timeline!.events.firstWhereOrNull((event) => event.eventId == eventId);
|
||||
|
||||
final eventIndex = foundEvent == null
|
||||
? -1
|
||||
: timeline!.events
|
||||
.where((event) => event.isVisibleInGui || event.eventId == eventId)
|
||||
.toList()
|
||||
.indexOf(foundEvent);
|
||||
|
||||
if (eventIndex == -1) {
|
||||
setState(() {
|
||||
timeline = null;
|
||||
|
|
@ -1173,7 +1200,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
duration: FluffyThemes.animationDuration,
|
||||
preferPosition: AutoScrollPosition.middle,
|
||||
);
|
||||
_updateScrollController();
|
||||
updateScrollController();
|
||||
}
|
||||
|
||||
void scrollDown() async {
|
||||
|
|
@ -1466,18 +1493,18 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
|
||||
Timer? _storeInputTimeoutTimer;
|
||||
static const Duration _storeInputTimeout = Duration(milliseconds: 500);
|
||||
Timer? storeInputTimeoutTimer;
|
||||
static const storeInputTimeout = Duration(milliseconds: 500);
|
||||
|
||||
void onInputBarChanged(String text) {
|
||||
if (_inputTextIsEmpty != text.isEmpty) {
|
||||
if (inputTextIsEmpty != text.isEmpty) {
|
||||
setState(() {
|
||||
_inputTextIsEmpty = text.isEmpty;
|
||||
inputTextIsEmpty = text.isEmpty;
|
||||
});
|
||||
}
|
||||
|
||||
_storeInputTimeoutTimer?.cancel();
|
||||
_storeInputTimeoutTimer = Timer(_storeInputTimeout, () async {
|
||||
storeInputTimeoutTimer?.cancel();
|
||||
storeInputTimeoutTimer = Timer(storeInputTimeout, () async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('draft_$roomId', text);
|
||||
});
|
||||
|
|
@ -1516,7 +1543,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}
|
||||
}
|
||||
|
||||
bool _inputTextIsEmpty = true;
|
||||
var inputTextIsEmpty = true;
|
||||
|
||||
bool get isArchived =>
|
||||
{Membership.leave, Membership.ban}.contains(room.membership);
|
||||
|
|
@ -1583,7 +1610,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
});
|
||||
|
||||
// #Pangea
|
||||
MessageTextSelection textSelection = MessageTextSelection();
|
||||
final textSelection = MessageTextSelection();
|
||||
|
||||
void showToolbar(
|
||||
PangeaMessageEvent pangeaMessageEvent, {
|
||||
|
|
@ -1592,9 +1619,10 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
Event? prevEvent,
|
||||
}) {
|
||||
if (![
|
||||
MessageTypes.Text,
|
||||
MessageTypes.Audio,
|
||||
].contains(pangeaMessageEvent.event.messageType)) {
|
||||
MessageTypes.Text,
|
||||
MessageTypes.Audio,
|
||||
].contains(pangeaMessageEvent.event.messageType) ||
|
||||
pangeaMessageEvent.event.redacted) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1632,7 +1660,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
context: context,
|
||||
child: overlayEntry,
|
||||
transformTargetId: "",
|
||||
backgroundColor: const Color.fromRGBO(0, 0, 0, 1).withAlpha(100),
|
||||
backgroundColor: const Color.fromRGBO(0, 0, 0, 1).withAlpha(150),
|
||||
closePrevOverlay:
|
||||
MatrixState.pangeaController.subscriptionController.isSubscribed,
|
||||
position: OverlayPositionEnum.centered,
|
||||
|
|
@ -1645,69 +1673,63 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}
|
||||
// Pangea#
|
||||
|
||||
late final ValueNotifier<bool> _displayChatDetailsColumn;
|
||||
late final ValueNotifier<bool> displayChatDetailsColumn;
|
||||
|
||||
void toggleDisplayChatDetailsColumn() async {
|
||||
await Matrix.of(context).store.setBool(
|
||||
SettingKeys.displayChatDetailsColumn,
|
||||
!_displayChatDetailsColumn.value,
|
||||
!displayChatDetailsColumn.value,
|
||||
);
|
||||
_displayChatDetailsColumn.value = !_displayChatDetailsColumn.value;
|
||||
displayChatDetailsColumn.value = !displayChatDetailsColumn.value;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ChatView(this),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _displayChatDetailsColumn,
|
||||
builder: (context, displayChatDetailsColumn, _) {
|
||||
if (!FluffyThemes.isThreeColumnMode(context) ||
|
||||
room.membership != Membership.join ||
|
||||
!displayChatDetailsColumn) {
|
||||
return const SizedBox(
|
||||
height: double.infinity,
|
||||
width: 0,
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
width: FluffyThemes.columnWidth,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
width: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: ChatDetails(
|
||||
roomId: roomId,
|
||||
embeddedCloseButton: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: toggleDisplayChatDetailsColumn,
|
||||
),
|
||||
),
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ChatView(this),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: displayChatDetailsColumn,
|
||||
builder: (context, displayChatDetailsColumn, _) {
|
||||
if (!FluffyThemes.isThreeColumnMode(context) ||
|
||||
room.membership != Membership.join ||
|
||||
!displayChatDetailsColumn) {
|
||||
return const SizedBox(
|
||||
height: double.infinity,
|
||||
width: 0,
|
||||
);
|
||||
},
|
||||
),
|
||||
}
|
||||
return Container(
|
||||
width: FluffyThemes.columnWidth,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
width: 1,
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: ChatDetails(
|
||||
roomId: roomId,
|
||||
embeddedCloseButton: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: toggleDisplayChatDetailsColumn,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum EmojiPickerType { reaction, keyboard }
|
||||
|
||||
extension on List<Event> {
|
||||
int get firstIndexWhereNotError {
|
||||
if (isEmpty) return 0;
|
||||
final index = indexWhere((event) => !event.status.isError);
|
||||
if (index == -1) return length;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/utils/url_launcher.dart';
|
||||
|
||||
class ChatAppBarListTile extends StatelessWidget {
|
||||
|
|
@ -11,6 +10,8 @@ class ChatAppBarListTile extends StatelessWidget {
|
|||
final Widget? trailing;
|
||||
final void Function()? onTap;
|
||||
|
||||
static const double fixedHeight = 40.0;
|
||||
|
||||
const ChatAppBarListTile({
|
||||
super.key,
|
||||
this.leading,
|
||||
|
|
@ -21,40 +22,42 @@ class ChatAppBarListTile extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final leading = this.leading;
|
||||
final trailing = this.trailing;
|
||||
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
if (leading != null) leading,
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Linkify(
|
||||
text: title,
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
return SizedBox(
|
||||
height: fixedHeight,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
if (leading != null) leading,
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Linkify(
|
||||
text: title,
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontSize: fontSize,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontSize: 14,
|
||||
),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
linkStyle: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: fontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor:
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (trailing != null) trailing,
|
||||
],
|
||||
if (trailing != null) trailing,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,96 +13,90 @@ class ChatEmojiPicker extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
// #Pangea
|
||||
return Material(
|
||||
// Pangea#
|
||||
child: AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
height: controller.showEmojiPicker
|
||||
? MediaQuery.of(context).size.height / 2
|
||||
: 0,
|
||||
child: controller.showEmojiPicker
|
||||
? DefaultTabController(
|
||||
length: 2,
|
||||
child: Column(
|
||||
children: [
|
||||
TabBar(
|
||||
tabs: [
|
||||
Tab(text: L10n.of(context)!.emojis),
|
||||
Tab(text: L10n.of(context)!.stickers),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: [
|
||||
EmojiPicker(
|
||||
onEmojiSelected: controller.onEmojiSelected,
|
||||
onBackspacePressed: controller.emojiPickerBackspace,
|
||||
config: Config(
|
||||
emojiViewConfig: EmojiViewConfig(
|
||||
noRecents: const NoRecent(),
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
),
|
||||
bottomActionBarConfig:
|
||||
const BottomActionBarConfig(
|
||||
enabled: false,
|
||||
),
|
||||
categoryViewConfig: CategoryViewConfig(
|
||||
backspaceColor: theme.colorScheme.primary,
|
||||
iconColor:
|
||||
theme.colorScheme.primary.withOpacity(0.5),
|
||||
iconColorSelected: theme.colorScheme.primary,
|
||||
indicatorColor: theme.colorScheme.primary,
|
||||
),
|
||||
skinToneConfig: SkinToneConfig(
|
||||
dialogBackgroundColor: Color.lerp(
|
||||
theme.colorScheme.surface,
|
||||
theme.colorScheme.primaryContainer,
|
||||
0.75,
|
||||
)!,
|
||||
indicatorColor: theme.colorScheme.onSurface,
|
||||
),
|
||||
final theme = Theme.of(context);
|
||||
return AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
height: controller.showEmojiPicker
|
||||
? MediaQuery.of(context).size.height / 2
|
||||
: 0,
|
||||
child: controller.showEmojiPicker
|
||||
? DefaultTabController(
|
||||
length: 2,
|
||||
child: Column(
|
||||
children: [
|
||||
TabBar(
|
||||
tabs: [
|
||||
Tab(text: L10n.of(context)!.emojis),
|
||||
Tab(text: L10n.of(context)!.stickers),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: [
|
||||
EmojiPicker(
|
||||
onEmojiSelected: controller.onEmojiSelected,
|
||||
onBackspacePressed: controller.emojiPickerBackspace,
|
||||
config: Config(
|
||||
emojiViewConfig: EmojiViewConfig(
|
||||
noRecents: const NoRecent(),
|
||||
backgroundColor:
|
||||
theme.colorScheme.onInverseSurface,
|
||||
),
|
||||
bottomActionBarConfig: const BottomActionBarConfig(
|
||||
enabled: false,
|
||||
),
|
||||
categoryViewConfig: CategoryViewConfig(
|
||||
backspaceColor: theme.colorScheme.primary,
|
||||
iconColor:
|
||||
theme.colorScheme.primary.withOpacity(0.5),
|
||||
iconColorSelected: theme.colorScheme.primary,
|
||||
indicatorColor: theme.colorScheme.primary,
|
||||
),
|
||||
skinToneConfig: SkinToneConfig(
|
||||
dialogBackgroundColor: Color.lerp(
|
||||
theme.colorScheme.surface,
|
||||
theme.colorScheme.primaryContainer,
|
||||
0.75,
|
||||
)!,
|
||||
indicatorColor: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
StickerPickerDialog(
|
||||
room: controller.room,
|
||||
onSelected: (sticker) {
|
||||
controller.room.sendEvent(
|
||||
{
|
||||
'body': sticker.body,
|
||||
'info': sticker.info ?? {},
|
||||
'url': sticker.url.toString(),
|
||||
},
|
||||
type: EventTypes.Sticker,
|
||||
);
|
||||
controller.hideEmojiPicker();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
StickerPickerDialog(
|
||||
room: controller.room,
|
||||
onSelected: (sticker) {
|
||||
controller.room.sendEvent(
|
||||
{
|
||||
'body': sticker.body,
|
||||
'info': sticker.info ?? {},
|
||||
'url': sticker.url.toString(),
|
||||
},
|
||||
type: EventTypes.Sticker,
|
||||
);
|
||||
controller.hideEmojiPicker();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
// #Pangea
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: controller.hideEmojiPicker,
|
||||
shape: const CircleBorder(),
|
||||
mini: true,
|
||||
child: const Icon(Icons.close),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: controller.hideEmojiPicker,
|
||||
shape: const CircleBorder(),
|
||||
mini: true,
|
||||
child: const Icon(Icons.close),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,8 +205,7 @@ class ChatEventList extends StatelessWidget {
|
|||
.any((e) => e.eventId == event.eventId),
|
||||
timeline: controller.timeline!,
|
||||
displayReadMarker:
|
||||
controller.readMarkerEventId == event.eventId &&
|
||||
controller.timeline?.allowNewEvent == false,
|
||||
i > 0 && controller.readMarkerEventId == event.eventId,
|
||||
nextEvent: i + 1 < events.length ? events[i + 1] : null,
|
||||
previousEvent: i > 0 ? events[i - 1] : null,
|
||||
avatarPresenceBackgroundColor:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:animations/animations.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/chat/input_bar.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/send_button.dart';
|
||||
import 'package:fluffychat/pangea/constants/language_constants.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
|
|
@ -12,6 +11,7 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import '../../config/themes.dart';
|
||||
import 'chat.dart';
|
||||
import 'input_bar.dart';
|
||||
|
||||
class ChatInputRow extends StatelessWidget {
|
||||
final ChatController controller;
|
||||
|
|
@ -20,6 +20,7 @@ class ChatInputRow extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
if (controller.showEmojiPicker &&
|
||||
controller.emojiPickerType == EmojiPickerType.reaction) {
|
||||
return const SizedBox.shrink();
|
||||
|
|
@ -66,7 +67,7 @@ class ChatInputRow extends StatelessWidget {
|
|||
height: height,
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.error,
|
||||
foregroundColor: theme.colorScheme.error,
|
||||
),
|
||||
onPressed: controller.deleteErrorEventsAction,
|
||||
child: Row(
|
||||
|
|
@ -139,15 +140,7 @@ class ChatInputRow extends StatelessWidget {
|
|||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
height: height,
|
||||
// #Pangea
|
||||
// width:
|
||||
// controller.sendController.text.isEmpty ? height : 0,
|
||||
width: controller.sendController.text.isEmpty &&
|
||||
controller.pangeaController.permissionsController
|
||||
.showChatInputAddButton(controller.roomId)
|
||||
? height
|
||||
: 0,
|
||||
// Pangea#
|
||||
width: controller.sendController.text.isEmpty ? height : 0,
|
||||
alignment: Alignment.center,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
|
|
@ -224,21 +217,6 @@ class ChatInputRow extends StatelessWidget {
|
|||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
if (controller.room
|
||||
.getImagePacks(ImagePackUsage.sticker)
|
||||
.isNotEmpty)
|
||||
PopupMenuItem<String>(
|
||||
value: 'sticker',
|
||||
child: ListTile(
|
||||
leading: const CircleAvatar(
|
||||
backgroundColor: Colors.orange,
|
||||
foregroundColor: Colors.white,
|
||||
child: Icon(Icons.emoji_emotions_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context)!.sendSticker),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
//#Pangea
|
||||
// if (PlatformInfos.isMobile)
|
||||
if (PlatformInfos.isMobile &&
|
||||
|
|
@ -260,9 +238,6 @@ class ChatInputRow extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// ),
|
||||
// Pangea#
|
||||
Container(
|
||||
height: height,
|
||||
width: height,
|
||||
|
|
@ -303,9 +278,6 @@ class ChatInputRow extends StatelessWidget {
|
|||
),
|
||||
onPressed: controller.emojiPickerAction,
|
||||
),
|
||||
// #Pangea
|
||||
// ),
|
||||
// Pangea#
|
||||
),
|
||||
// #Pangea
|
||||
// if (Matrix.of(context).isMultiAccount &&
|
||||
|
|
@ -373,10 +345,8 @@ class ChatInputRow extends StatelessWidget {
|
|||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(height),
|
||||
),
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimary,
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: theme.colorScheme.onPrimary,
|
||||
child: const Icon(Icons.mic_none_outlined),
|
||||
)
|
||||
:
|
||||
|
|
@ -390,11 +360,9 @@ class ChatInputRow extends StatelessWidget {
|
|||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(height),
|
||||
// ),
|
||||
// backgroundColor: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .onPrimaryContainer,
|
||||
// foregroundColor:
|
||||
// Theme.of(context).colorScheme.onPrimary,
|
||||
// backgroundColor:
|
||||
// theme.colorScheme.onPrimaryContainer,
|
||||
// foregroundColor: theme.colorScheme.onPrimary,
|
||||
// child: const Icon(Icons.send_outlined),
|
||||
// ),
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:fluffychat/pages/chat/reply_display.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/widgets/animations/gain_points.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/chat_floating_action_button.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/input_bar_wrapper.dart';
|
||||
import 'package:fluffychat/utils/account_config.dart';
|
||||
|
|
@ -42,15 +43,11 @@ class ChatView extends StatelessWidget {
|
|||
tooltip: L10n.of(context)!.edit,
|
||||
onPressed: controller.editSelectedEventAction,
|
||||
),
|
||||
// #Pangea
|
||||
if (controller.selectedEvents.length == 1 &&
|
||||
controller.selectedEvents.single.messageType == MessageTypes.Text)
|
||||
// Pangea#
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy_outlined),
|
||||
tooltip: L10n.of(context)!.copy,
|
||||
onPressed: controller.copyEventsAction,
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy_outlined),
|
||||
tooltip: L10n.of(context)!.copy,
|
||||
onPressed: controller.copyEventsAction,
|
||||
),
|
||||
if (controller.canSaveSelectedEvent)
|
||||
// Use builder context to correctly position the share dialog on iPad
|
||||
Builder(
|
||||
|
|
@ -143,6 +140,7 @@ class ChatView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
if (controller.room.membership == Membership.invite) {
|
||||
showFutureLoadingDialog(
|
||||
context: context,
|
||||
|
|
@ -161,7 +159,7 @@ class ChatView extends StatelessWidget {
|
|||
|
||||
return PopScope(
|
||||
canPop: controller.selectedEvents.isEmpty && !controller.showEmojiPicker,
|
||||
onPopInvoked: (pop) async {
|
||||
onPopInvokedWithResult: (pop, _) async {
|
||||
if (pop) return;
|
||||
if (controller.selectedEvents.isNotEmpty) {
|
||||
controller.clearSelectedEvents();
|
||||
|
|
@ -178,39 +176,46 @@ class ChatView extends StatelessWidget {
|
|||
builder: (BuildContext context, snapshot) {
|
||||
var appbarBottomHeight = 0.0;
|
||||
if (controller.room.pinnedEventIds.isNotEmpty) {
|
||||
appbarBottomHeight += 42;
|
||||
appbarBottomHeight += ChatAppBarListTile.fixedHeight;
|
||||
}
|
||||
if (scrollUpBannerEventId != null) {
|
||||
appbarBottomHeight += 42;
|
||||
appbarBottomHeight += ChatAppBarListTile.fixedHeight;
|
||||
}
|
||||
final tombstoneEvent =
|
||||
controller.room.getState(EventTypes.RoomTombstone);
|
||||
if (tombstoneEvent != null) {
|
||||
appbarBottomHeight += 42;
|
||||
appbarBottomHeight += ChatAppBarListTile.fixedHeight;
|
||||
}
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
actionsIconTheme: IconThemeData(
|
||||
color: controller.selectedEvents.isEmpty
|
||||
? null
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
: theme.colorScheme.primary,
|
||||
),
|
||||
leading: controller.selectMode
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: controller.clearSelectedEvents,
|
||||
tooltip: L10n.of(context)!.close,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
)
|
||||
: UnreadRoomsBadge(
|
||||
filter: (r) =>
|
||||
r.id != controller.roomId
|
||||
// #Pangea
|
||||
&&
|
||||
!r.isAnalyticsRoom,
|
||||
// Pangea#
|
||||
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
|
||||
child: const Center(child: BackButton()),
|
||||
: StreamBuilder<Object>(
|
||||
stream: Matrix.of(context)
|
||||
.client
|
||||
.onSync
|
||||
.stream
|
||||
.where((syncUpdate) => syncUpdate.hasRoomUpdate),
|
||||
builder: (context, _) => UnreadRoomsBadge(
|
||||
filter: (r) =>
|
||||
r.id != controller.roomId
|
||||
// #Pangea
|
||||
&&
|
||||
!r.isAnalyticsRoom,
|
||||
// Pangea#
|
||||
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
|
||||
child: const Center(child: BackButton()),
|
||||
),
|
||||
),
|
||||
titleSpacing: 0,
|
||||
title: ChatAppBarTitle(controller),
|
||||
|
|
@ -236,8 +241,7 @@ class ChatView extends StatelessWidget {
|
|||
if (scrollUpBannerEventId != null)
|
||||
ChatAppBarListTile(
|
||||
leading: IconButton(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
icon: const Icon(Icons.close),
|
||||
tooltip: L10n.of(context)!.close,
|
||||
onPressed: () {
|
||||
|
|
@ -338,7 +342,7 @@ class ChatView extends StatelessWidget {
|
|||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context)
|
||||
color: theme
|
||||
.colorScheme
|
||||
// ignore: deprecated_member_use
|
||||
.surfaceVariant,
|
||||
|
|
@ -351,43 +355,16 @@ class ChatView extends StatelessWidget {
|
|||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// #Pangea
|
||||
if (controller.room.isRoomAdmin)
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor:
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.archive_outlined,
|
||||
),
|
||||
onPressed:
|
||||
controller.archiveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.archive,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor:
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
theme.colorScheme.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
// #Pangea
|
||||
// Icons.archive_outlined,
|
||||
Icons.arrow_forward,
|
||||
// Pangea#
|
||||
Icons.archive_outlined,
|
||||
),
|
||||
onPressed: controller.leaveChat,
|
||||
label: Text(
|
||||
|
|
@ -417,18 +394,20 @@ class ChatView extends StatelessWidget {
|
|||
// Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// const ConnectionStatusHeader(),
|
||||
// ITBar(
|
||||
// choreographer:
|
||||
// controller.choreographer,
|
||||
// ),
|
||||
// ReactionsPicker(controller),
|
||||
// ReplyDisplay(controller),
|
||||
// ChatInputRow(controller),
|
||||
// ChatEmojiPicker(controller),
|
||||
// ],
|
||||
// ),
|
||||
// const ConnectionStatusHeader(),
|
||||
// // #Pangea
|
||||
// ITBar(
|
||||
// choreographer:
|
||||
// controller.choreographer,
|
||||
// ),
|
||||
// // ReactionsPicker(controller),
|
||||
// // Pangea#
|
||||
// ReplyDisplay(controller),
|
||||
// ChatInputRow(controller),
|
||||
// Pangea#
|
||||
// ChatEmojiPicker(controller),
|
||||
// ],
|
||||
// ),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
|
|
@ -465,8 +444,18 @@ class ChatView extends StatelessWidget {
|
|||
StartIGCButton(
|
||||
controller: controller,
|
||||
),
|
||||
ChatFloatingActionButton(
|
||||
controller: controller,
|
||||
Row(
|
||||
children: [
|
||||
PointsGainedAnimation(
|
||||
gainColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimary,
|
||||
),
|
||||
const SizedBox(width: 100),
|
||||
ChatFloatingActionButton(
|
||||
controller: controller,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -510,20 +499,18 @@ class ChatView extends StatelessWidget {
|
|||
// Pangea#
|
||||
],
|
||||
),
|
||||
// #Pangea
|
||||
// if (controller.dragging)
|
||||
// Container(
|
||||
// color: Theme.of(context)
|
||||
// .scaffoldBackgroundColor
|
||||
// .withOpacity(0.9),
|
||||
// alignment: Alignment.center,
|
||||
// child: const Icon(
|
||||
// Icons.upload_outlined,
|
||||
// size: 100,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
),
|
||||
// #Pangea
|
||||
// if (controller.dragging)
|
||||
// Container(
|
||||
// color: theme.scaffoldBackgroundColor.withOpacity(0.9),
|
||||
// alignment: Alignment.center,
|
||||
// child: const Icon(
|
||||
// Icons.upload_outlined,
|
||||
// size: 100,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,14 +28,15 @@ class EventInfoDialog extends StatelessWidget {
|
|||
});
|
||||
|
||||
String get prettyJson {
|
||||
const JsonDecoder decoder = JsonDecoder();
|
||||
const JsonEncoder encoder = JsonEncoder.withIndent(' ');
|
||||
const decoder = JsonDecoder();
|
||||
const encoder = JsonEncoder.withIndent(' ');
|
||||
final object = decoder.convert(jsonEncode(event.toJson()));
|
||||
return encoder.convert(object);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context)!.messageInfo),
|
||||
|
|
@ -72,14 +73,14 @@ class EventInfoDialog extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(12.0),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
color: Theme.of(context).colorScheme.inverseSurface,
|
||||
color: theme.colorScheme.inverseSurface,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(8),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SelectableText(
|
||||
prettyJson,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:opus_caf_converter_dart/opus_caf_converter_dart.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../../../utils/matrix_sdk_extensions/event_extension.dart';
|
||||
|
|
@ -95,7 +96,18 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
// Pangea#
|
||||
);
|
||||
file = File('${tempDir.path}/${fileName}_${matrixFile.name}');
|
||||
|
||||
await file.writeAsBytes(matrixFile.bytes);
|
||||
|
||||
if (Platform.isIOS &&
|
||||
matrixFile.mimeType.toLowerCase() == 'audio/ogg') {
|
||||
Logs().v('Convert ogg audio file for iOS...');
|
||||
final convertedFile = File('${file.path}.caf');
|
||||
if (await convertedFile.exists() == false) {
|
||||
OpusCaf().convertOpusToCaf(file.path, convertedFile.path);
|
||||
}
|
||||
file = convertedFile;
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
|
|
@ -295,6 +307,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final statusText = this.statusText ??= _durationString ?? '00:00';
|
||||
final audioPlayer = this.audioPlayer;
|
||||
return Padding(
|
||||
|
|
@ -397,8 +411,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
// : Text(
|
||||
// '${audioPlayer.speed.toString()}x',
|
||||
// ),
|
||||
// backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
// textColor: Theme.of(context).colorScheme.onSecondary,
|
||||
// backgroundColor: theme.colorScheme.secondary,
|
||||
// textColor: theme.colorScheme.onSecondary,
|
||||
// child: InkWell(
|
||||
// splashColor: widget.color.withAlpha(128),
|
||||
// borderRadius: BorderRadius.circular(64),
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ class ImageBubble extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final borderRadius =
|
||||
this.borderRadius ?? BorderRadius.circular(AppConfig.borderRadius);
|
||||
return Material(
|
||||
|
|
@ -77,7 +79,7 @@ class ImageBubble extends StatelessWidget {
|
|||
side: BorderSide(
|
||||
color: event.messageType == MessageTypes.Sticker
|
||||
? Colors.transparent
|
||||
: Theme.of(context).dividerColor,
|
||||
: theme.dividerColor,
|
||||
),
|
||||
),
|
||||
child: InkWell(
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ class MapBubble extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
child: Container(
|
||||
|
|
@ -71,11 +73,10 @@ class MapBubble extends StatelessWidget {
|
|||
child: Text(
|
||||
' © OpenStreetMap contributors ',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
color: theme.brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
backgroundColor:
|
||||
Theme.of(context).appBarTheme.backgroundColor,
|
||||
backgroundColor: theme.appBarTheme.backgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ class Message extends StatelessWidget {
|
|||
}
|
||||
});
|
||||
// Pangea#
|
||||
final theme = Theme.of(context);
|
||||
|
||||
if (!{
|
||||
EventTypes.Message,
|
||||
EventTypes.Sticker,
|
||||
|
|
@ -120,7 +122,7 @@ class Message extends StatelessWidget {
|
|||
final ownMessage = event.senderId == client.userID;
|
||||
final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft;
|
||||
// ignore: deprecated_member_use
|
||||
var color = Theme.of(context).colorScheme.surfaceVariant;
|
||||
var color = theme.colorScheme.surfaceVariant;
|
||||
final displayTime = event.type == EventTypes.RoomCreate ||
|
||||
nextEvent == null ||
|
||||
!event.originServerTs.sameEnvironment(nextEvent!.originServerTs);
|
||||
|
|
@ -142,9 +144,8 @@ class Message extends StatelessWidget {
|
|||
previousEvent!.senderId == event.senderId &&
|
||||
previousEvent!.originServerTs.sameEnvironment(event.originServerTs);
|
||||
|
||||
final textColor = ownMessage
|
||||
? Theme.of(context).colorScheme.onPrimary
|
||||
: Theme.of(context).colorScheme.onSurface;
|
||||
final textColor =
|
||||
ownMessage ? theme.colorScheme.onPrimary : theme.colorScheme.onSurface;
|
||||
final rowMainAxisAlignment =
|
||||
ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start;
|
||||
|
||||
|
|
@ -173,7 +174,7 @@ class Message extends StatelessWidget {
|
|||
if (ownMessage) {
|
||||
color = displayEvent.status.isError
|
||||
? Colors.redAccent
|
||||
: Theme.of(context).colorScheme.primary;
|
||||
: theme.colorScheme.primary;
|
||||
}
|
||||
|
||||
final resetAnimateIn = this.resetAnimateIn;
|
||||
|
|
@ -213,14 +214,10 @@ class Message extends StatelessWidget {
|
|||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
color: selected
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer
|
||||
? theme.colorScheme.secondaryContainer
|
||||
.withAlpha(100)
|
||||
: highlightMarker
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiaryContainer
|
||||
? theme.colorScheme.tertiaryContainer
|
||||
.withAlpha(100)
|
||||
: Colors.transparent,
|
||||
),
|
||||
|
|
@ -304,8 +301,7 @@ class Message extends StatelessWidget {
|
|||
displayname,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: (Theme.of(context)
|
||||
.brightness ==
|
||||
color: (theme.brightness ==
|
||||
Brightness.light
|
||||
? displayname.color
|
||||
: displayname
|
||||
|
|
@ -557,22 +553,20 @@ class Message extends StatelessWidget {
|
|||
? const EdgeInsets.symmetric(vertical: 8.0)
|
||||
: EdgeInsets.zero,
|
||||
child: Center(
|
||||
child: Material(
|
||||
color: displayTime
|
||||
? Theme.of(context).colorScheme.surface
|
||||
: Theme.of(context).colorScheme.surface.withOpacity(0.33),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: Text(
|
||||
event.originServerTs.localizedTime(context),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12 * AppConfig.fontSizeFactor,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: Text(
|
||||
event.originServerTs.localizedTime(context),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12 * AppConfig.fontSizeFactor,
|
||||
color: theme.colorScheme.secondary,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: theme.colorScheme.surface,
|
||||
blurRadius: 3,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -619,14 +613,14 @@ class Message extends StatelessWidget {
|
|||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Divider(color: Theme.of(context).colorScheme.primary),
|
||||
child: Divider(color: theme.colorScheme.primary),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
color: theme.colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
|
|
@ -635,12 +629,11 @@ class Message extends StatelessWidget {
|
|||
),
|
||||
child: Text(
|
||||
L10n.of(context)!.readUpToHere,
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||
style: TextStyle(color: theme.colorScheme.primary),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Divider(color: Theme.of(context).colorScheme.primary),
|
||||
child: Divider(color: theme.colorScheme.primary),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -108,10 +108,10 @@ class _Reaction extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textColor = Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black;
|
||||
final color = Theme.of(context).colorScheme.surface;
|
||||
final theme = Theme.of(context);
|
||||
final textColor =
|
||||
theme.brightness == Brightness.dark ? Colors.white : Colors.black;
|
||||
final color = theme.colorScheme.surface;
|
||||
Widget content;
|
||||
if (reactionKey.startsWith('mxc://')) {
|
||||
content = Row(
|
||||
|
|
@ -158,8 +158,8 @@ class _Reaction extends StatelessWidget {
|
|||
border: Border.all(
|
||||
width: 1,
|
||||
color: reacted!
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.primaryContainer,
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primaryContainer,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -26,20 +26,19 @@ class ReplyContent extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final timeline = this.timeline;
|
||||
final displayEvent =
|
||||
timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent;
|
||||
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
|
||||
final color = ownMessage
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: Theme.of(context).colorScheme.primary;
|
||||
? theme.colorScheme.primaryContainer
|
||||
: theme.colorScheme.primary;
|
||||
|
||||
return Material(
|
||||
color: backgroundColor ??
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.surface
|
||||
.withOpacity(ownMessage ? 0.2 : 0.33),
|
||||
theme.colorScheme.surface.withOpacity(ownMessage ? 0.2 : 0.33),
|
||||
borderRadius: borderRadius,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -80,8 +79,8 @@ class ReplyContent extends StatelessWidget {
|
|||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
color: ownMessage
|
||||
? Theme.of(context).colorScheme.onPrimary
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface,
|
||||
fontSize: fontSize,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -12,15 +12,13 @@ class StateMessage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
),
|
||||
child: Text(
|
||||
event.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
|
|
@ -29,6 +27,12 @@ class StateMessage extends StatelessWidget {
|
|||
style: TextStyle(
|
||||
fontSize: 12 * AppConfig.fontSizeFactor,
|
||||
decoration: event.redacted ? TextDecoration.lineThrough : null,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: theme.colorScheme.surface,
|
||||
blurRadius: 3,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ class VerificationRequestContent extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final events = event.aggregatedEvents(timeline, 'm.reference');
|
||||
final done = events.where((e) => e.type == EventTypes.KeyVerificationDone);
|
||||
final start =
|
||||
|
|
@ -36,10 +38,10 @@ class VerificationRequestContent extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).dividerColor,
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
color: theme.colorScheme.surface,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
autoInitialize: true,
|
||||
);
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
} on IOException catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(e.toLocalizedString(context)),
|
||||
|
|
@ -95,6 +95,8 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final hasThumbnail = widget.event.hasThumbnail;
|
||||
final blurHash = (widget.event.infoMap as Map<String, dynamic>)
|
||||
.tryGet<String>('xyz.amorgan.blurhash') ??
|
||||
|
|
@ -122,7 +124,7 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
Center(
|
||||
child: IconButton(
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
),
|
||||
icon: _isDownloading
|
||||
? const SizedBox(
|
||||
|
|
|
|||
|
|
@ -51,9 +51,6 @@ class InputBar extends StatelessWidget {
|
|||
});
|
||||
|
||||
List<Map<String, String?>> getSuggestions(String text) {
|
||||
// #Pangea
|
||||
final List<Map<String, String?>> ret = <Map<String, String?>>[];
|
||||
// Pangea#
|
||||
if (controller!.selection.baseOffset !=
|
||||
controller!.selection.extentOffset ||
|
||||
controller!.selection.baseOffset < 0) {
|
||||
|
|
@ -61,9 +58,7 @@ class InputBar extends StatelessWidget {
|
|||
}
|
||||
final searchText =
|
||||
controller!.text.substring(0, controller!.selection.baseOffset);
|
||||
// #Pangea
|
||||
// final List<Map<String, String?>> ret = <Map<String, String?>>[];
|
||||
// Pangea#
|
||||
final ret = <Map<String, String?>>[];
|
||||
const maxResults = 30;
|
||||
|
||||
final commandMatch = RegExp(r'^/(\w*)$').firstMatch(searchText);
|
||||
|
|
@ -229,6 +224,7 @@ class InputBar extends StatelessWidget {
|
|||
Map<String, String?> suggestion,
|
||||
Client? client,
|
||||
) {
|
||||
final theme = Theme.of(context);
|
||||
const size = 30.0;
|
||||
// #Pangea
|
||||
// const padding = EdgeInsets.all(4.0);
|
||||
|
|
@ -253,7 +249,7 @@ class InputBar extends StatelessWidget {
|
|||
hint,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -481,6 +477,7 @@ class InputBar extends StatelessWidget {
|
|||
key: controller?.choreographer.inputLayerLinkAndKey.key,
|
||||
// builder: (context, controller, focusNode) => TextField(
|
||||
builder: (context, _, focusNode) => TextField(
|
||||
enableSuggestions: false,
|
||||
// Pangea#
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ class PinnedEvents extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final pinnedEventIds = controller.room.pinnedEventIds;
|
||||
|
||||
if (pinnedEventIds.isEmpty) {
|
||||
|
|
@ -71,9 +73,9 @@ class PinnedEvents extends StatelessWidget {
|
|||
) ??
|
||||
L10n.of(context)!.loadingPleaseWait,
|
||||
leading: IconButton(
|
||||
splashRadius: 20,
|
||||
iconSize: 20,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
splashRadius: 18,
|
||||
iconSize: 18,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
icon: const Icon(Icons.push_pin),
|
||||
tooltip: L10n.of(context)!.unpin,
|
||||
onPressed: controller.room.canSendEvent(EventTypes.RoomPinnedEvents)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:emoji_proposal/emoji_proposal.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/app_emojis.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
|
|
@ -14,6 +13,8 @@ class ReactionsPicker extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
if (controller.showEmojiPicker) return const SizedBox.shrink();
|
||||
final display = controller.editEvent == null &&
|
||||
controller.replyEvent == null &&
|
||||
|
|
@ -30,14 +31,7 @@ class ReactionsPicker extends StatelessWidget {
|
|||
if (!display) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final proposals = proposeEmojis(
|
||||
controller.selectedEvents.first.plaintextBody,
|
||||
number: 25,
|
||||
languageCodes: EmojiProposalLanguageCodes.values.toSet(),
|
||||
);
|
||||
final emojis = proposals.isNotEmpty
|
||||
? proposals.map((e) => e.char).toList()
|
||||
: List<String>.from(AppEmojis.emojis);
|
||||
final emojis = List<String>.from(AppEmojis.emojis);
|
||||
final allReactionEvents = controller.selectedEvents.first
|
||||
.aggregatedEvents(
|
||||
controller.timeline!,
|
||||
|
|
@ -59,7 +53,7 @@ class ReactionsPicker extends StatelessWidget {
|
|||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomRight: Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
|
|
@ -91,7 +85,7 @@ class ReactionsPicker extends StatelessWidget {
|
|||
width: 36,
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.add_outlined),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/utils/update_version_dialog.dart';
|
||||
|
|
@ -8,7 +7,7 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart' as path_lib;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:record/record.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
|
|
@ -16,7 +15,6 @@ import 'package:wakelock_plus/wakelock_plus.dart';
|
|||
import 'events/audio_player.dart';
|
||||
|
||||
class RecordingDialog extends StatefulWidget {
|
||||
static const String recordingFileType = 'wav';
|
||||
const RecordingDialog({
|
||||
super.key,
|
||||
});
|
||||
|
|
@ -30,10 +28,12 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
Duration _duration = Duration.zero;
|
||||
|
||||
bool error = false;
|
||||
String? _recordedPath;
|
||||
|
||||
final _audioRecorder = AudioRecorder();
|
||||
final List<double> amplitudeTimeline = [];
|
||||
|
||||
String? fileName;
|
||||
|
||||
static const int bitRate = 64000;
|
||||
// #Pangea
|
||||
// static const int samplingRate = 44100;
|
||||
|
|
@ -43,14 +43,23 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
Future<void> startRecording() async {
|
||||
try {
|
||||
// #Pangea
|
||||
// enable recording on web
|
||||
// final tempDir = await getTemporaryDirectory();
|
||||
// final path = _recordedPath =
|
||||
// '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}';
|
||||
final tempDirPath = kIsWeb ? "." : (await getTemporaryDirectory()).path;
|
||||
_recordedPath =
|
||||
'$tempDirPath/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}';
|
||||
// final codec = kIsWeb
|
||||
// // Web seems to create webm instead of ogg when using opus encoder
|
||||
// // which does not play on iOS right now. So we use wav for now:
|
||||
// ? AudioEncoder.wav
|
||||
// // Everywhere else we use opus if supported by the platform:
|
||||
// : await _audioRecorder.isEncoderSupported(AudioEncoder.opus)
|
||||
// ? AudioEncoder.opus
|
||||
// : AudioEncoder.aacLc;
|
||||
const codec = AudioEncoder.wav;
|
||||
// Pangea#
|
||||
fileName =
|
||||
'recording${DateTime.now().microsecondsSinceEpoch}.${codec.fileExtension}';
|
||||
String? path;
|
||||
if (!kIsWeb) {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
path = path_lib.join(tempDir.path, fileName);
|
||||
}
|
||||
|
||||
final result = await _audioRecorder.hasPermission();
|
||||
if (result != true) {
|
||||
|
|
@ -58,12 +67,13 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
return;
|
||||
}
|
||||
await WakelockPlus.enable();
|
||||
|
||||
// #Pangea
|
||||
final bool isNotError = await showUpdateVersionDialog(
|
||||
final isNotError = await showUpdateVersionDialog(
|
||||
future: () =>
|
||||
// Pangea#
|
||||
|
||||
_audioRecorder.start(
|
||||
path: _recordedPath!,
|
||||
const RecordConfig(
|
||||
bitRate: bitRate,
|
||||
sampleRate: samplingRate,
|
||||
|
|
@ -71,10 +81,9 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
autoGain: true,
|
||||
echoCancel: true,
|
||||
noiseSuppress: true,
|
||||
// #Pangea
|
||||
encoder: AudioEncoder.wav,
|
||||
// Pangea#
|
||||
encoder: codec,
|
||||
),
|
||||
path: path ?? '',
|
||||
),
|
||||
// #Pangea
|
||||
context: context,
|
||||
|
|
@ -119,25 +128,9 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
|
||||
void _stopAndSend() async {
|
||||
_recorderSubscription?.cancel();
|
||||
// #Pangea
|
||||
// await _audioRecorder.stop();
|
||||
final outputPath = await _audioRecorder.stop();
|
||||
// Pangea#
|
||||
final path = _recordedPath;
|
||||
final path = await _audioRecorder.stop();
|
||||
|
||||
if (path == null) throw ('Recording failed!');
|
||||
|
||||
// #Pangea
|
||||
Uint8List bytes;
|
||||
if (kIsWeb) {
|
||||
if (outputPath == null) throw ('Recording failed!');
|
||||
final response = await http.get(Uri.parse(outputPath));
|
||||
bytes = response.bodyBytes;
|
||||
} else {
|
||||
final audioFile = File(path);
|
||||
bytes = audioFile.readAsBytesSync();
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
const waveCount = AudioPlayerWidget.wavesCount;
|
||||
final step = amplitudeTimeline.length < waveCount
|
||||
? 1
|
||||
|
|
@ -151,15 +144,15 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
path: path,
|
||||
duration: _duration.inMilliseconds,
|
||||
waveform: waveform,
|
||||
// #Pangea
|
||||
bytes: bytes,
|
||||
// Pangea#
|
||||
fileName: fileName,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
const maxDecibalWidth = 64.0;
|
||||
final time =
|
||||
'${_duration.inMinutes.toString().padLeft(2, '0')}:${(_duration.inSeconds % 60).toString().padLeft(2, '0')}';
|
||||
|
|
@ -188,7 +181,7 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
margin: const EdgeInsets.only(left: 2),
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
|
|
@ -214,11 +207,7 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
child: Text(
|
||||
L10n.of(context)!.cancel.toUpperCase(),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.color
|
||||
?.withAlpha(150),
|
||||
color: theme.textTheme.bodyMedium?.color?.withAlpha(150),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -238,8 +227,7 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
child: Text(
|
||||
L10n.of(context)!.cancel.toUpperCase(),
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).textTheme.bodyMedium?.color?.withAlpha(150),
|
||||
color: theme.textTheme.bodyMedium?.color?.withAlpha(150),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -264,35 +252,32 @@ class RecordingResult {
|
|||
final String path;
|
||||
final int duration;
|
||||
final List<int> waveform;
|
||||
// #Pangea
|
||||
final Uint8List bytes;
|
||||
// Pangea#
|
||||
final String? fileName;
|
||||
|
||||
const RecordingResult({
|
||||
required this.path,
|
||||
required this.duration,
|
||||
required this.waveform,
|
||||
// #Pangea
|
||||
required this.bytes,
|
||||
// Pangea#
|
||||
required this.fileName,
|
||||
});
|
||||
|
||||
factory RecordingResult.fromJson(Map<String, dynamic> json) =>
|
||||
RecordingResult(
|
||||
path: json['path'],
|
||||
duration: json['duration'],
|
||||
waveform: List<int>.from(json['waveform']),
|
||||
// #Pangea
|
||||
bytes: Uint8List.fromList(json['bytes']),
|
||||
// Pangea#
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'path': path,
|
||||
'duration': duration,
|
||||
'waveform': waveform,
|
||||
// #Pangea
|
||||
'bytes': bytes,
|
||||
// Pangea#
|
||||
};
|
||||
}
|
||||
|
||||
extension on AudioEncoder {
|
||||
String get fileExtension {
|
||||
switch (this) {
|
||||
case AudioEncoder.aacLc:
|
||||
case AudioEncoder.aacEld:
|
||||
case AudioEncoder.aacHe:
|
||||
return 'm4a';
|
||||
case AudioEncoder.opus:
|
||||
return 'ogg';
|
||||
case AudioEncoder.wav:
|
||||
return 'wav';
|
||||
case AudioEncoder.amrNb:
|
||||
case AudioEncoder.amrWb:
|
||||
case AudioEncoder.flac:
|
||||
case AudioEncoder.pcm16bits:
|
||||
throw UnsupportedError('Not yet used');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ class ReplyDisplay extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
|
|
@ -21,7 +23,7 @@ class ReplyDisplay extends StatelessWidget {
|
|||
: 0,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
|
|
@ -54,6 +56,7 @@ class _EditContent extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final event = this.event;
|
||||
if (event == null) {
|
||||
return const SizedBox.shrink();
|
||||
|
|
@ -62,7 +65,7 @@ class _EditContent extends StatelessWidget {
|
|||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.edit,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
Container(width: 15.0),
|
||||
Text(
|
||||
|
|
@ -74,7 +77,7 @@ class _EditContent extends StatelessWidget {
|
|||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme.bodyMedium!.color,
|
||||
color: theme.textTheme.bodyMedium!.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ class SeenByRow extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final seenByUsers = controller.room.getSeenByUsers(controller.timeline!);
|
||||
const maxAvatars = 7;
|
||||
return Container(
|
||||
|
|
@ -48,7 +50,7 @@ class SeenByRow extends StatelessWidget {
|
|||
width: 16,
|
||||
height: 16,
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
color: theme.colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
child: Center(
|
||||
child: Text(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/utils/error_reporter.dart';
|
||||
import 'package:fluffychat/utils/size_string.dart';
|
||||
import '../../utils/resize_image.dart';
|
||||
|
||||
|
|
@ -31,6 +31,8 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
static const int minSizeToCompress = 20 * 1024;
|
||||
|
||||
Future<void> _send() async {
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final l10n = L10n.of(context)!;
|
||||
for (var file in widget.files) {
|
||||
MatrixImageFile? thumbnail;
|
||||
if (file is MatrixVideoFile && file.bytes.length > minSizeToCompress) {
|
||||
|
|
@ -42,19 +44,24 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
},
|
||||
);
|
||||
}
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
widget.room
|
||||
.sendFileEvent(
|
||||
file,
|
||||
thumbnail: thumbnail,
|
||||
shrinkImageMaxDimension: origImage ? null : 1600,
|
||||
)
|
||||
.catchError((e) {
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(content: Text((e as Object).toLocalizedString(context))),
|
||||
);
|
||||
return null;
|
||||
});
|
||||
.catchError(
|
||||
(e, s) {
|
||||
if (e is FileTooBigMatrixException) {
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(content: Text(l10n.fileIsTooBigForServer)),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
ErrorReporter(context, 'Unable to send file').onErrorCallback(e, s);
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
|
||||
|
|
@ -63,8 +70,10 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
var sendStr = L10n.of(context)!.sendFile;
|
||||
final bool allFilesAreImages =
|
||||
final allFilesAreImages =
|
||||
widget.files.every((file) => file is MatrixImageFile);
|
||||
final sizeString = widget.files
|
||||
.fold<double>(0, (p, file) => p + file.bytes.length)
|
||||
|
|
@ -88,9 +97,8 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
Flexible(
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
elevation:
|
||||
Theme.of(context).appBarTheme.scrolledUnderElevation ?? 4,
|
||||
shadowColor: Theme.of(context).appBarTheme.shadowColor,
|
||||
elevation: theme.appBarTheme.scrolledUnderElevation ?? 4,
|
||||
shadowColor: theme.appBarTheme.shadowColor,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Image.memory(
|
||||
widget.files.first.bytes,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final stickerPacks = widget.room.getImagePacks(ImagePackUsage.sticker);
|
||||
final packSlugs = stickerPacks.keys.toList();
|
||||
|
||||
|
|
@ -98,7 +100,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||
};
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.onInverseSurface,
|
||||
backgroundColor: theme.colorScheme.onInverseSurface,
|
||||
body: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: CustomScrollView(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ class TypingIndicators extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
const avatarSize = Avatar.defaultSize / 2;
|
||||
|
||||
return StreamBuilder<Object>(
|
||||
|
|
@ -81,7 +83,7 @@ class TypingIndicators extends StatelessWidget {
|
|||
Material(
|
||||
color:
|
||||
// ignore: deprecated_member_use
|
||||
Theme.of(context).colorScheme.surfaceVariant,
|
||||
theme.colorScheme.surfaceVariant,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
|
|
@ -137,6 +139,7 @@ class __TypingDotsState extends State<_TypingDots> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
const size = 8.0;
|
||||
|
||||
return Row(
|
||||
|
|
@ -154,7 +157,7 @@ class __TypingDotsState extends State<_TypingDots> {
|
|||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(size * 2),
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -24,6 +24,36 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
bool guestAccessLoading = false;
|
||||
Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!;
|
||||
|
||||
String get roomVersion =>
|
||||
room
|
||||
.getState(EventTypes.RoomCreate)!
|
||||
.content
|
||||
.tryGet<String>('room_version') ??
|
||||
'Unknown';
|
||||
|
||||
/// Calculates which join rules are available based on the information on
|
||||
/// https://spec.matrix.org/v1.11/rooms/#feature-matrix
|
||||
List<JoinRules> get availableJoinRules {
|
||||
final joinRules = Set<JoinRules>.from(JoinRules.values);
|
||||
|
||||
final roomVersionInt = int.tryParse(roomVersion);
|
||||
|
||||
// Knock is only supported for rooms up from version 7:
|
||||
if (roomVersionInt != null && roomVersionInt <= 6) {
|
||||
joinRules.remove(JoinRules.knock);
|
||||
}
|
||||
|
||||
// Not yet supported in FluffyChat:
|
||||
joinRules.remove(JoinRules.restricted);
|
||||
joinRules.remove(JoinRules.knockRestricted);
|
||||
|
||||
// If an unsupported join rule is the current join rule, display it:
|
||||
final currentJoinRule = room.joinRules;
|
||||
if (currentJoinRule != null) joinRules.add(currentJoinRule);
|
||||
|
||||
return joinRules.toList();
|
||||
}
|
||||
|
||||
void setJoinRule(JoinRules? newJoinRules) async {
|
||||
if (newJoinRules == null) return;
|
||||
setState(() {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final room = controller.room;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
|
@ -38,7 +40,7 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
title: Text(
|
||||
L10n.of(context)!.visibilityOfTheChatHistory,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
@ -56,17 +58,17 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
? null
|
||||
: controller.setHistoryVisibility,
|
||||
),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.whoIsAllowedToJoinThisGroup,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final joinRule in JoinRules.values)
|
||||
for (final joinRule in controller.availableJoinRules)
|
||||
if (joinRule != JoinRules.private)
|
||||
RadioListTile<JoinRules>.adaptive(
|
||||
title: Text(
|
||||
|
|
@ -79,14 +81,14 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
? null
|
||||
: controller.setJoinRule,
|
||||
),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
Divider(color: theme.dividerColor),
|
||||
if ({JoinRules.public, JoinRules.knock}
|
||||
.contains(room.joinRules)) ...[
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.areGuestsAllowedToJoin,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
@ -105,12 +107,12 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
? null
|
||||
: controller.setGuestAccess,
|
||||
),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.publicChatAddresses,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
@ -163,7 +165,7 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
Divider(color: theme.dividerColor),
|
||||
FutureBuilder(
|
||||
future: room.client.getRoomVisibilityOnDirectory(room.id),
|
||||
builder: (context, snapshot) => SwitchListTile.adaptive(
|
||||
|
|
@ -225,6 +227,8 @@ class _AliasListTile extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return ListTile(
|
||||
leading: isCanonicalAlias
|
||||
? const Icon(Icons.star)
|
||||
|
|
@ -238,15 +242,15 @@ class _AliasListTile extends StatelessWidget {
|
|||
'https://matrix.to/#/$alias',
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: Theme.of(context).colorScheme.primary,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decorationColor: theme.colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
trailing: onDelete != null
|
||||
? IconButton(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
color: theme.colorScheme.error,
|
||||
icon: const Icon(Icons.delete_outlined),
|
||||
onPressed: onDelete,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final room = Matrix.of(context).client.getRoomById(controller.roomId!);
|
||||
// #Pangea
|
||||
if (room == null || room.membership == Membership.leave) {
|
||||
|
|
@ -56,37 +58,26 @@ class ChatDetailsView extends StatelessWidget {
|
|||
final actualMembersCount = (room.summary.mInvitedMemberCount ?? 0) +
|
||||
(room.summary.mJoinedMemberCount ?? 0);
|
||||
final canRequestMoreMembers = members.length < actualMembersCount;
|
||||
final iconColor = Theme.of(context).textTheme.bodyLarge!.color;
|
||||
final iconColor = theme.textTheme.bodyLarge!.color;
|
||||
final displayname = room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading:
|
||||
// #Pangea
|
||||
!room.isSpace
|
||||
?
|
||||
// Pangea#
|
||||
controller.widget.embeddedCloseButton ??
|
||||
const Center(child: BackButton())
|
||||
// #Pangea
|
||||
: BackButton(
|
||||
onPressed: () => context.go("/rooms"),
|
||||
)
|
||||
// Pangea#
|
||||
,
|
||||
elevation: Theme.of(context).appBarTheme.elevation,
|
||||
leading: controller.widget.embeddedCloseButton ??
|
||||
const Center(child: BackButton()),
|
||||
elevation: theme.appBarTheme.elevation,
|
||||
actions: <Widget>[
|
||||
// #Pangea
|
||||
// if (room.canonicalAlias.isNotEmpty)
|
||||
// IconButton(
|
||||
// tooltip: L10n.of(context)!.share,
|
||||
// icon: Icon(Icons.adaptive.share_outlined),
|
||||
// onPressed: () => FluffyShare.share(
|
||||
// AppConfig.inviteLinkPrefix + room.canonicalAlias,
|
||||
// context,
|
||||
// IconButton(
|
||||
// tooltip: L10n.of(context)!.share,
|
||||
// icon: Icon(Icons.adaptive.share_outlined),
|
||||
// onPressed: () => FluffyShare.share(
|
||||
// AppConfig.inviteLinkPrefix + room.canonicalAlias,
|
||||
// context,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
if (controller.widget.embeddedCloseButton == null)
|
||||
ChatSettingsPopupMenu(room, false),
|
||||
|
|
@ -98,7 +89,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||
),
|
||||
// title: Text(L10n.of(context)!.chatDetails),
|
||||
// Pangea#
|
||||
backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
|
||||
backgroundColor: theme.appBarTheme.backgroundColor,
|
||||
),
|
||||
body: MaxWidthBody(
|
||||
// #Pangea
|
||||
|
|
@ -122,32 +113,12 @@ class ChatDetailsView extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(32.0),
|
||||
child: Stack(
|
||||
children: [
|
||||
Material(
|
||||
elevation: Theme.of(context)
|
||||
.appBarTheme
|
||||
.scrolledUnderElevation ??
|
||||
4,
|
||||
shadowColor: Theme.of(context)
|
||||
.appBarTheme
|
||||
.shadowColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
Avatar.defaultSize * 2.5,
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// Hero animation is causing weird visual glitch
|
||||
// Probably not worth keeping
|
||||
// child: Hero(
|
||||
// tag: controller.widget
|
||||
// .embeddedCloseButton !=
|
||||
// null
|
||||
// ? 'embedded_content_banner'
|
||||
// : 'content_banner',
|
||||
// Pangea#
|
||||
Hero(
|
||||
tag: controller
|
||||
.widget.embeddedCloseButton !=
|
||||
null
|
||||
? 'embedded_content_banner'
|
||||
: 'content_banner',
|
||||
child: Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
|
|
@ -201,9 +172,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||
size: 16,
|
||||
),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onSurface,
|
||||
),
|
||||
label: Text(
|
||||
room.isDirectChat
|
||||
|
|
@ -211,7 +181,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||
: displayname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// style: const TextStyle(fontSize: 18),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
|
|
@ -225,9 +195,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||
size: 14,
|
||||
),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary,
|
||||
foregroundColor:
|
||||
theme.colorScheme.secondary,
|
||||
),
|
||||
label: Text(
|
||||
L10n.of(context)!.countParticipants(
|
||||
|
|
@ -243,13 +212,9 @@ class ChatDetailsView extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
// if (room.canSendEvent('m.room.name'))
|
||||
Divider(color: theme.dividerColor),
|
||||
// #Pangea
|
||||
if (room.isRoomAdmin)
|
||||
// #Pangea
|
||||
ClassNameButton(
|
||||
room: room,
|
||||
controller: controller,
|
||||
|
|
@ -259,32 +224,16 @@ class ChatDetailsView extends StatelessWidget {
|
|||
room: room,
|
||||
controller: controller,
|
||||
),
|
||||
// #Pangea
|
||||
RoomCapacityButton(
|
||||
room: room,
|
||||
controller: controller,
|
||||
),
|
||||
// Pangea#
|
||||
// commenting out language settings in spaces for now
|
||||
// if (room.languageSettings != null && room.isRoomAdmin)
|
||||
// LanguageSettings(
|
||||
// roomId: controller.roomId,
|
||||
// startOpen: false,
|
||||
// ),
|
||||
|
||||
// Commenting out pangea room rules for now
|
||||
// if (room.pangeaRoomRules != null)
|
||||
// RoomRulesEditor(
|
||||
// roomId: controller.roomId,
|
||||
// startOpen: false,
|
||||
// ),
|
||||
|
||||
// if (!room.canChangeStateEvent(EventTypes.RoomTopic))
|
||||
// ListTile(
|
||||
// title: Text(
|
||||
// L10n.of(context)!.chatDescription,
|
||||
// style: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.secondary,
|
||||
// color: theme.colorScheme.secondary,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// ),
|
||||
// ),
|
||||
|
|
@ -294,15 +243,14 @@ class ChatDetailsView extends StatelessWidget {
|
|||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: TextButton.icon(
|
||||
// onPressed: controller.setTopicAction,
|
||||
// label: Text(L10n.of(context)!.setChatDescription),
|
||||
// label:
|
||||
// Text(L10n.of(context)!.setChatDescription),
|
||||
// icon: const Icon(Icons.edit_outlined),
|
||||
// style: TextButton.styleFrom(
|
||||
// backgroundColor: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .secondaryContainer,
|
||||
// foregroundColor: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .onSecondaryContainer,
|
||||
// backgroundColor:
|
||||
// theme.colorScheme.secondaryContainer,
|
||||
// foregroundColor:
|
||||
// theme.colorScheme.onSecondaryContainer,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
|
|
@ -324,24 +272,19 @@ class ChatDetailsView extends StatelessWidget {
|
|||
// fontStyle: room.topic.isEmpty
|
||||
// ? FontStyle.italic
|
||||
// : FontStyle.normal,
|
||||
// color:
|
||||
// Theme.of(context).textTheme.bodyMedium!.color,
|
||||
// color: theme.textTheme.bodyMedium!.color,
|
||||
// decorationColor:
|
||||
// Theme.of(context).textTheme.bodyMedium!.color,
|
||||
// theme.textTheme.bodyMedium!.color,
|
||||
// ),
|
||||
// onOpen: (url) =>
|
||||
// UrlLauncher(context, url.url).launchUrl(),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 16),
|
||||
// Divider(
|
||||
// height: 1,
|
||||
// color: Theme.of(context).dividerColor,
|
||||
// ),
|
||||
// Divider(color: theme.dividerColor),
|
||||
// ListTile(
|
||||
// leading: CircleAvatar(
|
||||
// backgroundColor:
|
||||
// Theme.of(context).scaffoldBackgroundColor,
|
||||
// backgroundColor: theme.scaffoldBackgroundColor,
|
||||
// foregroundColor: iconColor,
|
||||
// child: const Icon(
|
||||
// Icons.insert_emoticon_outlined,
|
||||
|
|
@ -354,23 +297,24 @@ class ChatDetailsView extends StatelessWidget {
|
|||
// trailing: const Icon(Icons.chevron_right_outlined),
|
||||
// ),
|
||||
// if (!room.isDirectChat)
|
||||
// ListTile(
|
||||
// leading: CircleAvatar(
|
||||
// backgroundColor:
|
||||
// Theme.of(context).scaffoldBackgroundColor,
|
||||
// foregroundColor: iconColor,
|
||||
// child: const Icon(Icons.shield_outlined),
|
||||
// ListTile(
|
||||
// leading: CircleAvatar(
|
||||
// backgroundColor: theme.scaffoldBackgroundColor,
|
||||
// foregroundColor: iconColor,
|
||||
// child: const Icon(Icons.shield_outlined),
|
||||
// ),
|
||||
// title: Text(
|
||||
// L10n.of(context)!.accessAndVisibility,
|
||||
// ),
|
||||
// subtitle: Text(
|
||||
// L10n.of(context)!
|
||||
// .accessAndVisibilityDescription,
|
||||
// ),
|
||||
// onTap: () => context
|
||||
// .push('/rooms/${room.id}/details/access'),
|
||||
// trailing:
|
||||
// const Icon(Icons.chevron_right_outlined),
|
||||
// ),
|
||||
// title: Text(
|
||||
// L10n.of(context)!.accessAndVisibility,
|
||||
// ),
|
||||
// subtitle: Text(
|
||||
// L10n.of(context)!.accessAndVisibilityDescription,
|
||||
// ),
|
||||
// onTap: () => context
|
||||
// .push('/rooms/${room.id}/details/access'),
|
||||
// trailing: const Icon(Icons.chevron_right_outlined),
|
||||
// ),
|
||||
// if (!room.isDirectChat)
|
||||
if (!room.isDirectChat &&
|
||||
!room.isSpace &&
|
||||
|
|
@ -382,8 +326,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||
title: Text(
|
||||
L10n.of(context)!.editChatPermissions,
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
@ -392,24 +335,19 @@ class ChatDetailsView extends StatelessWidget {
|
|||
L10n.of(context)!.whoCanPerformWhichAction,
|
||||
),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
foregroundColor: iconColor,
|
||||
child: const Icon(
|
||||
Icons.edit_attributes_outlined,
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// trailing: const Icon(Icons.chevron_right_outlined),
|
||||
// Pangea#
|
||||
trailing:
|
||||
const Icon(Icons.chevron_right_outlined),
|
||||
onTap: () => context.push(
|
||||
'/rooms/${room.id}/details/permissions',
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
// #Pangea
|
||||
if (room.canInvite &&
|
||||
!room.isDirectChat &&
|
||||
|
|
@ -486,8 +424,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
onTap: () async {
|
||||
OkCancelResult confirmed = OkCancelResult.ok;
|
||||
bool shouldGo = false;
|
||||
var confirmed = OkCancelResult.ok;
|
||||
var shouldGo = false;
|
||||
// archiveSpace has its own popup; only show if not space
|
||||
if (!room.isSpace) {
|
||||
confirmed = await showOkCancelAlertDialog(
|
||||
|
|
@ -539,10 +477,10 @@ class ChatDetailsView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
onTap: () async {
|
||||
OkCancelResult confirmed = OkCancelResult.ok;
|
||||
bool shouldGo = false;
|
||||
var confirmed = OkCancelResult.ok;
|
||||
var shouldGo = false;
|
||||
// If user is only admin, room will be archived
|
||||
final bool onlyAdmin = await room.isOnlyAdmin();
|
||||
final onlyAdmin = await room.isOnlyAdmin();
|
||||
// archiveSpace has its own popup; only show if not space
|
||||
if (!room.isSpace) {
|
||||
confirmed = await showOkCancelAlertDialog(
|
||||
|
|
@ -630,7 +568,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||
actualMembersCount.toString(),
|
||||
),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
@ -640,17 +578,17 @@ class ChatDetailsView extends StatelessWidget {
|
|||
// ListTile(
|
||||
// title: Text(L10n.of(context)!.inviteContact),
|
||||
// leading: CircleAvatar(
|
||||
// backgroundColor: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .primaryContainer,
|
||||
// foregroundColor: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .onPrimaryContainer,
|
||||
// backgroundColor:
|
||||
// theme.colorScheme.primaryContainer,
|
||||
// foregroundColor:
|
||||
// theme.colorScheme.onPrimaryContainer,
|
||||
// radius: Avatar.defaultSize / 2,
|
||||
// child: const Icon(Icons.add_outlined),
|
||||
// ),
|
||||
// trailing: const Icon(Icons.chevron_right_outlined),
|
||||
// onTap: () => context.go('/rooms/${room.id}/invite'),
|
||||
// trailing:
|
||||
// const Icon(Icons.chevron_right_outlined),
|
||||
// onTap: () =>
|
||||
// context.go('/rooms/${room.id}/invite'),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
|
|
@ -665,8 +603,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
child: const Icon(
|
||||
Icons.group_outlined,
|
||||
color: Colors.grey,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ class ParticipantListItem extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final membershipBatch = switch (user.membership) {
|
||||
Membership.ban => L10n.of(context)!.banned,
|
||||
Membership.invite => L10n.of(context)!.invited,
|
||||
|
|
@ -54,11 +56,13 @@ class ParticipantListItem extends StatelessWidget {
|
|||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
// #Pangea
|
||||
// color: Theme.of(context).colorScheme.primaryContainer,
|
||||
color: Theme.of(context).secondaryHeaderColor,
|
||||
// color: theme.colorScheme.primaryContainer,
|
||||
color: theme.secondaryHeaderColor,
|
||||
// Pangea#
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
// #Pangea
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
// color: theme.colorScheme.primary,
|
||||
// ),
|
||||
// Pangea#
|
||||
),
|
||||
|
|
@ -67,7 +71,7 @@ class ParticipantListItem extends StatelessWidget {
|
|||
// #Pangea
|
||||
// style: TextStyle(
|
||||
// fontSize: 14,
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
// color: theme.colorScheme.primary,
|
||||
// ),
|
||||
// Pangea#
|
||||
),
|
||||
|
|
@ -78,7 +82,7 @@ class ParticipantListItem extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(4),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).secondaryHeaderColor,
|
||||
color: theme.secondaryHeaderColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(child: Text(membershipBatch)),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart';
|
||||
import 'package:fluffychat/utils/beautify_string_extension.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class ChatEncryptionSettingsView extends StatelessWidget {
|
||||
final ChatEncryptionSettingsController controller;
|
||||
|
|
@ -17,6 +15,8 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final room = controller.room;
|
||||
return StreamBuilder<Object>(
|
||||
stream: room.client.onSync.stream.where(
|
||||
|
|
@ -42,10 +42,8 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
children: [
|
||||
SwitchListTile(
|
||||
secondary: CircleAvatar(
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primaryContainer,
|
||||
foregroundColor: theme.colorScheme.onPrimaryContainer,
|
||||
backgroundColor: theme.colorScheme.primaryContainer,
|
||||
child: const Icon(Icons.lock_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context)!.encryptThisChat),
|
||||
|
|
@ -56,7 +54,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
// Icon(
|
||||
// CupertinoIcons.lock_shield,
|
||||
// size: 128,
|
||||
// color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
// color: theme.colorScheme.onInverseSurface,
|
||||
// ),
|
||||
// Pangea#
|
||||
const Divider(),
|
||||
|
|
@ -145,13 +143,10 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
AppConfig.borderRadius,
|
||||
),
|
||||
side: BorderSide(
|
||||
color:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primaryContainer,
|
||||
color: theme.colorScheme.primaryContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Text(
|
||||
|
|
@ -159,9 +154,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
fontSize: 12,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
|
|
@ -176,7 +169,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
L10n.of(context)!.unknownEncryptionAlgorithm,
|
||||
style: TextStyle(
|
||||
fontFamily: 'RobotoMono',
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,17 @@
|
|||
import 'package:animations/animations.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_header.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
|
||||
import 'package:fluffychat/pages/chat_list/dummy_chat_list_item.dart';
|
||||
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
||||
import 'package:fluffychat/pages/chat_list/space_view.dart';
|
||||
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat_list/chat_list_body_text.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat_list/chat_list_header_wrapper.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat_list/chat_list_item_wrapper.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/hover_builder.dart';
|
||||
import 'package:fluffychat/widgets/public_room_bottom_sheet.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
|
@ -25,6 +27,34 @@ class ChatListViewBody extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final client = Matrix.of(context).client;
|
||||
final activeSpace = controller.activeSpaceId;
|
||||
if (activeSpace != null) {
|
||||
return SpaceView(
|
||||
spaceId: activeSpace,
|
||||
onBack: controller.clearActiveSpace,
|
||||
onChatTab: (room) => controller.onChatTap(room),
|
||||
onChatContext: (room, context) =>
|
||||
controller.chatContextAction(room, context),
|
||||
activeChat: controller.activeChat,
|
||||
toParentSpace: controller.setActiveSpace,
|
||||
// #Pangea
|
||||
controller: controller,
|
||||
// Pangea#
|
||||
);
|
||||
}
|
||||
final spaces = client.rooms.where((r) => r.isSpace);
|
||||
final spaceDelegateCandidates = <String, Room>{};
|
||||
for (final space in spaces) {
|
||||
for (final spaceChild in space.spaceChildren) {
|
||||
final roomId = spaceChild.roomId;
|
||||
if (roomId == null) continue;
|
||||
spaceDelegateCandidates[roomId] = space;
|
||||
}
|
||||
}
|
||||
|
||||
final publicRooms = controller.roomSearchResult?.chunk
|
||||
.where((room) => room.roomType != 'm.space')
|
||||
.toList();
|
||||
|
|
@ -32,249 +62,270 @@ class ChatListViewBody extends StatelessWidget {
|
|||
.where((room) => room.roomType == 'm.space')
|
||||
.toList();
|
||||
final userSearchResult = controller.userSearchResult;
|
||||
final client = Matrix.of(context).client;
|
||||
const dummyChatCount = 4;
|
||||
final titleColor =
|
||||
Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(100);
|
||||
final subtitleColor =
|
||||
Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(50);
|
||||
final filter = controller.searchController.text.toLowerCase();
|
||||
return PageTransitionSwitcher(
|
||||
transitionBuilder: (
|
||||
Widget child,
|
||||
Animation<double> primaryAnimation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) {
|
||||
return SharedAxisTransition(
|
||||
animation: primaryAnimation,
|
||||
secondaryAnimation: secondaryAnimation,
|
||||
transitionType: SharedAxisTransitionType.vertical,
|
||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: StreamBuilder(
|
||||
key: ValueKey(
|
||||
client.userID.toString() +
|
||||
controller.activeFilter.toString() +
|
||||
controller.activeSpaceId.toString(),
|
||||
),
|
||||
stream: client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, _) {
|
||||
if (controller.activeFilter == ActiveFilter.spaces) {
|
||||
return SpaceView(
|
||||
controller,
|
||||
scrollController: controller.scrollController,
|
||||
key: Key(controller.activeSpaceId ?? 'Spaces'),
|
||||
);
|
||||
}
|
||||
final rooms = controller.filteredRooms;
|
||||
return SafeArea(
|
||||
child: CustomScrollView(
|
||||
controller: controller.scrollController,
|
||||
slivers: [
|
||||
// #Pangea
|
||||
// ChatListHeader(controller: controller),
|
||||
ChatListHeaderWrapper(controller: controller),
|
||||
// Pangea#
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
if (controller.isSearchMode) ...[
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicRooms,
|
||||
icon: const Icon(Icons.explore_outlined),
|
||||
return StreamBuilder(
|
||||
key: ValueKey(
|
||||
client.userID.toString(),
|
||||
),
|
||||
stream: client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, _) {
|
||||
final rooms = controller.filteredRooms;
|
||||
|
||||
return SafeArea(
|
||||
child: CustomScrollView(
|
||||
controller: controller.scrollController,
|
||||
slivers: [
|
||||
ChatListHeader(controller: controller),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
if (controller.isSearchMode) ...[
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicRooms,
|
||||
icon: const Icon(Icons.explore_outlined),
|
||||
),
|
||||
PublicRoomsHorizontalList(publicRooms: publicRooms),
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicSpaces,
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
),
|
||||
PublicRoomsHorizontalList(publicRooms: publicSpaces),
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.users,
|
||||
icon: const Icon(Icons.group_outlined),
|
||||
),
|
||||
AnimatedContainer(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
height: userSearchResult == null ||
|
||||
userSearchResult.results.isEmpty
|
||||
? 0
|
||||
: 106,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: userSearchResult == null
|
||||
? null
|
||||
: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: userSearchResult.results.length,
|
||||
itemBuilder: (context, i) => _SearchItem(
|
||||
title:
|
||||
userSearchResult.results[i].displayName ??
|
||||
userSearchResult
|
||||
.results[i].userId.localpart ??
|
||||
L10n.of(context)!.unknownDevice,
|
||||
avatar: userSearchResult.results[i].avatarUrl,
|
||||
onPressed: () => showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
profile: userSearchResult.results[i],
|
||||
outerContext: context,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
// #Pangea
|
||||
// if (!controller.isSearchMode && AppConfig.showPresences)
|
||||
// GestureDetector(
|
||||
// onLongPress: () => controller.dismissStatusList(),
|
||||
// child: StatusMessageList(
|
||||
// onStatusEdit: controller.setStatus,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
const ConnectionStatusHeader(),
|
||||
AnimatedContainer(
|
||||
height: controller.isTorBrowser ? 64 : 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
child: Material(
|
||||
color: theme.colorScheme.surface,
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.vpn_key),
|
||||
title: Text(L10n.of(context)!.dehydrateTor),
|
||||
subtitle: Text(L10n.of(context)!.dehydrateTorLong),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
onTap: controller.dehydrate,
|
||||
),
|
||||
PublicRoomsHorizontalList(publicRooms: publicRooms),
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicSpaces,
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
),
|
||||
PublicRoomsHorizontalList(publicRooms: publicSpaces),
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.users,
|
||||
icon: const Icon(Icons.group_outlined),
|
||||
),
|
||||
AnimatedContainer(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
height: userSearchResult == null ||
|
||||
userSearchResult.results.isEmpty
|
||||
? 0
|
||||
: 106,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: userSearchResult == null
|
||||
? null
|
||||
: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: userSearchResult.results.length,
|
||||
itemBuilder: (context, i) => _SearchItem(
|
||||
title: userSearchResult
|
||||
.results[i].displayName ??
|
||||
userSearchResult
|
||||
.results[i].userId.localpart ??
|
||||
L10n.of(context)!.unknownDevice,
|
||||
avatar:
|
||||
userSearchResult.results[i].avatarUrl,
|
||||
onPressed: () => showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
profile: userSearchResult.results[i],
|
||||
outerContext: context,
|
||||
),
|
||||
),
|
||||
if (client.rooms.isNotEmpty && !controller.isSearchMode)
|
||||
SizedBox(
|
||||
height: 64,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 16.0,
|
||||
),
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
if (AppConfig.separateChatTypes)
|
||||
ActiveFilter.messages
|
||||
else
|
||||
ActiveFilter.allChats,
|
||||
ActiveFilter.groups,
|
||||
ActiveFilter.unread,
|
||||
if (spaceDelegateCandidates.isNotEmpty &&
|
||||
!controller.widget.displayNavigationRail)
|
||||
ActiveFilter.spaces,
|
||||
]
|
||||
.map(
|
||||
(filter) => Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: HoverBuilder(
|
||||
builder: (context, hovered) =>
|
||||
AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered ? 1.1 : 1.0,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
onTap: () =>
|
||||
controller.setActiveFilter(filter),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: filter ==
|
||||
controller.activeFilter
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme
|
||||
.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
filter.toLocalizedString(context),
|
||||
style: TextStyle(
|
||||
fontWeight: filter ==
|
||||
controller.activeFilter
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
color: filter ==
|
||||
controller.activeFilter
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme
|
||||
.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
// #Pangea
|
||||
// if (!controller.isSearchMode &&
|
||||
// controller.activeFilter != ActiveFilter.groups &&
|
||||
// AppConfig.showPresences)
|
||||
// GestureDetector(
|
||||
// onLongPress: () => controller.dismissStatusList(),
|
||||
// child: StatusMessageList(
|
||||
// onStatusEdit: controller.setStatus,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
const ConnectionStatusHeader(),
|
||||
AnimatedContainer(
|
||||
height: controller.isTorBrowser ? 64 : 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.vpn_key),
|
||||
title: Text(L10n.of(context)!.dehydrateTor),
|
||||
subtitle: Text(L10n.of(context)!.dehydrateTorLong),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
onTap: controller.dehydrate,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
if (controller.isSearchMode)
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.chats,
|
||||
icon: const Icon(Icons.forum_outlined),
|
||||
),
|
||||
if (client.prevBatch != null &&
|
||||
rooms.isEmpty &&
|
||||
!controller.isSearchMode) ...[
|
||||
// #Pangea
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(32.0),
|
||||
// child: Icon(
|
||||
// CupertinoIcons.chat_bubble_2,
|
||||
// size: 128,
|
||||
// color:
|
||||
// Theme.of(context).colorScheme.onInverseSurface,
|
||||
// ),
|
||||
// ),
|
||||
Center(
|
||||
child: ChatListBodyStartText(
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (client.prevBatch == null)
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, i) => Opacity(
|
||||
opacity: (dummyChatCount - i) / dummyChatCount,
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: titleColor,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
color:
|
||||
Theme.of(context).textTheme.bodyLarge!.color,
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
if (controller.isSearchMode)
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.chats,
|
||||
icon: const Icon(Icons.forum_outlined),
|
||||
),
|
||||
if (client.prevBatch != null &&
|
||||
rooms.isEmpty &&
|
||||
!controller.isSearchMode) ...[
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: titleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
const Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DummyChatListItem(
|
||||
opacity: 0.5,
|
||||
animate: false,
|
||||
),
|
||||
),
|
||||
DummyChatListItem(
|
||||
opacity: 0.3,
|
||||
animate: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 36),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
Icon(
|
||||
CupertinoIcons.chat_bubble_text_fill,
|
||||
size: 128,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
client.rooms.isEmpty
|
||||
? L10n.of(context)!.noChatsFoundHere
|
||||
: L10n.of(context)!.noMoreChatsFound,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
height: 12,
|
||||
margin: const EdgeInsets.only(right: 22),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
childCount: dummyChatCount,
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(32.0),
|
||||
// child: Icon(
|
||||
// CupertinoIcons.chat_bubble_2,
|
||||
// size: 128,
|
||||
// color: theme.colorScheme.secondary,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (client.prevBatch == null)
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, i) => DummyChatListItem(
|
||||
opacity: (dummyChatCount - i) / dummyChatCount,
|
||||
animate: true,
|
||||
),
|
||||
childCount: dummyChatCount,
|
||||
),
|
||||
if (client.prevBatch != null)
|
||||
SliverList.builder(
|
||||
itemCount: rooms.length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
// #Pangea
|
||||
// return ChatListItem(
|
||||
return ChatListItemWrapper(
|
||||
controller: controller,
|
||||
// Pangea#
|
||||
rooms[i],
|
||||
key: Key('chat_list_item_${rooms[i].id}'),
|
||||
filter: filter,
|
||||
// #Pangea
|
||||
// selected:
|
||||
// controller.selectedRoomIds.contains(rooms[i].id),
|
||||
// onTap: controller.selectMode == SelectMode.select
|
||||
// ? () => controller.toggleSelection(rooms[i].id)
|
||||
// : () => onChatTap(rooms[i], context),
|
||||
// onLongPress: () =>
|
||||
// controller.toggleSelection(rooms[i].id),
|
||||
// Pangea#
|
||||
activeChat: controller.activeChat == rooms[i].id,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (client.prevBatch != null)
|
||||
SliverList.builder(
|
||||
itemCount: rooms.length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
final room = rooms[i];
|
||||
final space = spaceDelegateCandidates[room.id];
|
||||
return ChatListItem(
|
||||
room,
|
||||
space: space,
|
||||
key: Key('chat_list_item_${room.id}'),
|
||||
filter: filter,
|
||||
onTap: () => controller.onChatTap(room),
|
||||
onLongPress: (context) =>
|
||||
controller.chatContextAction(room, context, space),
|
||||
activeChat: controller.activeChat == room.id,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
|
|
@ -18,11 +17,13 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final selectMode = controller.selectMode;
|
||||
|
||||
return SliverAppBar(
|
||||
floating: true,
|
||||
toolbarHeight: 72,
|
||||
toolbarHeight: 175,
|
||||
pinned:
|
||||
FluffyThemes.isColumnMode(context) || selectMode != SelectMode.normal,
|
||||
scrolledUnderElevation: selectMode == SelectMode.normal ? 0 : null,
|
||||
|
|
@ -37,40 +38,39 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
tooltip: L10n.of(context)!.cancel,
|
||||
icon: const Icon(Icons.close_outlined),
|
||||
onPressed: controller.cancelAction,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
title:
|
||||
title: selectMode == SelectMode.share
|
||||
? Text(
|
||||
L10n.of(context)!.share,
|
||||
key: const ValueKey(SelectMode.share),
|
||||
)
|
||||
// #Pangea
|
||||
Material(
|
||||
child:
|
||||
// Pangea#
|
||||
selectMode == SelectMode.share
|
||||
? Text(
|
||||
L10n.of(context)!.share,
|
||||
key: const ValueKey(SelectMode.share),
|
||||
)
|
||||
: selectMode == SelectMode.select
|
||||
? Text(
|
||||
controller.selectedRoomIds.length.toString(),
|
||||
key: const ValueKey(SelectMode.select),
|
||||
)
|
||||
// #Pangea
|
||||
: ClientChooserButton(controller),
|
||||
),
|
||||
: Column(
|
||||
children: [
|
||||
ClientChooserButton(controller),
|
||||
const LearningProgressIndicators(),
|
||||
],
|
||||
),
|
||||
// : TextField(
|
||||
// controller: controller.searchController,
|
||||
// focusNode: controller.searchFocusNode,
|
||||
// textInputAction: TextInputAction.search,
|
||||
// onChanged: controller.onSearchEnter,
|
||||
// onChanged: (text) => controller.onSearchEnter(
|
||||
// text,
|
||||
// globalSearch: globalSearch,
|
||||
// ),
|
||||
// decoration: InputDecoration(
|
||||
// fillColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
// border: UnderlineInputBorder(
|
||||
// filled: true,
|
||||
// fillColor: theme.colorScheme.secondaryContainer,
|
||||
// border: OutlineInputBorder(
|
||||
// borderSide: BorderSide.none,
|
||||
// borderRadius: BorderRadius.circular(99),
|
||||
// ),
|
||||
// contentPadding: EdgeInsets.zero,
|
||||
// hintText: L10n.of(context)!.searchChatsRooms,
|
||||
// hintStyle: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
// color: theme.colorScheme.onPrimaryContainer,
|
||||
// fontWeight: FontWeight.normal,
|
||||
// ),
|
||||
// floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
|
|
@ -79,20 +79,16 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
// tooltip: L10n.of(context)!.cancel,
|
||||
// icon: const Icon(Icons.close_outlined),
|
||||
// onPressed: controller.cancelSearch,
|
||||
// color: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .onPrimaryContainer,
|
||||
// color: theme.colorScheme.onPrimaryContainer,
|
||||
// )
|
||||
// : IconButton(
|
||||
// onPressed: controller.startSearch,
|
||||
// icon: Icon(
|
||||
// Icons.search_outlined,
|
||||
// color: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .onPrimaryContainer,
|
||||
// color: theme.colorScheme.onPrimaryContainer,
|
||||
// ),
|
||||
// ),
|
||||
// suffixIcon: controller.isSearchMode
|
||||
// suffixIcon: controller.isSearchMode && globalSearch
|
||||
// ? controller.isSearching
|
||||
// ? const Padding(
|
||||
// padding: EdgeInsets.symmetric(
|
||||
|
|
@ -117,10 +113,7 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
// icon: const Icon(Icons.edit_outlined, size: 16),
|
||||
// label: Text(
|
||||
// controller.searchServer ??
|
||||
// Matrix.of(context)
|
||||
// .client
|
||||
// .homeserver!
|
||||
// .host,
|
||||
// Matrix.of(context).client.homeserver!.host,
|
||||
// maxLines: 2,
|
||||
// ),
|
||||
// )
|
||||
|
|
@ -143,80 +136,7 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
// ),
|
||||
// Pangea#
|
||||
]
|
||||
: selectMode == SelectMode.select
|
||||
? [
|
||||
// #Pangea
|
||||
// if (controller.spaces.isNotEmpty)
|
||||
if (controller.spaces.isNotEmpty &&
|
||||
controller.selectedRoomIds.length == 1)
|
||||
// Pangea#
|
||||
IconButton(
|
||||
tooltip: L10n.of(context)!.addToSpace,
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
onPressed: controller.addToSpace,
|
||||
),
|
||||
IconButton(
|
||||
tooltip: L10n.of(context)!.toggleUnread,
|
||||
icon: Icon(
|
||||
controller.anySelectedRoomNotMarkedUnread
|
||||
? Icons.mark_chat_unread_outlined
|
||||
: Icons.mark_chat_read_outlined,
|
||||
),
|
||||
onPressed: controller.toggleUnread,
|
||||
),
|
||||
IconButton(
|
||||
tooltip: L10n.of(context)!.toggleFavorite,
|
||||
icon: Icon(
|
||||
controller.anySelectedRoomNotFavorite
|
||||
? Icons.push_pin
|
||||
: Icons.push_pin_outlined,
|
||||
),
|
||||
onPressed: controller.toggleFavouriteRoom,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
controller.anySelectedRoomNotMuted
|
||||
? Icons.notifications_off_outlined
|
||||
: Icons.notifications_outlined,
|
||||
),
|
||||
tooltip: L10n.of(context)!.toggleMuted,
|
||||
onPressed: controller.toggleMuted,
|
||||
),
|
||||
// #Pangea
|
||||
if (controller.selectedRoomIds.length > 1)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
tooltip: L10n.of(context)!.leave,
|
||||
onPressed: controller.leaveAction,
|
||||
),
|
||||
if (controller.selectedRoomIds.length == 1 &&
|
||||
!(Matrix.of(context)
|
||||
.client
|
||||
.getRoomById(controller.selectedRoomIds.single)
|
||||
?.isRoomAdmin ??
|
||||
false))
|
||||
IconButton(
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
tooltip: L10n.of(context)!.leave,
|
||||
onPressed: controller.leaveAction,
|
||||
),
|
||||
if (controller.selectedRoomIds.length == 1 &&
|
||||
(Matrix.of(context)
|
||||
.client
|
||||
.getRoomById(controller.selectedRoomIds.single)
|
||||
?.isRoomAdmin ??
|
||||
false))
|
||||
// Pangea#
|
||||
IconButton(
|
||||
// #Pangea
|
||||
// icon: const Icon(Icons.delete_outlined),
|
||||
icon: const Icon(Icons.archive_outlined),
|
||||
// Pangea#
|
||||
tooltip: L10n.of(context)!.archive,
|
||||
onPressed: controller.archiveAction,
|
||||
),
|
||||
]
|
||||
: null,
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ enum ArchivedRoomAction { delete, rejoin }
|
|||
|
||||
class ChatListItem extends StatelessWidget {
|
||||
final Room room;
|
||||
final Room? space;
|
||||
final bool activeChat;
|
||||
final bool selected;
|
||||
final void Function()? onLongPress;
|
||||
final void Function(BuildContext context)? onLongPress;
|
||||
final void Function()? onForget;
|
||||
final void Function() onTap;
|
||||
final String? filter;
|
||||
|
|
@ -29,11 +29,11 @@ class ChatListItem extends StatelessWidget {
|
|||
const ChatListItem(
|
||||
this.room, {
|
||||
this.activeChat = false,
|
||||
this.selected = false,
|
||||
required this.onTap,
|
||||
this.onLongPress,
|
||||
this.onForget,
|
||||
this.filter,
|
||||
this.space,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -57,10 +57,7 @@ class ChatListItem extends StatelessWidget {
|
|||
if (confirmed == OkCancelResult.cancel) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
// #Pangea
|
||||
// future: () => room.leave(),
|
||||
future: () => room.archive(),
|
||||
// Pangea#
|
||||
future: () => room.leave(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -68,12 +65,13 @@ class ChatListItem extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final isMuted = room.pushRuleState != PushRuleState.notify;
|
||||
final typingText = room.getLocalizedTypingText(context);
|
||||
final lastEvent = room.lastEvent;
|
||||
final ownMessage = lastEvent?.senderId == room.client.userID;
|
||||
final unread = room.isUnread || room.membership == Membership.invite;
|
||||
final theme = Theme.of(context);
|
||||
final directChatMatrixId = room.directChatMatrixID;
|
||||
final isDirectChat = directChatMatrixId != null;
|
||||
final unreadBubbleSize = unread || room.hasNewMessages
|
||||
|
|
@ -82,11 +80,8 @@ class ChatListItem extends StatelessWidget {
|
|||
: 14.0
|
||||
: 0.0;
|
||||
final hasNotifications = room.notificationCount > 0;
|
||||
final backgroundColor = selected
|
||||
? theme.colorScheme.primaryContainer
|
||||
: activeChat
|
||||
? theme.colorScheme.secondaryContainer
|
||||
: null;
|
||||
final backgroundColor =
|
||||
activeChat ? theme.colorScheme.secondaryContainer : null;
|
||||
final displayname = room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
);
|
||||
|
|
@ -98,6 +93,7 @@ class ChatListItem extends StatelessWidget {
|
|||
final needLastEventSender = lastEvent == null
|
||||
? false
|
||||
: room.getState(EventTypes.RoomMember, lastEvent.senderId) == null;
|
||||
final space = this.space;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
|
@ -111,50 +107,89 @@ class ChatListItem extends StatelessWidget {
|
|||
child: FutureBuilder(
|
||||
future: room.loadHeroUsers(),
|
||||
builder: (context, snapshot) => HoverBuilder(
|
||||
builder: (context, hovered) => ListTile(
|
||||
builder: (context, listTileHovered) => ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -0.5),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
onLongPress: onLongPress,
|
||||
leading: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
HoverBuilder(
|
||||
builder: (context, hovered) => AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered ? 1.1 : 1.0,
|
||||
child: Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
//#Pangea
|
||||
littleIcon: room.roomTypeIcon,
|
||||
// Pangea#
|
||||
presenceUserId: directChatMatrixId,
|
||||
presenceBackgroundColor: backgroundColor,
|
||||
onTap: onLongPress,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: -2,
|
||||
right: -2,
|
||||
child: AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: (hovered || selected) ? 1.0 : 0.0,
|
||||
child: Material(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Icon(
|
||||
selected
|
||||
? Icons.check_circle
|
||||
: Icons.check_circle_outlined,
|
||||
size: 18,
|
||||
onLongPress: () => onLongPress?.call(context),
|
||||
leading: HoverBuilder(
|
||||
builder: (context, hovered) => AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered ? 1.1 : 1.0,
|
||||
child: SizedBox(
|
||||
width: Avatar.defaultSize,
|
||||
height: Avatar.defaultSize,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (space != null)
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
child: Avatar(
|
||||
border: BorderSide(
|
||||
width: 2,
|
||||
color: backgroundColor ??
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
),
|
||||
mxContent: space.avatar,
|
||||
size: Avatar.defaultSize * 0.75,
|
||||
name: space.getLocalizedDisplayname(),
|
||||
onTap: () => onLongPress?.call(context),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: Avatar(
|
||||
border: space == null
|
||||
? null
|
||||
: BorderSide(
|
||||
width: 2,
|
||||
color: backgroundColor ??
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
borderRadius: room.isSpace
|
||||
? BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
)
|
||||
: null,
|
||||
mxContent: room.avatar,
|
||||
size: space != null
|
||||
? Avatar.defaultSize * 0.75
|
||||
: Avatar.defaultSize,
|
||||
name: displayname,
|
||||
presenceUserId: directChatMatrixId,
|
||||
presenceBackgroundColor: backgroundColor,
|
||||
onTap: () => onLongPress?.call(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: GestureDetector(
|
||||
onTap: () => onLongPress?.call(context),
|
||||
child: AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: listTileHovered ? 1.0 : 0.0,
|
||||
child: Material(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: const Icon(
|
||||
Icons.arrow_drop_down_circle_outlined,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: <Widget>[
|
||||
|
|
@ -188,7 +223,9 @@ class ChatListItem extends StatelessWidget {
|
|||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
if (lastEvent != null && room.membership != Membership.invite)
|
||||
if (!room.isSpace &&
|
||||
lastEvent != null &&
|
||||
room.membership != Membership.invite)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: Text(
|
||||
|
|
@ -201,6 +238,11 @@ class ChatListItem extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (room.isSpace)
|
||||
const Icon(
|
||||
Icons.arrow_circle_right_outlined,
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
|
|
@ -230,61 +272,82 @@ class ChatListItem extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Expanded(
|
||||
child: typingText.isNotEmpty
|
||||
child: room.isSpace && room.membership == Membership.join
|
||||
? Text(
|
||||
typingText,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
L10n.of(context)!.countChatsAndCountParticipants(
|
||||
room.spaceChildren.length.toString(),
|
||||
(room.summary.mJoinedMemberCount ?? 1).toString(),
|
||||
),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
)
|
||||
// #Pangea
|
||||
: FutureBuilder<String>(
|
||||
future: room.lastEvent != null
|
||||
? GetChatListItemSubtitle().getSubtitle(
|
||||
L10n.of(context)!,
|
||||
room.lastEvent,
|
||||
MatrixState.pangeaController,
|
||||
)
|
||||
: Future.value(L10n.of(context)!.emptyChat),
|
||||
builder: (context, snapshot) {
|
||||
// Pangea#
|
||||
return Text(
|
||||
room.membership == Membership.invite
|
||||
? isDirectChat
|
||||
? L10n.of(context)!.invitePrivateChat
|
||||
: L10n.of(context)!.inviteGroupChat
|
||||
// #Pangea
|
||||
: snapshot.data ??
|
||||
// Pangea#
|
||||
room.lastEvent
|
||||
?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix: !isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId,
|
||||
) ??
|
||||
L10n.of(context)!.emptyChat,
|
||||
softWrap: false,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
: typingText.isNotEmpty
|
||||
? Text(
|
||||
typingText,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
decoration: room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
)
|
||||
: FutureBuilder(
|
||||
key: ValueKey(
|
||||
'${lastEvent?.eventId}_${lastEvent?.type}',
|
||||
),
|
||||
// #Pangea
|
||||
future: room.lastEvent != null
|
||||
? GetChatListItemSubtitle().getSubtitle(
|
||||
L10n.of(context)!,
|
||||
room.lastEvent,
|
||||
MatrixState.pangeaController,
|
||||
)
|
||||
: Future.value(L10n.of(context)!.emptyChat),
|
||||
// future: needLastEventSender
|
||||
// ? lastEvent.calcLocalizedBody(
|
||||
// MatrixLocals(L10n.of(context)!),
|
||||
// hideReply: true,
|
||||
// hideEdit: true,
|
||||
// plaintextBody: true,
|
||||
// removeMarkdown: true,
|
||||
// withSenderNamePrefix: (!isDirectChat ||
|
||||
// directChatMatrixId !=
|
||||
// room.lastEvent?.senderId),
|
||||
// )
|
||||
// : null,
|
||||
// Pangea#
|
||||
initialData:
|
||||
lastEvent?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix: (!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId),
|
||||
),
|
||||
builder: (context, snapshot) => Text(
|
||||
room.membership == Membership.invite
|
||||
? isDirectChat
|
||||
? L10n.of(context)!.invitePrivateChat
|
||||
// #Pangea
|
||||
// : L10n.of(context)!.inviteGroupChat
|
||||
: L10n.of(context)!.inviteChat
|
||||
// Pangea#
|
||||
: snapshot.data ??
|
||||
L10n.of(context)!.emptyChat,
|
||||
softWrap: false,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
decoration: room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -1,101 +1,23 @@
|
|||
import 'package:badges/badges.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/navi_rail_item.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/utils/chat_list_handle_space_tap.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'chat_list_body.dart';
|
||||
import 'start_chat_fab.dart';
|
||||
|
||||
class ChatListView extends StatelessWidget {
|
||||
final ChatListController controller;
|
||||
|
||||
const ChatListView(this.controller, {super.key});
|
||||
|
||||
List<NavigationDestination> getNavigationDestinations(BuildContext context) {
|
||||
final badgePosition = BadgePosition.topEnd(top: -12, end: -8);
|
||||
return [
|
||||
if (AppConfig.separateChatTypes) ...[
|
||||
NavigationDestination(
|
||||
icon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter:
|
||||
controller.getRoomFilterByActiveFilter(ActiveFilter.messages),
|
||||
child: const Icon(Icons.chat_outlined),
|
||||
),
|
||||
selectedIcon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter:
|
||||
controller.getRoomFilterByActiveFilter(ActiveFilter.messages),
|
||||
child: const Icon(Icons.chat),
|
||||
),
|
||||
//#Pangea
|
||||
// label: L10n.of(context)!.messages,
|
||||
label: L10n.of(context)!.directChats,
|
||||
//Pangea#
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
||||
child: const Icon(Icons.group_outlined),
|
||||
),
|
||||
selectedIcon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
||||
child: const Icon(Icons.group),
|
||||
),
|
||||
label: L10n.of(context)!.groups,
|
||||
),
|
||||
] else
|
||||
NavigationDestination(
|
||||
icon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter:
|
||||
controller.getRoomFilterByActiveFilter(ActiveFilter.allChats),
|
||||
child: const Icon(Icons.chat_outlined),
|
||||
),
|
||||
selectedIcon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter:
|
||||
controller.getRoomFilterByActiveFilter(ActiveFilter.allChats),
|
||||
child: const Icon(Icons.chat),
|
||||
),
|
||||
// #Pangea
|
||||
// label: L10n.of(context)!.chats,
|
||||
label: L10n.of(context)!.allChats,
|
||||
// Pangea#
|
||||
),
|
||||
if (controller.spaces.isNotEmpty
|
||||
// #Pangea
|
||||
&&
|
||||
!FluffyThemes.isColumnMode(context)
|
||||
// Pangea#
|
||||
)
|
||||
// #Pangea
|
||||
// const NavigationDestination(
|
||||
// icon: Icon(Icons.workspaces_outlined),
|
||||
// selectedIcon: Icon(Icons.workspaces),
|
||||
// label: 'Spaces',
|
||||
// ),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
selectedIcon: const Icon(Icons.workspaces),
|
||||
label: L10n.of(context)!.allSpaces,
|
||||
),
|
||||
// Pangea#
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = Matrix.of(context).client;
|
||||
|
|
@ -106,12 +28,13 @@ class ChatListView extends StatelessWidget {
|
|||
return PopScope(
|
||||
canPop: controller.selectMode == SelectMode.normal &&
|
||||
!controller.isSearchMode &&
|
||||
controller.activeFilter ==
|
||||
(AppConfig.separateChatTypes
|
||||
? ActiveFilter.messages
|
||||
: ActiveFilter.allChats),
|
||||
onPopInvoked: (pop) async {
|
||||
controller.activeSpaceId == null,
|
||||
onPopInvokedWithResult: (pop, _) {
|
||||
if (pop) return;
|
||||
if (controller.activeSpaceId != null) {
|
||||
controller.clearActiveSpace();
|
||||
return;
|
||||
}
|
||||
final selMode = controller.selectMode;
|
||||
if (controller.isSearchMode) {
|
||||
controller.cancelSearch();
|
||||
|
|
@ -121,23 +44,23 @@ class ChatListView extends StatelessWidget {
|
|||
controller.cancelAction();
|
||||
return;
|
||||
}
|
||||
if (controller.activeFilter !=
|
||||
(AppConfig.separateChatTypes
|
||||
? ActiveFilter.messages
|
||||
: ActiveFilter.allChats)) {
|
||||
controller
|
||||
.onDestinationSelected(AppConfig.separateChatTypes ? 1 : 0);
|
||||
return;
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
if (FluffyThemes.isColumnMode(context) &&
|
||||
controller.widget.displayNavigationRail) ...[
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final allSpaces =
|
||||
client.rooms.where((room) => room.isSpace);
|
||||
StreamBuilder(
|
||||
key: ValueKey(
|
||||
client.userID.toString(),
|
||||
),
|
||||
stream: client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, _) {
|
||||
final allSpaces = Matrix.of(context)
|
||||
.client
|
||||
.rooms
|
||||
.where((room) => room.isSpace);
|
||||
final rootSpaces = allSpaces
|
||||
.where(
|
||||
(space) => !allSpaces.any(
|
||||
|
|
@ -146,58 +69,53 @@ class ChatListView extends StatelessWidget {
|
|||
),
|
||||
)
|
||||
.toList();
|
||||
final destinations = getNavigationDestinations(context);
|
||||
|
||||
return SizedBox(
|
||||
width: FluffyThemes.navRailWidth,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
itemCount: rootSpaces.length + destinations.length,
|
||||
itemCount: rootSpaces.length + 2,
|
||||
itemBuilder: (context, i) {
|
||||
if (i < destinations.length) {
|
||||
if (i == 0) {
|
||||
return NaviRailItem(
|
||||
// #Pangea
|
||||
// isSelected: i == controller.selectedIndex,
|
||||
isSelected: controller.isSelected(i),
|
||||
// Pangea#
|
||||
onTap: () => controller.onDestinationSelected(i),
|
||||
icon: destinations[i].icon,
|
||||
selectedIcon: destinations[i].selectedIcon,
|
||||
toolTip: destinations[i].label,
|
||||
isSelected: controller.activeSpaceId == null,
|
||||
onTap: controller.clearActiveSpace,
|
||||
icon: const Icon(Icons.forum_outlined),
|
||||
selectedIcon: const Icon(Icons.forum),
|
||||
toolTip: L10n.of(context)!.chats,
|
||||
unreadBadgeFilter: (room) => true,
|
||||
);
|
||||
}
|
||||
i -= destinations.length;
|
||||
final isSelected =
|
||||
controller.activeFilter == ActiveFilter.spaces &&
|
||||
rootSpaces[i].id == controller.activeSpaceId;
|
||||
//#Pangea
|
||||
final Room? room = Matrix.of(context)
|
||||
.client
|
||||
.getRoomById(rootSpaces[i].id);
|
||||
// Pangea#
|
||||
i--;
|
||||
if (i == rootSpaces.length) {
|
||||
return NaviRailItem(
|
||||
isSelected: false,
|
||||
onTap: () => context.go('/rooms/newspace'),
|
||||
icon: const Icon(Icons.add),
|
||||
toolTip: L10n.of(context)!.createNewSpace,
|
||||
);
|
||||
}
|
||||
final space = rootSpaces[i];
|
||||
final displayname =
|
||||
rootSpaces[i].getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
);
|
||||
final spaceChildrenIds =
|
||||
space.spaceChildren.map((c) => c.roomId).toSet();
|
||||
return NaviRailItem(
|
||||
toolTip: rootSpaces[i].getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
),
|
||||
isSelected: isSelected,
|
||||
// #Pangea
|
||||
// onTap: () =>
|
||||
// controller.setActiveSpace(rootSpaces[i].id),
|
||||
onTap: () => chatListHandleSpaceTap(
|
||||
context,
|
||||
controller,
|
||||
rootSpaces[i],
|
||||
),
|
||||
// Pangea#
|
||||
toolTip: displayname,
|
||||
isSelected: controller.activeSpaceId == space.id,
|
||||
onTap: () =>
|
||||
controller.setActiveSpace(rootSpaces[i].id),
|
||||
unreadBadgeFilter: (room) =>
|
||||
spaceChildrenIds.contains(room.id),
|
||||
icon: Avatar(
|
||||
mxContent: rootSpaces[i].avatar,
|
||||
name: rootSpaces[i].getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
),
|
||||
name: displayname,
|
||||
size: 32,
|
||||
// #Pangea
|
||||
littleIcon: room?.roomTypeIcon,
|
||||
// Pangea#
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -217,51 +135,33 @@ class ChatListView extends StatelessWidget {
|
|||
behavior: HitTestBehavior.translucent,
|
||||
child: Scaffold(
|
||||
body: ChatListViewBody(controller),
|
||||
bottomNavigationBar: controller.displayNavigationBar
|
||||
? NavigationBar(
|
||||
elevation: 4,
|
||||
labelBehavior:
|
||||
NavigationDestinationLabelBehavior.alwaysShow,
|
||||
shadowColor:
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.surface,
|
||||
surfaceTintColor:
|
||||
Theme.of(context).colorScheme.surface,
|
||||
selectedIndex: controller.selectedIndex,
|
||||
onDestinationSelected:
|
||||
controller.onDestinationSelected,
|
||||
destinations: getNavigationDestinations(context),
|
||||
)
|
||||
: null,
|
||||
// #Pangea
|
||||
// floatingActionButton: KeyBoardShortcuts(
|
||||
// keysToPress: {
|
||||
// LogicalKeyboardKey.controlLeft,
|
||||
// LogicalKeyboardKey.keyN,
|
||||
// },
|
||||
// onKeysPressed: () => context.go('/rooms/newprivatechat'),
|
||||
// helpLabel: L10n.of(context)!.newChat,
|
||||
// child: selectMode == SelectMode.normal &&
|
||||
// !controller.isSearchMode
|
||||
// ? StartChatFloatingActionButton(
|
||||
// activeFilter: controller.activeFilter,
|
||||
// roomsIsEmpty: false,
|
||||
// scrolledToTop: controller.scrolledToTop,
|
||||
// createNewSpace: controller.createNewSpace,
|
||||
// )
|
||||
// : const SizedBox.shrink(),
|
||||
// ),
|
||||
floatingActionButton: selectMode == SelectMode.normal
|
||||
? StartChatFloatingActionButton(
|
||||
activeFilter: controller.activeFilter,
|
||||
roomsIsEmpty: false,
|
||||
scrolledToTop: controller.scrolledToTop,
|
||||
controller: controller,
|
||||
createNewSpace: () {},
|
||||
)
|
||||
: null,
|
||||
// Pangea#
|
||||
floatingActionButton:
|
||||
// #Pangea
|
||||
// KeyBoardShortcuts(
|
||||
// keysToPress: {
|
||||
// LogicalKeyboardKey.controlLeft,
|
||||
// LogicalKeyboardKey.keyN,
|
||||
// },
|
||||
// onKeysPressed: () => context.go('/rooms/newprivatechat'),
|
||||
// helpLabel: L10n.of(context)!.newChat,
|
||||
// child:
|
||||
// Pangea#
|
||||
selectMode == SelectMode.normal &&
|
||||
!controller.isSearchMode &&
|
||||
controller.activeSpaceId == null
|
||||
? FloatingActionButton.extended(
|
||||
// #Pangea
|
||||
// onPressed: () =>
|
||||
// context.go('/rooms/newprivatechat'),
|
||||
onPressed: () => context.go('/rooms/newgroup'),
|
||||
// Pangea#
|
||||
icon: const Icon(Icons.add_outlined),
|
||||
label: Text(
|
||||
L10n.of(context)!.chat,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/utils/find_conversation_partner_dialog.dart';
|
||||
import 'package:fluffychat/pangea/utils/logout.dart';
|
||||
import 'package:fluffychat/pangea/utils/space_code.dart';
|
||||
|
|
@ -32,16 +31,6 @@ class ClientChooserButton extends StatelessWidget {
|
|||
// Pangea#
|
||||
return <PopupMenuEntry<Object>>[
|
||||
// #Pangea
|
||||
// PopupMenuItem(
|
||||
// value: SettingsAction.newGroup,
|
||||
// child: Row(
|
||||
// children: [
|
||||
// const Icon(Icons.group_add_outlined),
|
||||
// const SizedBox(width: 18),
|
||||
// Text(L10n.of(context)!.createGroup),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.joinWithClassCode,
|
||||
child: Row(
|
||||
|
|
@ -67,50 +56,44 @@ class ClientChooserButton extends StatelessWidget {
|
|||
// ],
|
||||
// ),
|
||||
// ),
|
||||
PopupMenuItem(
|
||||
enabled: matrix.client.rooms.any(
|
||||
(room) => !room.isSpace && !room.isArchived && !room.isAnalyticsRoom,
|
||||
),
|
||||
value: SettingsAction.myAnalytics,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.analytics_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Expanded(child: Text(L10n.of(context)!.myLearning)),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.newClass,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.school),
|
||||
const SizedBox(width: 18),
|
||||
Expanded(child: Text(L10n.of(context)!.createNewSpace)),
|
||||
],
|
||||
),
|
||||
),
|
||||
// PopupMenuItem(
|
||||
// value: SettingsAction.newSpace,
|
||||
// enabled: matrix.client.rooms.any(
|
||||
// (room) => !room.isSpace && !room.isArchived && !room.isAnalyticsRoom,
|
||||
// ),
|
||||
// value: SettingsAction.myAnalytics,
|
||||
// child: Row(
|
||||
// children: [
|
||||
// const Icon(Icons.workspaces_outlined),
|
||||
// const Icon(Icons.analytics_outlined),
|
||||
// const SizedBox(width: 18),
|
||||
// Text(L10n.of(context)!.createNewSpace),
|
||||
// Expanded(child: Text(L10n.of(context)!.myLearning)),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// if (controller.pangeaController.permissionsController.isUser18())
|
||||
// PopupMenuItem(
|
||||
// value: SettingsAction.findAConversationPartner,
|
||||
// child: Row(
|
||||
// children: [
|
||||
// const Icon(Icons.add_circle_outline),
|
||||
// const SizedBox(width: 18),
|
||||
// Expanded(child: Text(L10n.of(context)!.findALanguagePartner)),
|
||||
// ],
|
||||
// ),
|
||||
// PopupMenuItem(
|
||||
// value: SettingsAction.newGroup,
|
||||
// child: Row(
|
||||
// children: [
|
||||
// const Icon(Icons.group_add_outlined),
|
||||
// const SizedBox(width: 18),
|
||||
// Text(L10n.of(context)!.createGroup),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.newSpace,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.workspaces_outlined),
|
||||
const SizedBox(width: 18),
|
||||
// #Pangea
|
||||
Expanded(child: Text(L10n.of(context)!.createNewSpace)),
|
||||
// Text(L10n.of(context)!.createNewSpace),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// PopupMenuItem(
|
||||
// value: SettingsAction.setStatus,
|
||||
// child: Row(
|
||||
|
|
@ -132,19 +115,18 @@ class ClientChooserButton extends StatelessWidget {
|
|||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
PopupMenuItem(
|
||||
// Currently disabled because of:
|
||||
// https://github.com/matrix-org/matrix-react-sdk/pull/12286
|
||||
/*PopupMenuItem(
|
||||
value: SettingsAction.archive,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.archive_outlined),
|
||||
const SizedBox(width: 18),
|
||||
// #Pangea
|
||||
// Text(L10n.of(context)!.archive),
|
||||
Expanded(child: Text(L10n.of(context)!.viewArchive)),
|
||||
// Pangea#
|
||||
Text(L10n.of(context)!.archive),
|
||||
],
|
||||
),
|
||||
),
|
||||
),*/
|
||||
// #Pangea
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.learning,
|
||||
|
|
@ -259,7 +241,7 @@ class ClientChooserButton extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final matrix = Matrix.of(context);
|
||||
|
||||
int clientCount = 0;
|
||||
var clientCount = 0;
|
||||
matrix.accountBundles.forEach((key, value) => clientCount += value.length);
|
||||
return FutureBuilder<Profile>(
|
||||
future: matrix.client.fetchOwnProfile(),
|
||||
|
|
@ -392,21 +374,21 @@ class ClientChooserButton extends StatelessWidget {
|
|||
case SettingsAction.joinWithClassCode:
|
||||
SpaceCodeUtil.joinWithSpaceCodeDialog(
|
||||
context,
|
||||
controller.pangeaController,
|
||||
MatrixState.pangeaController,
|
||||
);
|
||||
break;
|
||||
case SettingsAction.findAConversationPartner:
|
||||
findConversationPartnerDialog(
|
||||
context,
|
||||
controller.pangeaController,
|
||||
MatrixState.pangeaController,
|
||||
);
|
||||
break;
|
||||
// case SettingsAction.spaceAnalytics:
|
||||
// context.go('/rooms/analytics');
|
||||
// break;
|
||||
case SettingsAction.myAnalytics:
|
||||
context.go('/rooms/mylearning');
|
||||
break;
|
||||
// case SettingsAction.myAnalytics:
|
||||
// context.go('/rooms/mylearning');
|
||||
// break;
|
||||
case SettingsAction.logout:
|
||||
pLogoutAction(context);
|
||||
break;
|
||||
|
|
@ -430,7 +412,7 @@ class ClientChooserButton extends StatelessWidget {
|
|||
);
|
||||
// beginning from end if negative
|
||||
if (index < 0) {
|
||||
int clientCount = 0;
|
||||
var clientCount = 0;
|
||||
matrix.accountBundles
|
||||
.forEach((key, value) => clientCount += value.length);
|
||||
_handleKeyboardShortcut(matrix, clientCount, context);
|
||||
|
|
@ -450,7 +432,7 @@ class ClientChooserButton extends StatelessWidget {
|
|||
}
|
||||
|
||||
int? _shortcutIndexOfClient(MatrixState matrix, Client client) {
|
||||
int index = 0;
|
||||
var index = 0;
|
||||
|
||||
final bundles = matrix.accountBundles.keys.toList()
|
||||
..sort(
|
||||
|
|
@ -497,7 +479,7 @@ enum SettingsAction {
|
|||
learning,
|
||||
joinWithClassCode,
|
||||
// spaceAnalytics,
|
||||
myAnalytics,
|
||||
// myAnalytics,
|
||||
findAConversationPartner,
|
||||
logout,
|
||||
newClass,
|
||||
|
|
|
|||
72
lib/pages/chat_list/dummy_chat_list_item.dart
Normal file
72
lib/pages/chat_list/dummy_chat_list_item.dart
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class DummyChatListItem extends StatelessWidget {
|
||||
final double opacity;
|
||||
final bool animate;
|
||||
|
||||
const DummyChatListItem({
|
||||
required this.opacity,
|
||||
required this.animate,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final titleColor = theme.textTheme.bodyLarge!.color!.withAlpha(100);
|
||||
final subtitleColor = theme.textTheme.bodyLarge!.color!.withAlpha(50);
|
||||
return Opacity(
|
||||
opacity: opacity,
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: titleColor,
|
||||
child: animate
|
||||
? CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
color: theme.textTheme.bodyLarge!.color,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: titleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 36),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
height: 12,
|
||||
margin: const EdgeInsets.only(right: 22),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
97
lib/pages/chat_list/nav_rail_item.dart
Normal file
97
lib/pages/chat_list/nav_rail_item.dart
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import '../../config/themes.dart';
|
||||
|
||||
class NaviRailItem extends StatefulWidget {
|
||||
final String toolTip;
|
||||
final bool isSelected;
|
||||
final void Function() onTap;
|
||||
final Widget icon;
|
||||
final Widget? selectedIcon;
|
||||
|
||||
const NaviRailItem({
|
||||
required this.toolTip,
|
||||
required this.isSelected,
|
||||
required this.onTap,
|
||||
required this.icon,
|
||||
this.selectedIcon,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<NaviRailItem> createState() => _NaviRailItemState();
|
||||
}
|
||||
|
||||
class _NaviRailItemState extends State<NaviRailItem> {
|
||||
bool _hovered = false;
|
||||
|
||||
void _onHover(bool hover) {
|
||||
if (hover == _hovered) return;
|
||||
setState(() {
|
||||
_hovered = hover;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final borderRadius = BorderRadius.circular(AppConfig.borderRadius);
|
||||
return SizedBox(
|
||||
height: 64,
|
||||
width: 64,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
left: 0,
|
||||
child: AnimatedContainer(
|
||||
width: widget.isSelected ? 4 : 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(90),
|
||||
bottomRight: Radius.circular(90),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: AnimatedScale(
|
||||
scale: _hovered ? 1.2 : 1.0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: Material(
|
||||
borderRadius: borderRadius,
|
||||
color: widget.isSelected
|
||||
? theme.colorScheme.primaryContainer
|
||||
: theme.colorScheme.surface,
|
||||
child: Tooltip(
|
||||
message: widget.toolTip,
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: widget.onTap,
|
||||
onHover: _onHover,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
child: widget.isSelected
|
||||
? widget.selectedIcon ?? widget.icon
|
||||
: widget.icon,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,20 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:badges/badges.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/widgets/hover_builder.dart';
|
||||
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
|
||||
import '../../config/themes.dart';
|
||||
|
||||
class NaviRailItem extends StatefulWidget {
|
||||
class NaviRailItem extends StatelessWidget {
|
||||
final String toolTip;
|
||||
final bool isSelected;
|
||||
final void Function() onTap;
|
||||
final Widget icon;
|
||||
final Widget? selectedIcon;
|
||||
final bool Function(Room)? unreadBadgeFilter;
|
||||
|
||||
const NaviRailItem({
|
||||
required this.toolTip,
|
||||
|
|
@ -16,80 +22,80 @@ class NaviRailItem extends StatefulWidget {
|
|||
required this.onTap,
|
||||
required this.icon,
|
||||
this.selectedIcon,
|
||||
this.unreadBadgeFilter,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<NaviRailItem> createState() => _NaviRailItemState();
|
||||
}
|
||||
|
||||
class _NaviRailItemState extends State<NaviRailItem> {
|
||||
bool _hovered = false;
|
||||
|
||||
void _onHover(bool hover) {
|
||||
if (hover == _hovered) return;
|
||||
setState(() {
|
||||
_hovered = hover;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final borderRadius = BorderRadius.circular(AppConfig.borderRadius);
|
||||
return SizedBox(
|
||||
height: 64,
|
||||
width: 64,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
left: 0,
|
||||
child: AnimatedContainer(
|
||||
width: widget.isSelected ? 4 : 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(90),
|
||||
bottomRight: Radius.circular(90),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: AnimatedScale(
|
||||
scale: _hovered ? 1.2 : 1.0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: Material(
|
||||
borderRadius: borderRadius,
|
||||
color: widget.isSelected
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: Theme.of(context).colorScheme.surface,
|
||||
child: Tooltip(
|
||||
message: widget.toolTip,
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: widget.onTap,
|
||||
onHover: _onHover,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
child: widget.isSelected
|
||||
? widget.selectedIcon ?? widget.icon
|
||||
: widget.icon,
|
||||
final icon = isSelected ? selectedIcon ?? this.icon : this.icon;
|
||||
final unreadBadgeFilter = this.unreadBadgeFilter;
|
||||
return HoverBuilder(
|
||||
builder: (context, hovered) {
|
||||
return SizedBox(
|
||||
height: FluffyThemes.navRailWidth,
|
||||
width: FluffyThemes.navRailWidth,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
left: 0,
|
||||
child: AnimatedContainer(
|
||||
width: isSelected ? 4 : 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(90),
|
||||
bottomRight: Radius.circular(90),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: AnimatedScale(
|
||||
scale: hovered ? 1.2 : 1.0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: Material(
|
||||
borderRadius: borderRadius,
|
||||
color: isSelected
|
||||
? theme.colorScheme.primaryContainer
|
||||
: theme.colorScheme.surface,
|
||||
child: Tooltip(
|
||||
message: toolTip,
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
child: unreadBadgeFilter == null
|
||||
? icon
|
||||
: UnreadRoomsBadge(
|
||||
filter: unreadBadgeFilter,
|
||||
badgePosition: BadgePosition.topEnd(
|
||||
top: -12,
|
||||
end: -8,
|
||||
),
|
||||
child: icon,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,55 +17,59 @@ class SearchTitle extends StatelessWidget {
|
|||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Material(
|
||||
shape: Border(
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Material(
|
||||
shape: Border(
|
||||
top: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
color: color ?? Theme.of(context).colorScheme.surface,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
splashColor: Theme.of(context).colorScheme.surface,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: IconTheme(
|
||||
data: Theme.of(context).iconTheme.copyWith(size: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
icon,
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
bottom: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
color: color ?? theme.colorScheme.surface,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
splashColor: theme.colorScheme.surface,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: IconTheme(
|
||||
data: theme.iconTheme.copyWith(size: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
icon,
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
if (trailing != null)
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: trailing!,
|
||||
),
|
||||
),
|
||||
if (trailing != null)
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: trailing!,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,125 +0,0 @@
|
|||
import 'dart:core';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../config/themes.dart';
|
||||
import 'chat_list.dart';
|
||||
|
||||
class StartChatFloatingActionButton extends StatelessWidget {
|
||||
final ActiveFilter activeFilter;
|
||||
final ValueNotifier<bool> scrolledToTop;
|
||||
final bool roomsIsEmpty;
|
||||
// #Pangea
|
||||
final ChatListController controller;
|
||||
// Pangea#
|
||||
final void Function() createNewSpace;
|
||||
|
||||
const StartChatFloatingActionButton({
|
||||
super.key,
|
||||
required this.activeFilter,
|
||||
required this.scrolledToTop,
|
||||
required this.roomsIsEmpty,
|
||||
required this.createNewSpace,
|
||||
// #Pangea
|
||||
required this.controller,
|
||||
// Pangea#
|
||||
});
|
||||
|
||||
void _onPressed(BuildContext context) async {
|
||||
//#Pangea
|
||||
if (controller.activeSpaceId != null) {
|
||||
context.go(
|
||||
'/rooms/newgroup${controller.activeSpaceId != null ? '?spaceId=${controller.activeSpaceId}' : ''}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
//Pangea#
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
// #Pangea
|
||||
// context.go('/rooms/newprivatechat');
|
||||
// break;
|
||||
// Pangea#
|
||||
case ActiveFilter.groups:
|
||||
// #Pangea
|
||||
// context.go('/rooms/newgroup');
|
||||
context.go(
|
||||
'/rooms/newgroup${controller.activeSpaceId != null ? '?spaceId=${controller.activeSpaceId}' : ''}',
|
||||
);
|
||||
// Pangea#
|
||||
break;
|
||||
case ActiveFilter.spaces:
|
||||
// #Pangea
|
||||
// createNewSpace();
|
||||
context.go('/rooms/newspace');
|
||||
// Pangea#
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
// #Pangea
|
||||
if (controller.activeSpaceId != null) {
|
||||
return Icons.group_add_outlined;
|
||||
}
|
||||
// Pangea#
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
// #Pangea
|
||||
// return Icons.add_outlined;
|
||||
// Pangea#
|
||||
case ActiveFilter.groups:
|
||||
return Icons.group_add_outlined;
|
||||
case ActiveFilter.spaces:
|
||||
return Icons.workspaces_outlined;
|
||||
}
|
||||
}
|
||||
|
||||
String getLabel(BuildContext context) {
|
||||
// #Pangea
|
||||
if (controller.activeSpaceId != null) {
|
||||
return L10n.of(context)!.newGroup;
|
||||
}
|
||||
// Pangea#
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
return roomsIsEmpty
|
||||
? L10n.of(context)!.startFirstChat
|
||||
: L10n.of(context)!.newChat;
|
||||
case ActiveFilter.groups:
|
||||
return L10n.of(context)!.newGroup;
|
||||
case ActiveFilter.spaces:
|
||||
return L10n.of(context)!.newSpace;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: scrolledToTop,
|
||||
builder: (context, scrolledToTop, _) => AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.none,
|
||||
child: scrolledToTop
|
||||
? FloatingActionButton.extended(
|
||||
onPressed: () => _onPressed(context),
|
||||
icon: Icon(icon),
|
||||
label: Text(
|
||||
getLabel(context),
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
)
|
||||
: FloatingActionButton(
|
||||
onPressed: () => _onPressed(context),
|
||||
child: Icon(icon),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -116,17 +116,17 @@ class PresenceAvatar extends StatelessWidget {
|
|||
return FutureBuilder<Profile>(
|
||||
future: client.getProfileFromUserId(presence.userid),
|
||||
builder: (context, snapshot) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final profile = snapshot.data;
|
||||
final displayName = profile?.displayName ??
|
||||
presence.userid.localpart ??
|
||||
presence.userid;
|
||||
final statusMsg = presence.statusMsg;
|
||||
|
||||
final statusMsgBubbleElevation =
|
||||
Theme.of(context).appBarTheme.scrolledUnderElevation ?? 4;
|
||||
final statusMsgBubbleShadowColor =
|
||||
Theme.of(context).colorScheme.onSurface;
|
||||
final statusMsgBubbleColor = Colors.white.withAlpha(245);
|
||||
const statusMsgBubbleElevation = 6.0;
|
||||
final statusMsgBubbleShadowColor = theme.colorScheme.surface;
|
||||
final statusMsgBubbleColor = Colors.white.withOpacity(0.9);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: SizedBox(
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:fluffychat/pages/chat/send_file_dialog.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
void onChatTap(Room room, BuildContext context) async {
|
||||
if (room.membership == Membership.invite) {
|
||||
final inviterId =
|
||||
room.getState(EventTypes.RoomMember, room.client.userID!)?.senderId;
|
||||
final inviteAction = await showModalActionSheet<InviteActions>(
|
||||
context: context,
|
||||
message: room.isDirectChat
|
||||
? L10n.of(context)!.invitePrivateChat
|
||||
: L10n.of(context)!.inviteGroupChat,
|
||||
title: room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||
actions: [
|
||||
SheetAction(
|
||||
key: InviteActions.accept,
|
||||
label: L10n.of(context)!.accept,
|
||||
icon: Icons.check_outlined,
|
||||
isDefaultAction: true,
|
||||
),
|
||||
SheetAction(
|
||||
key: InviteActions.decline,
|
||||
label: L10n.of(context)!.decline,
|
||||
icon: Icons.close_outlined,
|
||||
isDestructiveAction: true,
|
||||
),
|
||||
SheetAction(
|
||||
key: InviteActions.block,
|
||||
label: L10n.of(context)!.block,
|
||||
icon: Icons.block_outlined,
|
||||
isDestructiveAction: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (inviteAction == null) return;
|
||||
if (inviteAction == InviteActions.block) {
|
||||
context.go('/rooms/settings/security/ignorelist', extra: inviterId);
|
||||
return;
|
||||
}
|
||||
if (inviteAction == InviteActions.decline) {
|
||||
// #Pangea
|
||||
if (!room.isSpace &&
|
||||
room.membership == Membership.join &&
|
||||
room.isUnread) {
|
||||
await room.markUnread(false);
|
||||
}
|
||||
// Pangea#
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.leave,
|
||||
);
|
||||
return;
|
||||
}
|
||||
final joinResult = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
final waitForRoom = room.client.waitForRoomInSync(
|
||||
room.id,
|
||||
join: true,
|
||||
);
|
||||
if (await room.leaveIfFull()) {
|
||||
throw L10n.of(context)!.roomFull;
|
||||
}
|
||||
await room.join();
|
||||
await waitForRoom;
|
||||
},
|
||||
);
|
||||
if (joinResult.error != null) return;
|
||||
}
|
||||
|
||||
if (room.membership == Membership.ban) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context)!.youHaveBeenBannedFromThisChat),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (room.membership == Membership.leave) {
|
||||
context.go('/rooms/archive/${room.id}');
|
||||
return;
|
||||
}
|
||||
|
||||
// Share content into this room
|
||||
final shareContent = Matrix.of(context).shareContent;
|
||||
if (shareContent != null) {
|
||||
final shareFile = shareContent.tryGet<MatrixFile>('file');
|
||||
if (shareContent.tryGet<String>('msgtype') == 'chat.fluffy.shared_file' &&
|
||||
shareFile != null) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (c) => SendFileDialog(
|
||||
files: [shareFile],
|
||||
room: room,
|
||||
),
|
||||
);
|
||||
Matrix.of(context).shareContent = null;
|
||||
} else {
|
||||
final consent = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context)!.forward,
|
||||
message: L10n.of(context)!.forwardMessageTo(
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||
),
|
||||
okLabel: L10n.of(context)!.forward,
|
||||
cancelLabel: L10n.of(context)!.cancel,
|
||||
);
|
||||
if (consent == OkCancelResult.cancel) {
|
||||
Matrix.of(context).shareContent = null;
|
||||
return;
|
||||
}
|
||||
if (consent == OkCancelResult.ok) {
|
||||
room.sendEvent(shareContent);
|
||||
Matrix.of(context).shareContent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.go('/rooms/${room.id}');
|
||||
}
|
||||
|
||||
enum InviteActions {
|
||||
accept,
|
||||
decline,
|
||||
block,
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@ class ChatMembersView extends StatelessWidget {
|
|||
(room.summary.mInvitedMemberCount ?? 0);
|
||||
|
||||
final error = controller.error;
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
|
@ -90,6 +91,16 @@ class ChatMembersView extends StatelessWidget {
|
|||
controller: controller.filterController,
|
||||
onChanged: controller.setFilter,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
hintText: L10n.of(context)!.search,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const Center(child: BackButton()),
|
||||
|
|
@ -57,6 +59,22 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
// Pangea#
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.info_outlined),
|
||||
subtitle: Text(
|
||||
L10n.of(context)!.chatPermissionsDescription,
|
||||
),
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.chatPermissions,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
|
@ -73,13 +91,12 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
canEdit: room.canChangePowerLevel,
|
||||
),
|
||||
// #Pangea
|
||||
// Why would teacher need to stop students from seeing notifications?
|
||||
// Divider(color: Theme.of(context).dividerColor),
|
||||
// Divider(color: theme.dividerColor),
|
||||
// ListTile(
|
||||
// title: Text(
|
||||
// L10n.of(context)!.notifications,
|
||||
// style: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
// color: theme.colorScheme.primary,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// ),
|
||||
// ),
|
||||
|
|
@ -109,22 +126,17 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
// );
|
||||
// },
|
||||
// ),
|
||||
// Only show if there are actually items in this category
|
||||
if (eventsPowerLevels.isNotEmpty)
|
||||
// Pangea#
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
// #Pangea
|
||||
if (eventsPowerLevels.isNotEmpty)
|
||||
// Pangea#
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.configureChat,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
// Pangea#
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.configureChat,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final entry in eventsPowerLevels.entries)
|
||||
PermissionsListTile(
|
||||
permissionKey: entry.key,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
|
@ -29,7 +27,7 @@ class PermissionsListTile extends StatelessWidget {
|
|||
case 'events_default':
|
||||
return L10n.of(context)!.sendMessages;
|
||||
case 'state_default':
|
||||
return L10n.of(context)!.configureChat;
|
||||
return L10n.of(context)!.changeGeneralChatSettings;
|
||||
case 'ban':
|
||||
return L10n.of(context)!.banFromChat;
|
||||
case 'kick':
|
||||
|
|
@ -37,23 +35,25 @@ class PermissionsListTile extends StatelessWidget {
|
|||
case 'redact':
|
||||
return L10n.of(context)!.deleteMessage;
|
||||
case 'invite':
|
||||
return L10n.of(context)!.inviteContact;
|
||||
return L10n.of(context)!.inviteOtherUsers;
|
||||
}
|
||||
} else if (category == 'notifications') {
|
||||
switch (permissionKey) {
|
||||
case 'rooms':
|
||||
return L10n.of(context)!.notifications;
|
||||
return L10n.of(context)!.sendRoomNotifications;
|
||||
}
|
||||
} else if (category == 'events') {
|
||||
switch (permissionKey) {
|
||||
case EventTypes.RoomName:
|
||||
return L10n.of(context)!.changeTheNameOfTheGroup;
|
||||
case EventTypes.RoomTopic:
|
||||
return L10n.of(context)!.changeTheDescriptionOfTheGroup;
|
||||
case EventTypes.RoomPowerLevels:
|
||||
return L10n.of(context)!.chatPermissions;
|
||||
return L10n.of(context)!.changeTheChatPermissions;
|
||||
case EventTypes.HistoryVisibility:
|
||||
return L10n.of(context)!.visibilityOfTheChatHistory;
|
||||
return L10n.of(context)!.changeTheVisibilityOfChatHistory;
|
||||
case EventTypes.RoomCanonicalAlias:
|
||||
return L10n.of(context)!.setInvitationLink;
|
||||
return L10n.of(context)!.changeTheCanonicalRoomAlias;
|
||||
case EventTypes.RoomAvatar:
|
||||
return L10n.of(context)!.editRoomAvatar;
|
||||
case EventTypes.RoomTombstone:
|
||||
|
|
@ -69,41 +69,48 @@ class PermissionsListTile extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final color = permission >= 100
|
||||
? Colors.orangeAccent
|
||||
: permission >= 50
|
||||
? Colors.blueAccent
|
||||
: Colors.greenAccent;
|
||||
return ListTile(
|
||||
title: Text(getLocalizedPowerLevelString(context)),
|
||||
subtitle: Text(
|
||||
// #Pangea
|
||||
// L10n.of(context)!.minimumPowerLevel(permission.toString()),
|
||||
L10n.of(context)!.minimumPowerLevel(
|
||||
Matrix.of(context).client.powerLevelName(
|
||||
permission,
|
||||
L10n.of(context)!,
|
||||
) ??
|
||||
permission.toString(),
|
||||
),
|
||||
// Pangea#
|
||||
title: Text(
|
||||
getLocalizedPowerLevelString(context),
|
||||
style: theme.textTheme.titleSmall,
|
||||
),
|
||||
trailing: Material(
|
||||
color: color.withAlpha(32),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
child: DropdownButton<int>(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
underline: const SizedBox.shrink(),
|
||||
onChanged: canEdit ? onChanged : null,
|
||||
value: {0, 50, 100}.contains(permission) ? permission : null,
|
||||
value: permission,
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: 0,
|
||||
child: Text(L10n.of(context)!.user),
|
||||
value: permission < 50 ? permission : 0,
|
||||
child: Text(
|
||||
L10n.of(context)!.userLevel(permission < 50 ? permission : 0),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 50,
|
||||
child: Text(L10n.of(context)!.moderator),
|
||||
value: permission < 100 && permission >= 50 ? permission : 50,
|
||||
child: Text(
|
||||
L10n.of(context)!.moderatorLevel(
|
||||
permission < 100 && permission >= 50 ? permission : 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 100,
|
||||
child: Text(L10n.of(context)!.admin),
|
||||
value: permission >= 100 ? permission : 100,
|
||||
child: Text(
|
||||
L10n.of(context)!
|
||||
.adminLevel(permission >= 100 ? permission : 100),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: null,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
return StreamBuilder(
|
||||
stream: searchStream,
|
||||
builder: (context, snapshot) {
|
||||
final theme = Theme.of(context);
|
||||
final events = snapshot.data?.$1;
|
||||
if (searchStream == null || events == null) {
|
||||
return Column(
|
||||
|
|
@ -82,10 +83,8 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.secondaryContainer,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
backgroundColor: theme.colorScheme.secondaryContainer,
|
||||
foregroundColor: theme.colorScheme.onSecondaryContainer,
|
||||
),
|
||||
onPressed: () => startSearch(
|
||||
prevBatch: nextBatch,
|
||||
|
|
@ -127,21 +126,21 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
Expanded(
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
event.originServerTs.localizedTime(context),
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
style: theme.textTheme.labelSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -151,7 +150,7 @@ class ChatSearchFilesTab extends StatelessWidget {
|
|||
Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.file_present_outlined),
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue