diff --git a/android/VERSION_CODE b/android/VERSION_CODE
index 871cae9..5f1a9f3 100644
--- a/android/VERSION_CODE
+++ b/android/VERSION_CODE
@@ -1 +1 @@
-323
\ No newline at end of file
+324
\ No newline at end of file
diff --git a/android/VERSION_NAME b/android/VERSION_NAME
index 06eda28..9b7a431 100644
--- a/android/VERSION_NAME
+++ b/android/VERSION_NAME
@@ -1 +1 @@
-3.2.3
\ No newline at end of file
+3.2.4
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index a2bdc00..fad7eb0 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,244 +1,244 @@
apply plugin: "com.android.application"
apply plugin: 'kotlin-android'
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
entryFile: "index.js",
enableHermes: false, // clean and rebuild if changing
]
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and mirrored here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
// Load keystore
def keystorePropertiesFile = rootProject.file("keystore.properties");
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion rootProject.ext.compileSdkVersion
dexOptions {
javaMaxHeapSize "3g"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "com.agprojects.sylk"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60"
missingDimensionStrategy "react-native-camera", "general"
- versionCode 323
- versionName "3.2.3"
+ versionCode 324
+ versionName "3.2.4"
multiDexEnabled true
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
signingConfigs {
debug {
// storeFile file('debug.keystore')
// storePassword 'android'
// keyAlias 'androiddebugkey'
// keyPassword 'android'
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://facebook.github.io/react-native/docs/signed-apk-android.
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
configurations {
all {
exclude group: "com.google.android.gms", module: "play-services-measurement-impl"
exclude group: "com.google.android.gms", module: "play-services-measurement"
exclude group: "com.google.android.gms", module: "play-services-measurement-base"
exclude group: "com.google.android.gms", module: "play-services-measurement-api"
exclude group: "com.google.firebase", module: "firebase-iid"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.activity:activity-ktx:1.2.0-alpha08'
implementation 'androidx.fragment:fragment:1.3.0-alpha08'
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.multidex:multidex:2.0.1"
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
implementation 'net.java.dev.jna:jna:5.2.0'
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
apply plugin: 'com.google.gms.google-services' // Google Play services Gradle plugin
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
diff --git a/app/components/ContactsListBox.js b/app/components/ContactsListBox.js
index 579e022..3f26dcf 100644
--- a/app/components/ContactsListBox.js
+++ b/app/components/ContactsListBox.js
@@ -1,2346 +1,2350 @@
import React, { Component} from 'react';
import autoBind from 'auto-bind';
import PropTypes from 'prop-types';
import { Image, Clipboard, Dimensions, SafeAreaView, View, FlatList, Text, Linking, PermissionsAndroid, Switch, TouchableOpacity, BackHandler, TouchableHighlight} from 'react-native';
import ContactCard from './ContactCard';
import utils from '../utils';
import DigestAuthRequest from 'digest-auth-request';
import uuid from 'react-native-uuid';
import { GiftedChat, IMessage, Bubble, MessageText, Send, InputToolbar, MessageImage, Time} from 'react-native-gifted-chat'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
import MessageInfoModal from './MessageInfoModal';
import EditMessageModal from './EditMessageModal';
import ShareMessageModal from './ShareMessageModal';
import DeleteMessageModal from './DeleteMessageModal';
import CustomChatActions from './ChatActions';
import FileViewer from 'react-native-file-viewer';
import OpenPGP from "react-native-fast-openpgp";
import DocumentPicker from 'react-native-document-picker';
import AudioRecorderPlayer from 'react-native-audio-recorder-player';
import VideoPlayer from 'react-native-video-player';
import { IconButton} from 'react-native-paper';
import ImageViewer from 'react-native-image-zoom-viewer';
import path from 'react-native-path';
import Sound from 'react-native-sound';
import SoundPlayer from 'react-native-sound-player';
import moment from 'moment';
import momenttz from 'moment-timezone';
import Video from 'react-native-video';
const RNFS = require('react-native-fs');
import CameraRoll from "@react-native-community/cameraroll";
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
import AudioRecord from 'react-native-audio-record';
import FastImage from 'react-native-fast-image';
import styles from '../assets/styles/blink/_ContactsListBox.scss';
String.prototype.toDate = function(format)
{
var normalized = this.replace(/[^a-zA-Z0-9]/g, '-');
var normalizedFormat= format.toLowerCase().replace(/[^a-zA-Z0-9]/g, '-');
var formatItems = normalizedFormat.split('-');
var dateItems = normalized.split('-');
var monthIndex = formatItems.indexOf("mm");
var dayIndex = formatItems.indexOf("dd");
var yearIndex = formatItems.indexOf("yyyy");
var hourIndex = formatItems.indexOf("hh");
var minutesIndex = formatItems.indexOf("ii");
var secondsIndex = formatItems.indexOf("ss");
var today = new Date();
var year = yearIndex>-1 ? dateItems[yearIndex] : today.getFullYear();
var month = monthIndex>-1 ? dateItems[monthIndex]-1 : today.getMonth()-1;
var day = dayIndex>-1 ? dateItems[dayIndex] : today.getDate();
var hour = hourIndex>-1 ? dateItems[hourIndex] : today.getHours();
var minute = minutesIndex>-1 ? dateItems[minutesIndex] : today.getMinutes();
var second = secondsIndex>-1 ? dateItems[secondsIndex] : today.getSeconds();
return new Date(year,month,day,hour,minute,second);
};
const audioRecorderPlayer = new AudioRecorderPlayer();
const options = {
sampleRate: 16000, // default 44100
channels: 1, // 1 or 2, default 1
bitsPerSample: 16, // 8 or 16, default 16
audioSource: 6, // android only (see below)
wavFile: 'sylk-audio-recording.wav' // default 'audio.wav'
};
// Note: copy and paste all styles in App.js from my repository
function renderBubble (props) {
let leftColor = 'green';
let rightColor = '#fff';
if (props.currentMessage.failed) {
rightColor = 'red';
leftColor = 'red';
} else {
if (props.currentMessage.pinned) {
rightColor = '#2ecc71';
leftColor = '#2ecc71';
}
}
if (props.currentMessage.image) {
return (
)
} else if (props.currentMessage.video) {
return (
)
} else if (props.currentMessage.audio) {
return (
)
} else {
return (
)
}
}
class ContactsListBox extends Component {
constructor(props) {
super(props);
autoBind(this);
this.chatListRef = React.createRef();
this.default_placeholder = 'Enter message...'
let renderMessages = [];
if (this.props.selectedContact) {
let uri = this.props.selectedContact.uri;
if (uri in this.props.messages) {
renderMessages = this.props.messages[uri];
//renderMessages.sort((a, b) => (a.createdAt < b.createdAt) ? 1 : -1);
renderMessages = renderMessages.sort(function(a, b) {
if (a.createdAt < b.createdAt) {
return 1; //nameA comes first
}
if (a.createdAt > b.createdAt) {
return -1; // nameB comes first
}
if (a.createdAt === b.createdAt) {
if (a.msg_id < b.msg_id) {
return 1; //nameA comes first
}
if (a.msg_id > b.msg_id) {
return -1; // nameB comes first
}
}
return 0; // names must be equal
});
}
}
this.state = {
accountId: this.props.account ? this.props.account.id : null,
password: this.props.password,
targetUri: this.props.selectedContact ? this.props.selectedContact.uri : this.props.targetUri,
favoriteUris: this.props.favoriteUris,
blockedUris: this.props.blockedUris,
isRefreshing: false,
isLandscape: this.props.isLandscape,
contacts: this.props.contacts,
myInvitedParties: this.props.myInvitedParties,
refreshHistory: this.props.refreshHistory,
selectedContact: this.props.selectedContact,
myContacts: this.props.myContacts,
messages: this.props.messages,
renderMessages: GiftedChat.append(renderMessages, []),
chat: this.props.chat,
pinned: false,
message: null,
inviteContacts: this.props.inviteContacts,
shareToContacts: this.props.shareToContacts,
selectMode: this.props.shareToContacts || this.props.inviteContacts,
selectedContacts: this.props.selectedContacts,
pinned: this.props.pinned,
filter: this.props.filter,
periodFilter: this.props.periodFilter,
scrollToBottom: true,
messageZoomFactor: this.props.messageZoomFactor,
isTyping: false,
isLoadingEarlier: false,
fontScale: this.props.fontScale,
call: this.props.call,
isTablet: this.props.isTablet,
ssiCredentials: this.props.ssiCredentials,
ssiConnections: this.props.ssiConnections,
keys: this.props.keys,
recording: false,
playing: false,
texting: false,
audioRecording: null,
cameraAsset: null,
placeholder: this.default_placeholder,
audioSendFinished: false,
messagesCategoryFilter: this.props.messagesCategoryFilter,
isTexting: this.props.isTexting,
showDeleteMessageModal: false,
sourceContact: this.props.sourceContact
}
this.ended = false;
this.recordingTimer = null;
this.outgoingPendMessages = {};
BackHandler.addEventListener('hardwareBackPress', this.backPressed);
this.listenforSoundNotifications()
}
componentDidMount() {
this.ended = false;
}
componentWillUnmount() {
this.ended = true;
this.stopRecordingTimer()
}
backPressed() {
this.stopRecordingTimer()
}
//getDerivedStateFromProps(nextProps, state) {
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.ended) {
return;
}
if (nextProps.myInvitedParties !== this.state.myInvitedParties) {
this.setState({myInvitedParties: nextProps.myInvitedParties});
}
if (nextProps.contacts !== this.state.contacts) {
this.setState({contacts: nextProps.contacts});
}
if (nextProps.favoriteUris !== this.state.favoriteUris) {
this.setState({favoriteUris: nextProps.favoriteUris});
}
if (nextProps.blockedUris !== this.state.blockedUris) {
this.setState({blockedUris: nextProps.blockedUris});
}
if (nextProps.account !== null && nextProps.account !== this.props.account) {
this.setState({accountId: nextProps.account.id});
}
if (nextProps.refreshHistory !== this.state.refreshHistory) {
this.setState({refreshHistory: nextProps.refreshHistory});
this.getServerHistory();
}
if (nextProps.messageZoomFactor !== this.state.messageZoomFactor) {
this.setState({scrollToBottom: false, messageZoomFactor: nextProps.messageZoomFactor});
}
if (nextProps.messagesCategoryFilter !== this.state.messagesCategoryFilter && nextProps.selectedContact) {
this.props.getMessages(nextProps.selectedContact.uri, {category: nextProps.messagesCategoryFilter, pinned: this.state.pinned});
}
if (nextProps.pinned !== this.state.pinned && nextProps.selectedContact) {
this.props.getMessages(nextProps.selectedContact.uri, {category: nextProps.messagesCategoryFilter, pinned: nextProps.pinned});
}
if (nextProps.selectedContact !== this.state.selectedContact) {
//console.log('Selected contact changed to', nextProps.selectedContact);
this.resetContact()
this.setState({selectedContact: nextProps.selectedContact});
if (nextProps.selectedContact) {
this.setState({scrollToBottom: true});
if (Object.keys(this.state.messages).indexOf(nextProps.selectedContact.uri) === -1 && nextProps.selectedContact) {
this.props.getMessages(nextProps.selectedContact.uri);
}
} else {
this.setState({renderMessages: []});
}
};
if (nextProps.myContacts !== this.state.myContacts) {
this.setState({myContacts: nextProps.myContacts});
};
if (nextProps.selectedContact) {
let renderMessages = [];
let uri = nextProps.selectedContact.uri;
if (uri in nextProps.messages) {
renderMessages = nextProps.messages[uri];
// remove duplicate messages no mater what
renderMessages = renderMessages.filter((v,i,a)=>a.findIndex(v2=>['_id'].every(k=>v2[k] ===v[k]))===i);
if (this.state.renderMessages.length < renderMessages.length) {
//console.log('Number of messages changed', this.state.renderMessages.length, '->', renderMessages.length);
this.setState({isLoadingEarlier: false});
this.props.confirmRead(uri);
if (this.state.renderMessages.length > 0 && renderMessages.length > 0) {
let last_message_ts = this.state.renderMessages[0].createdAt;
if (renderMessages[0].createdAt > last_message_ts) {
this.setState({scrollToBottom: true});
}
}
}
}
let delete_ids = [];
Object.keys(this.outgoingPendMessages).forEach((_id) => {
if (renderMessages.some((obj) => obj._id === _id)) {
//console.log('Remove pending message id', _id);
delete_ids.push(_id);
// message exists
} else {
if (this.state.renderMessages.some((obj) => obj._id === _id)) {
//console.log('Pending message id', _id, 'already exists');
} else {
//console.log('Adding pending message id', _id);
renderMessages.push(this.outgoingPendMessages[_id]);
}
}
});
delete_ids.forEach((_id) => {
delete this.outgoingPendMessages[_id];
});
renderMessages = renderMessages.sort(function(a, b) {
if (a.createdAt < b.createdAt) {
return 1; //nameA comes first
}
if (a.createdAt > b.createdAt) {
return -1; // nameB comes first
}
if (a.createdAt === b.createdAt) {
if (a.msg_id < b.msg_id) {
return 1; //nameA comes first
}
if (a.msg_id > b.msg_id) {
return -1; // nameB comes first
}
}
return 0; // names must be equal
});
this.setState({renderMessages: GiftedChat.append(renderMessages, [])});
if (!this.state.scrollToBottom && renderMessages.length > 0) {
//console.log('Scroll to first message');
//this.scrollToMessage(0);
}
}
this.setState({isLandscape: nextProps.isLandscape,
isTablet: nextProps.isTablet,
chat: nextProps.chat,
fontScale: nextProps.fontScale,
filter: nextProps.filter,
call: nextProps.call,
password: nextProps.password,
messages: nextProps.messages,
inviteContacts: nextProps.inviteContacts,
shareToContacts: nextProps.shareToContacts,
selectedContacts: nextProps.selectedContacts,
pinned: nextProps.pinned,
isTyping: nextProps.isTyping,
periodFilter: nextProps.periodFilter,
ssiCredentials: nextProps.ssiCredentials,
ssiConnections: nextProps.ssiConnections,
messagesCategoryFilter: nextProps.messagesCategoryFilter,
targetUri: nextProps.selectedContact ? nextProps.selectedContact.uri : nextProps.targetUri,
keys: nextProps.keys,
sourceContact: nextProps.sourceContact,
isTexting: nextProps.isTexting,
showDeleteMessageModal: nextProps.showDeleteMessageModal,
selectMode: nextProps.shareToContacts || nextProps.inviteContacts
});
if (nextProps.isTyping) {
setTimeout(() => {
this.setState({isTyping: false});
}, 3000);
}
}
listenforSoundNotifications() {
// Subscribe to event(s) you want when component mounted
this._onFinishedPlayingSubscription = SoundPlayer.addEventListener('FinishedPlaying', ({ success }) => {
//console.log('finished playing', success)
this.setState({playing: false, placeholder: this.default_placeholder});
})
this._onFinishedLoadingSubscription = SoundPlayer.addEventListener('FinishedLoading', ({ success }) => {
//console.log('finished loading', success)
})
this._onFinishedLoadingFileSubscription = SoundPlayer.addEventListener('FinishedLoadingFile', ({ success, name, type }) => {
//console.log('finished loading file', success, name, type)
})
this._onFinishedLoadingURLSubscription = SoundPlayer.addEventListener('FinishedLoadingURL', ({ success, url }) => {
//console.log('finished loading url', success, url)
})
}
async _launchCamera() {
let options = {maxWidth: 2000,
maxHeight: 2000,
mediaType: 'mixed',
quality:0.8,
cameraType: 'front',
formatAsMp4: true
}
const cameraAllowed = await this.props.requestCameraPermission();
if (cameraAllowed) {
await launchCamera(options, this.cameraCallback);
}
}
async _launchImageLibrary() {
let options = {maxWidth: 2000,
maxHeight: 2000,
mediaType: 'mixed',
formatAsMp4: true
}
await launchImageLibrary(options, this.libraryCallback);
}
async libraryCallback(result) {
if (!result.assets || result.assets.length === 0) {
return;
}
result.assets.forEach((asset) => {
this.cameraCallback({assets: [asset]});
});
}
async cameraCallback(result) {
if (!result.assets || result.assets.length === 0) {
return;
}
this.setState({scrollToBottom: true});
let asset = result.assets[0];
asset.preview = true;
let msg = await this.props.file2GiftedChat(asset);
let assetType = 'file';
if (msg.video) {
assetType = 'movie';
} else if (msg.image) {
assetType = 'photo';
}
this.outgoingPendMessages[msg.metadata.transfer_id] = msg;
this.setState({renderMessages: GiftedChat.append(this.state.renderMessages, [msg]),
cameraAsset: msg,
placeholder: 'Send ' + assetType + ' of ' + utils.beautySize(msg.metadata.filesize)
});
}
renderCustomActions = props =>
(
)
customInputToolbar = props => {
return (
{this.renderComposer}}
containerStyle={styles.chatInsideRightActionsContainer}
/>
);
};
chatInputChanged(text) {
this.setState({texting: (text.length > 0)})
}
async recordAudio() {
const micAllowed = await this.props.requestMicPermission();
console.log('micAllowed', micAllowed);
if (!micAllowed) {
return;
}
if (!this.state.recording) {
if (this.state.audioRecording) {
this.deleteAudio();
} else {
this.onStartRecord();
}
} else {
this.onStopRecord();
}
}
deleteAudio() {
console.log('Delete audio');
this.setState({audioRecording: null, recording: false});
if (this.recordingStopTimer !== null) {
clearTimeout(this.recordingStopTimer);
this.recordingStopTimer = null;
}
}
async onStartRecord () {
console.log('Start recording...');
this.setState({recording: true});
this.recordingStopTimer = setTimeout(() => {
this.stopRecording();
}, 20000);
if (!AudioRecord) {
AudioRecord.init(options);
}
try {
AudioRecord.start();
} catch (e) {
console.log(e.message);
}
};
stopRecording() {
console.log('Stop recording...');
this.setState({recording: false});
if (this.recordingStopTimer !== null) {
clearTimeout(this.recordingStopTimer);
this.recordingStopTimer = null;
}
this.onRecording(false);
this.onStopRecord();
}
async onStopRecord () {
console.log('Stop recording...');
const result = await AudioRecord.stop();
this.audioRecorded(result);
this.setState({audioRecording: result});
};
resetContact() {
this.stopRecordingTimer()
this.outgoingPendMessages = {};
this.setState({
recording: false,
texting: false,
audioRecording: null,
cameraAsset: null,
placeholder: this.default_placeholder,
audioSendFinished: false
});
}
renderComposer(props) {
return(
this.setState({ composerText: text })}
text={this.state.composerText}
multiline={true}
placeholderTextColor={'red'}
>
)
}
onRecording(state) {
this.setState({recording: state});
if (state) {
this.startRecordingTimer();
} else {
this.stopRecordingTimer()
}
}
startRecordingTimer() {
let i = 0;
this.setState({placeholder: 'Recording audio'});
this.recordingTimer = setInterval(() => {
i = i + 1
this.setState({placeholder: 'Recording audio ' + i + 's'});
}, 1000);
}
stopRecordingTimer() {
if (this.recordingTimer) {
clearInterval(this.recordingTimer);
this.recordingTimer = null;
this.setState({placeholder: this.default_placeholder});
}
}
updateMessageMetadata(metadata) {
let renderMessages = this.state.renderMessages;
let newRenderMessages = [];
renderMessages.forEach((message) => {
if (metadata.transfer_id === message._id) {
message.metadata = metadata;
}
newRenderMessages.push(message);
});
this.setState({renderMessages: GiftedChat.append(newRenderMessages, [])});
}
async startPlaying(message) {
if (this.state.playing || this.state.recording) {
console.log('Already playing or recording');
return;
}
this.setState({playing: true, placeholder: 'Playing audio message'});
message.metadata.playing = true;
this.updateMessageMetadata(message.metadata);
if (Platform.OS === "android") {
const msg = await audioRecorderPlayer.startPlayer(message.audio);
console.log('Audio playback started', message.audio);
audioRecorderPlayer.addPlayBackListener((e) => {
//console.log('duration', e.duration, e.currentPosition);
if (e.duration === e.currentPosition) {
this.setState({playing: false, placeholder: this.default_placeholder});
//console.log('Audio playback ended', message.audio);
message.metadata.playing = false;
this.updateMessageMetadata(message.metadata);
}
this.setState({
currentPositionSec: e.currentPosition,
currentDurationSec: e.duration,
playTime: audioRecorderPlayer.mmssss(Math.floor(e.currentPosition)),
duration: audioRecorderPlayer.mmssss(Math.floor(e.duration)),
});
});
} else {
/*
console.log('startPlaying', file);
this.sound = new Sound(file, '', error => {
if (error) {
console.log('failed to load the file', file, error);
}
});
return;
*/
try {
SoundPlayer.playUrl('file://'+message.audio);
this.setState({playing: true, placeholder: 'Playing audio message'});
} catch (e) {
console.log(`cannot play the sound file`, e)
}
try {
const info = await SoundPlayer.getInfo() // Also, you need to await this because it is async
console.log('Sound info', info) // {duration: 12.416, currentTime: 7.691}
} catch (e) {
console.log('There is no song playing', e)
}
}
};
async stopPlaying(message) {
console.log('Audio playback ended', message.audio);
this.setState({playing: false, placeholder: this.default_placeholder});
message.metadata.playing = false;
this.updateMessageMetadata(message.metadata);
if (Platform.OS === "android") {
const msg = await audioRecorderPlayer.stopPlayer();
} else {
SoundPlayer.stop();
}
}
async audioRecorded(file) {
const placeholder = file ? 'Delete or send audio...' : this.default_placeholder;
if (file) {
console.log('Audio recording ready to send', file);
} else {
console.log('Audio recording removed');
}
this.setState({recording: false, placeholder: placeholder, audioRecording: file});
}
renderSend = (props) => {
let chatRightActionsContainer = Platform.OS === 'ios' ? styles.chatRightActionsContaineriOS : styles.chatRightActionsContainer;
if (this.state.recording) {
return (
);
} else {
if (this.state.cameraAsset) {
return (
);
} else if (this.state.audioRecording) {
return (
);
} else {
if (this.state.playing || (this.state.selectedContact && this.state.selectedContact.tags.indexOf('test') > -1)) {
return ;
} else {
return (
{this.state.texting ?
null
:
}
{this.state.texting ?
null
:
}
);
}
}
}
};
setTargetUri(uri, contact) {
//console.log('Set target uri uri in history list', uri);
this.props.setTargetUri(uri, contact);
}
setFavoriteUri(uri) {
return this.props.setFavoriteUri(uri);
}
setBlockedUri(uri) {
return this.props.setBlockedUri(uri);
}
renderItem(object) {
let item = object.item || object;
let invitedParties = [];
let uri = item.uri;
let myDisplayName;
let username = uri.split('@')[0];
if (this.state.myContacts && this.state.myContacts.hasOwnProperty(uri)) {
myDisplayName = this.state.myContacts[uri].name;
}
if (this.state.myInvitedParties && this.state.myInvitedParties.hasOwnProperty(username)) {
invitedParties = this.state.myInvitedParties[username];
}
if (myDisplayName) {
if (item.name === item.uri || item.name !== myDisplayName) {
item.name = myDisplayName;
}
}
return(
);
}
findObjectByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
}
closeMessageModal() {
this.setState({showMessageModal: false, message: null});
}
closeEditMessageModal() {
this.setState({showEditMessageModal: false, message: null});
}
loadEarlierMessages() {
//console.log('Load earlier messages...');
this.setState({scrollToBottom: false, isLoadingEarlier: true});
this.props.loadEarlierMessages();
}
sendEditedMessage(message, text) {
if (!this.state.selectedContact.uri) {
return;
}
if (message.text === text) {
return;
}
this.props.deleteMessage(message._id, this.state.selectedContact.uri);
message._id = uuid.v4();
message.key = message._id;
message.text = text;
this.props.sendMessage(this.state.selectedContact.uri, message);
}
onSendMessage(messages) {
let uri;
if (!this.state.selectedContact) {
if (this.state.targetUri && this.state.chat) {
let contacts = this.searchedContact(this.state.targetUri);
if (contacts.length !== 1) {
return;
}
uri = contacts[0].uri;
} else {
return;
}
} else {
uri = this.state.selectedContact.uri;
}
messages.forEach((message) => {
/*
sent: true,
// Mark the message as received, using two tick
received: true,
// Mark the message as pending with a clock loader
pending: true,
*/
message.encrypted = this.state.selectedContact && this.state.selectedContact.publicKey ? 2 : 0;
this.props.sendMessage(uri, message);
});
this.setState({renderMessages: GiftedChat.append(this.state.renderMessages, messages)});
}
searchedContact(uri, contact=null) {
if (uri.indexOf(' ') > -1) {
return [];
}
const item = this.props.newContactFunc(uri.toLowerCase(), null, {src: 'search_contact'});
if (!item) {
return [];
}
if (contact) {
item.name = contact.name;
item.photo = contact.photo;
}
return [item];
}
getServerHistory() {
if (!this.state.accountId) {
return;
}
if (this.ended || !this.state.accountId || this.state.isRefreshing) {
return;
}
//console.log('Get server history...');
this.setState({isRefreshing: true});
let history = [];
let localTime;
let getServerCallHistory = new DigestAuthRequest(
'GET',
`${this.props.config.serverCallHistoryUrl}?action=get_history&realm=${this.state.accountId.split('@')[1]}`,
this.state.accountId.split('@')[0],
this.state.password
);
// Disable logging
getServerCallHistory.loggingOn = false;
getServerCallHistory.request((data) => {
if (data.success !== undefined && data.success === false) {
console.log('Error getting call history from server', data.error_message);
return;
}
if (data.received) {
data.received.map(elem => {elem.direction = 'incoming'; return elem});
history = history.concat(data.received);
}
if (data.placed) {
data.placed.map(elem => {elem.direction = 'outgoing'; return elem});
history = history.concat(data.placed);
}
history.sort((a, b) => (a.startTime < b.startTime) ? 1 : -1)
if (history) {
const known = [];
history = history.filter((elem) => {
elem.conference = false;
elem.id = uuid.v4();
if (!elem.tags) {
elem.tags = [];
}
if (elem.remoteParty.indexOf('@conference.') > -1) {
return null;
}
elem.uri = elem.remoteParty.toLowerCase();
let uri_els = elem.uri.split('@');
let username = uri_els[0];
let domain;
if (uri_els.length > 1) {
domain = uri_els[1];
}
if (elem.uri.indexOf('@guest.') > -1) {
if (!elem.displayName) {
elem.uri = 'guest@' + elem.uri.split('@')[1];
} else {
elem.uri = elem.displayName.toLowerCase().replace(/\s|\-|\(|\)/g, '') + '@' + elem.uri.split('@')[1];
}
}
if (utils.isPhoneNumber(elem.uri)) {
username = username.replace(/\s|\-|\(|\)/g, '');
username = username.replace(/^00/, "+");
elem.uri = username;
}
if (known.indexOf(elem.uri) > -1) {
return null;
}
known.push(elem.uri);
if (elem.displayName) {
elem.name = elem.displayName;
} else {
elem.name = elem.uri;
}
if (elem.remoteParty.indexOf('@videoconference.') > -1) {
elem.conference = true;
elem.media = ['audio', 'video', 'chat'];
}
if (elem.uri === this.state.accountId) {
elem.name = this.props.myDisplayName || 'Myself';
}
if (!elem.media || !Array.isArray(elem.media)) {
elem.media = ['audio'];
}
if (elem.timezone !== undefined) {
localTime = momenttz.tz(elem.startTime, elem.timezone).toDate();
elem.startTime = localTime;
elem.timestamp = localTime;
localTime = momenttz.tz(elem.stopTime, elem.timezone).toDate();
elem.stopTime = localTime;
}
if (elem.direction === 'incoming' && elem.duration === 0) {
elem.tags.push('missed');
}
return elem;
});
this.props.saveHistory(history);
if (this.ended) {
return;
}
this.setState({isRefreshing: false});
}
}, (errorCode) => {
console.log('Error getting call history from server', errorCode);
});
this.setState({isRefreshing: false});
}
deleteCameraAsset() {
if (this.state.cameraAsset && this.state.cameraAsset.metadata.transfer_id in this.outgoingPendMessages) {
delete this.outgoingPendMessages[this.state.cameraAsset.metadata.transfer_id]
}
this.setState({cameraAsset: null, placeholder: this.default_placeholder});
this.props.getMessages(this.state.selectedContact.uri);
}
sendCameraAsset() {
this.transferFile(this.state.cameraAsset);
this.setState({cameraAsset: null, placeholder: this.default_placeholder});
}
async sendAudioFile() {
if (this.state.audioRecording) {
this.setState({audioSendFinished: true, placeholder: this.default_placeholder});
setTimeout(() => {
this.setState({audioSendFinished: false});
}, 10);
let msg = await this.props.file2GiftedChat(this.state.audioRecording);
this.transferFile(msg);
this.setState({audioRecording: null});
}
}
async _pickDocument() {
const storageAllowed = await this.props.requestStoragePermission();
if (!storageAllowed) {
return;
}
try {
const result = await DocumentPicker.pick({
type: [DocumentPicker.types.allFiles],
copyTo: 'documentDirectory',
mode: 'import',
allowMultiSelection: false,
});
const fileUri = result[0].fileCopyUri;
if (!fileUri) {
console.log('File URI is undefined or null');
return;
}
let msg = await this.props.file2GiftedChat(fileUri);
this.transferFile(msg);
} catch (err) {
if (DocumentPicker.isCancel(err)) {
console.log('User cancelled file picker');
} else {
console.log('DocumentPicker err => ', err);
throw err;
}
}
};
renderMessageImageOld =(props) => {
/*
return(
this.onMessagePress(context, props.currentMessage)}>
);
*/
return (
)
}
renderMessageImage = (props: any) => {
// https://github.com/FaridSafi/react-native-gifted-chat/issues/1950
const images = [
{
url: props.currentMessage.image
}
];
/*
console.log('single press')}
onLongPress={() => console.log('longpress')}
style={{ backgroundColor: "transparent" }}
>
*/
return (
);
};
postChatSystemMessage(text, imagePath=null) {
var id = uuid.v4();
let giftedChatMessage;
if (imagePath) {
giftedChatMessage = {
_id: id,
key: id,
createdAt: new Date(),
text: text,
image: 'file://' + imagePath,
user: {}
};
} else {
giftedChatMessage = {
_id: id,
key: id,
createdAt: new Date(),
text: text,
system: true,
};
}
this.setState({renderMessages: GiftedChat.append(this.state.renderMessages, [giftedChatMessage])});
}
transferComplete(evt) {
console.log("Upload has finished", evt);
this.postChatSystemMessage('Upload has finished');
}
transferFailed(evt) {
console.log("An error occurred while transferring the file.", evt);
this.postChatSystemMessage('Upload failed')
}
transferCanceled(evt) {
console.log("The transfer has been canceled by the user.");
this.postChatSystemMessage('Upload has canceled')
}
async transferFile(msg) {
msg.metadata.preview = false;
this.props.sendMessage(msg.metadata.receiver.uri, msg, 'application/sylk-file-transfer');
}
matchContact(contact, filter='', tags=[]) {
if (!contact) {
return false;
}
if (tags.indexOf('conference') > -1 && contact.conference) {
return true;
}
if (tags.length > 0 && !tags.some(item => contact.tags.includes(item))) {
return false;
}
if (contact.name && contact.name.toLowerCase().indexOf(filter.toLowerCase()) > -1) {
return true;
}
if (contact.uri.toLowerCase().startsWith(filter.toLowerCase())) {
return true;
}
if (!this.state.selectedContact && contact.conference && contact.metadata && filter.length > 2 && contact.metadata.indexOf(filter) > -1) {
return true;
}
return false;
}
noChatInputToolbar () {
return null;
}
onMessagePress(context, message) {
if (message.metadata && message.metadata.filename) {
//console.log('File metadata', message.metadata);
let file_transfer = message.metadata;
if (!file_transfer.local_url) {
if (!file_transfer.path) {
// this was a local created upload, don't download as the file has not yet been uploaded
this.props.downloadFunc(message.metadata, true);
}
return;
}
RNFS.exists(file_transfer.local_url).then((exists) => {
if (exists) {
if (file_transfer.local_url.endsWith('.asc')) {
if (file_transfer.error) {
this.onLongMessagePress(context, message);
} else {
this.props.decryptFunc(message.metadata);
}
} else {
this.onLongMessagePress(context, message);
//this.openFile(message)
}
} else {
if (file_transfer.path) {
// this was a local created upload, don't download as the file has not yet been uploaded
this.onLongMessagePress(context, message);
} else {
this.props.downloadFunc(message.metadata, true);
}
}
});
} else {
this.onLongMessagePress(context, message);
}
}
openFile(message) {
let file_transfer = message.metadata;
let file_path = file_transfer.local_url;
if (!file_path) {
console.log('Cannot open empty path');
return;
}
if (file_path.endsWith('.asc')) {
file_path = file_path.slice(0, -4);
console.log('Open decrypted file', file_path)
} else {
console.log('Open file', file_path)
}
if (utils.isAudio(file_transfer.filename)) {
// this.startPlaying(file_path);
return;
}
RNFS.exists(file_path).then((exists) => {
if (exists) {
FileViewer.open(file_path, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
// error
});
} else {
console.log(file_path, 'does not exist');
return;
}
});
}
onLongMessagePress(context, currentMessage) {
if (!currentMessage.metadata) {
currentMessage.metadata = {};
}
//console.log('currentMessage', currentMessage);
if (currentMessage && currentMessage.text) {
let isSsiMessage = this.state.selectedContact && this.state.selectedContact.tags.indexOf('ssi') > -1;
let options = []
if (currentMessage.metadata && !currentMessage.metadata.error) {
if (!isSsiMessage && this.isMessageEditable(currentMessage)) {
options.push('Edit');
}
if (currentMessage.metadata && currentMessage.metadata.local_url) {
options.push('Open')
//
} else {
options.push('Copy');
}
}
if (!isSsiMessage) {
options.push('Delete');
}
let showResend = currentMessage.failed || (currentMessage.direction === 'outgoing' && !currentMessage.sent && !currentMessage.received && !currentMessage.pending);
if (currentMessage.metadata && currentMessage.metadata.error) {
showResend = false;
}
if (this.state.targetUri.indexOf('@videoconference') === -1) {
if (currentMessage.direction === 'outgoing') {
if (showResend) {
options.push('Resend')
}
}
}
if (currentMessage.pinned) {
options.push('Unpin');
} else {
if (!isSsiMessage && !currentMessage.metadata.error) {
options.push('Pin');
}
}
//options.push('Info');
if (!isSsiMessage && !currentMessage.metadata.error) {
options.push('Forward');
}
if (!isSsiMessage && !currentMessage.metadata.error) {
options.push('Share');
}
if (currentMessage.metadata && currentMessage.metadata.filename) {
if (!currentMessage.metadata.filename.local_url || currentMessage.metadata.filename.error) {
options.push('Download again');
} else {
options.push('Download');
}
}
options.push('Cancel');
let l = options.length - 1;
context.actionSheet().showActionSheetWithOptions({options, l}, (buttonIndex) => {
let action = options[buttonIndex];
if (action === 'Copy') {
Clipboard.setString(currentMessage.text);
} else if (action === 'Delete') {
this.setState({showDeleteMessageModal: true, currentMessage: currentMessage});
} else if (action === 'Pin') {
this.props.pinMessage(currentMessage._id);
} else if (action === 'Unpin') {
this.props.unpinMessage(currentMessage._id);
} else if (action === 'Info') {
this.setState({message: currentMessage, showMessageModal: true});
} else if (action === 'Edit') {
this.setState({message: currentMessage, showEditMessageModal: true});
} else if (action.startsWith('Share')) {
this.setState({message: currentMessage, showShareMessageModal: true});
} else if (action.startsWith('Forward')) {
this.props.forwardMessageFunc(currentMessage, this.state.targetUri);
} else if (action === 'Resend') {
this.props.reSendMessage(currentMessage, this.state.targetUri);
} else if (action === 'Save') {
this.savePicture(currentMessage.local_url);
} else if (action.startsWith('Download')) {
console.log('Starting download...');
this.props.downloadFunc(currentMessage.metadata, true);
} else if (action === 'Open') {
FileViewer.open(currentMessage.metadata.local_url, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
console.log('Failed to open', currentMessage, error.message);
});
}
});
}
};
isMessageEditable(message) {
if (message.direction === 'incoming') {
return false;
}
if (message.image || message.audio || message.video) {
return false;
}
if (message.metadata && message.metadata.filename) {
return false;
}
return true;
}
closeDeleteMessageModal() {
this.setState({showDeleteMessageModal: false});
}
async hasAndroidPermission() {
const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;
const hasPermission = await PermissionsAndroid.check(permission);
if (hasPermission) {
return true;
}
const status = await PermissionsAndroid.request(permission);
return status === 'granted';
}
async savePicture(file) {
if (Platform.OS === "android" && !(await this.hasAndroidPermission())) {
return;
}
file = 'file://' + file;
console.log('Save to camera roll', file);
CameraRoll.save(file);
};
shouldUpdateMessage(props, nextProps) {
return true;
}
toggleShareMessageModal() {
this.setState({showShareMessageModal: !this.state.showShareMessageModal});
}
renderMessageVideo(props){
const { currentMessage } = props;
return (
);
};
renderMessageAudio(props){
const { currentMessage } = props;
let playAudioButtonStyle = Platform.OS === 'ios' ? styles.playAudioButtoniOS : styles.playAudioButton;
if (currentMessage.metadata.playing === true) {
return (
this.stopPlaying(currentMessage)}
style={playAudioButtonStyle}
icon="pause"
/>
);
} else {
return (
this.startPlaying(currentMessage)}
style={playAudioButtonStyle}
icon="play"
/>
);
}
};
videoError() {
console.log('Video streaming error');
}
onBuffer() {
console.log('Video buffer error');
}
// https://github.com/FaridSafi/react-native-gifted-chat/issues/571
// add view after bubble
renderMessageText(props) {
const { currentMessage } = props;
if (currentMessage.video || currentMessage.audio) {
return (
);
} else if (currentMessage.image) {
return (
);
} else {
return (
);
}
};
renderTime = (props) => {
const { currentMessage } = props;
if (currentMessage.metadata && currentMessage.metadata.preview) {
return null;
}
if (currentMessage.video) {
return (
)
} else if (currentMessage.audio) {
return (
)
} else {
return (
)
}
}
scrollToMessage(id) {
//console.log('scrollToMessage', id);
//https://github.com/FaridSafi/react-native-gifted-chat/issues/938
this.chatListRef.current?._messageContainerRef?.current?.scrollToIndex({
animated: true,
index: id
});
}
get showChat() {
if (this.state.selectedContact) {
if (this.state.selectedContact.tags && this.state.selectedContact.tags.indexOf('blocked') > -1) {
return false;
}
if (this.state.selectedContact.uri.indexOf('@guest.') > -1) {
return false;
}
if (this.state.selectedContact.uri.indexOf('anonymous@') > -1) {
return false;
}
}
let username = this.state.targetUri ? this.state.targetUri.split('@')[0] : null;
let isPhoneNumber = username ? username.match(/^(\+|0)(\d+)$/) : false;
if (isPhoneNumber) {
return false;
}
if (this.props.selectedContact) {
return true;
}
return false;
}
ssi2GiftedChat(from_uri, content, timestamp) {
let id = uuid.v4();
let msg;
msg = {
_id: id,
key: id,
text: content,
createdAt: timestamp,
direction: 'incoming',
sent: false,
received: true,
pending: false,
system: false,
failed: false,
user: {_id: from_uri, name: from_uri}
}
return msg;
}
getSsiContacts() {
//console.log('Get SSI contacts');
let contacts = [];
if (this.state.ssiCredentials) {
this.state.ssiCredentials.forEach((item) => {
let contact = this.props.newContactFunc(item.id, 'Credential');
contact.ssiCredential = item;
contact.credential = new Object();
const schemaId = item.metadata.data['_internal/indyCredential'].schemaId;
if (schemaId === 'EwAf16U6ZphXsZq6E5qmPz:2:Bloqzone_IDIN_ver5:5.0') {
contact.schemaId = schemaId;
item.credentialAttributes.forEach((attribute) => {
contact.credential[attribute.name] = attribute.value;
if (attribute.value.length > 0) {
if (attribute.name === 'legalName') {
contact.name = attribute.value;
} else if (attribute.name === 'acceptDateTime') {
contact.timestamp = attribute.value.toDate("dd-mm-yy hh:ii:ss");
} else if (attribute.name === 'createdAt') {
contact.timestamp = attribute.value;
} else if (attribute.name === 'emailAddress') {
contact.email = attribute.value;
}
}
});
}
if (contact.credential.initials) {
contact.name = contact.credential.initials;
}
if (contact.credential.legalName) {
contact.name = contact.name + ' ' + contact.credential.legalName;
}
if (contact.credential.dob) {
contact.name = contact.name + ' (' + contact.credential.dob + ')';
}
if (contact.credential.birthDate) {
contact.name = contact.name + ' (' + contact.credential.birthDate + ')';
}
if (contact.credential.acceptDateTime && item.state === 'done') {
contact.lastMessage = 'Credential issued at ' + contact.credential.acceptDateTime + ' (' + item.state + ')';
}
contact.tags.push('ssi');
contact.tags.push('ssi-credential');
contact.tags.push('readonly');
contacts.push(contact);
});
}
if (this.state.ssiConnections) {
this.state.ssiConnections.forEach((item) => {
//console.log('Contacts SSI connection', item);
let uri = item.id;
let contact = this.props.newContactFunc(uri, item.theirLabel);
contact.credential = new Object();
contact.timestamp = item.createdAt;
contact.lastMessage = 'Connection is ' + item.state;
contact.tags.push('ssi');
contact.tags.push('ssi-connection');
contact.ssiConnection = item;
contacts.push(contact);
});
}
return contacts;
}
render() {
let searchExtraItems = [];
let items = [];
let matchedContacts = [];
let ssiContacts = [];
let messages = this.state.renderMessages;
let contacts = [];
//console.log('----');
//console.log('--- Render contacts with filter', this.state.filter);
//console.log('--- Render contacts', this.state.selectedContact);
//console.log(this.state.renderMessages);
if (this.state.filter === 'ssi') {
contacts = this.getSsiContacts();
} else {
Object.keys(this.state.myContacts).forEach((uri) => {
contacts.push(this.state.myContacts[uri]);
});
}
let chatInputClass = this.customInputToolbar;
if (this.state.selectedContact) {
if (this.state.selectedContact.uri.indexOf('@videoconference') > -1) {
chatInputClass = this.noChatInputToolbar;
}
if (this.state.selectedContact.tags.indexOf('test') > -1) {
chatInputClass = this.noChatInputToolbar;
}
} else if (!this.state.chat) {
chatInputClass = this.noChatInputToolbar;
}
if (!this.state.selectedContact && this.state.filter) {
items = contacts.filter(contact => this.matchContact(contact, this.state.targetUri, [this.state.filter]));
} else {
items = contacts.filter(contact => this.matchContact(contact, this.state.targetUri));
searchExtraItems = searchExtraItems.concat(this.state.contacts);
if (this.state.targetUri && this.state.targetUri.length > 2 && !this.state.selectedContact && !this.state.inviteContacts) {
matchedContacts = searchExtraItems.filter(contact => this.matchContact(contact, this.state.targetUri));
} else if (this.state.selectedContact && this.state.selectedContact.type === 'contact') {
matchedContacts.push(this.state.selectedContact);
} else if (this.state.selectedContact) {
items = [this.state.selectedContact];
}
items = items.concat(matchedContacts);
}
if (this.state.targetUri) {
items = items.concat(this.searchedContact(this.state.targetUri, this.state.selectedContact));
}
if (this.state.filter && this.state.targetUri) {
items = contacts.filter(contact => this.matchContact(contact, this.state.targetUri));
}
const known = [];
items = items.filter((elem) => {
if (this.state.shareToContacts && elem.tags.indexOf('test') > -1) {
return;
}
+ if (this.state.shareToContacts && elem.uri.indexOf('videoconference') > -1) {
+ return;
+ }
+
if (this.state.sourceContact && this.state.sourceContact.uri === elem.uri) {
return;
}
if (this.state.inviteContacts && elem.tags.indexOf('conference') > -1 ) {
return;
}
if (this.state.shareToContacts && elem.uri === this.state.accountId) {
return;
}
if (this.state.accountId === elem.uri && elem.tags.length === 0) {
return;
}
if (this.state.shareToContacts && elem.uri.indexOf('@') === -1) {
return;
}
if (known.indexOf(elem.uri) <= -1) {
known.push(elem.uri);
return elem;
}
});
items.forEach((item) => {
item.showActions = false;
if (item.uri.indexOf('@videoconference.') === -1) {
item.conference = false;
} else {
item.conference = true;
}
if (this.state.selectedContacts && this.state.selectedContacts.indexOf(item.uri) > -1) {
item.selected = true;
} else {
item.selected = false;
}
});
let filteredItems = [];
items.reverse();
var todayStart = new Date();
todayStart.setHours(0,0,0,0);
var yesterdayStart = new Date();
yesterdayStart.setDate(todayStart.getDate() - 2);
yesterdayStart.setHours(0,0,0,0);
items.forEach((item) => {
const fromDomain = '@' + item.uri.split('@')[1];
if (this.state.periodFilter === 'today') {
if(item.timestamp < todayStart) {
return;
}
}
if (item.uri === 'anonymous@anonymous.invalid' && this.state.filter !== 'blocked') {
return;
}
if (this.state.periodFilter === 'yesterday') {
if(item.timestamp < yesterdayStart || item.timestamp > todayStart) {
return;
}
}
if (this.state.inviteContacts && item.uri.indexOf('@videoconference.') > -1) {
return;
}
if (item.uri === this.state.accountId && !item.direction) {
return;
}
if (this.state.filter && item.tags.indexOf(this.state.filter) > -1) {
filteredItems.push(item);
} else if (this.state.blockedUris.indexOf(item.uri) === -1 && this.state.blockedUris.indexOf(fromDomain) === -1) {
filteredItems.push(item);
}
//console.log(item.uri, item.tags);
});
items = filteredItems;
items.sort((a, b) => (a.timestamp < b.timestamp) ? 1 : -1)
if (items.length === 1) {
items[0].showActions = true;
if (items[0].tags.indexOf('ssi-credential') > -1) {
let content = '';
let m;
chatInputClass = this.noChatInputToolbar;
items[0].ssiCredential.credentialAttributes.forEach((attribute) => {
content = content + attribute.name + ": " + attribute.value + '\n';
});
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
m = this.ssi2GiftedChat(items[0].uri, 'SSI credential body' , items[0].timestamp);
m.system = true;
messages.push(m);
content = '';
content = content + 'Id: ' + items[0].ssiCredential.id;
content = content + '\nState: ' + items[0].ssiCredential.state;
content = content + '\nSchema Id:' + items[0].schemaId;
let issuer = this.state.ssiConnections.filter(x => x.id === items[0].ssiCredential.connectionId);
if (issuer.length === 1) {
content = content + '\nIssuer: ' + issuer[0].theirLabel;
} else {
content = content + '\nIssuer: : ' + items[0].ssiCredential.connectionId;
}
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
m = this.ssi2GiftedChat(items[0].uri, 'SSI credential details' , items[0].timestamp);
m.system = true;
messages.push(m);
}
if (items[0].tags.indexOf('ssi-connection') > -1) {
let content = '';
let m;
m = this.ssi2GiftedChat(items[0].uri, 'SSI messages' , items[0].timestamp);
m.system = true;
messages.push(m);
chatInputClass = this.noChatInputToolbar;
content = 'Role: ' + items[0].ssiConnection.role;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
content = 'State: ' + items[0].ssiConnection.state;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
content = 'Multiple use: ' + items[0].ssiConnection.multiUseInvitation;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
if (items[0].ssiConnection.mediatorId) {
content = 'Mediator: ' + items[0].ssiConnection.mediatorId;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
}
content = 'Id: ' + items[0].ssiConnection.id;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
content = 'Did: ' + items[0].ssiConnection.did;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
content = 'From: ' + items[0].ssiConnection.theirLabel;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
m = this.ssi2GiftedChat(items[0].uri, 'SSI connection details' , items[0].timestamp);
m.system = true;
messages.push(m);
}
}
let columns = 1;
if (this.state.isTablet) {
columns = this.props.orientation === 'landscape' ? 3 : 2;
} else {
columns = this.props.orientation === 'landscape' ? 2 : 1;
}
const chatContainer = this.props.orientation === 'landscape' ? styles.chatLandscapeContainer : styles.chatPortraitContainer;
const container = this.props.orientation === 'landscape' ? styles.landscapeContainer : styles.portraitContainer;
const contactsContainer = this.props.orientation === 'landscape' ? styles.contactsLandscapeContainer : styles.contactsPortraitContainer;
const borderClass = (messages.length > 0 && !this.state.chat) ? styles.chatBorder : null;
let pinned_messages = [];
if (this.state.pinned) {
messages.forEach((m) => {
if (m.pinned) {
pinned_messages.push(m);
}
});
messages = pinned_messages;
if (pinned_messages.length === 0) {
let msg = {
_id: uuid.v4(),
text: 'No pinned messages found. Touch individual messages to pin them.',
system: true
}
pinned_messages.push(msg);
}
}
let showLoadEarlier = (this.state.myContacts && this.state.selectedContact && this.state.selectedContact.uri in this.state.myContacts && this.state.myContacts[this.state.selectedContact.uri].totalMessages && this.state.myContacts[this.state.selectedContact.uri].totalMessages > messages.length) ? true: false;
messages.forEach((m) => {
});
//
return (
{items.length === 1 ?
(null) // this.renderItem(items[0])
:
item.id}
key={this.props.orientation}
loadEarlier
/>
}
{this.showChat && !this.state.inviteContacts?
this.chatInputChanged(text)}
/>
: (items.length === 1) ?
{ return null }}
renderBubble={renderBubble}
renderMessageText={this.renderMessageText}
renderMessageImage={this.renderMessageImage}
renderMessageAudio={this.renderMessageAudio}
renderMessageVideo={this.renderMessageVideo}
onSend={this.onSendMessage}
lockStyle={styles.lock}
onLongPress={this.onLongMessagePress}
shouldUpdateMessage={this.shouldUpdateMessage}
onPress={this.onMessagePress}
scrollToBottom={this.state.scrollToBottom}
inverted={true}
timeTextStyle={{ left: { color: 'red' }, right: { color: 'black' } }}
infiniteScroll
loadEarlier={showLoadEarlier}
onLoadEarlier={this.loadEarlierMessages}
/>
: null
}
);
}
}
ContactsListBox.propTypes = {
account : PropTypes.object,
password : PropTypes.string.isRequired,
config : PropTypes.object.isRequired,
targetUri : PropTypes.string,
selectedContact : PropTypes.object,
contacts : PropTypes.array,
chat : PropTypes.bool,
orientation : PropTypes.string,
setTargetUri : PropTypes.func,
isTablet : PropTypes.bool,
isLandscape : PropTypes.bool,
refreshHistory : PropTypes.bool,
saveHistory : PropTypes.func,
myDisplayName : PropTypes.string,
myPhoneNumber : PropTypes.string,
setFavoriteUri : PropTypes.func,
saveConference : PropTypes.func,
myInvitedParties: PropTypes.object,
setBlockedUri : PropTypes.func,
favoriteUris : PropTypes.array,
blockedUris : PropTypes.array,
filter : PropTypes.string,
periodFilter : PropTypes.string,
defaultDomain : PropTypes.string,
saveContact : PropTypes.func,
myContacts : PropTypes.object,
messages : PropTypes.object,
getMessages : PropTypes.func,
sendMessage : PropTypes.func,
reSendMessage : PropTypes.func,
deleteMessage : PropTypes.func,
pinMessage : PropTypes.func,
unpinMessage : PropTypes.func,
deleteMessages : PropTypes.func,
sendPublicKey : PropTypes.func,
inviteContacts : PropTypes.bool,
shareToContacts : PropTypes.bool,
selectedContacts: PropTypes.array,
toggleBlocked : PropTypes.func,
togglePinned : PropTypes.func,
loadEarlierMessages: PropTypes.func,
newContactFunc : PropTypes.func,
messageZoomFactor: PropTypes.string,
isTyping : PropTypes.bool,
fontScale : PropTypes.number,
call : PropTypes.object,
ssiCredentials : PropTypes.array,
ssiConnections : PropTypes.array,
keys : PropTypes.object,
downloadFunc : PropTypes.func,
decryptFunc : PropTypes.func,
forwardMessageFunc: PropTypes.func,
messagesCategoryFilter: PropTypes.string,
startCall: PropTypes.func,
sourceContact: PropTypes.object,
requestCameraPermission: PropTypes.func,
requestMicPermission: PropTypes.func,
requestStoragePermissions: PropTypes.func,
file2GiftedChat: PropTypes.func
};
export default ContactsListBox;
exports.renderBubble = renderBubble;
diff --git a/app/components/ReadyBox.js b/app/components/ReadyBox.js
index fcf327c..b4efca6 100644
--- a/app/components/ReadyBox.js
+++ b/app/components/ReadyBox.js
@@ -1,1544 +1,1551 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import autoBind from 'auto-bind';
import { FlatList, View, Platform, TouchableHighlight, TouchableOpacity} from 'react-native';
import { IconButton, Title, Button, Colors, Text, ActivityIndicator } from 'react-native-paper';
import ConferenceModal from './ConferenceModal';
import ContactsListBox from './ContactsListBox';
import FooterBox from './FooterBox';
import URIInput from './URIInput';
import config from '../config';
import utils from '../utils';
import styles from '../assets/styles/blink/_ReadyBox.scss';
import {Keyboard} from 'react-native';
import QRCodeScanner from 'react-native-qrcode-scanner';
import { RNCamera } from 'react-native-camera';
import AudioRecord from 'react-native-audio-record';
import uuid from 'react-native-uuid';
import RNFetchBlob from "rn-fetch-blob";
import fileType from 'react-native-file-type';
class ReadyBox extends Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
targetUri: this.props.selectedContact ? this.props.selectedContact.uri : '',
contacts: this.props.contacts,
selectedContact: this.props.selectedContact,
showConferenceModal: this.props.showConferenceModal,
sticky: false,
favoriteUris: this.props.favoriteUris,
blockedUris: this.props.blockedUris,
historyCategoryFilter: null,
messagesCategoryFilter: null,
historyPeriodFilter: null,
missedCalls: this.props.missedCalls,
isLandscape: this.props.isLandscape,
participants: null,
myInvitedParties: this.props.myInvitedParties,
messages: this.props.messages,
myDisplayName: this.props.myDisplayName,
chat: (this.props.selectedContact !== null) && (this.props.call !== null),
call: this.props.call,
inviteContacts: this.props.inviteContacts,
shareToContacts: this.props.shareToContacts,
selectedContacts: this.props.selectedContacts,
pinned: this.props.pinned,
messageZoomFactor: this.props.messageZoomFactor,
isTyping: this.props.isTyping,
navigationItems: this.props.navigationItems,
fontScale: this.props.fontScale,
historyFilter: this.props.historyFilter,
isTablet: this.props.isTablet,
myContacts: this.props.myContacts,
showQRCodeScanner: this.props.showQRCodeScanner,
ssiCredentials: this.props.ssiCredentials,
ssiConnections: this.props.ssiConnections,
keys: this.props.keys,
isTexting: this.props.isTexting,
keyboardVisible: this.props.keyboardVisible,
contentTypes: this.props.contentTypes,
sourceContact: this.props.sourceContact
};
this.ended = false;
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.ended) {
return;
}
if (this.state.selectedContact) {
this.setState({targetUri: nextProps.selectedContact ? nextProps.selectedContact.uri : '', chat: false});
}
if (!this.state.inviteContacts && nextProps.inviteContacts) {
this.handleTargetChange('');
this.setState({chat: false});
}
if (this.state.selectedContact !== nextProps.selectedContact && nextProps.selectedContact) {
this.setState({chat: !this.chatDisabledForUri(nextProps.selectedContact.uri)});
}
if (nextProps.selectedContact !== this.state.selectedContact) {
this.resetContact()
this.setState({'messagesCategoryFilter': null});
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: 0});
}
if (this.state.selectedContact && this.state.pinned) {
this.props.togglePinned(this.state.selectedContact.uri);
}
}
if (!nextProps.historyFilter && this.state.historyFilter) {
this.filterHistory(null);
}
if (nextProps.historyFilter === 'ssi' && !nextProps.selectedContact) {
this.setState({'historyCategoryFilter': 'ssi'});
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: this.navigationItems.length-1});
}
}
if (nextProps.missedCalls.length === 0 && this.state.historyCategoryFilter === 'missed') {
this.setState({'historyCategoryFilter': null});
}
if (nextProps.blockedUris.length === 0 && this.state.historyCategoryFilter === 'blocked') {
this.setState({'historyCategoryFilter': null});
}
if (nextProps.favoriteUris.length === 0 && this.state.historyCategoryFilter === 'favorite') {
this.setState({'historyCategoryFilter': null});
}
if (Object.keys(this.state.myContacts).length === 0 && nextProps.myContacts && Object.keys(nextProps.myContacts).length > 0) {
this.bounceNavigation();
}
this.setState({myInvitedParties: nextProps.myInvitedParties,
myContacts: nextProps.myContacts,
messages: nextProps.messages,
historyFilter: nextProps.historyFilter,
myDisplayName: nextProps.myDisplayName,
call: nextProps.call,
showConferenceModal: nextProps.showConferenceModal,
isTyping: nextProps.isTyping,
navigationItems: nextProps.navigationItems,
messageZoomFactor: nextProps.messageZoomFactor,
contacts: nextProps.contacts,
inviteContacts: nextProps.inviteContacts,
shareToContacts: nextProps.shareToContacts,
selectedContacts: nextProps.selectedContacts,
selectedContact: nextProps.selectedContact,
pinned: nextProps.pinned,
favoriteUris: nextProps.favoriteUris,
blockedUris: nextProps.blockedUris,
missedCalls: nextProps.missedCalls,
fontScale: nextProps.fontScale,
isTablet: nextProps.isTablet,
showQRCodeScanner: nextProps.showQRCodeScanner,
isLandscape: nextProps.isLandscape,
ssiCredentials: nextProps.ssiCredentials,
ssiConnections: nextProps.ssiConnections,
keys: nextProps.keys,
isTexting: nextProps.isTexting,
keyboardVisible: nextProps.keyboardVisible,
contentTypes: nextProps.contentTypes,
sourceContact: nextProps.sourceContact
});
}
getTargetUri(uri) {
return utils.normalizeUri(uri, this.props.defaultDomain);
}
async componentDidMount() {
this.ended = false;
}
componentWillUnmount() {
this.ended = true;
}
filterHistory(filter) {
if (this.ended) {
return;
}
//console.log('filterHistory', filter);
if (this.state.selectedContact) {
if (!filter && this.state.pinned) {
this.props.togglePinned(this.state.selectedContact.uri);
}
if (filter === 'pinned') {
this.props.togglePinned(this.state.selectedContact.uri);
return;
}
if (filter === this.state.messagesCategoryFilter) {
this.setState({'messagesCategoryFilter': null});
} else {
this.setState({'messagesCategoryFilter': filter});
}
return;
}
this.props.filterHistoryFunc(filter);
if (!filter) {
this.setState({'historyPeriodFilter': null, historyCategoryFilter: null});
} else if (filter === 'today' || filter === 'yesterday') {
filter = this.state.historyPeriodFilter === filter ? null : filter;
this.setState({'historyPeriodFilter': filter});
} else {
this.setState({'historyCategoryFilter': filter});
}
this.handleTargetChange('');
}
chatDisabledForUri(uri) {
if (uri.indexOf('@videoconference') > -1) {
return true;
}
if (uri.indexOf('@guest') > -1) {
return true;
}
if (uri.indexOf('3333@') > -1) {
return true;
}
if (uri.indexOf('4444@') > -1) {
return true;
}
return false;
}
get showNavigationBar() {
if (this.state.keyboardVisible) {
return;
}
if (this.state.selectedContact) {
//return false;
}
if (this.state.recording) {
return false;
}
- if (this.state.audioRecording) {
- return false;
- }
-
return true;
}
get showSearchBar() {
if (this.state.selectedContact && !this.state.isTablet) {
return false;
}
if (this.state.showQRCodeScanner) {
return false;
}
if (this.state.isTablet || (!this.state.isLandscape && this.state.selectedContact)) {
return true;
}
if (this.state.call && this.state.call.state !== 'incoming' && !this.state.inviteContacts) {
return false;
}
return true;
}
get showConferenceButton() {
if (this.state.selectedContact) {
return false;
}
if (this.state.shareToContacts) {
return false;
}
return true;
}
get showAudioSendButton() {
if (!this.state.selectedContact) {
return false;
}
return true;
}
get showAudioCancelButton() {
if (!this.state.audioRecording) {
return false;
}
return true;
}
get showAudioRecordButton() {
if (!this.state.selectedContact) {
return false;
}
if (this.state.audioRecording) {
return false;
}
return true;
}
get showButtonsBar() {
if (this.state.historyCategoryFilter === 'blocked') {
return false;
}
if (this.state.historyCategoryFilter === 'ssi') {
return false;
}
if (this.state.showQRCodeScanner) {
return false;
}
if (this.state.isTablet) {
return true;
}
if (this.state.call) {
return true;
}
if (!this.state.targetUri) {
return true;
}
/*
if (this.state.selectedContact) {
if (this.state.isLandscape && !this.state.isTablet) {
return false;
}
return false;
}
*/
return true;
}
handleTargetChange(new_uri, contact) {
//console.log('---handleTargetChange new_uri =', new_uri);
//console.log('handleTargetChange contact =', contact);
if ((this.state.inviteContacts || this.state.shareToContacts) && contact) {
const uri = contact.uri;
this.props.updateSelection(uri);
return;
}
// This URLs are used to request SSI credentials
if (new_uri && new_uri.startsWith('https://didcomm.issuer.bloqzone.com?c_i=')) {
this.props.handleSSIEnrolment(new_uri);
return;
}
// This URLs are used to request SSI credentials
if (new_uri && new_uri.startsWith('https://ssimandate.vismaconnect.nl/api/acapy?c_i=')) {
this.props.handleSSIEnrolment(new_uri);
return;
}
if (contact && contact.tags.indexOf('ssi') > -1 && this.state.selectedContact !== contact) {
this.setState({'historyCategoryFilter': 'ssi'});
}
if (this.state.selectedContact === contact) {
if (this.state.chat) {
this.setState({chat: false});
}
return;
} else {
this.setState({chat: false});
}
let new_value = new_uri;
if (contact) {
if (this.state.targetUri === contact.uri) {
new_value = '';
}
} else {
contact = null;
}
if (this.state.targetUri === new_uri) {
new_value = '';
}
if (new_value === '') {
contact = null;
}
if (new_value.indexOf(' ') === -1) {
new_value = new_value.trim().toLowerCase();
}
//new_value = new_value.replace(' ','');
//console.log('--- Select new contact', contact? contact.uri : null);
//console.log('--- Select new targetUri', new_value);
this.props.selectContact(contact);
this.setState({targetUri: new_value});
}
handleTargetSelect() {
if (this.props.connection === null) {
this.props._notificationCenter.postSystemNotification("Server unreachable");
return;
}
let uri = this.state.targetUri.toLowerCase();
if (uri.endsWith(`@${config.defaultConferenceDomain}`)) {
let participants;
if (this.state.myInvitedParties && this.state.myInvitedParties.hasOwnProperty(uri)) {
participants = this.state.myInvitedParties[uri];
}
this.props.startConference(uri, {audio: true, video: true, participants: this.state.participants});
} else {
this.props.startCall(this.getTargetUri(uri), {audio: true, video: true});
}
}
shareContent() {
this.props.shareContent();
}
cancelShareContent() {
this.props.cancelShareContent();
}
showConferenceModal(event) {
event.preventDefault();
this.props.showConferenceModalFunc();
}
handleChat(event) {
event.preventDefault();
let targetUri;
if (!this.state.chat && !this.state.selectedContact) {
targetUri = this.getTargetUri(this.state.targetUri);
this.setState({targetUri: targetUri});
}
let uri = this.state.targetUri.trim().toLowerCase();
if (!this.state.chat && !this.selectedContact && uri) {
if (uri.indexOf('@') === -1) {
uri = uri + '@' + this.props.defaultDomain;
}
let contact = this.props.newContactFunc(uri, null, {src: 'new chat'});
console.log('Create synthetic contact', contact);
this.props.selectContact(contact);
this.setState({targetUri: uri, chat: true});
Keyboard.dismiss();
}
this.setState({chat: !this.state.chat});
}
handleAudioCall(event) {
let uri;
if (this.state.selectedContact) {
uri = this.state.selectedContact.uri;
} else {
event.preventDefault();
Keyboard.dismiss();
uri = this.state.targetUri.trim().toLowerCase();
var uri_parts = uri.split("/");
if (uri_parts.length === 5 && uri_parts[0] === 'https:') {
// https://webrtc.sipthor.net/conference/DaffodilFlyChill0 from external web link
// https://webrtc.sipthor.net/call/alice@example.com from external web link
let event = uri_parts[3];
uri = uri_parts[4];
if (event === 'conference') {
uri = uri.split("@")[0] + '@' + config.defaultConferenceDomain;
}
}
}
if (uri.endsWith(`@${config.defaultConferenceDomain}`)) {
this.props.startConference(uri, {audio: true, video: false});
} else {
this.props.startCall(this.getTargetUri(uri), {audio: true, video: false});
}
}
handleVideoCall(event) {
let uri;
if (this.state.selectedContact) {
uri = this.state.selectedContact.uri;
} else {
event.preventDefault();
Keyboard.dismiss();
uri = this.state.targetUri.trim().toLowerCase();
var uri_parts = uri.split("/");
if (uri_parts.length === 5 && uri_parts[0] === 'https:') {
// https://webrtc.sipthor.net/conference/DaffodilFlyChill0 from external web link
// https://webrtc.sipthor.net/call/alice@example.com from external web link
let event = uri_parts[3];
uri = uri_parts[4];
if (event === 'conference') {
uri = uri.split("@")[0] + '@' + config.defaultConferenceDomain;
}
}
}
if (uri.endsWith(`@${config.defaultConferenceDomain}`)) {
this.props.startConference(uri, {audio: true, video: true});
} else {
this.props.startCall(this.getTargetUri(uri), {audio: true, video: true});
}
}
handleConferenceCall(targetUri, options={audio: true, video: true, participants: []}) {
Keyboard.dismiss();
this.props.startConference(targetUri, {audio: options.audio, video: options.video, participants: options.participants});
this.props.hideConferenceModalFunc();
}
get chatButtonDisabled() {
let uri = this.state.targetUri.trim();
if (this.state.selectedContact) {
return true;
}
if (this.state.shareToContacts) {
return true;
}
if (!uri || uri.indexOf(' ') > -1 || uri.indexOf('@guest.') > -1 || uri.indexOf('@videoconference') > -1) {
return true;
}
let username = uri.split('@')[0];
let isPhoneNumber = username.match(/^(\+|0)(\d+)$/);
if (isPhoneNumber) {
return true;
}
if (uri.indexOf('@') > -1) {
let email_reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/;
let validEmail = email_reg.test(uri);
if (!validEmail) {
return true;
}
}
if (this.chatDisabledForUri(uri)) {
return true;
}
return false;
}
get callButtonDisabled() {
let uri = this.state.targetUri.trim();
if (!uri || uri.indexOf(' ') > -1 || uri.indexOf('@guest.') > -1) {
return true;
}
if (this.state.shareToContacts) {
return true;
}
if (this.state.recording) {
return true;
}
if (this.state.audioRecording) {
return true;
}
if (uri.indexOf('@') > -1) {
let email_reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/;
let validEmail = email_reg.test(uri);
if (!validEmail) {
return true;
}
}
return false;
}
get videoButtonDisabled() {
let uri = this.state.targetUri.trim();
if (!uri || uri.indexOf(' ') > -1 || uri.indexOf('@guest.') > -1) {
return true;
}
if (uri.indexOf('4444@') > -1) {
return true;
}
if (this.state.shareToContacts) {
return true;
}
if (this.state.recording) {
return true;
}
if (this.state.audioRecording) {
return true;
}
let username = uri.split('@')[0];
let isPhoneNumber = username.match(/^(\+|0)(\d+)$/);
if (isPhoneNumber) {
return true;
}
return this.callButtonDisabled;
}
get conferenceButtonDisabled() {
if (!this.props.canSend()) {
return true;
}
let uri = this.state.targetUri.trim();
if (uri.indexOf(' ') > -1) {
return true;
}
if (this.state.shareToContacts) {
return true;
}
let username = uri.split('@')[0];
let isPhoneNumber = username.match(/^(\+|0)(\d+)$/);
if (isPhoneNumber) {
return true;
}
if (uri.indexOf('@') > -1 && uri.indexOf(config.defaultConferenceDomain) === -1) {
return true;
}
var uri_parts = uri.split("/");
if (uri_parts.length === 5 && uri_parts[0] === 'https:') {
// https://webrtc.sipthor.net/conference/DaffodilFlyChill0 from external web link
// https://webrtc.sipthor.net/call/alice@example.com from external web link
let event = uri_parts[3];
if (event === 'call') {
return true;
}
}
return false;
}
renderNavigationItem(object) {
if (!object.item.enabled) {
return (null);
}
let title = object.item.title;
let key = object.item.key;
let buttonStyle = object.item.selected ? styles.navigationButtonSelected : styles.navigationButton;
if (key === "hideQRCodeScanner") {
return ();
}
+ if (key === "deleteAudio") {
+ return ();
+ }
+
+ if (key === "sendAudio") {
+ return ();
+ }
+
return ();
}
bounceNavigation() {
if (this.ended) {
return;
}
setTimeout(() => {
if (this.ended) {
return;
}
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: Math.floor(this.navigationItems.length / 2)});
}
}, 3000);
setTimeout(() => {
if (this.ended) {
return;
}
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: this.navigationItems.length-1});
}
}, 4500);
setTimeout(() => {
if (this.ended) {
return;
}
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: 0});
}
}, 6000);
}
get navigationItems() {
let conferenceEnabled = Object.keys(this.state.myInvitedParties).length > 0 || this.state.navigationItems['conference'];
if (this.state.inviteContacts) {
conferenceEnabled = false;
}
+ if (this.state.audioRecording) {
+ return [
+ {key: "deleteAudio", title: 'Delete', enabled: true, selected: false},
+ {key: "sendAudio", title: 'Send', enabled: true, selected: false}
+ ];
+ }
+
if (this.state.showQRCodeScanner) {
return [
{key: "hideQRCodeScanner", title: 'Cancel', enabled: true, selected: false}
];
}
if (this.state.selectedContact) {
-
let content_items = [];
if ('pinned' in this.state.contentTypes) {
content_items.push({key: 'pinned', title: 'Pinned', enabled: true, selected: this.state.pinned});
}
if ('text' in this.state.contentTypes) {
content_items.push({key: 'text', title: 'Text', enabled: true, selected: this.state.messagesCategoryFilter === 'text'});
}
if ('audio' in this.state.contentTypes) {
content_items.push({key: 'audio', title: 'Audio', enabled: true, selected: this.state.messagesCategoryFilter === 'audio'});
}
if ('image' in this.state.contentTypes) {
content_items.push({key: 'image', title: 'Images', enabled: true, selected: this.state.messagesCategoryFilter === 'image'});
}
if ('movie' in this.state.contentTypes) {
content_items.push({key: 'movie', title: 'Movies', enabled: true, selected: this.state.messagesCategoryFilter === 'movie'});
}
if ('failed' in this.state.contentTypes) {
content_items.push({key: 'failed', title: 'Failed', enabled: true, selected: this.state.messagesCategoryFilter === 'failed'});
}
if ('paused' in this.state.contentTypes) {
content_items.push({key: 'paused', title: 'Paused', enabled: true, selected: this.state.messagesCategoryFilter === 'paused'});
}
if ('large' in this.state.contentTypes) {
content_items.push({key: 'large', title: 'Large', enabled: true, selected: this.state.messagesCategoryFilter === 'large'});
}
return content_items;
}
return [
{key: null, title: 'All', enabled: true, selected: false},
{key: 'favorite', title: 'Favorites', enabled: this.state.favoriteUris.length > 0, selected: this.state.historyCategoryFilter === 'favorite'},
{key: 'history', title: 'Calls', enabled: true, selected: this.state.historyCategoryFilter === 'history'},
{key: 'chat', title: 'Chat', enabled: true, selected: this.state.historyCategoryFilter === 'chat'},
{key: 'today', title: 'Today', enabled: this.state.navigationItems['today'], selected: this.state.historyPeriodFilter === 'today'},
{key: 'yesterday', title: 'Yesterday', enabled: this.state.navigationItems['yesterday'], selected: this.state.historyPeriodFilter === 'yesterday'},
{key: 'missed', title: 'Missed', enabled: this.state.missedCalls.length > 0, selected: this.state.historyCategoryFilter === 'missed'},
{key: 'blocked', title: 'Blocked', enabled: this.state.blockedUris.length > 0, selected: this.state.historyCategoryFilter === 'blocked'},
{key: 'conference', title: 'Conference', enabled: conferenceEnabled, selected: this.state.historyCategoryFilter === 'conference'},
{key: 'test', title: 'Test', enabled: !this.state.shareToContacts && !this.state.inviteContacts, selected: this.state.historyCategoryFilter === 'test'},
{key: 'ssi', title: 'SSI', enabled: (this.state.ssiConnections && this.state.ssiConnections.length > 0) || (this.state.ssiCredentials && this.state.ssiCredentials.length > 0), selected: this.state.historyCategoryFilter === 'ssi'},
];
}
toggleQRCodeScanner(event) {
if (event) {
event.preventDefault();
}
console.log('Scan QR code...');
this.props.toggleQRCodeScannerFunc();
}
QRCodeRead(e) {
//console.log('QR code object:', e);
console.log('QR code data:', e.data);
this.props.toggleQRCodeScannerFunc();
this.handleTargetChange(e.data);
}
get showContactsList() {
if (this.state.recording) {
return false;
}
if (this.state.audioRecording) {
return false;
}
return true;
}
get showQRCodeButton() {
if (!this.props.canSend()) {
return false;
}
if (this.state.shareToContacts) {
return false;
}
let uri = this.state.targetUri.toLowerCase();
return uri.length === 0 && !this.state.shareToContacts && !this.state.inviteContacts;
}
async recordAudio() {
const micAllowed = await this.props.requestMicPermission();
console.log('micAllowed', micAllowed);
if (!micAllowed) {
return;
}
if (!this.state.recording) {
if (this.state.audioRecording) {
this.deleteAudio();
} else {
this.onStartRecord();
}
} else {
this.onStopRecord();
}
}
recordAudio(event) {
event.preventDefault();
Keyboard.dismiss();
this.props.recordAudio();
}
async file2GiftedChat(fileObject) {
var id = uuid.v4();
let uri = this.state.selectedContact.uri;
let filepath = fileObject.uri ? fileObject.uri : fileObject;
let basename = fileObject.fileName || filepath.split('\\').pop().split('/').pop();
basename = basename.replace(/\s|:/g, '_');
let file_transfer = { 'path': filepath,
'filename': basename,
'sender': {'uri': this.state.accountId},
'receiver': {'uri': uri},
'transfer_id': id,
'direction': 'outgoing'
};
if (filepath.startsWith('content://')) {
// on android we must copy this file early
const localPath = RNFS.DocumentDirectoryPath + "/" + this.state.accountId + "/" + uri + "/" + id + "/" + basename;
const dirname = path.dirname(localPath);
await RNFS.mkdir(dirname);
console.log('Copy', filepath, localPath);
await RNFS.copyFile(filepath, localPath);
filepath = localPath;
file_transfer.local_url = localPath;
}
let stats_filename = filepath.startsWith('file://') ? filepath.substr(7, filepath.length - 1) : filepath;
const { size } = await RNFetchBlob.fs.stat(stats_filename);
file_transfer.filesize = fileObject.fileSize || size;
if (fileObject.preview) {
file_transfer.preview = fileObject.preview;
}
if (fileObject.duration) {
file_transfer.duration = fileObject.duration;
}
if (fileObject.fileType) {
file_transfer.filetype = fileObject.fileType;
} else {
try {
let mime = await fileType(filepath);
if (mime.mime) {
file_transfer.filetype = mime.mime;
}
} catch (e) {
console.log('Error getting mime type', e.message);
}
}
let text = utils.beautyFileNameForBubble(file_transfer);
let msg = {
_id: id,
key: id,
text: text,
metadata: file_transfer,
createdAt: new Date(),
direction: 'outgoing',
user: {}
}
if (utils.isImage(basename, file_transfer.filetype)) {
msg.image = filepath;
} else if (utils.isAudio(basename)) {
msg.audio = filepath;
} else if (utils.isVideo(basename) || file_transfer.duration) {
msg.video = filepath;
}
return msg;
}
- async sendAudioFile(event) {
- event.preventDefault();
- Keyboard.dismiss();
+ async sendAudioFile() {
if (this.state.audioRecording) {
this.setState({audioSendFinished: true});
setTimeout(() => {
this.setState({audioSendFinished: false});
}, 10);
let msg = await this.file2GiftedChat(this.state.audioRecording);
this.transferFile(msg);
this.setState({audioRecording: null});
}
}
async transferFile(msg) {
msg.metadata.preview = false;
this.props.sendMessage(msg.metadata.receiver.uri, msg, 'application/sylk-file-transfer');
}
deleteAudio(event) {
event.preventDefault();
Keyboard.dismiss();
this.props.deleteAudio();
}
async recordAudio() {
/*
const micAllowed = await this.props.requestMicPermission();
console.log('micAllowed', micAllowed);
if (!micAllowed) {
return;
}
*/
if (!this.state.recording) {
if (this.state.audioRecording) {
this.deleteAudio();
} else {
this.onStartRecord();
}
} else {
this.onStopRecord();
}
}
deleteAudio() {
console.log('Delete audio');
this.setState({audioRecording: null, recording: false});
if (this.recordingStopTimer !== null) {
clearTimeout(this.recordingStopTimer);
this.recordingStopTimer = null;
}
}
async onStartRecord () {
console.log('Start recording...');
this.setState({recording: true});
this.recordingStopTimer = setTimeout(() => {
this.stopRecording();
}, 20000);
if (!AudioRecord) {
AudioRecord.init(options);
}
try {
AudioRecord.start();
} catch (e) {
console.log(e.message);
}
};
stopRecording() {
console.log('Stop recording...');
this.setState({recording: false});
if (this.recordingStopTimer !== null) {
clearTimeout(this.recordingStopTimer);
this.recordingStopTimer = null;
}
this.onRecording(false);
this.onStopRecord();
}
async onStopRecord () {
console.log('Stop recording...');
const result = await AudioRecord.stop();
this.audioRecorded(result);
this.setState({audioRecording: result});
};
onRecording(state) {
this.setState({recording: state});
if (state) {
this.startRecordingTimer();
} else {
this.stopRecordingTimer()
}
}
resetContact() {
this.stopRecordingTimer()
this.setState({
recording: false,
audioRecording: null,
audioSendFinished: false
});
}
startRecordingTimer() {
let i = 0;
this.recordingTimer = setInterval(() => {
i = i + 1
this.setState({placeholder: 'Recording audio ' + i + 's'});
}, 1000);
}
stopRecordingTimer() {
if (this.recordingTimer) {
clearInterval(this.recordingTimer);
this.recordingTimer = null;
}
}
async audioRecorded(file) {
if (file) {
console.log('Audio recording ready to send', file);
} else {
console.log('Audio recording removed');
}
this.setState({recording: false, audioRecording: file});
}
render() {
let URIContainerClass = styles.portraitUriInputBox;
let uriGroupClass = styles.portraitUriButtonGroup;
let titleClass = styles.portraitTitle;
let uri = this.state.targetUri.toLowerCase();
var uri_parts = uri.split("/");
if (uri_parts.length === 5 && uri_parts[0] === 'https:') {
// https://webrtc.sipthor.net/conference/DaffodilFlyChill0 from external web link
// https://webrtc.sipthor.net/call/alice@example.com from external web link
let event = uri_parts[3];
uri = uri_parts[4];
if (event === 'conference') {
uri = uri.split("@")[0] + '@' + config.defaultConferenceDomain;
}
}
//console.log('RB', this.state.myContacts);
if (this.state.isTablet) {
titleClass = this.props.orientation === 'landscape' ? styles.landscapeTabletTitle : styles.portraitTabletTitle;
} else {
titleClass = this.props.orientation === 'landscape' ? styles.landscapeTitle : styles.portraitTitle;
}
if (this.state.isTablet) {
uriGroupClass = this.props.orientation === 'landscape' ? styles.landscapeTabletUriButtonGroup : styles.portraitTabletUriButtonGroup;
} else {
uriGroupClass = this.props.orientation === 'landscape' ? styles.landscapeUriButtonGroup : styles.portraitUriButtonGroup;
}
if (this.state.isTablet) {
URIContainerClass = this.props.orientation === 'landscape' ? styles.landscapeTabletUriInputBox : styles.portraitTabletUriInputBox;
} else {
URIContainerClass = this.props.orientation === 'landscape' ? styles.landscapeUriInputBox : styles.portraitUriInputBox;
}
const historyContainer = this.props.orientation === 'landscape' ? styles.historyLandscapeContainer : styles.historyPortraitContainer;
const buttonGroupClass = this.props.orientation === 'landscape' ? styles.landscapeButtonGroup : styles.buttonGroup;
const borderClass = this.state.chat ? null : styles.historyBorder;
let backButtonTitle = 'Back to call';
const showBackToCallButton = this.state.call && this.state.call.state !== 'incoming' && this.state.call.state !== 'terminated' ? true : false ;
if (showBackToCallButton) {
if (this.state.call.hasOwnProperty('_participants')) {
backButtonTitle = this.state.selectedContacts.length > 0 ? 'Invite people' : 'Back to conference';
} else {
backButtonTitle = this.state.selectedContacts.length > 0 ? 'Invite people' : 'Back to call';
}
}
let greenButtonClass = Platform.OS === 'ios' ? styles.greenButtoniOS : styles.greenButton;
let blueButtonClass = Platform.OS === 'ios' ? styles.blueButtoniOS : styles.blueButton;
let redButtonClass = Platform.OS === 'ios' ? styles.redButtoniOS : styles.redButton;
let disabledGreenButtonClass = Platform.OS === 'ios' ? styles.disabledGreenButtoniOS : styles.disabledGreenButton;
let disabledBlueButtonClass = Platform.OS === 'ios' ? styles.disabledBlueButtoniOS : styles.disabledBlueButton;
let recordIcon = this.state.recording ? 'pause' : 'microphone';
let activityTitle = this.state.recording ? "Recording audio..." : "Audio recording ready";
-
let fileTransfersDisabled = false;
if (this.state.selectedContact) {
fileTransfersDisabled = false;
if (this.state.selectedContact.tags.indexOf('test') > -1) {
fileTransfersDisabled = true;
}
if (this.state.selectedContact.uri.indexOf('@videoconference') > -1) {
fileTransfersDisabled = true;
}
if (this.state.selectedContact.uri.indexOf('@conference') > -1) {
fileTransfersDisabled = true;
}
}
return (
{this.showSearchBar && !this.state.isLandscape ?
: null}
{this.showButtonsBar ?
{this.showSearchBar && this.state.isLandscape ?
: null}
{showBackToCallButton ?
:
{!this.state.selectedContact ?
: null }
{this.showAudioRecordButton?
: null }
{this.showAudioCancelButton ?
: null }
{ this.state.shareToContacts ?
: null}
{this.showConferenceButton ?
: null }
{this.showAudioSendButton ?
: null }
{ this.state.shareToContacts ?
: null }
{ this.showQRCodeButton ?
: null}
}
: null}
{this.showContactsList ?
{this.state.showQRCodeScanner ?
:
}
: null
}
{ this.state.recording ?
{activityTitle}
: null
}
{ this.state.audioRecording ?
{activityTitle}
: null
}
{this.showNavigationBar ?
{ this.navigationRef = ref; }}
onScrollToIndexFailed={info => {
const wait = new Promise(resolve => setTimeout(resolve, 10));
wait.then(() => {
if (!this.state.selectedContact) {
this.navigationRef.current?.scrollToIndex({ index: info.index, animated: true/false });
}
});
}}
data={this.navigationItems}
extraData={this.state}
keyExtractor={(item, index) => item.key}
renderItem={this.renderNavigationItem}
/>
: null}
{this.state.isTablet && 0?
: null}
);
}
}
ReadyBox.propTypes = {
account : PropTypes.object,
password : PropTypes.string.isRequired,
config : PropTypes.object.isRequired,
startCall : PropTypes.func.isRequired,
startConference : PropTypes.func.isRequired,
contacts : PropTypes.array,
orientation : PropTypes.string,
isTablet : PropTypes.bool,
isLandscape : PropTypes.bool,
refreshHistory : PropTypes.bool,
refreshFavorites: PropTypes.bool,
saveHistory : PropTypes.func,
localHistory : PropTypes.array,
myDisplayName : PropTypes.string,
myPhoneNumber : PropTypes.string,
toggleFavorite : PropTypes.func,
myInvitedParties: PropTypes.object,
toggleBlocked : PropTypes.func,
favoriteUris : PropTypes.array,
blockedUris : PropTypes.array,
defaultDomain : PropTypes.string,
saveContact : PropTypes.func,
selectContact : PropTypes.func,
lookupContacts : PropTypes.func,
call : PropTypes.object,
goBackFunc : PropTypes.func,
messages : PropTypes.object,
sendMessage : PropTypes.func,
reSendMessage : PropTypes.func,
confirmRead : PropTypes.func,
deleteMessage : PropTypes.func,
expireMessage : PropTypes.func,
getMessages : PropTypes.func,
deleteMessages : PropTypes.func,
pinMessage : PropTypes.func,
unpinMessage : PropTypes.func,
sendPublicKey : PropTypes.func,
inviteContacts : PropTypes.bool,
shareToContacts : PropTypes.bool,
showQRCodeScanner : PropTypes.bool,
selectedContacts: PropTypes.array,
updateSelection : PropTypes.func,
loadEarlierMessages: PropTypes.func,
newContactFunc : PropTypes.func,
missedCalls : PropTypes.array,
messageZoomFactor: PropTypes.string,
isTyping: PropTypes.bool,
navigationItems: PropTypes.object,
showConferenceModal: PropTypes.bool,
showConferenceModalFunc: PropTypes.func,
hideConferenceModalFunc: PropTypes.func,
shareContent: PropTypes.func,
cancelShareContent: PropTypes.func,
filterHistoryFunc: PropTypes.func,
historyFilter: PropTypes.string,
fontScale: PropTypes.number,
inviteToConferenceFunc: PropTypes.func,
toggleQRCodeScannerFunc: PropTypes.func,
myContacts: PropTypes.object,
handleSSIEnrolment: PropTypes.func,
ssiCredentials: PropTypes.array,
ssiConnections: PropTypes.array,
keys : PropTypes.object,
downloadFunc : PropTypes.func,
decryptFunc : PropTypes.func,
isTexting :PropTypes.bool,
keyboardVisible: PropTypes.bool,
filteredMessageIds: PropTypes.array,
contentTypes: PropTypes.object,
canSend: PropTypes.func,
forwardMessageFunc: PropTypes.func,
sourceContact: PropTypes.object,
requestCameraPermission: PropTypes.func,
requestStoragePermissions: PropTypes.func,
requestMicPermission: PropTypes.func
};
export default ReadyBox;
diff --git a/ios/sylk/AppDelegate.m b/ios/sylk/AppDelegate.m
index 4b19ed4..60f1896 100644
--- a/ios/sylk/AppDelegate.m
+++ b/ios/sylk/AppDelegate.m
@@ -1,209 +1,209 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "AppDelegate.h"
#import
#import
#import
#import
#import
#import
@import Firebase;
#import "RNCallKeep.h"
#import "RNVoipPushNotificationManager.h"
#import
#import
#import
#import
@implementation AppDelegate
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
{
[RNBackgroundDownloader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// RCTSetLogThreshold(RCTLogLevelInfo - 1);
// RTCSetMinDebugLogLevel(RTCLoggingSeverityInfo);
if ([FIRApp defaultApp] == nil) {
[FIRApp configure];
}
// Define UNUserNotificationCenter
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"Sylk"
initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
- completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
+ completionHandler(UNNotificationPresentationOptionNone);
}
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler
{
BOOL handled = [RNCallKeep application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
if (!handled) {
handled = [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
return handled;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(@"Got a PUSH NOTIFICATION");
NSString *eventType = userInfo[@"event"];
NSLog(@"Value of eventType = %@", eventType);
if ([eventType isEqualToString:@"cancel"])
{
NSString *calluuid = userInfo[@"session-id"];
BOOL active = [RNCallKeep isCallActive:calluuid];
if (active) {
[RNCallKeep endCallWithUUID:calluuid reason:2];
}
return completionHandler(UIBackgroundFetchResultNoData);
}
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// IOS 10+ Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
completionHandler();
}
// IOS 4-10 Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[RNCPushNotificationIOS didReceiveLocalNotification:notification];
}
// --- Handle updated push credentials
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
// Register VoIP push token (a property of PKPushCredentials) with server
[RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];
}
- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type
{
// --- The system calls this method when a previously provided push token is no longer valid for use. No action is necessary on your part to reregister the push type. Instead, use this method to notify your server not to send push notifications using the matching push token.
}
// --- Handle incoming pushes (for ios <= 10)
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type {
[RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
}
// Handle incoming pushes
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type withCompletionHandler:(void (^)(void))completion{
NSLog(@"Got a PUSHKIT NOTIFICATION");
[RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
// Retrieve information like handle and callerName here
NSString *eventType = [payload.dictionaryPayload valueForKey:@"event"];
NSLog(@"Value of eventType = %@", eventType);
// if ([eventType isEqualToString:@"incoming_session"])
// {
NSString *calluuid = [payload.dictionaryPayload valueForKey:@"session-id"];
NSString *mediaType = [payload.dictionaryPayload valueForKey:@"media-type"];
NSString *callerName = [payload.dictionaryPayload valueForKey:@"from_display_name"];
NSString *handle = [payload.dictionaryPayload valueForKey:@"from_uri"];
[RNVoipPushNotificationManager addCompletionHandler:calluuid completionHandler:completion];
//you can't do this check - you HAVE to call reportNewIncomingCall otherwise apple will start killing your app
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
[RNCallKeep reportNewIncomingCall: calluuid
handle: handle
handleType: @"generic"
hasVideo: [mediaType isEqualToString:@"video"]
localizedCallerName: callerName
supportsHolding: NO
supportsDTMF: YES
supportsGrouping: YES
supportsUngrouping: YES
fromPushKit: YES
payload: payload.dictionaryPayload
withCompletionHandler: completion];
}
// }
// else {
//completion();
// }
}
@end