rebasing fdroid branch

This commit is contained in:
Roland Osborne 2024-05-06 19:06:23 -07:00
parent 20d05ca258
commit 91af7cd011
52 changed files with 3137 additions and 3703 deletions

View File

@ -34,13 +34,10 @@ Databag is designed for efficiency, consuming minimal hosting resources. Notable
- Lightweight (server can run on a raspberry pi zero v1.3)
- Low latency (use of websockets for push events to avoid polling)
- Unlimited accounts per node (host for your whole family)
- Mobile alerts for new contacts, messages, and calls (supports UnifiedPush, FCM, APN)
- Mobile alerts (push notifications on new contacts, messages, and calls)
<br>
<p align="center">
<a href="https://f-droid.org/en/packages/com.databag/">
<img src="/doc/fdroid.png" width="18%">
</a>
<a href="https://apps.apple.com/us/app/databag/id6443741428">
<img src="/doc/astore.png" width="18%">
</a>
@ -49,7 +46,7 @@ Databag is designed for efficiency, consuming minimal hosting resources. Notable
</a>
</p>
The app is available on fdroid as well as the google and apple stores. You can test out the project [here](https://databag.coredb.org/#/create), but don't post anything important as this server is regularly wiped. Feedback on the UI/UX, bugs or features is greatly appreciated.
The app is available in the google and apple stores. You can also test out the project [here](https://databag.coredb.org/#/create), but don't post anything important as this server is regularly wiped. Feedback on the UI/UX, bugs or features is greatly appreciated.
## Installation
@ -63,7 +60,7 @@ From the net/container sub directory:
### Example with Portainer and Nginx Proxy Manager
From Portainer:
- In the volume view, click add volume:
- In the volume view, click add volumen:
- Enter a name, then click 'Create the volume'
- In the container view, click add container:
- In the 'Image' field enter 'balzack/databag:latest'
@ -143,7 +140,7 @@ Integrate Databag in an OpenWrt firmware [here](/doc/openwrt.md).
## Audio and Video Calls
Databag provides audio and video calling and relies on a STUN/TURN relay server for NAT traversal. Testing was done with both [coturn](https://github.com/coturn/coturn) and [pion](https://github.com/pion/turn) and should work with any implementation. Instructions for installing a coturn server are provided [here](https://gabrieltanner.org/blog/turn-server/).
Databag provides audio and video calling and relies on a STUN/TURN relay server for NAT traversal. Testing was done with both [cuturn](https://github.com/coturn/coturn) and [pion](https://github.com/pion/turn) and should work with any implementation. Instructions for installing a coturn server are provided [here](https://gabrieltanner.org/blog/turn-server/).
If you want to enable audio and video calls, you should setup your own relay server. For testing purposes you can however use the demo relay server configuration. In the admin configuration modal, set:
- Enable WebRTC Calls: -switch on-

View File

@ -20,7 +20,7 @@ import { Dashboard } from 'src/dashboard/Dashboard';
import { Session } from 'src/session/Session';
import { Prompt } from 'utils/Prompt';
import ReceiveSharingIntent from 'react-native-receive-sharing-intent';
import { Platform, PermissionsAndroid } from 'react-native';
import {PermissionsAndroid} from 'react-native';
import { initUnifiedPush } from 'react-native-unifiedpush-connector';
import { MenuProvider } from 'react-native-popup-menu';
@ -32,11 +32,8 @@ export default function App() {
const [sharing, setSharing] = useState();
useEffect(() => {
if (Platform.OS !== 'ios') {
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
initUnifiedPush();
}
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
ReceiveSharingIntent.getReceivedFiles(files => {
setSharing(files);

View File

@ -1,5 +1,4 @@
apply plugin: "com.android.application"
apply plugin: 'com.google.gms.google-services'
apply plugin: "com.facebook.react"
import com.android.build.OutputFile
@ -59,7 +58,7 @@ react {
* use App Bundles (https://developer.android.com/guide/app-bundle/)
* and want to have separate APKs to upload to the Play Store.
*/
def enableSeparateBuildPerCPUArchitecture = false
def enableSeparateBuildPerCPUArchitecture = true
/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
@ -94,18 +93,18 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
namespace "com.databag"
defaultConfig {
applicationId "com.databag"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1018
versionName "1.5"
versionCode 13
versionName "1.1"
}
splits {
@ -116,6 +115,7 @@ compileOptions {
include (*reactNativeArchitectures())
}
}
signingConfigs {
release {
storeFile file('balzack.keystore')
@ -124,6 +124,7 @@ compileOptions {
keyPassword 'balzack'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
@ -158,7 +159,6 @@ dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation 'androidx.core:core:1.8.0'
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")

View File

@ -1,39 +0,0 @@
{
"project_info": {
"project_number": "627079362503",
"project_id": "databag-b46e0",
"storage_bucket": "databag-b46e0.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:627079362503:android:6f3dfcb2c255787b4a8be2",
"android_client_info": {
"package_name": "com.databag"
}
},
"oauth_client": [
{
"client_id": "627079362503-8bk8o4hcv5rgdgrhik6nu9cjp3poisv7.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAb92cvtSnaoQzhbDizg0dFskOtZFp_58M"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "627079362503-8bk8o4hcv5rgdgrhik6nu9cjp3poisv7.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

View File

@ -17,9 +17,10 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission tools:node="remove" android:name="android.vending.CHECK_LICENSE" />
<uses-permission tools:node="remove" android:name="com.android.vending.CHECK_LICENSE" />
<application
android:usesCleartextTraffic="true"
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
@ -78,7 +79,7 @@
<receiver
android:enabled="true"
android:name="com.unifiedpushconnector.CustomReceiver"
android:name=".CustomReceiver"
android:exported="true">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>

View File

@ -0,0 +1,103 @@
package com.databag;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import org.unifiedpush.android.connector.MessagingReceiver;
import android.util.Log;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManager;
import android.os.Build;
import android.net.Uri;
import android.media.RingtoneManager;
import androidx.core.app.NotificationCompat;
import java.nio.charset.StandardCharsets;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
public class CustomReceiver extends MessagingReceiver {
public CustomReceiver() {
super();
}
private boolean forgrounded () {
ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(appProcessInfo);
return (appProcessInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND || appProcessInfo.importance == RunningAppProcessInfo.IMPORTANCE_VISIBLE);
}
@Override
public void onNewEndpoint(@NotNull Context context, @NotNull String endpoint, @NotNull String instance) {
final ReactInstanceManager reactInstanceManager =
((ReactApplication) context.getApplicationContext())
.getReactNativeHost()
.getReactInstanceManager();
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
WritableMap params = Arguments.createMap();
params.putString("instance", instance);
params.putString("endpoint", endpoint);
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("unifiedPushURL", params);
// Called when a new endpoint be used for sending push messages
}
@Override
public void onRegistrationFailed(@NotNull Context context, @NotNull String instance) {
// called when the registration is not possible, eg. no network
}
@Override
public void onUnregistered(@NotNull Context context, @NotNull String instance) {
// called when this application is unregistered from receiving push messages
}
@Override
public void onMessage(@NotNull Context context, @NotNull byte[] message, @NotNull String instance) {
if (forgrounded()) {
return;
}
String strMessage = new String(message, StandardCharsets.UTF_8);
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0 /* Request code */, intent,
PendingIntent.FLAG_IMMUTABLE);
String channelId = "fcm_default_channel";
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context,
channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(strMessage).setAutoCancel(true).setSound(
defaultSoundUri).setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId, "Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(0, notificationBuilder.build());
}
}

View File

@ -35,6 +35,25 @@ public class MainActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
MainActivity activityContext = this;
this.getSharedPreferences("unifiedpush.connector", Context.MODE_PRIVATE).edit().putBoolean("unifiedpush.no_distrib_dialog", true).apply();
ReactInstanceManager mReactInstanceManager = getReactNativeHost().getReactInstanceManager();
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext validContext) {
UnifiedPush.registerAppWithDialog(
activityContext,
"default",
new RegistrationDialogContent(),
new ArrayList<String>(),
getApplicationContext().getPackageName()
);
}
});
}
/**

View File

@ -16,7 +16,6 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.google.gms:google-services:4.3.15'
classpath("com.android.tools.build:gradle:7.3.1")
classpath("com.facebook.react:react-native-gradle-plugin")
}
@ -32,3 +31,4 @@ allprojects {
}
}
}

View File

@ -7,10 +7,5 @@ import 'react-native-gesture-handler';
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import messaging from '@react-native-firebase/messaging';
messaging().registerDeviceForRemoteMessages().then(() => {});
messaging().setBackgroundMessageHandler(async remoteMessage => {});
AppRegistry.registerComponent(appName, () => App);

File diff suppressed because one or more lines are too long

View File

@ -9,64 +9,8 @@ PODS:
- React-Core (= 0.71.3)
- React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- Firebase/CoreOnly (10.7.0):
- FirebaseCore (= 10.7.0)
- Firebase/Messaging (10.7.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 10.7.0)
- FirebaseCore (10.7.0):
- FirebaseCoreInternal (~> 10.0)
- GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/Logger (~> 7.8)
- FirebaseCoreExtension (10.7.0):
- FirebaseCore (~> 10.0)
- FirebaseCoreInternal (10.22.0):
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- FirebaseInstallations (10.22.0):
- FirebaseCore (~> 10.0)
- GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/UserDefaults (~> 7.8)
- PromisesObjC (~> 2.1)
- FirebaseMessaging (10.7.0):
- FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0)
- GoogleDataTransport (~> 9.2)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/Reachability (~> 7.8)
- GoogleUtilities/UserDefaults (~> 7.8)
- nanopb (< 2.30910.0, >= 2.30908.0)
- fmt (6.2.1)
- glog (0.3.5)
- GoogleDataTransport (9.4.1):
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30911.0, >= 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/AppDelegateSwizzler (7.13.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Privacy
- GoogleUtilities/Environment (7.13.0):
- GoogleUtilities/Privacy
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.13.0):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
- GoogleUtilities/Network (7.13.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.13.0)":
- GoogleUtilities/Privacy
- GoogleUtilities/Privacy (7.13.0)
- GoogleUtilities/Reachability (7.13.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/UserDefaults (7.13.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- hermes-engine (0.71.3):
- hermes-engine/Pre-built (= 0.71.3)
- hermes-engine/Pre-built (0.71.3)
@ -81,15 +25,7 @@ PODS:
- libwebp/webp
- libwebp/mux (1.3.2):
- libwebp/demux
- libwebp/sharpyuv (1.3.2)
- libwebp/webp (1.3.2):
- libwebp/sharpyuv
- nanopb (2.30909.1):
- nanopb/decode (= 2.30909.1)
- nanopb/encode (= 2.30909.1)
- nanopb/decode (2.30909.1)
- nanopb/encode (2.30909.1)
- PromisesObjC (2.4.0)
- libwebp/webp (1.2.4)
- RCT-Folly (2021.07.22.00):
- boost
- DoubleConversion
@ -341,21 +277,19 @@ PODS:
- React-jsinspector (0.71.3)
- React-logger (0.71.3):
- glog
- react-native-blur (4.3.2):
- React-Core
- react-native-create-thumbnail (1.6.4):
- React-Core
- react-native-document-picker (8.2.1):
- react-native-document-picker (8.1.3):
- React-Core
- react-native-image-resizer (3.0.5):
- React-Core
- react-native-keep-awake (1.2.0):
- react-native-keep-awake (1.1.0):
- React-Core
- react-native-receive-sharing-intent (2.0.0):
- React-Core
- react-native-rsa-native (2.0.5):
- React
- react-native-safe-area-context (4.6.3):
- react-native-safe-area-context (4.5.0):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
@ -363,8 +297,6 @@ PODS:
- ReactCommon/turbomodule/core
- react-native-sqlite-storage (6.0.1):
- React-Core
- react-native-unifiedpush-connector (0.1.8):
- React-Core
- react-native-video (5.2.1):
- React-Core
- react-native-video/Video (= 5.2.1)
@ -461,25 +393,17 @@ PODS:
- React-Core
- rn-fetch-blob (0.12.0):
- React-Core
- RNCClipboard (1.11.2):
- RNCClipboard (1.11.1):
- React-Core
- RNDeviceInfo (10.6.1):
- RNDeviceInfo (10.4.0):
- React-Core
- RNFastImage (8.6.3):
- React-Core
- SDWebImage (~> 5.11.1)
- SDWebImageWebPCoder (~> 0.8.4)
- RNFBApp (17.5.0):
- Firebase/CoreOnly (= 10.7.0)
- React-Core
- RNFBMessaging (17.5.0):
- Firebase/Messaging (= 10.7.0)
- FirebaseCoreExtension (= 10.7.0)
- React-Core
- RNFBApp
- RNFS (2.20.0):
- React-Core
- RNGestureHandler (2.12.0):
- RNGestureHandler (2.9.0):
- React-Core
- RNImageCropPicker (0.39.0):
- React-Core
@ -490,7 +414,7 @@ PODS:
- React-Core
- React-RCTImage
- TOCropViewController
- RNReanimated (2.17.0):
- RNReanimated (2.14.4):
- DoubleConversion
- FBLazyVector
- FBReactNativeSpec
@ -517,7 +441,7 @@ PODS:
- React-RCTText
- ReactCommon/turbomodule/core
- Yoga
- RNScreens (3.22.0):
- RNScreens (3.20.0):
- React-Core
- React-RCTImage
- RNShare (8.2.2):
@ -556,7 +480,6 @@ DEPENDENCIES:
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
- react-native-create-thumbnail (from `../node_modules/react-native-create-thumbnail`)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- "react-native-image-resizer (from `../node_modules/@bam.tech/react-native-image-resizer`)"
@ -565,7 +488,6 @@ DEPENDENCIES:
- react-native-rsa-native (from `../node_modules/react-native-rsa-native`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-sqlite-storage (from `../node_modules/react-native-sqlite-storage`)
- react-native-unifiedpush-connector (from `../node_modules/react-native-unifiedpush-connector`)
- react-native-video (from `../node_modules/react-native-video`)
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@ -586,8 +508,6 @@ DEPENDENCIES:
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- RNFastImage (from `../node_modules/react-native-fast-image`)
- "RNFBApp (from `../node_modules/@react-native-firebase/app`)"
- "RNFBMessaging (from `../node_modules/@react-native-firebase/messaging`)"
- RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
@ -599,20 +519,10 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- Firebase
- FirebaseCore
- FirebaseCoreExtension
- FirebaseCoreInternal
- FirebaseInstallations
- FirebaseMessaging
- fmt
- GoogleDataTransport
- GoogleUtilities
- JitsiWebRTC
- libevent
- libwebp
- nanopb
- PromisesObjC
- SDWebImage
- SDWebImageWebPCoder
- TOCropViewController
@ -658,8 +568,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
React-logger:
:path: "../node_modules/react-native/ReactCommon/logger"
react-native-blur:
:path: "../node_modules/@react-native-community/blur"
react-native-create-thumbnail:
:path: "../node_modules/react-native-create-thumbnail"
react-native-document-picker:
@ -676,8 +584,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-safe-area-context"
react-native-sqlite-storage:
:path: "../node_modules/react-native-sqlite-storage"
react-native-unifiedpush-connector:
:path: "../node_modules/react-native-unifiedpush-connector"
react-native-video:
:path: "../node_modules/react-native-video"
react-native-webrtc:
@ -718,10 +624,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-device-info"
RNFastImage:
:path: "../node_modules/react-native-fast-image"
RNFBApp:
:path: "../node_modules/@react-native-firebase/app"
RNFBMessaging:
:path: "../node_modules/@react-native-firebase/messaging"
RNFS:
:path: "../node_modules/react-native-fs"
RNGestureHandler:
@ -744,22 +646,12 @@ SPEC CHECKSUMS:
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: 60195509584153283780abdac5569feffb8f08cc
FBReactNativeSpec: 9c191fb58d06dc05ab5559a5505fc32139e9e4a2
Firebase: 0219acf760880eeec8ce479895bd7767466d9f81
FirebaseCore: e317665b9d744727a97e623edbbed009320afdd7
FirebaseCoreExtension: f17247ba8c61e4d3c8d136b5e2de3cb4ac6a85b6
FirebaseCoreInternal: bca337352024b18424a61e478460547d46c4c753
FirebaseInstallations: 763814908793c0da14c18b3dcffdec71e29ed55e
FirebaseMessaging: ac9062bcc35ed56e15a0241d8fd317022499baf8
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleUtilities: d053d902a8edaa9904e1bd00c37535385b8ed152
hermes-engine: 38bfe887e456b33b697187570a08de33969f5db7
JitsiWebRTC: 3a41671ef65a51d7204323814b055a2690b921c7
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCTRequired: bec48f07daf7bcdc2655a0cde84e07d24d2a9e2a
RCTTypeSafety: 171394eebacf71e1cfad79dbfae7ee8fc16ca80a
@ -774,16 +666,14 @@ SPEC CHECKSUMS:
React-jsiexecutor: 515b703d23ffadeac7687bc2d12fb08b90f0aaa1
React-jsinspector: 9f7c9137605e72ca0343db4cea88006cb94856dd
React-logger: 957e5dc96d9dbffc6e0f15e0ee4d2b42829ff207
react-native-blur: cfdad7b3c01d725ab62a8a729f42ea463998afa2
react-native-create-thumbnail: e022bcdcba8a0b4529a50d3fa1a832ec921be39d
react-native-document-picker: 69ca2094d8780cfc1e7e613894d15290fdc54bba
react-native-document-picker: 958e2bc82e128be69055be261aeac8d872c8d34c
react-native-image-resizer: 00ceb0e05586c7aadf061eea676957a6c2ec60fa
react-native-keep-awake: caee3ff89eaa21dfe29010f0d143566874a04441
react-native-keep-awake: acbee258db16483744910f0da3ace39eb9ab47fd
react-native-receive-sharing-intent: 62ab28c50e6ae56d32b9e841d7452091312a0bc7
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
react-native-safe-area-context: 36cc67648134e89465663b8172336a19eeda493d
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
react-native-unifiedpush-connector: 4d60170e5eb46329f15e5da6c1171d3683480bf4
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
react-native-webrtc: a0a8a1730b6cc5a5bda8a6e2166a74c9b78029e2
React-perflogger: af8a3d31546077f42d729b949925cc4549f14def
@ -801,16 +691,14 @@ SPEC CHECKSUMS:
ReactCommon: 5f9a24e64c1c3e2b719014f07cb2acf628983000
ReactNativeIncallManager: 0d2cf9f4d50359728a30c08549762fe67a2efb81
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNCClipboard: 3f0451a8100393908bea5c5c5b16f96d45f30bfc
RNDeviceInfo: ab292735ad4fccc5f2aec0c773f7a7f03c7073ae
RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd
RNDeviceInfo: 749f2e049dcd79e2e44f134f66b73a06951b5066
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
RNFBApp: d59efa0872fff4e27de03cca3c528c203a436ae5
RNFBMessaging: 216693dd5ba4f18ba65fb39fc73a44a23c26190f
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: dec4645026e7401a0899f2846d864403478ff6a5
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
RNImageCropPicker: 14fe1c29298fb4018f3186f455c475ab107da332
RNReanimated: f186e85d9f28c9383d05ca39e11dd194f59093ec
RNScreens: 68fd1060f57dd1023880bf4c05d74784b5392789
RNReanimated: cc5e3aa479cb9170bcccf8204291a6950a3be128
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
RNShare: d82e10f6b7677f4b0048c23709bd04098d5aee6c
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d

View File

@ -13,9 +13,6 @@
"@bam.tech/react-native-image-resizer": "^3.0.5",
"@braintree/sanitize-url": "^6.0.2",
"@react-native-clipboard/clipboard": "^1.11.1",
"@react-native-community/blur": "^4.3.2",
"@react-native-firebase/app": "^17.2.0",
"@react-native-firebase/messaging": "^17.2.0",
"@react-navigation/bottom-tabs": "^6.5.5",
"@react-navigation/drawer": "^6.6.0",
"@react-navigation/native": "^6.1.4",
@ -46,7 +43,6 @@
"react-native-screens": "^3.20.0",
"react-native-share": "^8.2.2",
"react-native-sqlite-storage": "^6.0.1",
"react-native-unifiedpush-connector": "^0.1.5",
"react-native-vector-icons": "^9.2.0",
"react-native-video": "^5.2.1",
"react-native-webrtc": "^118.0.2",

View File

@ -57,24 +57,7 @@ export function Admin() {
</TouchableOpacity>
</View>
{ Platform.OS !== 'ios' && (
<View style={styles.tos}>
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
<Text style={styles.viewtermstext}>{ state.strings.terms }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.agreeterms} onPress={() => actions.agree(!state.agree)}>
{ state.agree && (
<MatIcons name={'checkbox-outline'} size={20} color={Colors.primary} />
)}
{ !state.agree && (
<MatIcons name={'checkbox-blank-outline'} size={20} color={Colors.primary} />
)}
<Text style={styles.agreetermstext}>{ state.strings.agree }</Text>
</TouchableOpacity>
</View>
)}
{ state.enabled && (Platform.OS === 'ios' || state.agree) && (
{ state.enabled && (
<TouchableOpacity style={styles.reset} onPress={admin}>
{ state.busy && (
<ActivityIndicator size="small" color="#ffffff" />
@ -84,7 +67,7 @@ export function Admin() {
)}
</TouchableOpacity>
)}
{ (!state.enabled || (Platform.OS !== 'ios' && !state.agree)) && (
{ !state.enabled && (
<View style={styles.noreset}>
<Text style={styles.noresettext}>{ state.strings.access }</Text>
</View>

View File

@ -141,26 +141,8 @@ export function Create() {
</TouchableOpacity>
</View>
)}
{ Platform.OS !== 'ios' && (
<View style={styles.tos}>
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
<Text style={styles.viewtermstext}>{ state.strings.terms }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.agreeterms} onPress={() => actions.agree(!state.agree)}>
{ state.agree && (
<MatIcons name={'checkbox-outline'} size={20} color={Colors.primary} />
)}
{ !state.agree && (
<MatIcons name={'checkbox-blank-outline'} size={20} color={Colors.primary} />
)}
<Text style={styles.agreetermstext}>{ state.strings.agree }</Text>
</TouchableOpacity>
</View>
)}
<View style={styles.buttons}>
{ state.enabled && (Platform.OS === 'ios' || state.agree) && (
{ state.enabled && (
<TouchableOpacity style={styles.create} onPress={create}>
{ state.busy && (
<ActivityIndicator size="small" color="#ffffff" />
@ -170,7 +152,7 @@ export function Create() {
)}
</TouchableOpacity>
)}
{ (!state.enabled || (Platform.OS !== 'ios' && !state.agree)) && (
{ !state.enabled && (
<View style={styles.nocreate}>
<Text style={styles.nocreatetext}>{ state.strings.create }</Text>
</View>

View File

@ -65,24 +65,7 @@ export function Login() {
</View>
)}
{ Platform.OS !== 'ios' && (
<View style={styles.tos}>
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
<Text style={styles.viewtermstext}>{ state.strings.terms }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.agreeterms} onPress={() => actions.agree(!state.agree)}>
{ state.agree && (
<MatIcons name={'checkbox-outline'} size={20} color={Colors.primary} />
)}
{ !state.agree && (
<MatIcons name={'checkbox-blank-outline'} size={20} color={Colors.primary} />
)}
<Text style={styles.agreetermstext}>{ state.strings.agree }</Text>
</TouchableOpacity>
</View>
)}
{ state.enabled && (Platform.OS === 'ios' || state.agree) && (
{ state.enabled && (
<TouchableOpacity style={styles.login} onPress={login}>
{ state.busy && (
<ActivityIndicator size="small" color="#ffffff" />
@ -92,7 +75,7 @@ export function Login() {
)}
</TouchableOpacity>
)}
{ (!state.enabled || (Platform.OS !== 'ios' && !state.agree)) && (
{ !state.enabled && (
<View style={styles.nologin}>
<Text style={styles.nologintext}>{ state.strings.login }</Text>
</View>

View File

@ -48,24 +48,7 @@ export function Reset() {
<View style={styles.space} />
</View>
{ Platform.OS !== 'ios' && (
<View style={styles.tos}>
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
<Text style={styles.viewtermstext}>{ state.strings.terms }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.agreeterms} onPress={() => actions.agree(!state.agree)}>
{ state.agree && (
<MatIcons name={'checkbox-outline'} size={20} color={Colors.primary} />
)}
{ !state.agree && (
<MatIcons name={'checkbox-blank-outline'} size={20} color={Colors.primary} />
)}
<Text style={styles.agreetermstext}>{state.strings.agree}</Text>
</TouchableOpacity>
</View>
)}
{ state.enabled && (Platform.OS === 'ios' || state.agree) && (
{ state.enabled && (
<TouchableOpacity style={styles.reset} onPress={reset}>
{ state.busy && (
<ActivityIndicator size="small" color="#ffffff" />
@ -75,7 +58,7 @@ export function Reset() {
)}
</TouchableOpacity>
)}
{ (!state.enabled || (Platform.OS !== 'ios' && !state.agree)) && (
{ !state.enabled && (
<View style={styles.noreset}>
<Text style={styles.noresettext}>{ state.strings.access }</Text>
</View>

View File

@ -1,10 +1,9 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function setAccountAccess(server, token, appName, appVersion, platform, deviceToken, pushType, notifications) {
export async function setAccountAccess(server, token, appName, appVersion, platform, deviceToken, notifications) {
const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(server);
const protocol = insecure ? 'http' : 'https';
let access = await fetchWithTimeout(`${protocol}://${server}/account/access?token=${token}&appName=${appName}&appVersion=${appVersion}&platform=${platform}&deviceToken=${deviceToken}&pushType=${pushType}`, { method: 'PUT', body: JSON.stringify(notifications) })
let access = await fetchWithTimeout(`${protocol}://${server}/account/access?token=${token}&appName=${appName}&appVersion=${appVersion}&platform=${platform}&deviceToken=${deviceToken}&pushType=up`, { method: 'PUT', body: JSON.stringify(notifications) })
checkResponse(access)
return await access.json()
}

View File

@ -1,13 +1,12 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
import base64 from 'react-native-base64'
export async function setLogin(username, server, password, appName, appVersion, platform, deviceToken, pushType, notifications) {
export async function setLogin(username, server, password, appName, appVersion, platform, deviceToken, notifications) {
const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(server);
const protocol = insecure ? 'http' : 'https';
let headers = new Headers()
headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));
let login = await fetchWithTimeout(`${protocol}://${server}/account/apps?appName=${appName}&appVersion=${appVersion}&platform=${platform}&deviceToken=${deviceToken}&pushType=${pushType}`, { method: 'POST', body: JSON.stringify(notifications), headers: headers })
let login = await fetchWithTimeout(`${protocol}://${server}/account/apps?appName=${appName}&appVersion=${appVersion}&platform=${platform}&deviceToken=${deviceToken}&pushType=up`, { method: 'POST', body: JSON.stringify(notifications), headers: headers })
checkResponse(login)
return await login.json()
}

View File

@ -89,7 +89,7 @@ const DarkColors = {
contentBase: '#000000',
modalBase: '#1b1b1b',
modalBorder: '#555555',
modalOverlay: 'rgba(88,88,88,0.8)',
modalOverlay: '#333333',
headerBar: '#555555',
primaryButton: '#448866',
primaryButtonText: '#ffffff',

View File

@ -13,7 +13,6 @@ import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
import { RingContext } from 'context/RingContext';
import { getVersion, getApplicationName, getDeviceId } from 'react-native-device-info'
import messaging from '@react-native-firebase/messaging';
import { DeviceEventEmitter } from 'react-native';
export function useAppContext() {
@ -36,7 +35,6 @@ export function useAppContext() {
const ws = useRef(null);
const deviceToken = useRef(null);
const pushType = useRef(null);
const access = useRef(null);
const init = useRef(false);
@ -44,32 +42,15 @@ export function useAppContext() {
setState((s) => ({ ...s, ...value }))
}
const setDeviceToken = async () => {
if (!deviceToken.current) {
try {
const token = await messaging().getToken();
if (!token) {
throw new Error('null push token');
}
deviceToken.current = token;
pushType.current = "fcm";
}
catch(err) {
console.log(err);
}
}
}
useEffect(() => {
// select the unified token if available
DeviceEventEmitter.addListener('unifiedPushURL', (e) => {
deviceToken.current = e.endpoint;
pushType.current = "up";
});
(async () => {
await setDeviceToken();
access.current = await store.actions.init();
if (access.current) {
await setSession();
@ -118,62 +99,46 @@ export function useAppContext() {
if (!init.current || access.current) {
throw new Error('invalid session state');
}
await setDeviceToken();
updateState({ loggedOut: false });
await addAccount(server, username, password, token);
const session = await setLogin(username, server, password, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, pushType.current, notifications)
const session = await setLogin(username, server, password, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, notifications)
access.current = { loginTimestamp: session.created, server, token: session.appToken, guid: session.guid };
await store.actions.setSession(access.current);
await setSession();
if (session.pushSupported) {
messaging().requestPermission().then(status => {})
}
},
access: async (server, token) => {
if (!init.current || access.current) {
throw new Error('invalid session state');
}
await setDeviceToken();
updateState({ loggedOut: false });
const session = await setAccountAccess(server, token, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, pushType.current, notifications);
const session = await setAccountAccess(server, token, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, notifications);
access.current = { loginTimestamp: session.created, server, token: session.appToken, guid: session.guid };
await store.actions.setSession(access.current);
await setSession();
if (session.pushSupported) {
messaging().requestPermission().then(status => {})
}
},
login: async (username, password) => {
if (!init.current || access.current) {
throw new Error('invalid session state');
}
await setDeviceToken();
updateState({ loggedOut: false });
const acc = username.includes('/') ? username.split('/') : username.split('@');
const session = await setLogin(acc[0], acc[1], password, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, pushType.current, notifications)
const session = await setLogin(acc[0], acc[1], password, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, notifications)
access.current = { loginTimestamp: session.created, server: acc[1], token: session.appToken, guid: session.guid };
await store.actions.setSession(access.current);
await setSession();
if (session.pushSupported) {
messaging().requestPermission().then(status => {})
}
},
logout: async () => {
if (!access.current) {
throw new Error('invalid session state');
}
updateState({ loggingOut: true });
try {
if (pushType.current == "fcm") {
await messaging().deleteToken();
deviceToken.current = await messaging().getToken();
}
const { server, token } = access.current || {};
await clearLogin(server, token);
}
catch (err) {
console.log(err);
}
updateState({ loggingOut: true });
await clearSession();
access.current = null;
await store.actions.clearSession();

View File

@ -7,7 +7,6 @@ import { styles } from './Dashboard.styled';
import { useLocation } from 'react-router-dom';
import { useDashboard } from './useDashboard.hook';
import { Logo } from 'utils/Logo';
import { BlurView } from "@react-native-community/blur";
import { InputField } from 'utils/InputField';
export function Dashboard(props) {
@ -133,149 +132,146 @@ export function Dashboard(props) {
supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideEditConfig}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={'dark'} blurAmount={2} reducedTransparencyFallbackColor='black' />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<KeyboardAvoidingView style={styles.modalOverlay} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalHeader}>
<Text style={styles.modalHeaderText}>{ state.strings.settings }</Text>
</View>
<ScrollView style={styles.modalBody}>
<InputField style={styles.field}
label={state.strings.federatedHost}
value={state.domain}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setDomain}
/>
<InputField style={styles.field}
label={state.strings.storageLimit}
value={state.storage}
autoCapitalize={'none'}
spellCheck={false}
keyboardType={'numeric'}
onChangeText={actions.setStorage}
/>
<Text style={styles.modalLabel}>{ state.strings.keyType }</Text>
<View style={styles.keyType}>
<TouchableOpacity style={styles.optionLeft} activeOpacity={1}
onPress={() => actions.setKeyType('RSA2048')}>
{ state.keyType === 'RSA2048' && (
<View style={styles.selected} />
)}
{ state.keyType === 'RSA4096' && (
<View style={styles.radio} />
)}
<Text style={styles.option}>RSA 2048</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.optionRight} activeOpacity={1}
onPress={() => actions.setKeyType('RSA4096')}>
{ state.keyType === 'RSA2048' && (
<View style={styles.radio} />
)}
{ state.keyType === 'RSA4096' && (
<View style={styles.selected} />
)}
<Text style={styles.option}>RSA 4096</Text>
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setPushSupported(!state.pushSupported)}>
<Text style={styles.modalLabel}>{ state.strings.enableNotifications }</Text>
<Switch style={styles.switch} value={state.pushSupported}
onValueChange={actions.setPushSupported} trackColor={styles.track}/>
</TouchableOpacity>
{ state.transformSupported && (
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setAllowUnsealed(!state.allowUnsealed)}>
<Text style={styles.modalLabel}>{ state.strings.allowUnsealed }</Text>
<Switch style={styles.switch} value={state.allowUnsealed}
onValueChange={actions.setAllowUnsealed} trackColor={styles.track}/>
</TouchableOpacity>
)}
<View style={styles.label}></View>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableImage(!state.enableImage)}>
<Text style={styles.modalLabel}>{ state.strings.enableImage }</Text>
<Switch style={styles.switch} value={state.enableImage}
onValueChange={actions.setEnableImage} trackColor={styles.track}/>
</TouchableOpacity>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableAudio(!state.enableAudio)}>
<Text style={styles.modalLabel}>{ state.strings.enableAudio }</Text>
<Switch style={styles.switch} value={state.enableAudio}
onValueChange={actions.setEnableAudio} trackColor={styles.track}/>
</TouchableOpacity>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableVideo(!state.enableVideo)}>
<Text style={styles.modalLabel}>{ state.strings.enableVideo }</Text>
<Switch style={styles.switch} value={state.enableVideo}
onValueChange={actions.setEnableVideo} trackColor={styles.track}/>
</TouchableOpacity>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableBinary(!state.enableBinary)}>
<Text style={styles.modalLabel}>{ state.strings.enableBinary }</Text>
<Switch style={styles.switch} value={state.enableBinary}
onValueChange={actions.setEnableBinary} trackColor={styles.track}/>
</TouchableOpacity>
<View style={styles.label}></View>
<TouchableOpacity style={styles.ice} activeOpacity={1}
onPress={() => actions.setEnableIce(!state.enableIce)}>
<Text style={styles.modalLabel}>{ state.strings.enableCalls }</Text>
<Switch style={styles.switch} value={state.enableIce}
onValueChange={actions.setEnableIce} trackColor={styles.track}/>
</TouchableOpacity>
<InputField style={styles.field}
label={state.strings.relayUrl}
value={state.iceUrl}
autoCapitalize={'none'}
spellCheck={false}
disabled={!state.enableIce}
onChangeText={actions.setIceUrl}
/>
<InputField style={styles.field}
label={state.strings.relayUsername}
value={state.iceUsername}
autoCapitalize={'none'}
spellCheck={false}
disabled={!state.enableIce}
onChangeText={actions.setIceUsername}
/>
<InputField style={styles.field}
label={state.strings.relayPassword}
value={state.icePassword}
autoCapitalize={'none'}
spellCheck={false}
disabled={!state.enableIce}
onChangeText={actions.setIcePassword}
/>
<View style={styles.pad} />
</ScrollView>
<View style={styles.modalControls}>
<TouchableOpacity style={styles.cancel} onPress={actions.hideEditConfig}>
<Text style={styles.cancelText}>{ state.strings.cancel }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.save} onPress={saveConfig}>
<Text style={styles.saveText}>{ state.strings.save }</Text>
</TouchableOpacity>
</View>
<View style={styles.modalHeader}>
<Text style={styles.modalHeaderText}>{ state.strings.settings }</Text>
</View>
</KeyboardAvoidingView>
</View>
<ScrollView style={styles.modalBody}>
<InputField style={styles.field}
label={state.strings.federatedHost}
value={state.domain}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setDomain}
/>
<InputField style={styles.field}
label={state.strings.storageLimit}
value={state.storage}
autoCapitalize={'none'}
spellCheck={false}
keyboardType={'numeric'}
onChangeText={actions.setStorage}
/>
<Text style={styles.modalLabel}>{ state.strings.keyType }</Text>
<View style={styles.keyType}>
<TouchableOpacity style={styles.optionLeft} activeOpacity={1}
onPress={() => actions.setKeyType('RSA2048')}>
{ state.keyType === 'RSA2048' && (
<View style={styles.selected} />
)}
{ state.keyType === 'RSA4096' && (
<View style={styles.radio} />
)}
<Text style={styles.option}>RSA 2048</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.optionRight} activeOpacity={1}
onPress={() => actions.setKeyType('RSA4096')}>
{ state.keyType === 'RSA2048' && (
<View style={styles.radio} />
)}
{ state.keyType === 'RSA4096' && (
<View style={styles.selected} />
)}
<Text style={styles.option}>RSA 4096</Text>
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setPushSupported(!state.pushSupported)}>
<Text style={styles.modalLabel}>{ state.strings.enableNotifications }</Text>
<Switch style={styles.switch} value={state.pushSupported}
onValueChange={actions.setPushSupported} trackColor={styles.track}/>
</TouchableOpacity>
{ state.transformSupported && (
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setAllowUnsealed(!state.allowUnsealed)}>
<Text style={styles.modalLabel}>{ state.strings.allowUnsealed }</Text>
<Switch style={styles.switch} value={state.allowUnsealed}
onValueChange={actions.setAllowUnsealed} trackColor={styles.track}/>
</TouchableOpacity>
)}
<View style={styles.label}></View>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableImage(!state.enableImage)}>
<Text style={styles.modalLabel}>{ state.strings.enableImage }</Text>
<Switch style={styles.switch} value={state.enableImage}
onValueChange={actions.setEnableImage} trackColor={styles.track}/>
</TouchableOpacity>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableAudio(!state.enableAudio)}>
<Text style={styles.modalLabel}>{ state.strings.enableAudio }</Text>
<Switch style={styles.switch} value={state.enableAudio}
onValueChange={actions.setEnableAudio} trackColor={styles.track}/>
</TouchableOpacity>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableVideo(!state.enableVideo)}>
<Text style={styles.modalLabel}>{ state.strings.enableVideo }</Text>
<Switch style={styles.switch} value={state.enableVideo}
onValueChange={actions.setEnableVideo} trackColor={styles.track}/>
</TouchableOpacity>
<TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableBinary(!state.enableBinary)}>
<Text style={styles.modalLabel}>{ state.strings.enableBinary }</Text>
<Switch style={styles.switch} value={state.enableBinary}
onValueChange={actions.setEnableBinary} trackColor={styles.track}/>
</TouchableOpacity>
<View style={styles.label}></View>
<TouchableOpacity style={styles.ice} activeOpacity={1}
onPress={() => actions.setEnableIce(!state.enableIce)}>
<Text style={styles.modalLabel}>{ state.strings.enableCalls }</Text>
<Switch style={styles.switch} value={state.enableIce}
onValueChange={actions.setEnableIce} trackColor={styles.track}/>
</TouchableOpacity>
<InputField style={styles.field}
label={state.strings.relayUrl}
value={state.iceUrl}
autoCapitalize={'none'}
spellCheck={false}
disabled={!state.enableIce}
onChangeText={actions.setIceUrl}
/>
<InputField style={styles.field}
label={state.strings.relayUsername}
value={state.iceUsername}
autoCapitalize={'none'}
spellCheck={false}
disabled={!state.enableIce}
onChangeText={actions.setIceUsername}
/>
<InputField style={styles.field}
label={state.strings.relayPassword}
value={state.icePassword}
autoCapitalize={'none'}
spellCheck={false}
disabled={!state.enableIce}
onChangeText={actions.setIcePassword}
/>
<View style={styles.pad} />
</ScrollView>
<View style={styles.modalControls}>
<TouchableOpacity style={styles.cancel} onPress={actions.hideEditConfig}>
<Text style={styles.cancelText}>{ state.strings.cancel }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.save} onPress={saveConfig}>
<Text style={styles.saveText}>{ state.strings.save }</Text>
</TouchableOpacity>
</View>
</View>
</KeyboardAvoidingView>
</Modal>
<Modal
@ -286,24 +282,21 @@ export function Dashboard(props) {
onRequestClose={actions.hideAddUser}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={'dark'} blurAmount={2} reducedTransparencyFallbackColor='black' />
<View style={styles.modalBase}>
<View style={styles.modalContainer}>
<View style={styles.modalHeader}>
<Text style={styles.modalHeaderText}>{ state.strings.createAccount }</Text>
</View>
<View style={styles.accessToken}>
<Text style={styles.tokenLabel}>{ state.strings.token }</Text>
<TouchableOpacity style={styles.copy} onPress={() => Clipboard.setString(state.createToken)}>
<Text style={styles.token}>{ state.createToken }</Text>
<AntIcon style={styles.icon} name={'copy1'} size={20} />
</TouchableOpacity>
</View>
<View style={styles.modalControls}>
<TouchableOpacity style={styles.close} onPress={actions.hideAddUser}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
<View style={styles.modalContainer}>
<View style={styles.modalHeader}>
<Text style={styles.modalHeaderText}>{ state.strings.createAccount }</Text>
</View>
<View style={styles.accessToken}>
<Text style={styles.tokenLabel}>{ state.strings.token }</Text>
<TouchableOpacity style={styles.copy} onPress={() => Clipboard.setString(state.createToken)}>
<Text style={styles.token}>{ state.createToken }</Text>
<AntIcon style={styles.icon} name={'copy1'} size={20} />
</TouchableOpacity>
</View>
<View style={styles.modalControls}>
<TouchableOpacity style={styles.close} onPress={actions.hideAddUser}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
</View>
</View>
@ -317,24 +310,21 @@ export function Dashboard(props) {
onRequestClose={actions.hideAccessUser}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={'dark'} blurAmount={2} reducedTransparencyFallbackColor='black' />
<View style={styles.modalBase}>
<View style={styles.modalContainer}>
<View style={styles.modalHeader}>
<Text style={styles.modalHeaderText}>{ state.strings.accessAccount }</Text>
</View>
<View style={styles.accessToken}>
<Text style={styles.tokenLabel}>{ state.strings.token }</Text>
<TouchableOpacity style={styles.copy} onPress={() => Clipboard.setString(state.accessToken)}>
<Text style={styles.token}>{ state.accessToken }</Text>
<AntIcon style={styles.icon} name={'copy1'} size={20} />
</TouchableOpacity>
</View>
<View style={styles.modalControls}>
<TouchableOpacity style={styles.close} onPress={actions.hideAccessUser}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
<View style={styles.modalContainer}>
<View style={styles.modalHeader}>
<Text style={styles.modalHeaderText}>{ state.strings.accessAccount }</Text>
</View>
<View style={styles.accessToken}>
<Text style={styles.tokenLabel}>{ state.strings.token }</Text>
<TouchableOpacity style={styles.copy} onPress={() => Clipboard.setString(state.accessToken)}>
<Text style={styles.token}>{ state.accessToken }</Text>
<AntIcon style={styles.icon} name={'copy1'} size={20} />
</TouchableOpacity>
</View>
<View style={styles.modalControls}>
<TouchableOpacity style={styles.close} onPress={actions.hideAccessUser}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
</View>
</View>

View File

@ -174,6 +174,11 @@ export const styles = StyleSheet.create({
modalOverlay: {
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.modalOverlay,
opacity: 0.8,
},
modalBody: {
padding: 16,

View File

@ -32,7 +32,6 @@ import lightSplash from 'images/session.png';
import darkSplash from 'images/darksess.png';
import { useNavigate } from 'react-router-dom';
import { getLanguageStrings } from 'constants/Strings';
import { BlurView } from "@react-native-community/blur";
const ConversationStack = createStackNavigator();
const ProfileStack = createStackNavigator();
@ -494,7 +493,7 @@ export function Session({ sharing, clearSharing }) {
supportedOrientations={['portrait', 'landscape']}
>
<View style={styles.ringBase}>
<BlurView style={styles.ringBase} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.blur} />
<View style={styles.ringFrame}>
{ ringing }
</View>

View File

@ -7,6 +7,11 @@ export const styles = StyleSheet.create({
height: '100%',
backgroundColor: Colors.screenBase,
},
blur: {
width: '100%',
height: '100%',
backgroundColor: Colors.modalOverlay,
},
container: {
width: '100%',
height: '100%',

View File

@ -6,7 +6,6 @@ import { useChannels } from './useChannels.hook';
import { Colors } from 'constants/Colors';
import { ChannelItem } from './channelItem/ChannelItem';
import { AddMember } from './addMember/AddMember';
import { BlurView } from '@react-native-community/blur';
import { InputField } from 'utils/InputField';
export function Channels({ cardId, channelId, navigation, openConversation, dmChannel, shareChannel }) {
@ -102,60 +101,56 @@ export function Channels({ cardId, channelId, navigation, openConversation, dmCh
supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideAdding}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<KeyboardAvoidingView style={styles.modalOverlay} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<Text style={styles.addHeader}>{ state.strings.newTopic }</Text>
<View style={styles.modalContainer}>
<Text style={styles.addHeader}>{ state.strings.newTopic }</Text>
<InputField
label={state.strings.subject}
value={state.addSubject}
autoCapitalize={'words'}
spellCheck={false}
style={styles.field}
onChangeText={actions.setAddSubject}
/>
<InputField
label={state.strings.subject}
value={state.addSubject}
autoCapitalize={'words'}
spellCheck={false}
style={styles.field}
onChangeText={actions.setAddSubject}
/>
{ state.contacts.length == 0 && (
<View style={styles.emptyMembers}>
<Text style={styles.notfoundtext}>{ state.strings.noContacts }</Text>
</View>
)}
{ state.contacts.length > 0 && (
<FlatList style={styles.addMembers}
data={state.contacts}
renderItem={({ item }) => <AddMember members={state.addMembers} item={item}
setCard={actions.setAddMember} clearCard={actions.clearAddMember} />}
keyExtractor={item => item.cardId}
/>
)}
<View style={styles.addControls}>
<View style={styles.sealed}>
{ state.sealable && state.allowUnsealed && (
<>
<Switch style={styles.switch} trackColor={styles.track}
value={state.sealed} onValueChange={actions.setSealed} />
<Text style={styles.sealedText}>{ state.strings.sealed }</Text>
</>
)}
</View>
<TouchableOpacity style={styles.cancel} onPress={actions.hideAdding}>
<Text style={styles.cancelText}>{ state.strings.cancel }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.save} onPress={addChannel}>
{ state.busy && (
<ActivityIndicator color={Colors.text} />
)}
{ !state.busy && (
<Text style={styles.saveText}>{ state.strings.create }</Text>
)}
</TouchableOpacity>
{ state.contacts.length == 0 && (
<View style={styles.emptyMembers}>
<Text style={styles.empty}>{ state.strings.noContacts }</Text>
</View>
)}
{ state.contacts.length > 0 && (
<FlatList style={styles.addMembers}
data={state.contacts}
renderItem={({ item }) => <AddMember members={state.addMembers} item={item}
setCard={actions.setAddMember} clearCard={actions.clearAddMember} />}
keyExtractor={item => item.cardId}
/>
)}
<View style={styles.addControls}>
<View style={styles.sealed}>
{ state.sealable && state.allowUnsealed && (
<>
<Switch style={styles.switch} trackColor={styles.track}
value={state.sealed} onValueChange={actions.setSealed} />
<Text style={styles.sealedText}>{ state.strings.sealed }</Text>
</>
)}
</View>
<TouchableOpacity style={styles.cancel} onPress={actions.hideAdding}>
<Text style={styles.cancelText}>{ state.strings.cancel }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.save} onPress={addChannel}>
{ state.busy && (
<ActivityIndicator color={Colors.text} />
)}
{ !state.busy && (
<Text style={styles.saveText}>{ state.strings.create }</Text>
)}
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</View>
</View>
</KeyboardAvoidingView>
</Modal>
</View>
);

View File

@ -201,18 +201,13 @@ export const styles = StyleSheet.create({
transform: [{ scaleX: .7 }, { scaleY: .7 }],
},
modalOverlay: {
width: '100%',
height: '100%',
},
modalBase: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
justifyContent: 'center',
backgroundColor: Colors.modalOverlay,
opacity: 0.8,
},
modalContainer: {
backgroundColor: Colors.modalBase,

View File

@ -8,7 +8,6 @@ import Ionicons from 'react-native-vector-icons/AntDesign';
import { Logo } from 'utils/Logo';
import { AddTopic } from './addTopic/AddTopic';
import { TopicItem } from './topicItem/TopicItem';
import { BlurView } from "@react-native-community/blur";
export function Conversation({ navigation, cardId, channelId, closeConversation, openDetails, shareIntent, setShareIntent }) {
@ -123,7 +122,7 @@ export function Conversation({ navigation, cardId, channelId, closeConversation,
onRequestClose={actions.hideEdit}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.blur} />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<Text style={styles.editHeader}>{ state.strings.editMessage }</Text>

View File

@ -10,6 +10,11 @@ export const styles = StyleSheet.create({
alignItems: 'center',
flexShrink: 1,
},
blur: {
width: '100%',
height: '100%',
backgroundColor: Colors.modalOverlay,
},
more: {
marginTop: 8,
},

View File

@ -13,7 +13,6 @@ import { VideoFile } from './videoFile/VideoFile';
import { AudioFile } from './audioFile/AudioFile';
import { ImageFile } from './imageFile/ImageFile';
import { BinaryFile } from './binaryFile/BinaryFile';
import { BlurView } from "@react-native-community/blur";
export function AddTopic({ contentKey, shareIntent, setShareIntent }) {
@ -231,48 +230,45 @@ export function AddTopic({ contentKey, shareIntent, setShareIntent }) {
onRequestClose={actions.hideFontSize}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<Text style={styles.editHeader}>{ state.strings.fontSize }</Text>
<View style={styles.editSize}>
{ state.size === 'small' && (
<View style={styles.selected}>
<Text style={styles.selectedText}>{ state.strings.small }</Text>
</View>
)}
{ state.size !== 'small' && (
<TouchableOpacity style={styles.option} onPress={() => actions.setFontSize('small')}>
<Text style={styles.optionText}>{ state.strings.small }</Text>
</TouchableOpacity>
)}
{ state.size === 'medium' && (
<View style={styles.selected}>
<Text style={styles.selectedText}>{ state.strings.medium }</Text>
</View>
)}
{ state.size !== 'medium' && (
<TouchableOpacity style={styles.option} onPress={() => actions.setFontSize('medium')}>
<Text style={styles.optionText}>{ state.strings.medium }</Text>
</TouchableOpacity>
)}
{ state.size === 'large' && (
<View style={styles.selected}>
<Text style={styles.selectedText}>{ state.strings.large }</Text>
</View>
)}
{ state.size !== 'large' && (
<TouchableOpacity style={styles.option} onPress={() => actions.setFontSize('large')}>
<Text style={styles.optionText}>{ state.strings.large }</Text>
</TouchableOpacity>
)}
</View>
<View style={styles.editControls}>
<View style={styles.selection} />
<TouchableOpacity style={styles.close} onPress={actions.hideFontSize}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
<View style={styles.modalContainer}>
<Text style={styles.editHeader}>{ state.strings.fontSize }</Text>
<View style={styles.editSize}>
{ state.size === 'small' && (
<View style={styles.selected}>
<Text style={styles.selectedText}>{ state.strings.small }</Text>
</View>
)}
{ state.size !== 'small' && (
<TouchableOpacity style={styles.option} onPress={() => actions.setFontSize('small')}>
<Text style={styles.optionText}>{ state.strings.small }</Text>
</TouchableOpacity>
</View>
)}
{ state.size === 'medium' && (
<View style={styles.selected}>
<Text style={styles.selectedText}>{ state.strings.medium }</Text>
</View>
)}
{ state.size !== 'medium' && (
<TouchableOpacity style={styles.option} onPress={() => actions.setFontSize('medium')}>
<Text style={styles.optionText}>{ state.strings.medium }</Text>
</TouchableOpacity>
)}
{ state.size === 'large' && (
<View style={styles.selected}>
<Text style={styles.selectedText}>{ state.strings.large }</Text>
</View>
)}
{ state.size !== 'large' && (
<TouchableOpacity style={styles.option} onPress={() => actions.setFontSize('large')}>
<Text style={styles.optionText}>{ state.strings.large }</Text>
</TouchableOpacity>
)}
</View>
<View style={styles.editControls}>
<View style={styles.selection} />
<TouchableOpacity style={styles.close} onPress={actions.hideFontSize}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
</View>
</View>
@ -285,27 +281,24 @@ export function AddTopic({ contentKey, shareIntent, setShareIntent }) {
onRequestClose={actions.hideFontColor}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<Text style={styles.editHeader}>{ state.strings.fontColor }</Text>
<View style={styles.editColor}>
<ColorPicker
color={state.color}
onColorChange={actions.setFontColor}
onColorChangeComplete={actions.setFontColor}
swatched={false}
style={{flex: 1, padding: 8}} />
</View>
<View style={styles.editControls}>
<View style={styles.selection}>
<Text style={styles.selectionText}>{ state.strings.selectedColor }</Text>
<View style={{ marginLeft: 6, borderRadius: 4, width: 16, height: 16, backgroundColor: state.color }} />
</View>
<TouchableOpacity style={styles.close} onPress={actions.hideFontColor}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
<View style={styles.modalContainer}>
<Text style={styles.editHeader}>{ state.strings.fontColor }</Text>
<View style={styles.editColor}>
<ColorPicker
color={state.color}
onColorChange={actions.setFontColor}
onColorChangeComplete={actions.setFontColor}
swatched={false}
style={{flex: 1, padding: 8}} />
</View>
<View style={styles.editControls}>
<View style={styles.selection}>
<Text style={styles.selectionText}>{ state.strings.selectedColor }</Text>
<View style={{ marginLeft: 6, borderRadius: 4, width: 16, height: 16, backgroundColor: state.color }} />
</View>
<TouchableOpacity style={styles.close} onPress={actions.hideFontColor}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
</View>
</View>

View File

@ -136,19 +136,14 @@ export const styles = StyleSheet.create({
selectionText: {
color: Colors.text,
},
modalOverlay: {
modalOverlay: {
width: '100%',
height: '100%',
},
modalBase: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
justifyContent: 'center',
backgroundColor: Colors.modalOverlay,
opacity: 0.8,
},
modalContainer: {
backgroundColor: Colors.modalBase,

View File

@ -7,7 +7,6 @@ import AntIcons from 'react-native-vector-icons/AntDesign';
import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Colors from 'constants/Colors';
import { MemberItem } from './memberItem/MemberItem';
import { BlurView } from '@react-native-community/blur';
import { InputField } from 'utils/InputField';
export function Details({ channel, clearConversation }) {
@ -162,7 +161,7 @@ export function Details({ channel, clearConversation }) {
onRequestClose={actions.hideEditSubject}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.blur} />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<Text style={styles.editHeader}>{ state.strings.editSubject }</Text>
@ -196,24 +195,21 @@ export function Details({ channel, clearConversation }) {
supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideEditMembers}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<Text style={styles.editHeader}>{ state.strings.topicMembers }</Text>
<FlatList style={styles.editMembers}
data={state.connected}
renderItem={({ item }) => <MemberItem item={item} toggle={toggle} />}
keyExtractor={item => item.cardId}
/>
<View style={styles.editControls}>
<TouchableOpacity style={styles.close} onPress={actions.hideEditMembers}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
<KeyboardAvoidingView style={styles.modalOverlay} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<Text style={styles.editHeader}>{ state.strings.topicMembers }</Text>
<FlatList style={styles.editMembers}
data={state.connected}
renderItem={({ item }) => <MemberItem item={item} toggle={toggle} />}
keyExtractor={item => item.cardId}
/>
<View style={styles.editControls}>
<TouchableOpacity style={styles.close} onPress={actions.hideEditMembers}>
<Text style={styles.closeText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</View>
</View>
</KeyboardAvoidingView>
</Modal>
</View>

View File

@ -183,16 +183,11 @@ export const styles = StyleSheet.create({
modalOverlay: {
width: '100%',
height: '100%',
},
modalBase: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
justifyContent: 'center',
backgroundColor: Colors.modalOverlay,
opacity: 0.8,
},
modalContainer: {
backgroundColor: Colors.modalBase,

View File

@ -3,7 +3,6 @@ import { useState } from 'react';
import AntIcons from 'react-native-vector-icons/AntDesign';
import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import ImagePicker from 'react-native-image-crop-picker'
import { BlurView } from "@react-native-community/blur";
import { FloatingLabelInput } from 'react-native-floating-label-input';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import { Menu, MenuOptions, MenuOption, MenuTrigger } from 'react-native-popup-menu';
@ -201,64 +200,61 @@ export function Profile({ drawer }) {
supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideDetails}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideDetails}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.editDetails }</Text>
<InputField
label={state.strings.name}
value={state.detailName}
autoCapitalize={'none'}
spellCheck={false}
multiline={false}
style={styles.field}
onChangeText={actions.setDetailName}
/>
<InputField
label={state.strings.location}
value={state.detailLocation}
autoCapitalize={'none'}
spellCheck={false}
multiline={false}
style={styles.field}
onChangeText={actions.setDetailLocation}
/>
<InputField
label={state.strings.description}
value={state.detailDescription}
autoCapitalize={'none'}
spellCheck={false}
multiline={true}
style={styles.field}
onChangeText={actions.setDetailDescription}
/>
<View style={styles.buttons}>
<TouchableOpacity style={styles.cancelButton} activeOpacity={1} onPress={actions.hideDetails}>
<Text style={styles.cancelButtonText}>{ state.strings.cancel }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.saveButton} activeOpacity={1} onPress={saveDetails}>
{ busyDetail && (
<ActivityIndicator animating={true} color={Colors.primaryButtonText} />
)}
{ !busyDetail && (
<Text style={styles.saveButtonText}>{ state.strings.save }</Text>
)}
</TouchableOpacity>
</View>
<KeyboardAvoidingView style={styles.modalOverlay} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideDetails}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</View>
<Text style={styles.modalHeader}>{ state.strings.editDetails }</Text>
<InputField
label={state.strings.name}
value={state.detailName}
autoCapitalize={'none'}
spellCheck={false}
multiline={false}
style={styles.field}
onChangeText={actions.setDetailName}
/>
<InputField
label={state.strings.location}
value={state.detailLocation}
autoCapitalize={'none'}
spellCheck={false}
multiline={false}
style={styles.field}
onChangeText={actions.setDetailLocation}
/>
<InputField
label={state.strings.description}
value={state.detailDescription}
autoCapitalize={'none'}
spellCheck={false}
multiline={true}
style={styles.field}
onChangeText={actions.setDetailDescription}
/>
<View style={styles.buttons}>
<TouchableOpacity style={styles.cancelButton} activeOpacity={1} onPress={actions.hideDetails}>
<Text style={styles.cancelButtonText}>{ state.strings.cancel }</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.saveButton} activeOpacity={1} onPress={saveDetails}>
{ busyDetail && (
<ActivityIndicator animating={true} color={Colors.primaryButtonText} />
)}
{ !busyDetail && (
<Text style={styles.saveButtonText}>{ state.strings.save }</Text>
)}
</TouchableOpacity>
</View>
</View>
</KeyboardAvoidingView>
</Modal>
</>

View File

@ -281,16 +281,11 @@ export const styles = StyleSheet.create({
modalOverlay: {
width: '100%',
height: '100%',
},
modalBase: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
justifyContent: 'center',
backgroundColor: Colors.modalOverlay,
opacity: 0.8,
},
modalContainer: {
backgroundColor: Colors.modalBase,

View File

@ -6,7 +6,6 @@ import { styles } from './Settings.styled';
import { useSettings } from './useSettings.hook';
import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Colors from 'constants/Colors';
import { BlurView } from "@react-native-community/blur";
import { InputField } from 'utils/InputField';
import { Logo } from 'utils/Logo';
@ -427,175 +426,172 @@ export function Settings({ drawer }) {
supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideEditSeal}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideEditSeal}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.sealedTopics }</Text>
{ !state.sealEnabled && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealUnset }</Text>
<KeyboardAvoidingView style={styles.modalOverlay} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideEditSeal}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.sealedTopics }</Text>
{ !state.sealEnabled && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealUnset }</Text>
<InputField style={styles.field}
label={state.strings.password}
secret={true}
value={state.sealPassword}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setSealPassword}
/>
{ state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.generateKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !busy && (
<Text style={styles.enabledButtonText}>{ state.strings.generate }</Text>
)}
</TouchableOpacity>
)}
{ !state.sealPassword && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.generate }</Text>
</View>
)}
<Text style={styles.delayMessage}>{ state.strings.delayMessage }</Text>
</>
)}
{ state.sealEnabled && !state.sealUnlocked && !state.sealRemove && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealLocked }</Text>
<InputField style={styles.field}
label={state.strings.password}
secret={true}
value={state.sealPassword}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setSealPassword}
/>
{ state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.unlockKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !busy && (
<Text style={styles.enabledButtonText}>{ state.strings.unlock }</Text>
)}
</TouchableOpacity>
)}
{ !state.sealPassword && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.unlock }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.showSealRemove}>
<Text style={styles.dangerText}>{ state.strings.removeSeal }</Text>
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealUnlocked && !state.sealRemove && !state.sealUpdate && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealUnlocked }</Text>
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.disableKey)}>
<InputField style={styles.field}
label={state.strings.password}
secret={true}
value={state.sealPassword}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setSealPassword}
/>
{ state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.generateKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !busy && (
<Text style={styles.enabledButtonText}>{ state.strings.disable }</Text>
<Text style={styles.enabledButtonText}>{ state.strings.generate }</Text>
)}
</TouchableOpacity>
<TouchableOpacity activeOpacity={1} onPress={actions.showSealUpdate}>
<Text style={styles.modeText}>{ state.strings.changeKey }</Text>
</TouchableOpacity>
<TouchableOpacity activeOpacity={1} onPress={actions.showSealRemove}>
<Text style={styles.dangerText}>{ state.strings.removeSeal }</Text>
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealRemove && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealDelete }</Text>
<InputField style={styles.field}
label={state.strings.typeDelete}
value={state.sealDelete}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setSealDelete}
/>
{ state.sealDelete === state.strings.deleteKey && (
<TouchableOpacity style={styles.dangerButton} activeOpacity={1} onPress={() => sealAction(actions.removeKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !busy && (
<Text style={styles.dangerButtonText}>{ state.strings.delete }</Text>
)}
</TouchableOpacity>
)}
{ state.sealDelete !== state.strings.deleteKey && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.delete }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.hideSealRemove}>
{ state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.disableSeal }</Text>
)}
{ !state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.unlockSeal }</Text>
)}
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealUnlocked && state.sealUpdate && (
<>
<Text style={styles.modalDescription}>{ state.strings.changePassword }</Text>
)}
{ !state.sealPassword && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.generate }</Text>
</View>
)}
<Text style={styles.delayMessage}>{ state.strings.delayMessage }</Text>
</>
)}
{ state.sealEnabled && !state.sealUnlocked && !state.sealRemove && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealLocked }</Text>
<InputField style={styles.field}
label={state.strings.password}
isPassword={true}
value={state.sealPassword}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setSealPassword}
/>
<InputField style={styles.field}
label={state.strings.password}
secret={true}
value={state.sealPassword}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setSealPassword}
/>
{ state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.updateKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !busy && (
<Text style={styles.enabledButtonText}>{ state.strings.update }</Text>
)}
</TouchableOpacity>
)}
{ !state.sealPassword && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.update }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.hideSealUpdate}>
{ state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.disableSeal }</Text>
{ state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.unlockKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.unlockSeal }</Text>
{ !busy && (
<Text style={styles.enabledButtonText}>{ state.strings.unlock }</Text>
)}
</TouchableOpacity>
</>
)}
</View>
</KeyboardAvoidingView>
</View>
)}
{ !state.sealPassword && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.unlock }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.showSealRemove}>
<Text style={styles.dangerText}>{ state.strings.removeSeal }</Text>
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealUnlocked && !state.sealRemove && !state.sealUpdate && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealUnlocked }</Text>
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.disableKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !busy && (
<Text style={styles.enabledButtonText}>{ state.strings.disable }</Text>
)}
</TouchableOpacity>
<TouchableOpacity activeOpacity={1} onPress={actions.showSealUpdate}>
<Text style={styles.modeText}>{ state.strings.changeKey }</Text>
</TouchableOpacity>
<TouchableOpacity activeOpacity={1} onPress={actions.showSealRemove}>
<Text style={styles.dangerText}>{ state.strings.removeSeal }</Text>
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealRemove && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealDelete }</Text>
<InputField style={styles.field}
label={state.strings.typeDelete}
value={state.sealDelete}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setSealDelete}
/>
{ state.sealDelete === state.strings.deleteKey && (
<TouchableOpacity style={styles.dangerButton} activeOpacity={1} onPress={() => sealAction(actions.removeKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !busy && (
<Text style={styles.dangerButtonText}>{ state.strings.delete }</Text>
)}
</TouchableOpacity>
)}
{ state.sealDelete !== state.strings.deleteKey && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.delete }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.hideSealRemove}>
{ state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.disableSeal }</Text>
)}
{ !state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.unlockSeal }</Text>
)}
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealUnlocked && state.sealUpdate && (
<>
<Text style={styles.modalDescription}>{ state.strings.changePassword }</Text>
<InputField style={styles.field}
label={state.strings.password}
isPassword={true}
value={state.sealPassword}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setSealPassword}
/>
{ state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.updateKey)}>
{ busy && (
<ActivityIndicator style={styles.modalBusy} animating={true} color={Colors.primaryButtonText} />
)}
{ !busy && (
<Text style={styles.enabledButtonText}>{ state.strings.update }</Text>
)}
</TouchableOpacity>
)}
{ !state.sealPassword && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.update }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.hideSealUpdate}>
{ state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.disableSeal }</Text>
)}
{ !state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.unlockSeal }</Text>
)}
</TouchableOpacity>
</>
)}
</View>
</KeyboardAvoidingView>
</Modal>
<Modal
@ -605,62 +601,59 @@ export function Settings({ drawer }) {
supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideLogin}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideLogin}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.changeLogin }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<Text textAlign={'center'} style={styles.modalDescription}>{ state.strings.changeMessage }</Text>
<InputField
label={state.strings.username}
value={state.username}
autoCapitalize={'none'}
spellCheck={false}
style={styles.field}
onChangeText={actions.setUsername}
/>
<InputField
label={state.strings.password}
value={state.password}
autoCapitalize={'none'}
spellCheck={false}
style={styles.field}
onChangeText={actions.setPassword}
secret={true}
/>
<View style={styles.availableStatus}>
{ state.validated && !state.available && (
<Text style={styles.notAvailable}>{ state.strings.notAvailable }</Text>
)}
</View>
<View style={styles.hintButtons}>
<TouchableOpacity style={styles.cancelButton} activeOpacity={1} onPress={actions.hideLogin}>
<Text style={styles.cancelButtonText}>{ state.strings.cancel }</Text>
</TouchableOpacity>
{ (!state.available || !state.password || !state.validated || !state.username) && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.update }</Text>
</View>
)}
{ state.available && state.password && state.validated && state.username && (
<TouchableOpacity style={styles.promptButton} activeOpacity={1} onPress={changeLogin}>
<Text style={styles.enabledButtonText}>{ state.strings.update }</Text>
</TouchableOpacity>
)}
</View>
<KeyboardAvoidingView style={styles.modalOverlay} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideLogin}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</View>
<Text style={styles.modalHeader}>{ state.strings.changeLogin }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<Text textAlign={'center'} style={styles.modalDescription}>{ state.strings.changeMessage }</Text>
<InputField
label={state.strings.username}
value={state.username}
autoCapitalize={'none'}
spellCheck={false}
style={styles.field}
onChangeText={actions.setUsername}
/>
<InputField
label={state.strings.password}
value={state.password}
autoCapitalize={'none'}
spellCheck={false}
style={styles.field}
onChangeText={actions.setPassword}
secret={true}
/>
<View style={styles.availableStatus}>
{ state.validated && !state.available && (
<Text style={styles.notAvailable}>{ state.strings.notAvailable }</Text>
)}
</View>
<View style={styles.hintButtons}>
<TouchableOpacity style={styles.cancelButton} activeOpacity={1} onPress={actions.hideLogin}>
<Text style={styles.cancelButtonText}>{ state.strings.cancel }</Text>
</TouchableOpacity>
{ (!state.available || !state.password || !state.validated || !state.username) && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.update }</Text>
</View>
)}
{ state.available && state.password && state.validated && state.username && (
<TouchableOpacity style={styles.promptButton} activeOpacity={1} onPress={changeLogin}>
<Text style={styles.enabledButtonText}>{ state.strings.update }</Text>
</TouchableOpacity>
)}
</View>
</View>
</KeyboardAvoidingView>
</Modal>
<Modal
@ -670,41 +663,38 @@ export function Settings({ drawer }) {
supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideDelete}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<KeyboardAvoidingView style={styles.modalBase} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideDelete}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.deleteAccount }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<InputField style={styles.field}
label={state.strings.typeDelete}
value={state.confirm}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setConfirm}
/>
<View style={styles.buttons}>
{ state.confirm === state.strings.deleteKey && (
<TouchableOpacity style={styles.dangerButton} activeOpacity={1} onPress={deleteAccount}>
<Text style={styles.dangerButtonText}>{ state.strings.delete }</Text>
</TouchableOpacity>
)}
{ state.confirm !== state.strings.deleteKey && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.delete }</Text>
</View>
)}
</View>
<KeyboardAvoidingView style={styles.modalOverlay} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideDelete}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</View>
<Text style={styles.modalHeader}>{ state.strings.deleteAccount }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<InputField style={styles.field}
label={state.strings.typeDelete}
value={state.confirm}
autoCapitalize={'none'}
spellCheck={false}
onChangeText={actions.setConfirm}
/>
<View style={styles.buttons}>
{ state.confirm === state.strings.deleteKey && (
<TouchableOpacity style={styles.dangerButton} activeOpacity={1} onPress={deleteAccount}>
<Text style={styles.dangerButtonText}>{ state.strings.delete }</Text>
</TouchableOpacity>
)}
{ state.confirm !== state.strings.deleteKey && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.delete }</Text>
</View>
)}
</View>
</View>
</KeyboardAvoidingView>
</Modal>
<Modal
@ -715,35 +705,32 @@ export function Settings({ drawer }) {
onRequestClose={state.hideBlockedContacts}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.modalBase}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideBlockedContacts}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.blockedContacts }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<View style={styles.modalList}>
{ state.contacts.length === 0 && (
<View style={styles.emptyLabel}>
<Text style={styles.emptyLabelText}>{ state.strings.noBlockedContacts }</Text>
</View>
)}
{ state.contacts.length !== 0 && (
<FlatList
data={state.contacts}
renderItem={BlockedContact}
keyExtractor={item => item.cardId}
/>
)}
</View>
<View style={styles.rightButton}>
<TouchableOpacity style={styles.closeButton} activeOpacity={1} onPress={actions.hideBlockedContacts}>
<Text style={styles.closeButtonText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideBlockedContacts}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.blockedContacts }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<View style={styles.modalList}>
{ state.contacts.length === 0 && (
<View style={styles.emptyLabel}>
<Text style={styles.emptyLabelText}>{ state.strings.noBlockedContacts }</Text>
</View>
)}
{ state.contacts.length !== 0 && (
<FlatList
data={state.contacts}
renderItem={BlockedContact}
keyExtractor={item => item.cardId}
/>
)}
</View>
<View style={styles.rightButton}>
<TouchableOpacity style={styles.closeButton} activeOpacity={1} onPress={actions.hideBlockedContacts}>
<Text style={styles.closeButtonText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
</View>
</View>
@ -757,35 +744,32 @@ export function Settings({ drawer }) {
onRequestClose={state.hideBlockedTopics}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.modalBase}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideBlockedTopics}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.blockedTopics }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<View style={styles.modalList}>
{ state.topics.length === 0 && (
<View style={styles.emptyLabel}>
<Text style={styles.emptyLabelText}>{ state.strings.noBlockedTopics }</Text>
</View>
)}
{ state.topics.length !== 0 && (
<FlatList
data={state.topics}
renderItem={BlockedTopic}
keyExtractor={item => `${item.cardId}.${item.channelId}`}
/>
)}
</View>
<View style={styles.rightButton}>
<TouchableOpacity style={styles.closeButton} activeOpacity={1} onPress={actions.hideBlockedTopics}>
<Text style={styles.closeButtonText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideBlockedTopics}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.blockedTopics }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<View style={styles.modalList}>
{ state.topics.length === 0 && (
<View style={styles.emptyLabel}>
<Text style={styles.emptyLabelText}>{ state.strings.noBlockedTopics }</Text>
</View>
)}
{ state.topics.length !== 0 && (
<FlatList
data={state.topics}
renderItem={BlockedTopic}
keyExtractor={item => `${item.cardId}.${item.channelId}`}
/>
)}
</View>
<View style={styles.rightButton}>
<TouchableOpacity style={styles.closeButton} activeOpacity={1} onPress={actions.hideBlockedTopics}>
<Text style={styles.closeButtonText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
</View>
</View>
@ -799,35 +783,32 @@ export function Settings({ drawer }) {
onRequestClose={actions.hideBlockedMessages}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.modalBase}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideBlockedMessages}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.blockedMessages }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<View style={styles.modalList}>
{ state.messages.length === 0 && (
<View style={styles.emptyLabel}>
<Text style={styles.emptyLabelText}>{ state.strings.noBlockedMessages }</Text>
</View>
)}
{ state.messages.length !== 0 && (
<FlatList
data={state.messages}
renderItem={BlockedMessage}
keyExtractor={item => `${item.cardId}.${item.channelId}.${item.topicId}`}
/>
)}
</View>
<View style={styles.rightButton}>
<TouchableOpacity style={styles.closeButton} activeOpacity={1} onPress={actions.hideBlockedMessages}>
<Text style={styles.closeButtonText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity style={styles.dismissButton} activeOpacity={1} onPress={actions.hideBlockedMessages}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.blockedMessages }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
<View style={styles.modalList}>
{ state.messages.length === 0 && (
<View style={styles.emptyLabel}>
<Text style={styles.emptyLabelText}>{ state.strings.noBlockedMessages }</Text>
</View>
)}
{ state.messages.length !== 0 && (
<FlatList
data={state.messages}
renderItem={BlockedMessage}
keyExtractor={item => `${item.cardId}.${item.channelId}.${item.topicId}`}
/>
)}
</View>
<View style={styles.rightButton}>
<TouchableOpacity style={styles.closeButton} activeOpacity={1} onPress={actions.hideBlockedMessages}>
<Text style={styles.closeButtonText}>{ state.strings.close }</Text>
</TouchableOpacity>
</View>
</View>
</View>

View File

@ -130,16 +130,11 @@ export const styles = StyleSheet.create({
modalOverlay: {
width: '100%',
height: '100%',
},
modalBase: {
display: 'flex',
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
top: 0,
left: 0,
backgroundColor: Colors.modalOverlay,
opacity: 0.8,
},
modalContainer: {
backgroundColor: Colors.modalBase,

View File

@ -1,7 +1,6 @@
import { useContext, useState } from 'react';
import { Modal, View, Text, TouchableOpacity, ActivityIndicator } from 'react-native';
import { DisplayContext } from 'context/DisplayContext';
import { BlurView } from "@react-native-community/blur";
import { styles } from './Prompt.styled';
import { Colors } from 'constants/Colors';
@ -41,27 +40,24 @@ export function Prompt() {
onRequestClose={display.actions.hidePrompt}
>
<View style={styles.modalOverlay}>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black" />
<View style={styles.modalBase}>
<View style={styles.modalContainer}>
<Text style={styles.modalHeader}>{ display.state.prompt?.title }</Text>
<View style={display.state.prompt?.centerButtons ? styles.centerModalButtons : styles.modalButtons}>
{ display.state.prompt?.cancel && (
<TouchableOpacity style={styles.cancelButton} activeOpacity={1} onPress={display.actions.hidePrompt}>
<Text style={styles.cancelButtonText}>{ display.state.prompt?.cancel?.label }</Text>
</TouchableOpacity>
)}
{ display.state.prompt?.ok && (
<TouchableOpacity style={styles.okButton} activeOpacity={1} onPress={okPrompt}>
{ !busy && (
<Text style={styles.okButtonText}>{ display.state.prompt?.ok?.label }</Text>
)}
{ busy && (
<ActivityIndicator animating={true} color={Colors.primaryButtonText} />
)}
</TouchableOpacity>
)}
</View>
<View style={styles.modalContainer}>
<Text style={styles.modalHeader}>{ display.state.prompt?.title }</Text>
<View style={display.state.prompt?.centerButtons ? styles.centerModalButtons : styles.modalButtons}>
{ display.state.prompt?.cancel && (
<TouchableOpacity style={styles.cancelButton} activeOpacity={1} onPress={display.actions.hidePrompt}>
<Text style={styles.cancelButtonText}>{ display.state.prompt?.cancel?.label }</Text>
</TouchableOpacity>
)}
{ display.state.prompt?.ok && (
<TouchableOpacity style={styles.okButton} activeOpacity={1} onPress={okPrompt}>
{ !busy && (
<Text style={styles.okButtonText}>{ display.state.prompt?.ok?.label }</Text>
)}
{ busy && (
<ActivityIndicator animating={true} color={Colors.primaryButtonText} />
)}
</TouchableOpacity>
)}
</View>
</View>
</View>
@ -74,7 +70,7 @@ export function Prompt() {
supportedOrientations={['portrait', 'landscape']}
onRequestClose={display.actions.hideAlert}
>
<BlurView style={styles.modalOverlay} blurType={Colors.overlay} blurAmount={2} reducedTransparencyFallbackColor="black">
<View styles={styles.modalOverlay}>
<View style={styles.modalContainer}>
<Text style={styles.modalHeader}>{ display.state.alert?.title }</Text>
<Text style={styles.modalMessage}>{ display.state.alert?.message }</Text>
@ -82,7 +78,7 @@ export function Prompt() {
<Text style={styles.okButtonText}>{ display.state.alert?.ok }</Text>
</TouchableOpacity>
</View>
</BlurView>
</View>
</Modal>
</>
);

View File

@ -5,16 +5,11 @@ export const styles = StyleSheet.create({
modalOverlay: {
width: '100%',
height: '100%',
},
modalBase: {
backgroundColor: Colors.modalOverlay,
opacity: 0.8,
display: 'flex',
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
top: 0,
left: 0,
},
modalContainer: {
backgroundColor: Colors.modalBase,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
* Updated fastlane description

View File

@ -0,0 +1 @@
* Updated fastlane description

View File

@ -0,0 +1,11 @@
Notable features of Databag include:
- Decentralized (direct communication between app and selfhosted server)
- Federated (accounts on different nodes can communicate)
- Public-Private key based identity (not bound to any blockchain or hosting domain)
- End-to-End encryption (the hosting admin cannot view sealed topics, default unsealed)
- Audio and Video Calls (nat traversal requires separate relay server)
- Topic based threads (messages organized by topic not contacts)
- Lightweight (server can run on a raspberry pi zero v1.3)
- Low latency (use of websockets for push events to avoid polling)
- Unlimited accounts per node (host for your whole family)
- Mobile alerts for new contacts, messages, and calls (supports UnifiedPush)

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -0,0 +1 @@
A federated chat client designed for privacy and selfhosting.

View File

@ -0,0 +1 @@
Databag

View File

@ -0,0 +1 @@
https://www.youtube.com/watch?v=81WTK3rdqGE