Merge branch 'master' of https://github.com/snikket-im/snikket-ios into App-Freeze-Fix

This commit is contained in:
Muhammad Khalid 2021-11-21 14:31:34 +05:00 committed by Mehrooz Khan
commit d290d5a1b2
89 changed files with 1936 additions and 774 deletions

View file

@ -1,31 +0,0 @@
---
name: Bug report (Developer)
about: Reports from app developers
title: ''
labels: 'bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Open '....'
3. Do '....'
4. See error
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Details (please complete the following information):**
- App Version: [e.g. 1.0.0]
- iOS version [e.g. 11.0]
- iPhone model [e.g. iPhone 11]
Please also include versions of any other relevant software.
**Additional context**
Add any other context about the problem here.

View file

@ -1,6 +1,6 @@
---
name: Bug report (App user)
about: Reports for app download from App Store or TestFlight
name: Bug report (using Snikket server)
about: Report an issue using the iOS app with a Snikket server
title: ''
labels: 'bug'
assignees: ''
@ -21,7 +21,7 @@ If applicable, add screenshots to help explain your problem.
- iOS version: [e.g. 11.0]
- iPhone model: [e.g. iPhone 11]
- Server software: [e.g. Snikket]
- Server software: Snikket
If your problem involves communication with people using other apps/platforms,
please include details of their app and versions also when possible.

View file

@ -0,0 +1,30 @@
---
name: Bug report (using non-Snikket server)
about: Report an issue using the iOS app with a different XMPP server software or provider
title: ''
labels: ['bug','unofficial setup']
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Software versions:**
- App Version: [e.g. 1.1 (2)]
- iOS version: [e.g. 11.0]
- iPhone model: [e.g. iPhone 11]
- Server software or domain name: [e.g. Prosody 0.11.10]
If your problem involves communication with people using other apps/platforms,
please include details of their app and versions also when possible.
**Additional context**
Add any other context about the problem here.

View file

@ -8,7 +8,7 @@ assignees: ''
---
**Describe your feature proposal**
A cleare and concise description of your feature request.
A clear and concise description of your feature request.
**Is your feature request related to a problem? Please describe.**
Explain what problem this feature would solve for you, and what value it would

View file

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
373A8020271063E1000E50FE /* TelephonyProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A801F271063E1000E50FE /* TelephonyProviderViewController.swift */; };
3759635F26F72F1900831F4C /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3759636126F72F1900831F4C /* Welcome.storyboard */; };
3759636A26F72FD700831F4C /* Groupchat.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3759636C26F72FD700831F4C /* Groupchat.storyboard */; };
3759637126F7303100831F4C /* VoIP.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3759637326F7303100831F4C /* VoIP.storyboard */; };
@ -21,6 +22,7 @@
3797D5C026EA456F0091DAF8 /* HSLuvSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 3797D5BF26EA456F0091DAF8 /* HSLuvSwift */; };
379D914A26E8A0E300B877CA /* db-schema-15.sql in Resources */ = {isa = PBXBuildFile; fileRef = 379D914926E8A0E300B877CA /* db-schema-15.sql */; };
379D914C26E8A29800B877CA /* DBLastMessageSyncStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379D914B26E8A29800B877CA /* DBLastMessageSyncStore.swift */; };
37B93BDF2711792300B943D0 /* TelephonyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B93BDE2711792300B943D0 /* TelephonyManager.swift */; };
E928AD4326D6A08A00F29F93 /* db-schema-14.sql in Resources */ = {isa = PBXBuildFile; fileRef = E928AD4226D6A08A00F29F93 /* db-schema-14.sql */; };
E95AA70226D38B6F00A38D44 /* DisplayNameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95AA70126D38B6E00A38D44 /* DisplayNameViewController.swift */; };
E963721026D786D000332482 /* BlockingCommandModuleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E963720F26D786D000332482 /* BlockingCommandModuleExtension.swift */; };
@ -302,6 +304,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
373A801F271063E1000E50FE /* TelephonyProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelephonyProviderViewController.swift; sourceTree = "<group>"; };
3759635C26F72EAA00831F4C /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
3759635D26F72ED700831F4C /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Main.strings; sourceTree = "<group>"; };
3759635E26F72ED700831F4C /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/MainInterface.strings; sourceTree = "<group>"; };
@ -336,6 +339,7 @@
3797D5BC26E9E42E0091DAF8 /* AvatarColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarColors.swift; sourceTree = "<group>"; };
379D914926E8A0E300B877CA /* db-schema-15.sql */ = {isa = PBXFileReference; lastKnownFileType = text; path = "db-schema-15.sql"; sourceTree = "<group>"; };
379D914B26E8A29800B877CA /* DBLastMessageSyncStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBLastMessageSyncStore.swift; sourceTree = "<group>"; };
37B93BDE2711792300B943D0 /* TelephonyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelephonyManager.swift; sourceTree = "<group>"; };
591D5EFC26FCA0AC00B2F114 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
591D5EFD26FCA0AC00B2F114 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/VoIP.strings; sourceTree = "<group>"; };
591D5EFE26FCA0AD00B2F114 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Groupchat.strings; sourceTree = "<group>"; };
@ -782,6 +786,7 @@
FE17808C23EB4C7F00A1EA76 /* AccountQRCodeController.swift */,
FE0E31112537288A0030F8C5 /* MediaSettingsVIewController.swift */,
E963721126D79D3A00332482 /* PrivacyPolicyTableViewCell.swift */,
373A801F271063E1000E50FE /* TelephonyProviderViewController.swift */,
);
path = settings;
sourceTree = "<group>";
@ -1007,6 +1012,7 @@
FE3E3879242765E700D3A8E8 /* MixEventHandler.swift */,
E9EC2B3226D554C000222BB9 /* PEPDisplayNameModule.swift */,
E963720F26D786D000332482 /* BlockingCommandModuleExtension.swift */,
37B93BDE2711792300B943D0 /* TelephonyManager.swift */,
);
path = service;
sourceTree = "<group>";
@ -1424,6 +1430,7 @@
FE4071E821E2653700F09B58 /* RoundButton.swift in Sources */,
FEAC717B1CECE70100ABABEF /* MucChatOccupantsTableViewCell.swift in Sources */,
FE7F9303200FD5AC004C6195 /* AccountManagerScramSaltedPasswordCache.swift in Sources */,
37B93BDF2711792300B943D0 /* TelephonyManager.swift in Sources */,
FE9625A01D9AE7CB00D07118 /* RosterProvider.swift in Sources */,
FEF19F0423473C06005CFE9A /* MessageEventHandler.swift in Sources */,
FE137A4C21F75660006B7F7C /* ChatBottomView.swift in Sources */,
@ -1438,6 +1445,7 @@
FEBC12F524C70E7F00689475 /* DBChatHistorySyncStore.swift in Sources */,
FEDC6790238B05E4005C0FAB /* BlockedContactsController.swift in Sources */,
FE2332E1242CCDB400008ED4 /* InvitationChatTableViewCell.swift in Sources */,
373A8020271063E1000E50FE /* TelephonyProviderViewController.swift in Sources */,
FE3E38862428C21100D3A8E8 /* OSLog.swift in Sources */,
FECEF29423B7933A007EC323 /* MetadataCache.swift in Sources */,
E9D384B426CA95FF009BEAF3 /* UIDeviceExtension.swift in Sources */,
@ -1786,7 +1794,7 @@
CODE_SIGN_ENTITLEMENTS = "Snikket - Share/Snikket - Share.entitlements";
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = 2YH6MCD3C8;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@ -1819,7 +1827,7 @@
CODE_SIGN_ENTITLEMENTS = "Snikket - Share/Snikket - Share.entitlements";
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = 2YH6MCD3C8;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@ -1854,7 +1862,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = 2YH6MCD3C8;
ENABLE_BITCODE = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1894,7 +1902,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = 2YH6MCD3C8;
ENABLE_BITCODE = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1933,7 +1941,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 7;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 2YH6MCD3C8;
DYLIB_COMPATIBILITY_VERSION = 1;
@ -1983,7 +1991,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 7;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 2YH6MCD3C8;
DYLIB_COMPATIBILITY_VERSION = 1;
@ -2145,7 +2153,7 @@
CODE_SIGN_ENTITLEMENTS = Snikket/Snikket.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 7;
DEAD_CODE_STRIPPING = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 2YH6MCD3C8;
@ -2186,7 +2194,7 @@
CODE_SIGN_ENTITLEMENTS = Snikket/Snikket.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 7;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = 2YH6MCD3C8;
ENABLE_BITCODE = NO;

View file

@ -39,6 +39,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return DBConnection.main;
}
// Temporary Fix for Subscription Requests
static var subscriptionsRequest = [String:String]()
let notificationCenterDelegate = NotificationCenterDelegate();
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
@ -54,7 +57,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
//RTCSetupInternalTracer();
Log.initialize();
Settings.initialize();
AccountSettings.initialize();
if #available(iOS 13.0, *) {
switch Settings.appearance.string()! {
case "light":
@ -70,10 +73,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
_ = JingleManager.instance;
UINavigationBar.appearance().tintColor = UIColor(named: "tintColor");
setUIAppearances()
NotificationManager.instance.initialize(provider: MainNotificationManagerProvider());
xmppService.initialize();
Settings.setDefaultSettings()
AccountSettings.initialize();
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
// sending notifications not granted!
}
@ -129,6 +135,29 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
func setUIAppearances() {
if #available(iOS 15, *) {
let tabAppearance = UITabBarAppearance()
tabAppearance.configureWithOpaqueBackground()
UITabBar.appearance().scrollEdgeAppearance = tabAppearance
}
if #available(iOS 13, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(named: "AddToContactsBar")!]
appearance.backgroundColor = UIColor(named: "chatslistBackground")
UINavigationBar.appearance().standardAppearance = appearance;
UINavigationBar.appearance().scrollEdgeAppearance = appearance
UINavigationBar.appearance().compactAppearance = appearance
let tabAppearance = UITabBarAppearance()
tabAppearance.configureWithOpaqueBackground()
UITabBar.appearance().standardAppearance = tabAppearance
} else {
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
}
}
@objc func accountsChanged(_ notification: Notification) {
DispatchQueue.main.async {
if let rootView = self.window?.rootViewController {

View file

@ -0,0 +1,56 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -5,8 +5,8 @@
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "round_camera_alt_black_24pt_2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
@ -15,10 +15,10 @@
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
}

View file

@ -0,0 +1,83 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "checkmark.circle@2x.png",
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"filename" : "checkmark.circle@2x-1.png",
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "checkmark.circle@2x-2.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "chevron.down@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "info.circle@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -5,11 +5,73 @@
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "plus@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "plus.svg",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"filename" : "plus@2x-1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "plus@2x-3.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "3x"
}

View file

@ -1 +0,0 @@
<svg height="426.66667pt" viewBox="0 0 426.66667 426.66667" width="426.66667pt" xmlns="http://www.w3.org/2000/svg"><path d="m405.332031 192h-170.664062v-170.667969c0-11.773437-9.558594-21.332031-21.335938-21.332031-11.773437 0-21.332031 9.558594-21.332031 21.332031v170.667969h-170.667969c-11.773437 0-21.332031 9.558594-21.332031 21.332031 0 11.777344 9.558594 21.335938 21.332031 21.335938h170.667969v170.664062c0 11.777344 9.558594 21.335938 21.332031 21.335938 11.777344 0 21.335938-9.558594 21.335938-21.335938v-170.664062h170.664062c11.777344 0 21.335938-9.558594 21.335938-21.335938 0-11.773437-9.558594-21.332031-21.335938-21.332031zm0 0"/></svg>

Before

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

View file

@ -0,0 +1,83 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "xmark.circle@2x.png",
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"filename" : "xmark.circle@2x-1.png",
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "xmark.circle@2x-2.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
@ -254,7 +254,7 @@
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="StreamFeatureCell" textLabel="zhp-jp-Aoq" detailTextLabel="FeX-5F-H2m" style="IBUITableViewCellStyleSubtitle" id="nD7-jE-ZDb">
<rect key="frame" x="0.0" y="24.333333969116211" width="375" height="55.666667938232422"/>
<rect key="frame" x="0.0" y="44.666666030883789" width="375" height="55.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="nD7-jE-ZDb" id="yw2-KR-hxT">
<rect key="frame" x="0.0" y="0.0" width="375" height="55.666667938232422"/>
@ -366,11 +366,11 @@
<sections>
<tableViewSection id="8E6-U9-RVD">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" selectionStyle="default" indentationWidth="10" id="iOk-Vs-FvD">
<rect key="frame" x="0.0" y="18" width="375" height="80"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="iOk-Vs-FvD">
<rect key="frame" x="0.0" y="18" width="375" height="76.333335876464844"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="iOk-Vs-FvD" id="r6b-Ub-yxs">
<rect key="frame" x="0.0" y="0.0" width="375" height="80"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="76.333335876464844"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="1wx-dK-lja">
@ -424,14 +424,14 @@
<tableViewSection headerTitle="General" id="v8B-ee-zAk">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="hKf-J9-sQA">
<rect key="frame" x="0.0" y="147.66666603088379" width="375" height="44"/>
<rect key="frame" x="0.0" y="144.00000190734863" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hKf-J9-sQA" id="UtO-LI-Gqd">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LhN-Xx-9am">
<rect key="frame" x="318" y="6.6666666666666679" width="51" height="31.000000000000004"/>
<rect key="frame" x="318" y="6.3333333333333321" width="51" height="30.999999999999996"/>
<connections>
<action selector="enabledSwitchChangedValue:" destination="6yM-ZG-ryL" eventType="valueChanged" id="jJR-Cb-2Vq"/>
</connections>
@ -455,10 +455,10 @@
</constraints>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="nKL-rM-Rfj">
<rect key="frame" x="0.0" y="191.66666603088379" width="375" height="44"/>
<rect key="frame" x="0.0" y="187.66666984558105" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="nKL-rM-Rfj" id="lPm-Ma-b07">
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Server features" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="t3T-uh-mob">
@ -479,10 +479,10 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="Oaf-Ev-fol">
<rect key="frame" x="0.0" y="235.66666603088379" width="375" height="44"/>
<rect key="frame" x="0.0" y="231.33333778381348" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Oaf-Ev-fol" id="Cyo-35-u7d">
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Change account settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="70p-aF-3x5">
@ -503,10 +503,10 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="27D-rn-4zp" detailTextLabel="JkB-3h-A61" style="IBUITableViewCellStyleValue1" id="Jh1-9Y-aPh">
<rect key="frame" x="0.0" y="279.66666603088379" width="375" height="44"/>
<rect key="frame" x="0.0" y="275.0000057220459" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Jh1-9Y-aPh" id="ze1-5H-0b4">
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Nickname" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="27D-rn-4zp">
@ -531,10 +531,10 @@
<tableViewSection headerTitle="Push Notifications" id="PvC-LX-0Sp">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="HIy-qD-9WC">
<rect key="frame" x="0.0" y="373.33333206176758" width="375" height="44"/>
<rect key="frame" x="0.0" y="368.33333969116211" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="HIy-qD-9WC" id="xoJ-Lu-9Eg">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Re-register push notifications" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZHc-Ml-5eX">
@ -551,10 +551,10 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="opT-Sn-Rio">
<rect key="frame" x="0.0" y="417.33333206176758" width="375" height="44"/>
<rect key="frame" x="0.0" y="412.00000762939453" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="opT-Sn-Rio" id="7Lw-8e-1Ol">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="When in Away/XA/DND state" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aKW-CM-bl2">
@ -564,7 +564,7 @@
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cdb-o8-Y70">
<rect key="frame" x="318" y="6.6666666666666679" width="51" height="31.000000000000004"/>
<rect key="frame" x="318" y="6.3333333333333321" width="51" height="30.999999999999996"/>
<connections>
<action selector="pushNotificationsForAwaySwitchChangedValue:" destination="6yM-ZG-ryL" eventType="valueChanged" id="4Qx-7X-CZt"/>
</connections>
@ -583,85 +583,23 @@
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Message Archiving" id="9PW-Rb-rop">
<tableViewSection headerTitle="Telephony" id="WFs-jm-Wmk">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="2GC-EM-O0C">
<rect key="frame" x="0.0" y="510.99999809265137" width="375" height="44"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="9bb-Ha-oU0">
<rect key="frame" x="0.0" y="505.33334159851074" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2GC-EM-O0C" id="s6M-Zf-HWI">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="9bb-Ha-oU0" id="Rsa-rx-Qh1">
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enabled" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GTa-Ee-HQT">
<rect key="frame" x="16" y="11.666666666666666" width="62" height="20.666666666666671"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Provider" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YYb-TE-za0">
<rect key="frame" x="16.000000000000004" y="11.666666666666666" width="63.333333333333343" height="20.666666666666671"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="f1h-xR-qOY">
<rect key="frame" x="318" y="6.6666666666666679" width="51" height="31.000000000000004"/>
<connections>
<action selector="archivingSwitchChangedValue:" destination="6yM-ZG-ryL" eventType="valueChanged" id="lBh-gq-cj9"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstItem="GTa-Ee-HQT" firstAttribute="centerY" secondItem="s6M-Zf-HWI" secondAttribute="centerY" id="830-wL-H2z"/>
<constraint firstItem="f1h-xR-qOY" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="GTa-Ee-HQT" secondAttribute="trailing" constant="8" id="PQx-NG-e7N"/>
<constraint firstAttribute="leadingMargin" secondItem="GTa-Ee-HQT" secondAttribute="leading" id="QFD-5D-MFu"/>
<constraint firstItem="f1h-xR-qOY" firstAttribute="centerY" secondItem="s6M-Zf-HWI" secondAttribute="centerY" id="e9z-sL-qJ5"/>
</constraints>
</tableViewCellContentView>
<constraints>
<constraint firstAttribute="trailing" secondItem="f1h-xR-qOY" secondAttribute="trailing" constant="8" id="aK9-Hk-YiP"/>
</constraints>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="NGQ-lK-aj3">
<rect key="frame" x="0.0" y="554.99999809265137" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="NGQ-lK-aj3" id="Egv-qe-B19">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Automatic synchronization" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="J9o-cf-vMy">
<rect key="frame" x="16" y="11.666666666666666" width="202" height="20.666666666666671"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CcC-Ej-dJL">
<rect key="frame" x="318" y="6.6666666666666679" width="51" height="31.000000000000004"/>
<connections>
<action selector="messageSyncAutomaticSwitchChangedValue:" destination="6yM-ZG-ryL" eventType="valueChanged" id="SJE-Fj-aX5"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="leadingMargin" secondItem="J9o-cf-vMy" secondAttribute="leading" id="QAw-ON-EK6"/>
<constraint firstItem="CcC-Ej-dJL" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="J9o-cf-vMy" secondAttribute="trailing" constant="8" id="Vyh-FX-bjh"/>
<constraint firstItem="J9o-cf-vMy" firstAttribute="centerY" secondItem="Egv-qe-B19" secondAttribute="centerY" id="gaC-rV-i9A"/>
<constraint firstItem="CcC-Ej-dJL" firstAttribute="centerY" secondItem="Egv-qe-B19" secondAttribute="centerY" id="yY7-nf-Q6T"/>
</constraints>
</tableViewCellContentView>
<constraints>
<constraint firstAttribute="trailing" secondItem="CcC-Ej-dJL" secondAttribute="trailing" constant="8" id="A2E-Ta-Qke"/>
</constraints>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="L1L-Zt-4zr">
<rect key="frame" x="0.0" y="598.99999809265137" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="L1L-Zt-4zr" id="no3-p5-48l">
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Synchronization" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GNS-EF-AqY">
<rect key="frame" x="16" y="11.666666666666666" width="123" height="20.666666666666671"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Last 24 hours" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XLZ-oR-88H">
<rect key="frame" x="236.33333333333337" y="11.666666666666664" width="105" height="21"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="None" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="StS-4J-0Uu">
<rect key="frame" x="300.66666666666669" y="11.333333333333336" width="40.666666666666686" height="21"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" staticText="YES" summaryElement="YES"/>
</accessibility>
@ -671,33 +609,36 @@
</label>
</subviews>
<constraints>
<constraint firstAttribute="leadingMargin" secondItem="GNS-EF-AqY" secondAttribute="leading" id="5bg-ge-xHo"/>
<constraint firstItem="GNS-EF-AqY" firstAttribute="centerY" secondItem="no3-p5-48l" secondAttribute="centerY" id="Wya-Bq-UGR"/>
<constraint firstAttribute="trailingMargin" secondItem="XLZ-oR-88H" secondAttribute="trailing" id="XY4-DY-qmx"/>
<constraint firstItem="XLZ-oR-88H" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="GNS-EF-AqY" secondAttribute="trailing" constant="8" id="YmP-po-biI"/>
<constraint firstItem="XLZ-oR-88H" firstAttribute="centerY" secondItem="no3-p5-48l" secondAttribute="centerY" id="xpW-2U-ggu"/>
<constraint firstAttribute="trailingMargin" secondItem="StS-4J-0Uu" secondAttribute="trailing" id="7Im-pe-kFr"/>
<constraint firstItem="YYb-TE-za0" firstAttribute="centerY" secondItem="Rsa-rx-Qh1" secondAttribute="centerY" id="BWj-93-Qmw"/>
<constraint firstAttribute="leadingMargin" secondItem="YYb-TE-za0" secondAttribute="leading" id="HbM-k2-933"/>
<constraint firstItem="StS-4J-0Uu" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="YYb-TE-za0" secondAttribute="trailing" constant="8" id="QCX-Th-qNJ"/>
<constraint firstItem="StS-4J-0Uu" firstAttribute="centerY" secondItem="Rsa-rx-Qh1" secondAttribute="centerY" id="QOg-ta-NMp"/>
</constraints>
</tableViewCellContentView>
<connections>
<segue destination="Ey3-E6-jDR" kind="show" identifier="ShowTelephonyProviders" id="EXn-Gp-Svg"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Encryption" id="Et5-H6-t7C">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="gi7-rI-bRZ">
<rect key="frame" x="0.0" y="692.66666412353516" width="375" height="44"/>
<rect key="frame" x="0.0" y="598.66667556762695" width="375" height="68.333335876464844"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" tableViewCell="gi7-rI-bRZ" id="xQ3-o4-BUs">
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="44"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="gi7-rI-bRZ" id="xQ3-o4-BUs">
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="68.333335876464844"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="OMEMO fingerprint" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gUr-GY-P3A">
<rect key="frame" x="16" y="6" width="136" height="18"/>
<rect key="frame" x="16" y="6" width="136" height="18.333333333333332"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="9dfacd3d 410d52bc 35f8e816 c7efc5b6 efe138e5 452a9e96 f30c2839 63b46241 " lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BE2-bI-S0Z" userLabel="9dfacd3d 410d52bc 35f8e816 c7efc5b6 efe138e5 452a9e96 f30c2839 63b46241">
<rect key="frame" x="16" y="28" width="325.33333333333331" height="34"/>
<rect key="frame" x="16" y="28.333333333333329" width="325.33333333333331" height="34"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" staticText="YES" summaryElement="YES"/>
</accessibility>
@ -727,15 +668,32 @@
</tableViewSection>
<tableViewSection id="gLQ-zI-ugx">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="GWw-NK-6oU" style="IBUITableViewCellStyleDefault" id="TWF-EZ-7xa">
<rect key="frame" x="0.0" y="772.66666412353516" width="375" height="44"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="GWw-NK-6oU" style="IBUITableViewCellStyleDefault" id="TWF-EZ-7xa">
<rect key="frame" x="0.0" y="703.0000114440918" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="TWF-EZ-7xa" id="5Bw-5e-Taj">
<rect key="frame" x="0.0" y="0.0" width="349.33333333333331" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Delete" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="GWw-NK-6oU">
<rect key="frame" x="16" y="0.0" width="325.33333333333331" height="44"/>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Log out" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="GWw-NK-6oU">
<rect key="frame" x="16" y="0.0" width="343" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="lGT-wY-qtX" style="IBUITableViewCellStyleDefault" id="EDd-V2-4pv">
<rect key="frame" x="0.0" y="746.66667938232422" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="EDd-V2-4pv" id="aFo-M2-ndE">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Delete account" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="lGT-wY-qtX">
<rect key="frame" x="16" y="0.0" width="343" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<color key="textColor" systemColor="systemRedColor"/>
@ -762,16 +720,14 @@
</navigationItem>
<connections>
<outlet property="addressTextView" destination="i2f-mN-qTd" id="4nY-I1-VU4"/>
<outlet property="archivingEnabledSwitch" destination="f1h-xR-qOY" id="oSq-GV-fTU"/>
<outlet property="avatarView" destination="1wx-dK-lja" id="avF-X8-W0f"/>
<outlet property="companyTextView" destination="vF4-tE-C74" id="9JD-RQ-LF4"/>
<outlet property="enabledSwitch" destination="LhN-Xx-9am" id="2Wk-Hk-9kE"/>
<outlet property="fullNameTextView" destination="bDD-UG-Fxr" id="p8h-Gy-5oj"/>
<outlet property="messageSyncAutomaticSwitch" destination="CcC-Ej-dJL" id="U5U-cp-RyH"/>
<outlet property="messageSyncPeriodLabel" destination="XLZ-oR-88H" id="WTf-SJ-NC0"/>
<outlet property="nicknameLabel" destination="JkB-3h-A61" id="LTW-2n-tC2"/>
<outlet property="omemoFingerprint" destination="BE2-bI-S0Z" id="HXB-VX-Pvv"/>
<outlet property="pushNotificationsForAwaySwitch" destination="cdb-o8-Y70" id="GgR-H7-wXn"/>
<outlet property="telephonyProviderLabel" destination="StS-4J-0Uu" id="3FT-4X-Jcu"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Xuo-YR-3Bb" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -1166,7 +1122,7 @@
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="1qg-fn-RHu" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="6797" y="1967"/>
<point key="canvasLocation" x="7400" y="1967"/>
</scene>
<!--Display Name View Controller-->
<scene sceneID="FsV-4O-ZvF">
@ -1207,7 +1163,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CGa-gb-mjX" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="7973" y="1968"/>
<point key="canvasLocation" x="8606" y="1967"/>
</scene>
<!--OMEMO fingerprints-->
<scene sceneID="9pa-yB-KSS">
@ -1307,6 +1263,44 @@
</objects>
<point key="canvasLocation" x="4547.826086956522" y="2442.8571428571427"/>
</scene>
<!--Telephony Provider-->
<scene sceneID="pGy-5T-FFR">
<objects>
<tableViewController title="Telephony Provider" id="Ey3-E6-jDR" customClass="TelephonyProviderViewController" customModule="Snikket" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="xux-X1-2jt">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="TelephonyProviderCell" textLabel="IUH-ca-fBy" style="IBUITableViewCellStyleDefault" id="cL6-Qj-FQZ">
<rect key="frame" x="0.0" y="44.666666030883789" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="cL6-Qj-FQZ" id="Enj-ww-QHc">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="IUH-ca-fBy">
<rect key="frame" x="16" y="0.0" width="343" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="Ey3-E6-jDR" id="8yD-DI-iHN"/>
<outlet property="delegate" destination="Ey3-E6-jDR" id="aHX-hl-Hr5"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="yEk-1E-6Of"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="2f4-Tn-2EW" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="5842" y="1997"/>
</scene>
</scenes>
<inferredMetricsTieBreakers>
<segue reference="opM-M0-zrX"/>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
@ -15,7 +15,7 @@
<objects>
<viewController title="Room Controller" id="uV2-vL-T54" customClass="MucChatViewController" customModule="Snikket" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Sim-Jc-r23">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sgc-HQ-Zz2">
@ -24,13 +24,28 @@
<segue destination="leg-mN-YAd" kind="embed" id="FoG-5E-8zb"/>
</connections>
</containerView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Qmg-sW-dkA" customClass="RoundShadowButton" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="344" y="625" width="40" height="40"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="Twb-GC-ecD"/>
<constraint firstAttribute="width" constant="40" id="mMy-vZ-u3i"/>
</constraints>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" image="chevron.down"/>
<connections>
<action selector="scrollToBottomTapped:" destination="uV2-vL-T54" eventType="touchUpInside" id="VCo-Rf-hGo"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="7WM-tQ-tL0"/>
<constraints>
<constraint firstItem="7WM-tQ-tL0" firstAttribute="trailing" secondItem="sgc-HQ-Zz2" secondAttribute="trailing" id="5zj-hd-0Be"/>
<constraint firstItem="7WM-tQ-tL0" firstAttribute="bottom" secondItem="Qmg-sW-dkA" secondAttribute="bottom" constant="60" id="9tY-EV-jKc"/>
<constraint firstItem="7WM-tQ-tL0" firstAttribute="bottom" secondItem="sgc-HQ-Zz2" secondAttribute="bottom" id="9vQ-SR-CUT"/>
<constraint firstItem="sgc-HQ-Zz2" firstAttribute="top" secondItem="7WM-tQ-tL0" secondAttribute="top" id="KqV-2L-2ED"/>
<constraint firstItem="sgc-HQ-Zz2" firstAttribute="leading" secondItem="7WM-tQ-tL0" secondAttribute="leading" id="VxO-NM-7eE"/>
<constraint firstItem="7WM-tQ-tL0" firstAttribute="trailing" secondItem="Qmg-sW-dkA" secondAttribute="trailing" constant="30" id="dJY-ZD-mfK"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="jz1-lz-vTl">
@ -85,6 +100,7 @@
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="containerView" destination="sgc-HQ-Zz2" id="4MZ-gD-QS6"/>
<outlet property="scrollToBottomButton" destination="Qmg-sW-dkA" id="EfE-yv-5ew"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="GhF-oL-IbC" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -101,12 +117,12 @@
<objects>
<tableViewController id="EpF-TK-mfh" customClass="MucChatOccupantsTableViewController" customModule="Snikket" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="xPb-sa-myB">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="MucChatOccupantsTableViewCell" selectionStyle="default" indentationWidth="10" reuseIdentifier="MucChatOccupantsTableViewCell" id="7wm-Ue-Wm8" customClass="MucChatOccupantsTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="24.5" width="414" height="44"/>
<rect key="frame" x="0.0" y="44.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="7wm-Ue-Wm8" id="JkW-NK-c1c">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@ -245,15 +261,15 @@
</tableViewSection>
<tableViewSection headerTitle="Subject" id="iYv-zL-tZT">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="NK6-IG-GRa" style="IBUITableViewCellStyleDefault" id="MEa-vy-ELC">
<rect key="frame" x="0.0" y="128" width="414" height="44"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="NK6-IG-GRa" style="IBUITableViewCellStyleDefault" id="MEa-vy-ELC">
<rect key="frame" x="0.0" y="128" width="414" height="82"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MEa-vy-ELC" id="64p-68-kX3">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="82"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="When the value of this property is false, the queue actively starts operations that are in the queue and ready to execute." textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="NK6-IG-GRa">
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
<rect key="frame" x="20" y="0.0" width="374" height="82"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
@ -268,7 +284,7 @@
<string key="footerTitle">Push notification support depends on the XMPP server which you are using and may not work in some cases even if it's supported and enabled on the group chat</string>
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="02f-aw-3Zd" detailTextLabel="exK-ZH-jpA" style="IBUITableViewCellStyleValue1" id="KlQ-4F-rae">
<rect key="frame" x="0.0" y="229.5" width="414" height="44"/>
<rect key="frame" x="0.0" y="267.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="KlQ-4F-rae" id="ufE-bo-qG2">
<rect key="frame" x="0.0" y="0.0" width="384.5" height="44"/>
@ -292,7 +308,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="DQk-sn-hKj" detailTextLabel="YT2-1I-RlM" style="IBUITableViewCellStyleValue1" id="xQZ-ou-qXC">
<rect key="frame" x="0.0" y="273.5" width="414" height="44"/>
<rect key="frame" x="0.0" y="311.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xQZ-ou-qXC" id="LHJ-hk-TEJ">
<rect key="frame" x="0.0" y="0.0" width="384.5" height="44"/>
@ -316,7 +332,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="4wI-c9-pBG" customClass="SwitchTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="317.5" width="414" height="44"/>
<rect key="frame" x="0.0" y="355.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="4wI-c9-pBG" id="R5e-gd-jsF">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@ -349,7 +365,7 @@
<tableViewSection headerTitle=" " id="2ZD-5S-4hK">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="wBw-1J-Xau" style="IBUITableViewCellStyleDefault" id="3IN-v0-ayC">
<rect key="frame" x="0.0" y="448" width="414" height="44"/>
<rect key="frame" x="0.0" y="486" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3IN-v0-ayC" id="iXe-AG-FnD">
<rect key="frame" x="0.0" y="0.0" width="384.5" height="44"/>
@ -373,7 +389,7 @@
<tableViewSection headerTitle=" History" id="552-vb-Heg">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="wNk-Ce-zfI" style="IBUITableViewCellStyleDefault" id="kUN-Hr-2a1">
<rect key="frame" x="0.0" y="542" width="414" height="44"/>
<rect key="frame" x="0.0" y="580" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="kUN-Hr-2a1" id="oeh-ap-pGo">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@ -430,7 +446,7 @@
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="RosterItemTableViewCell" selectionStyle="default" indentationWidth="10" reuseIdentifier="RosterItemTableViewCell" rowHeight="48" id="mXn-Pc-kMI" customClass="RosterItemTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="24.5" width="414" height="48"/>
<rect key="frame" x="0.0" y="44.5" width="414" height="48"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="mXn-Pc-kMI" id="bfL-M0-loC">
<rect key="frame" x="0.0" y="0.0" width="414" height="48"/>
@ -533,6 +549,7 @@
</scene>
</scenes>
<resources>
<image name="chevron.down" width="16" height="9.5"/>
<image name="defaultGroupchatAvatarDark" width="100" height="100"/>
<image name="first" width="30" height="30"/>
<image name="participants" width="25" height="25"/>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="kwm-Ck-fLB">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="kwm-Ck-fLB">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -36,7 +36,7 @@
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="OMw-f8-Hbs">
<rect key="frame" x="0.0" y="293" width="375" height="374"/>
<rect key="frame" x="0.0" y="318" width="375" height="349"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<string key="text">============================
Snikket
@ -118,6 +118,12 @@ of the date such litigation is filed.
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Build:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="R7V-kJ-UYE">
<rect key="frame" x="166.5" y="277.5" width="42.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="oog-jF-q31"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
@ -129,13 +135,16 @@ of the date such litigation is filed.
<constraint firstItem="lRd-ky-eXs" firstAttribute="centerX" secondItem="oog-jF-q31" secondAttribute="centerX" id="NW1-WB-5w7"/>
<constraint firstItem="lRd-ky-eXs" firstAttribute="top" secondItem="Z7m-aB-9EO" secondAttribute="bottom" constant="10" id="SpV-wW-SsU"/>
<constraint firstItem="r21-ee-ggd" firstAttribute="top" secondItem="oog-jF-q31" secondAttribute="top" constant="10" id="Y2t-3I-xpX"/>
<constraint firstItem="R7V-kJ-UYE" firstAttribute="centerX" secondItem="0cP-EO-OEn" secondAttribute="centerX" id="aOr-if-zTp"/>
<constraint firstItem="r21-ee-ggd" firstAttribute="width" secondItem="0cP-EO-OEn" secondAttribute="width" multiplier="0.5" id="fpe-aE-YEg"/>
<constraint firstItem="OMw-f8-Hbs" firstAttribute="leading" secondItem="oog-jF-q31" secondAttribute="leading" id="gFU-7S-uBv"/>
<constraint firstItem="oog-jF-q31" firstAttribute="bottom" secondItem="OMw-f8-Hbs" secondAttribute="bottom" id="hSa-jm-9nr"/>
<constraint firstItem="OMw-f8-Hbs" firstAttribute="top" secondItem="lRd-ky-eXs" secondAttribute="bottom" constant="20.5" id="m9Q-8m-zw3"/>
<constraint firstItem="OMw-f8-Hbs" firstAttribute="top" secondItem="R7V-kJ-UYE" secondAttribute="bottom" constant="20" id="qjl-dk-Tcm"/>
<constraint firstItem="R7V-kJ-UYE" firstAttribute="top" secondItem="lRd-ky-eXs" secondAttribute="bottom" constant="5" id="vV9-Ao-GLo"/>
</constraints>
</view>
<connections>
<outlet property="buildLabel" destination="R7V-kJ-UYE" id="4Vg-PM-AhI"/>
<outlet property="copyrightTextView" destination="OMw-f8-Hbs" id="2oE-pU-TxQ"/>
<outlet property="nameLabel" destination="Z7m-aB-9EO" id="eel-CG-m8X"/>
<outlet property="versionLabel" destination="lRd-ky-eXs" id="ctg-F1-6aK"/>

View file

@ -176,13 +176,89 @@
<segue destination="GGV-aa-8IC" kind="embed" id="585-T5-dGR"/>
</connections>
</containerView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0oA-5d-4WD">
<rect key="frame" x="20" y="10" width="388" height="64"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fa9-4g-iNW">
<rect key="frame" x="319.66666666666669" y="15" width="58.333333333333314" height="34"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<connections>
<action selector="rejectSubscriptionTapped:" destination="WP2-Cp-7ZE" eventType="touchUpInside" id="ATX-p5-rfq"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kNl-ad-SgK">
<rect key="frame" x="261.66666666666669" y="15" width="58" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<connections>
<action selector="acceptSubscriptionTapped:" destination="WP2-Cp-7ZE" eventType="touchUpInside" id="bq5-18-Ns1"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Contact added you to their contact list. Add to contacts?" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="ezh-iK-Sk7">
<rect key="frame" x="10" y="10" width="232.66666666666666" height="44"/>
<fontDescription key="fontDescription" type="system" pointSize="19"/>
<color key="textColor" systemColor="systemBackgroundColor"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="xmark.circle" translatesAutoresizingMaskIntoConstraints="NO" id="AS0-Q7-MrW">
<rect key="frame" x="319.66666666666669" y="15" width="58.333333333333314" height="34"/>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="checkmark.circle" translatesAutoresizingMaskIntoConstraints="NO" id="BG2-fh-ymZ">
<rect key="frame" x="261.66666666666669" y="15" width="58" height="34"/>
</imageView>
</subviews>
<color key="backgroundColor" name="AddToContactsBar"/>
<constraints>
<constraint firstItem="kNl-ad-SgK" firstAttribute="top" secondItem="0oA-5d-4WD" secondAttribute="top" constant="15" id="0Td-UW-nUJ"/>
<constraint firstItem="ezh-iK-Sk7" firstAttribute="width" secondItem="0oA-5d-4WD" secondAttribute="width" multiplier="0.6" id="1xf-vG-mSe"/>
<constraint firstItem="fa9-4g-iNW" firstAttribute="leading" secondItem="AS0-Q7-MrW" secondAttribute="leading" id="3En-Wg-qUq"/>
<constraint firstAttribute="bottom" secondItem="fa9-4g-iNW" secondAttribute="bottom" constant="15" id="7Yj-Jo-hA5"/>
<constraint firstItem="ezh-iK-Sk7" firstAttribute="top" secondItem="0oA-5d-4WD" secondAttribute="top" constant="10" id="EH7-zf-Ylj"/>
<constraint firstItem="kNl-ad-SgK" firstAttribute="trailing" secondItem="BG2-fh-ymZ" secondAttribute="trailing" id="FHX-Pd-S30"/>
<constraint firstItem="fa9-4g-iNW" firstAttribute="width" secondItem="kNl-ad-SgK" secondAttribute="width" id="PIe-9c-N3k"/>
<constraint firstItem="fa9-4g-iNW" firstAttribute="leading" secondItem="kNl-ad-SgK" secondAttribute="trailing" id="QJ2-T7-JJc"/>
<constraint firstItem="kNl-ad-SgK" firstAttribute="top" secondItem="BG2-fh-ymZ" secondAttribute="top" id="USZ-x4-7hI"/>
<constraint firstItem="fa9-4g-iNW" firstAttribute="bottom" secondItem="AS0-Q7-MrW" secondAttribute="bottom" id="Yqj-wy-SHC"/>
<constraint firstItem="ezh-iK-Sk7" firstAttribute="leading" secondItem="0oA-5d-4WD" secondAttribute="leading" constant="10" id="acT-n4-ejR"/>
<constraint firstItem="fa9-4g-iNW" firstAttribute="width" secondItem="0oA-5d-4WD" secondAttribute="width" multiplier="0.15" id="f8K-1j-n7N"/>
<constraint firstItem="fa9-4g-iNW" firstAttribute="top" secondItem="AS0-Q7-MrW" secondAttribute="top" id="ge1-Dm-uDL"/>
<constraint firstItem="fa9-4g-iNW" firstAttribute="top" secondItem="0oA-5d-4WD" secondAttribute="top" constant="15" id="jFH-Ju-QWm"/>
<constraint firstItem="ezh-iK-Sk7" firstAttribute="centerY" secondItem="0oA-5d-4WD" secondAttribute="centerY" id="jG9-Cm-2jo"/>
<constraint firstAttribute="bottom" secondItem="ezh-iK-Sk7" secondAttribute="bottom" constant="10" id="lkV-5D-9hF"/>
<constraint firstItem="kNl-ad-SgK" firstAttribute="leading" secondItem="BG2-fh-ymZ" secondAttribute="leading" id="peb-R7-axT"/>
<constraint firstAttribute="bottom" secondItem="kNl-ad-SgK" secondAttribute="bottom" constant="15" id="rVU-fS-vCy"/>
<constraint firstItem="fa9-4g-iNW" firstAttribute="trailing" secondItem="AS0-Q7-MrW" secondAttribute="trailing" id="uga-Ok-yHK"/>
<constraint firstAttribute="trailing" secondItem="fa9-4g-iNW" secondAttribute="trailing" constant="10" id="wUE-ll-MrO"/>
<constraint firstItem="kNl-ad-SgK" firstAttribute="bottom" secondItem="BG2-fh-ymZ" secondAttribute="bottom" id="yEx-SG-x7Z"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="k9G-H8-3Hb" customClass="RoundShadowButton" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="358" y="655" width="40" height="40"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="O5s-Tu-hic"/>
<constraint firstAttribute="width" constant="40" id="a6B-NY-a5u"/>
</constraints>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" image="chevron.down" imagePadding="0.0">
<directionalEdgeInsets key="contentInsets" top="20" leading="20" bottom="20" trailing="20"/>
</buttonConfiguration>
<connections>
<action selector="scrollToBottomTapped:" destination="WP2-Cp-7ZE" eventType="touchUpInside" id="6kU-Zt-yfo"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="K7X-9F-pbS"/>
<constraints>
<constraint firstItem="K7X-9F-pbS" firstAttribute="bottom" secondItem="k9G-H8-3Hb" secondAttribute="bottom" constant="60" id="Lcy-t7-4dL"/>
<constraint firstItem="0oA-5d-4WD" firstAttribute="leading" secondItem="K7X-9F-pbS" secondAttribute="leading" constant="20" id="Se8-QK-Ism"/>
<constraint firstItem="K7X-9F-pbS" firstAttribute="bottom" secondItem="gYg-Wz-iZc" secondAttribute="bottom" id="UEL-s9-1Hy"/>
<constraint firstItem="gYg-Wz-iZc" firstAttribute="leading" secondItem="K7X-9F-pbS" secondAttribute="leading" id="Vw6-gf-z0R"/>
<constraint firstItem="0oA-5d-4WD" firstAttribute="top" secondItem="K7X-9F-pbS" secondAttribute="top" constant="10" id="hb2-it-kVe"/>
<constraint firstItem="gYg-Wz-iZc" firstAttribute="top" secondItem="K7X-9F-pbS" secondAttribute="top" id="lTO-xK-Ffx"/>
<constraint firstItem="K7X-9F-pbS" firstAttribute="trailing" secondItem="gYg-Wz-iZc" secondAttribute="trailing" id="mKL-x9-gDq"/>
<constraint firstItem="K7X-9F-pbS" firstAttribute="trailing" secondItem="k9G-H8-3Hb" secondAttribute="trailing" constant="30" id="phr-KD-Mqa"/>
<constraint firstItem="K7X-9F-pbS" firstAttribute="trailing" secondItem="0oA-5d-4WD" secondAttribute="trailing" constant="20" id="zna-7O-fWc"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="5u6-YP-VV1">
@ -225,7 +301,9 @@
</navigationItem>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="addContactView" destination="0oA-5d-4WD" id="0Oe-0Z-Fxi"/>
<outlet property="containerView" destination="gYg-Wz-iZc" id="bHR-j9-Idy"/>
<outlet property="scrollToBottomButton" destination="k9G-H8-3Hb" id="LvQ-cd-UTP"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="qn2-Sh-FRP" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -235,7 +313,7 @@
</connections>
</tapGestureRecognizer>
</objects>
<point key="canvasLocation" x="4859.420289855073" y="-737.94642857142856"/>
<point key="canvasLocation" x="4858.8785046728972" y="-738.01295896328304"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="Q1F-tR-kAo">
@ -349,7 +427,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Siskin IM" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="V18-Iv-6Re">
<rect key="frame" x="144" y="459.33333333333331" width="140" height="38.666666666666686"/>
<rect key="frame" x="144" y="459.33333333333331" width="140" height="39"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="32"/>
<nil key="highlightedColor"/>
<size key="shadowOffset" width="0.0" height="0.0"/>
@ -358,7 +436,7 @@
</attributedString>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="by Tigase, Inc." textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8ba-yZ-XRA">
<rect key="frame" x="161" y="498" width="146" height="26.333333333333371"/>
<rect key="frame" x="161" y="498.33333333333331" width="146" height="25.999999999999943"/>
<fontDescription key="fontDescription" name="HelveticaNeue-MediumItalic" family="Helvetica Neue" pointSize="22"/>
<nil key="highlightedColor"/>
<attributedString key="userComments">
@ -472,9 +550,9 @@
<!--Contacts-->
<scene sceneID="x6U-yq-dO2">
<objects>
<tableViewController id="mps-t3-QBq" customClass="RosterViewController" customModule="Snikket" customModuleProvider="target" sceneMemberID="viewController">
<tableViewController extendedLayoutIncludesOpaqueBars="YES" id="mps-t3-QBq" customClass="RosterViewController" customModule="Snikket" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="dbg-Pp-Ujo">
<rect key="frame" x="0.0" y="0.0" width="428" height="838"/>
<rect key="frame" x="0.0" y="0.0" width="428" height="926"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<color key="separatorColor" systemColor="systemBackgroundColor"/>
@ -553,7 +631,6 @@
<outlet property="delegate" destination="mps-t3-QBq" id="ZYI-Wf-ZtU"/>
</connections>
</tableView>
<extendedEdge key="edgesForExtendedLayout" bottom="YES"/>
<navigationItem key="navigationItem" title="Contacts" id="SEz-IM-IWL">
<barButtonItem key="backBarButtonItem" title=" " id="NYU-vU-24f">
<attributedString key="userComments">
@ -1176,6 +1253,23 @@
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="AddToContactsCell" textLabel="zrb-Yr-XQO" rowHeight="44" style="IBUITableViewCellStyleDefault" id="uOR-5c-dcP">
<rect key="frame" x="0.0" y="554" width="428" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uOR-5c-dcP" id="96g-I4-URe">
<rect key="frame" x="0.0" y="0.0" width="428" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add to contact list" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="zrb-Yr-XQO">
<rect key="frame" x="20" y="0.0" width="388" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="rSR-7g-CFO" id="lPG-hT-w4i"/>
@ -1380,13 +1474,19 @@
</scenes>
<resources>
<image name="appLogo" width="1024" height="1024"/>
<image name="checkmark.circle" width="13.333333015441895" height="13.333333015441895"/>
<image name="chevron.down" width="16" height="9.5"/>
<image name="defaultAvatar" width="100" height="100"/>
<image name="first" width="30" height="30"/>
<image name="gear" catalog="system" width="128" height="119"/>
<image name="message.fill" catalog="system" width="128" height="113"/>
<image name="messageArchiving" width="50" height="50"/>
<image name="person.crop.circle.fill" catalog="system" width="128" height="121"/>
<image name="xmark.circle" width="13.333333015441895" height="13.333333015441895"/>
<image name="xmark.circle.fill" catalog="system" width="128" height="121"/>
<namedColor name="AddToContactsBar">
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="chatslistBackground">
<color red="0.99607843137254903" green="0.85490196078431369" blue="0.023529411764705882" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</namedColor>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -20,7 +20,7 @@
<color key="separatorColor" name="conversationBackground"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="ChatTableViewSystemCell" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewSystemCell" rowHeight="33" id="9wb-BJ-QeF" customClass="ChatTableViewSystemCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="24.5" width="414" height="33"/>
<rect key="frame" x="0.0" y="44.5" width="414" height="33"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="9wb-BJ-QeF" id="14Y-vA-IHJ">
<rect key="frame" x="0.0" y="0.0" width="414" height="33"/>
@ -46,7 +46,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="ChatTableViewMeCell" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewMeCell" rowHeight="33" id="GBK-nY-egT" customClass="ChatTableViewMeCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="57.5" width="414" height="33"/>
<rect key="frame" x="0.0" y="77.5" width="414" height="33"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="GBK-nY-egT" id="kQM-yI-Mn0">
<rect key="frame" x="0.0" y="0.0" width="414" height="33"/>
@ -73,7 +73,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="ChatTableViewCellContinuation" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewMessageContinuationCell" id="8Et-xM-dnP" customClass="ChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="90.5" width="414" height="44"/>
<rect key="frame" x="0.0" y="110.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="8Et-xM-dnP" id="V41-Eh-z9O">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@ -136,7 +136,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="ChatTableViewCellContinuation2" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewMessageContinuationCell2" id="huG-Ws-60E" customClass="ChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="134.5" width="414" height="44"/>
<rect key="frame" x="0.0" y="154.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="huG-Ws-60E" id="MkF-ub-5jO">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@ -199,7 +199,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewLinkPreviewCell" rowHeight="71" id="RRh-Va-SRE" customClass="LinkPreviewChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="178.5" width="414" height="71"/>
<rect key="frame" x="0.0" y="198.5" width="414" height="71"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="RRh-Va-SRE" id="b4O-zc-oXg">
<rect key="frame" x="0.0" y="0.0" width="414" height="71"/>
@ -222,7 +222,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewAttachmentContinuationCell" rowHeight="71" id="RfX-hS-d2H" customClass="AttachmentChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="249.5" width="414" height="71"/>
<rect key="frame" x="0.0" y="269.5" width="414" height="71"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="RfX-hS-d2H" id="dLL-EZ-lYx">
<rect key="frame" x="0.0" y="0.0" width="414" height="71"/>
@ -277,7 +277,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewAttachmentContinuationCell2" rowHeight="71" id="MUh-Ya-KMJ" customClass="AttachmentChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="320.5" width="414" height="71"/>
<rect key="frame" x="0.0" y="340.5" width="414" height="71"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="MUh-Ya-KMJ" id="DIf-G5-2Wm">
<rect key="frame" x="0.0" y="0.0" width="414" height="71"/>
@ -332,7 +332,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="ChatTableViewCell" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewMessageCell" rowHeight="60" id="Edg-Ze-o8C" customClass="ChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="391.5" width="414" height="60"/>
<rect key="frame" x="0.0" y="411.5" width="414" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="Edg-Ze-o8C" id="017-Lx-5HC">
<rect key="frame" x="0.0" y="0.0" width="414" height="60"/>
@ -436,7 +436,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="ChatTableViewCell2" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewMessageCell2" rowHeight="60" id="5md-uM-UrG" userLabel="ChatTableViewMessageCell2" customClass="ChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="451.5" width="414" height="60"/>
<rect key="frame" x="0.0" y="471.5" width="414" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="5md-uM-UrG" id="Ti4-Ga-E6D">
<rect key="frame" x="0.0" y="0.0" width="414" height="60"/>
@ -536,7 +536,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewAttachmentCell" rowHeight="71" id="S8a-TQ-tLX" customClass="AttachmentChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="511.5" width="414" height="71"/>
<rect key="frame" x="0.0" y="531.5" width="414" height="71"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="S8a-TQ-tLX" id="3eM-Pw-ok6">
<rect key="frame" x="0.0" y="0.0" width="414" height="71"/>
@ -631,7 +631,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewAttachmentCell2" rowHeight="71" id="SxS-SO-af1" customClass="AttachmentChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="582.5" width="414" height="71"/>
<rect key="frame" x="0.0" y="602.5" width="414" height="71"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="SxS-SO-af1" id="ngq-dH-Zkb">
<rect key="frame" x="0.0" y="0.0" width="414" height="71"/>
@ -724,7 +724,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatTableViewInvitationCell" rowHeight="78" id="kHO-eT-tka" customClass="InvitationChatTableViewCell" customModule="Snikket" customModuleProvider="target">
<rect key="frame" x="0.0" y="653.5" width="414" height="78"/>
<rect key="frame" x="0.0" y="673.5" width="414" height="78"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" tableViewCell="kHO-eT-tka" id="ftW-8h-UsW">
<rect key="frame" x="0.0" y="0.0" width="414" height="78"/>

View file

@ -41,6 +41,13 @@ class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {
case .MUC_ROOM_INVITATION:
didReceive(mucInvitation: notification.request.content)
return
case .SUBSCRIPTION_REQUEST:
let account = notification.request.content.userInfo["account"] as? String;
let sender = notification.request.content.userInfo["sender"] as? String;
if let account = account, let sender = sender {
AppDelegate.subscriptionsRequest[account] = sender
NotificationCenter.default.post(name: Notification.Name("SUBSCRIPTION_REQUEST"), object: nil)
}
default:
completionHandler([.alert, .sound]);
}
@ -53,7 +60,8 @@ class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {
case .ERROR:
didReceive(error: content, withCompletionHandler: completionHandler);
case .SUBSCRIPTION_REQUEST:
didReceive(subscriptionRequest: content, withCompletionHandler: completionHandler);
break
//didReceive(subscriptionRequest: content, withCompletionHandler: completionHandler);
case .MUC_ROOM_INVITATION:
//didReceive(mucInvitation: content, withCompletionHandler: completionHandler);
break

View file

@ -98,7 +98,7 @@ class ChannelViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
}
cell.nicknameView?.text = item.authorNickname;
cell.set(message: item, maxMessageWidth: self.view.frame.width * 0.60)
cell.set(message: item, maxMessageWidth: self.view.frame.width * 0.60, indexPath: indexPath)
return cell;
}
case let item as ChatAttachment:
@ -116,7 +116,7 @@ class ChannelViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
}
cell.nicknameView?.text = item.authorNickname;
cell.set(attachment: item, maxImageWidth: self.view.frame.width * 0.60)
cell.set(attachment: item, maxImageWidth: self.view.frame.width * 0.60, indexPath: indexPath)
cell.setNeedsUpdateConstraints();
cell.updateConstraintsIfNeeded();
@ -125,7 +125,7 @@ class ChannelViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
let id = "ChatTableViewLinkPreviewCell";
let cell: LinkPreviewChatTableViewCell = tableView.dequeueReusableCell(withIdentifier: id, for: indexPath) as! LinkPreviewChatTableViewCell;
cell.contentView.transform = dataSource.inverted ? CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0) : CGAffineTransform.identity;
cell.set(linkPreview: item);
cell.set(linkPreview: item, indexPath: indexPath)
return cell;
case let item as SystemMessage:
let cell: ChatTableViewSystemCell = tableView.dequeueReusableCell(withIdentifier: "ChatTableViewSystemCell", for: indexPath) as! ChatTableViewSystemCell;
@ -146,7 +146,7 @@ class ChannelViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
}
}
cell.nicknameView?.text = item.authorNickname;
cell.set(invitation: item);
cell.set(invitation: item, indexPath: indexPath)
return cell;
default:
return tableView.dequeueReusableCell(withIdentifier: "ChatTableViewMessageCell", for: indexPath);

View file

@ -177,13 +177,13 @@ class AttachmentChatTableViewCell: BaseChatTableViewCell, UIContextMenuInteracti
audioPlayer?.play()
if let player = self.audioPlayer, let audioTimer = self.audioTimer, let sliderTimer = self.sliderTimer {
self.audioPlayerDelegate?.didPlayAudio(audioPlayer: player, audioTimer: audioTimer, sliderTimer: sliderTimer, playButton: self.playButton)
self.cellDelegate?.didPlayAudio(audioPlayer: player, audioTimer: audioTimer, sliderTimer: sliderTimer, playButton: self.playButton)
}
}
else {
audioPlayer?.pause()
self.audioPlayerDelegate?.didStopAudio()
self.cellDelegate?.didStopAudio()
}
}
@ -207,7 +207,7 @@ class AttachmentChatTableViewCell: BaseChatTableViewCell, UIContextMenuInteracti
if slider.value == 0.0, !player.isPlaying {
self.playButton.isSelected = false
self.audioPlayerDelegate?.didStopAudio()
self.cellDelegate?.didStopAudio()
}
}
@ -229,13 +229,13 @@ class AttachmentChatTableViewCell: BaseChatTableViewCell, UIContextMenuInteracti
audioTime.text = NSString(format: "%02d:%02d", minutes,seconds) as String
}
func set(attachment item: ChatAttachment, maxImageWidth: CGFloat) {
func set(attachment item: ChatAttachment, maxImageWidth: CGFloat, indexPath: IndexPath) {
self.item = item;
self.customView.subviews.forEach { view in
view.removeFromSuperview()
}
super.set(item: item);
super.set(item: item, indexPath: indexPath)
self.customView?.isOpaque = true;
self.customView?.backgroundColor = self.backgroundColor;
@ -586,7 +586,7 @@ class AttachmentChatTableViewCell: BaseChatTableViewCell, UIContextMenuInteracti
details.text = "\(typeName) - \(fileSize)";
if UTTypeConformsTo(uti, kUTTypeImage) {
iconView.image = UIImage(contentsOfFile: fileUrl.path)!
iconView.image = UIImage(contentsOfFile: fileUrl.path)
self.viewType = .imagePreview;
self.setImageConstraints(image: iconView.image, maxImageWidth: maxImageWidth)
} else if UTTypeConformsTo(uti, kUTTypeMovie) {

View file

@ -22,9 +22,10 @@
import UIKit
import AVKit
protocol AudioPlayerDelegate {
protocol CellDelegate {
func didPlayAudio(audioPlayer: AVAudioPlayer, audioTimer: Foundation.Timer, sliderTimer: Foundation.Timer, playButton: UIButton)
func didStopAudio()
func didTapResend(indexPath: IndexPath)
}
class BaseChatTableViewCellFormatter {
@ -58,8 +59,9 @@ class BaseChatTableViewCell: UITableViewCell, UIDocumentInteractionControllerDel
@IBOutlet var stateView: UILabel?;
@IBOutlet var bubbleImageView: UIImageView!
@IBOutlet weak var lockStateImageView: UIImageView?
var indexPath: IndexPath?
var audioPlayerDelegate: AudioPlayerDelegate?
var cellDelegate: CellDelegate?
var originalTimestampColor: UIColor!;
@ -108,7 +110,9 @@ class BaseChatTableViewCell: UITableViewCell, UIDocumentInteractionControllerDel
}
func set(item: ChatViewItemProtocol) {
func set(item: ChatViewItemProtocol, indexPath: IndexPath) {
self.indexPath = indexPath
lockStateImageView?.isHidden = true
let fgcolor = item.state.direction == .incoming ? "chatMessageText" : "chatMessageTextOutgoing";
let lockImage = item.state.direction == .incoming ? "lock-incoming" : "lock-outgoing"
@ -148,8 +152,11 @@ class BaseChatTableViewCell: UITableViewCell, UIDocumentInteractionControllerDel
if item.state.isError {
if item.state.direction == .outgoing {
self.accessoryType = .detailButton;
self.tintColor = UIColor.red;
let accessoryButton = UIButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
accessoryButton.setImage(UIImage(named: "info.circle"), for: .normal)
accessoryButton.addTarget(self, action: #selector(didTapResend), for: .touchUpInside)
accessoryButton.transform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0)
self.accessoryView = accessoryButton
}
} else {
self.accessoryType = .none;
@ -160,6 +167,14 @@ class BaseChatTableViewCell: UITableViewCell, UIDocumentInteractionControllerDel
self.timestampView?.textColor = item.state.isError && item.state.direction == .incoming ? UIColor.red : originalTimestampColor;
}
@objc func didTapResend() {
guard let indexPath = indexPath else {
return
}
self.cellDelegate?.didTapResend(indexPath: indexPath)
}
@objc func actionMore(_ sender: UIMenuController) {
NotificationCenter.default.post(name: NSNotification.Name("tableViewCellShowEditToolbar"), object: self);
}

View file

@ -139,73 +139,6 @@ class BaseChatViewController: UIViewController, UITextViewDelegate, ChatViewInpu
}
}
func presentSheet() {
let alertController = UIAlertController()
let camera = UIAlertAction(title: "Camera", style: .default) { (action: UIAlertAction!) in
self.selectPhoto(.camera)
}
let photo = UIAlertAction(title: "Photo & Video Library", style: .default) { (action: UIAlertAction!) in
if #available(iOS 14.0, *) {
self.selectPhotoFromLibrary();
} else {
self.selectPhoto(.photoLibrary)
}
}
let document = UIAlertAction(title: "Document", style: .default) { (action: UIAlertAction!) in
self.selectFile()
}
let location = UIAlertAction(title: "Location", style: .default) { (action: UIAlertAction!) in
self.selectLocation()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { (action: UIAlertAction!) in
}
let cameraImage = UIImage(named: "camera")
if let icon = cameraImage?.imageWithSize(scaledToSize: CGSize(width: 28, height: 28)) {
camera.setValue(icon, forKey: "image")
}
let photoImage = UIImage(named: "photo")
if let icon = photoImage?.imageWithSize(scaledToSize: CGSize(width: 28, height: 28)) {
photo.setValue(icon, forKey: "image")
}
let mapImage = UIImage(named: "map")
if let icon = mapImage?.imageWithSize(scaledToSize: CGSize(width: 28, height: 28)) {
location.setValue(icon, forKey: "image")
}
if #available(iOS 13.0, *) {
let documentImage = UIImage(systemName: "arrow.up.doc");
if let icon = documentImage?.imageWithSize(scaledToSize: CGSize(width: 28, height: 28)) {
document.setValue(icon, forKey: "image")
}
} else {
photo.setValue(UIImage(named: "arrow.up.doc"), forKey: "image")
}
camera.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
camera.setValue(UIColor.darkGray, forKey: "titleTextColor")
photo.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
photo.setValue(UIColor.darkGray, forKey: "titleTextColor")
document.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
document.setValue(UIColor.darkGray, forKey: "titleTextColor")
location.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
location.setValue(UIColor.darkGray, forKey: "titleTextColor")
alertController.addAction(camera)
alertController.addAction(photo)
alertController.addAction(document)
alertController.addAction(location)
alertController.addAction(cancel)
alertController.popoverPresentationController?.sourceView = self.view
alertController.popoverPresentationController?.sourceRect = chatViewInputBar.frame
self.present(alertController, animated: true, completion: nil)
}
@objc func chatClosed(_ notification: Notification) {
DispatchQueue.main.async {
if let navigationController = self.navigationController {
@ -315,21 +248,20 @@ class BaseChatViewController: UIViewController, UITextViewDelegate, ChatViewInpu
self.correctedMessageOriginId = originId;
}
func sendAttachment(originalUrl: URL?, uploadedUrl: String, appendix: ChatAttachmentAppendix, completionHandler: (() -> Void)?) {
assert(false, "This method should be overridden");
}
@objc func sendMessageClicked(_ sender: Any) {
self.sendMessage();
}
func sendMessage() {
assert(false, "This method should be overridden");
}
func sendAudioMessage(fileUrl: URL) {
uploadFile(url: fileUrl, filename: "recording.m4a", deleteSource: true)
//assert(false, "This method should be overridden");
}
func sendAttachment(originalUrl: URL?, uploadedUrl: String, appendix: ChatAttachmentAppendix, completionHandler: (() -> Void)?) {
assert(false, "This method should be overridden");
}
func messageTextCleared() {
self.correctedMessageOriginId = nil;
}
func cameraButtonTapped() {
@ -338,8 +270,80 @@ class BaseChatViewController: UIViewController, UITextViewDelegate, ChatViewInpu
}
}
@objc func sendMessageClicked(_ sender: Any) {
self.sendMessage();
func messageTextCleared() {
self.correctedMessageOriginId = nil;
}
func presentSheet() {
let alertController = UIAlertController()
let camera = UIAlertAction(title: "Camera", style: .default) { (action: UIAlertAction!) in
self.selectPhoto(.camera)
}
let photo = UIAlertAction(title: "Photo & Video Library", style: .default) { (action: UIAlertAction!) in
if #available(iOS 14.0, *) {
self.selectPhotoFromLibrary();
} else {
self.selectPhoto(.photoLibrary)
}
}
let document = UIAlertAction(title: "Document", style: .default) { (action: UIAlertAction!) in
self.selectFile()
}
let location = UIAlertAction(title: "Location", style: .default) { (action: UIAlertAction!) in
self.selectLocation()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { (action: UIAlertAction!) in
}
let cameraImage = UIImage(named: "camera")
if let icon = cameraImage?.imageWithSize(scaledToSize: CGSize(width: 28, height: 28)) {
camera.setValue(icon, forKey: "image")
}
let photoImage = UIImage(named: "photo")
if let icon = photoImage?.imageWithSize(scaledToSize: CGSize(width: 28, height: 28)) {
photo.setValue(icon, forKey: "image")
}
let mapImage = UIImage(named: "map")
if let icon = mapImage?.imageWithSize(scaledToSize: CGSize(width: 28, height: 28)) {
location.setValue(icon, forKey: "image")
}
if #available(iOS 13.0, *) {
let documentImage = UIImage(systemName: "arrow.up.doc");
if let icon = documentImage?.imageWithSize(scaledToSize: CGSize(width: 28, height: 28)) {
document.setValue(icon, forKey: "image")
}
} else {
photo.setValue(UIImage(named: "arrow.up.doc"), forKey: "image")
}
if #available(iOS 13.0, *) {
let color = UIColor(named: "AddToContactsBar")
camera.setValue(color, forKey: "titleTextColor")
photo.setValue(color, forKey: "titleTextColor")
document.setValue(color, forKey: "titleTextColor")
location.setValue(color, forKey: "titleTextColor")
}
camera.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
photo.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
document.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
location.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
alertController.addAction(camera)
alertController.addAction(photo)
alertController.addAction(document)
alertController.addAction(location)
alertController.addAction(cancel)
alertController.popoverPresentationController?.sourceView = self.view
alertController.popoverPresentationController?.sourceRect = chatViewInputBar.frame
self.present(alertController, animated: true, completion: nil)
}
}

View file

@ -61,9 +61,9 @@ class ChatTableViewCell: BaseChatTableViewCell, UITextViewDelegate {
originalTextColor = messageTextView.textColor;
}
func set(message item: ChatMessage, maxMessageWidth: CGFloat) {
func set(message item: ChatMessage, maxMessageWidth: CGFloat, indexPath: IndexPath) {
messageTextView.textView.delegate = self;
super.set(item: item)
super.set(item: item, indexPath: indexPath)
mapView.removeFromSuperview()
if isGeoLocation(message: item.message) {

View file

@ -46,7 +46,10 @@ class ChatViewController : BaseChatViewControllerWithDataSourceAndContextMenuAnd
override func conversationTableViewDelegate() -> UITableViewDelegate? {
return self;
}
@IBOutlet weak var scrollToBottomButton: RoundShadowButton!
@IBOutlet weak var addContactView: UIView!
override func viewDidLoad() {
let messageModule: MessageModule? = XmppService.instance.getClient(forJid: account)?.modulesManager.getModule(MessageModule.ID);
self.chat = messageModule?.chatManager.getChat(with: JID(self.jid), thread: nil) as? DBChat;
@ -54,18 +57,66 @@ class ChatViewController : BaseChatViewControllerWithDataSourceAndContextMenuAnd
super.viewDidLoad()
let recognizer = UITapGestureRecognizer(target: self, action: #selector(ChatViewController.showBuddyInfo));
self.titleView.isUserInteractionEnabled = true;
self.navigationController?.navigationBar.addGestureRecognizer(recognizer);
//initializeSharing();
setupViews()
NotificationCenter.default.addObserver(self, selector: #selector(ChatViewController.avatarChanged), name: AvatarManager.AVATAR_CHANGED, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(accountStateChanged), name: XmppService.ACCOUNT_STATE_CHANGED, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(chatChanged(_:)), name: DBChatStore.CHAT_UPDATED, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(contactPresenceChanged(_:)), name: XmppService.CONTACT_PRESENCE_CHANGED, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(rosterItemUpdated(_:)), name: DBRosterStore.ITEM_UPDATED, object: self);
NotificationCenter.default.addObserver(self, selector: #selector(subscriptionRequest), name: Notification.Name("SUBSCRIPTION_REQUEST"), object: nil);
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let contentOffset = self.conversationLogController?.tableView.contentOffset else { return }
if contentOffset.y > 400 {
scrollToBottomButton.isHidden = false
} else {
scrollToBottomButton.isHidden = true
}
}
func setupViews() {
let recognizer = UITapGestureRecognizer(target: self, action: #selector(ChatViewController.showBuddyInfo));
self.titleView.isUserInteractionEnabled = true;
self.navigationController?.navigationBar.addGestureRecognizer(recognizer);
addContactView.layer.cornerRadius = 10
addContactView.isHidden = true
scrollToBottomButton.cornerRadius = 20
scrollToBottomButton.isHidden = true
}
@objc func subscriptionRequest() {
if let contact = AppDelegate.subscriptionsRequest[account.stringValue], contact == self.jid.stringValue {
self.addContactView.isHidden = false
} else {
self.addContactView.isHidden = true
}
}
@IBAction func scrollToBottomTapped(_ sender: UIButton) {
self.conversationLogController?.tableView.setContentOffset(.zero, animated: true)
}
@IBAction func rejectSubscriptionTapped(_ sender: Any) {
guard let client = XmppService.instance.getClient(for: account), let presenceModule: PresenceModule = client.modulesManager.getModule(PresenceModule.ID) else { return }
presenceModule.unsubscribed(by: JID(jid, resource: nil))
AppDelegate.subscriptionsRequest.removeValue(forKey: self.account.stringValue)
DispatchQueue.main.async {
self.addContactView.isHidden = true
}
}
@IBAction func acceptSubscriptionTapped(_ sender: Any) {
if let navigationController = self.storyboard?.instantiateViewController(withIdentifier: "RosterItemEditNavigationController") as? UINavigationController {
let itemEditController = navigationController.visibleViewController as? RosterItemEditViewController
itemEditController?.hidesBottomBarWhenPushed = true
itemEditController?.account = account
itemEditController?.jid = JID(jid, resource: nil)
navigationController.modalPresentationStyle = .formSheet
self.present(navigationController, animated: true, completion: nil)
}
}
@objc func showBuddyInfo(_ button: Any) {
@ -89,6 +140,7 @@ class ChatViewController : BaseChatViewControllerWithDataSourceAndContextMenuAnd
let presenceModule: PresenceModule? = XmppService.instance.getClient(forJid: account)?.modulesManager.getModule(PresenceModule.ID);
titleView.status = presenceModule?.presenceStore.getBestPresence(for: jid);
subscriptionRequest()
}
override func viewWillDisappear(_ animated: Bool) {
@ -113,21 +165,7 @@ class ChatViewController : BaseChatViewControllerWithDataSourceAndContextMenuAnd
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let count = super.tableView(tableView, numberOfRowsInSection: section);
if count == 0 {
if self.conversationLogController!.tableView.backgroundView == nil {
let label = UILabel(frame: CGRect(x: 0, y:0, width: self.view.bounds.size.width, height: self.view.bounds.size.height));
label.text = NSLocalizedString("No messages yet. Say hi!", comment: "")
label.font = UIFont.systemFont(ofSize: UIFont.systemFontSize + 2, weight: .medium);
label.numberOfLines = 0;
label.textAlignment = .center;
label.transform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0);
label.sizeToFit();
self.conversationLogController!.tableView.backgroundView = label;
}
} else {
self.conversationLogController!.tableView.backgroundView = nil;
}
return count;
return count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@ -160,8 +198,9 @@ class ChatViewController : BaseChatViewControllerWithDataSourceAndContextMenuAnd
let color = incoming ? #colorLiteral(red: 0.01663736999, green: 0.4700628519, blue: 0.6680073142, alpha: 1) : #colorLiteral(red: 0.4562267661, green: 0.4913363457, blue: 0, alpha: 1)
cell.avatarView?.set(bareJID: jid, name: name, avatar: AvatarManager.instance.avatar(for: incoming ? jid : account, on: account), orDefault: AvatarManager.instance.defaultAvatar, backColor: color);
cell.nicknameView?.text = ""
cell.set(message: item, maxMessageWidth: self.view.frame.width * 0.60)
cell.set(message: item, maxMessageWidth: self.view.frame.width * 0.60, indexPath: indexPath)
cell.backgroundColor = .clear
cell.cellDelegate = self
cell.contentView.backgroundColor = .clear
cell.bubbleImageView.isHidden = false
return cell;
@ -175,15 +214,15 @@ class ChatViewController : BaseChatViewControllerWithDataSourceAndContextMenuAnd
let color = incoming ? #colorLiteral(red: 0.01663736999, green: 0.4700628519, blue: 0.6680073142, alpha: 1) : #colorLiteral(red: 0.4562267661, green: 0.4913363457, blue: 0, alpha: 1)
cell.avatarView?.set(bareJID: jid, name: name, avatar: AvatarManager.instance.avatar(for: incoming ? jid : account, on: account), orDefault: AvatarManager.instance.defaultAvatar, backColor: color);
cell.nicknameView?.text = ""
cell.set(attachment: item, maxImageWidth: self.view.frame.width * 0.60)
cell.audioPlayerDelegate = self
cell.set(attachment: item, maxImageWidth: self.view.frame.width * 0.60, indexPath: indexPath)
cell.cellDelegate = self
cell.bubbleImageView.isHidden = false
return cell;
case let item as ChatLinkPreview:
let id = "ChatTableViewLinkPreviewCell";
let cell: LinkPreviewChatTableViewCell = tableView.dequeueReusableCell(withIdentifier: id, for: indexPath) as! LinkPreviewChatTableViewCell;
cell.contentView.transform = dataSource.inverted ? CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0) : CGAffineTransform.identity;
cell.set(linkPreview: item);
cell.set(linkPreview: item, indexPath: indexPath)
return cell;
case let item as SystemMessage:
let cell: ChatTableViewSystemCell = tableView.dequeueReusableCell(withIdentifier: "ChatTableViewSystemCell", for: indexPath) as! ChatTableViewSystemCell;
@ -197,41 +236,12 @@ class ChatViewController : BaseChatViewControllerWithDataSourceAndContextMenuAnd
let name = incoming ? self.titleView.name : localNickname;
cell.avatarView?.set(bareJID: jid, name: name, avatar: AvatarManager.instance.avatar(for: incoming ? jid : account, on: account), orDefault: AvatarManager.instance.defaultAvatar);
cell.nicknameView?.text = name;
cell.set(invitation: item);
cell.set(invitation: item, indexPath: indexPath)
return cell;
default:
return tableView.dequeueReusableCell(withIdentifier: "ChatTableViewCellIncoming", for: indexPath);
}
}
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
print("accessory button cliecked at", indexPath)
guard let item = dataSource.getItem(at: indexPath.row) as? ChatEntry, let chat = self.chat as? DBChat else {
return;
}
DispatchQueue.main.async {
let alert = UIAlertController(title: NSLocalizedString("Details", comment: ""), message: item.error ?? NSLocalizedString("Unkown error occured", comment: ""), preferredStyle: .alert);
alert.addAction(UIAlertAction(title: NSLocalizedString("Resend", comment: ""), style: .default, handler: {(action) in
//print("resending message with body", item.message);
switch item {
case let item as ChatMessage:
MessageEventHandler.sendMessage(chat: chat, body: item.message, url: nil);
DBChatHistoryStore.instance.remove(item: item);
case let item as ChatAttachment:
let oldLocalFile = DownloadStore.instance.url(for: "\(item.id)");
MessageEventHandler.sendAttachment(chat: chat, originalUrl: oldLocalFile, uploadedUrl: item.url, appendix: item.appendix, completionHandler: {
DBChatHistoryStore.instance.remove(item: item);
});
default:
break;
}
}));
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil));
self.present(alert, animated: true, completion: nil);
}
}
override func canExecuteContext(action: BaseChatViewControllerWithDataSourceAndContextMenuAndToolbar.ContextAction, forItem item: ChatEntry, at indexPath: IndexPath) -> Bool {
switch action {
@ -552,7 +562,7 @@ class ChatTitleView: UIView {
}
}
extension ChatViewController: AudioPlayerDelegate {
extension ChatViewController: CellDelegate {
func didPlayAudio(audioPlayer: AVAudioPlayer, audioTimer: Foundation.Timer, sliderTimer: Foundation.Timer, playButton: UIButton) {
self.cellAudioPlayer?.pause()
@ -573,4 +583,33 @@ extension ChatViewController: AudioPlayerDelegate {
self.cellAudioPlayButton = nil
}
func didTapResend(indexPath: IndexPath) {
guard let item = dataSource.getItem(at: indexPath.row) as? ChatEntry, let chat = self.chat as? DBChat else {
return;
}
DispatchQueue.main.async {
let alert = UIAlertController(title: NSLocalizedString("Details", comment: ""), message: item.error ?? NSLocalizedString("Unkown error occured", comment: ""), preferredStyle: .alert);
alert.addAction(UIAlertAction(title: NSLocalizedString("Resend", comment: ""), style: .default, handler: {(action) in
//print("resending message with body", item.message);
switch item {
case let item as ChatMessage:
MessageEventHandler.sendMessage(chat: chat, body: item.message, url: nil);
DBChatHistoryStore.instance.remove(item: item);
case let item as ChatAttachment:
let oldLocalFile = DownloadStore.instance.url(for: "\(item.id)");
MessageEventHandler.sendAttachment(chat: chat, originalUrl: oldLocalFile, uploadedUrl: item.url, appendix: item.appendix, completionHandler: {
DBChatHistoryStore.instance.remove(item: item);
});
default:
break;
}
}));
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil));
self.present(alert, animated: true, completion: nil);
}
}
}

View file

@ -88,11 +88,7 @@ class ChatViewInputBar: UIView, UITextViewDelegate, NSTextStorageDelegate {
public let attachmentButton: UIButton = {
let button = UIButton()
button.setTitle("", for: .normal)
if #available(iOS 13.0, *) {
button.setBackgroundImage(UIImage(systemName: "plus"), for: .normal)
} else {
button.setBackgroundImage(UIImage(named: "plus"), for: .normal)
}
button.setBackgroundImage(UIImage(named: "plus"), for: .normal)
button.tintColor = .darkGray
button.imageView?.contentMode = .scaleToFill
button.translatesAutoresizingMaskIntoConstraints = false;
@ -126,7 +122,7 @@ class ChatViewInputBar: UIView, UITextViewDelegate, NSTextStorageDelegate {
view.translatesAutoresizingMaskIntoConstraints = false;
view.layer.masksToBounds = true;
// view.delegate = self;
view.isScrollEnabled = false;
view.isScrollEnabled = true
view.font = UIFont.systemFont(ofSize: UIFont.systemFontSize + 4);
if Settings.SendMessageOnReturn.getBool() {
view.returnKeyType = .send;
@ -169,6 +165,7 @@ class ChatViewInputBar: UIView, UITextViewDelegate, NSTextStorageDelegate {
set {
inputTextView.text = newValue ?? "";
placeholderLabel.isHidden = !inputTextView.text.isEmpty;
textViewDidChange(inputTextView)
}
}
@ -188,6 +185,8 @@ class ChatViewInputBar: UIView, UITextViewDelegate, NSTextStorageDelegate {
var recordingSession: AVAudioSession!
var audioRecorder: AVAudioRecorder!
var textViewContainerHeightConstraint: NSLayoutConstraint!
convenience init() {
self.init(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 30)));
}
@ -278,6 +277,8 @@ class ChatViewInputBar: UIView, UITextViewDelegate, NSTextStorageDelegate {
leftBottomViewWidthConstraint = bottomLeftView.widthAnchor.constraint(equalToConstant: 0)
voiceRecordingViewWidthConstraint = voiceRecordingView.widthAnchor.constraint(equalToConstant: 0)
voiceRecordingViewWidthConstraint.isActive = true
textViewContainerHeightConstraint = bottomLeftView.heightAnchor.constraint(equalToConstant: 50)
textViewContainerHeightConstraint.isActive = true
}
override func layoutIfNeeded() {
@ -344,6 +345,14 @@ class ChatViewInputBar: UIView, UITextViewDelegate, NSTextStorageDelegate {
self.micButton.isHidden = false
}
}
let oldHeight = textView.frame.size.height
let maxHeight: CGFloat = 120.0 //beyond this value the textView will scroll
var newHeight = min(textView.sizeThatFits(CGSize(width: textView.frame.width, height: CGFloat.greatestFiniteMagnitude)).height, maxHeight)
newHeight = ceil(newHeight+10)
if newHeight != oldHeight {
self.textViewContainerHeightConstraint.constant = max(newHeight, 50.0)
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
@ -406,6 +415,7 @@ class ChatViewInputBar: UIView, UITextViewDelegate, NSTextStorageDelegate {
self.cameraButton.isHidden = false
self.micButton.alpha = 1
self.cameraButton.alpha = 1
self.textViewContainerHeightConstraint.constant = 50
}
}

View file

@ -45,6 +45,7 @@ class ConversationLogController: UIViewController, ChatViewDataSourceDelegate {
tableView.rowHeight = UITableView.automaticDimension;
tableView.estimatedRowHeight = 160.0;
tableView.separatorStyle = .none;
tableView.scrollsToTop = false
tableView.transform = dataSource.inverted ? CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0) : CGAffineTransform.identity;
if let refreshControl = self.refreshControl {
@ -62,14 +63,34 @@ class ConversationLogController: UIViewController, ChatViewDataSourceDelegate {
if !loaded {
loaded = true;
self.dataSource.refreshData(unread: chat.unread) { (firstUnread) in
print("got first unread at:", firstUnread as Any);
if self.tableView.numberOfRows(inSection: 0) > 0 {
self.tableView.scrollToRow(at: IndexPath(row: firstUnread ?? 0, section: 0), at: .none, animated: true);
}
if self.dataSource.count == 0 {
self.toggleNoMessagesLabel(show: true)
} else {
self.toggleNoMessagesLabel(show: false)
}
}
}
}
func toggleNoMessagesLabel(show: Bool) {
if show {
let label = UILabel(frame: CGRect(x: 0, y:0, width: self.view.bounds.size.width, height: self.view.bounds.size.height))
label.text = NSLocalizedString("No messages yet. Say hi!", comment: "")
label.font = UIFont.systemFont(ofSize: UIFont.systemFontSize + 2, weight: .medium)
label.numberOfLines = 0
label.textAlignment = .center
label.transform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0)
label.sizeToFit()
self.tableView.backgroundView = label
} else {
self.tableView.backgroundView = nil
}
}
func itemAdded(at rows: IndexSet, shouldScroll: Bool) {
guard rows.count > 0 else {
return;

View file

@ -34,8 +34,8 @@ class InvitationChatTableViewCell: BaseChatTableViewCell {
private var buttonBottomContraint: NSLayoutConstraint?;
func set(invitation: ChatInvitation) {
super.set(item: invitation);
func set(invitation: ChatInvitation, indexPath: IndexPath) {
super.set(item: invitation, indexPath: indexPath)
self.account = invitation.account;
self.appendix = invitation.appendix;
acceptButton.layer.borderWidth = 2.0;

View file

@ -44,8 +44,8 @@ class LinkPreviewChatTableViewCell: BaseChatTableViewCell {
}
}
func set(linkPreview item: ChatLinkPreview) {
super.set(item: item);
func set(linkPreview item: ChatLinkPreview, indexPath: IndexPath) {
super.set(item: item, indexPath: indexPath)
if #available(iOS 13.0, *) {
var metadata = MetadataCache.instance.metadata(for: "\(item.id)");
var isNew = false;

View file

@ -52,6 +52,22 @@ class ContactViewController: UITableViewController {
return vcard?.emails ?? [];
}
var addToContacts = false
var roster: RosterProvider? {
get {
let rosterType = RosterType(rawValue: Settings.RosterType.getString() ?? "") ?? RosterType.flat
let displayHiddenGroup = Settings.RosterDisplayHiddenGroup.getBool()
let dbConnection = (UIApplication.shared.delegate as! AppDelegate).dbConnection!
switch rosterType {
case .flat:
return RosterProviderFlat(dbConnection: dbConnection, order: .alphabetical, availableOnly: false, displayHiddenGroup: displayHiddenGroup, updateNotificationName: Notification.Name("ROSTER_UPDATE"))
case .grouped:
return RosterProviderGrouped(dbConnection: dbConnection, order: .alphabetical, availableOnly: false, displayHiddenGroup: displayHiddenGroup, updateNotificationName: Notification.Name("ROSTER_UPDATE"))
}
}
}
fileprivate var sections: [Sections] = [.basic];
@ -62,6 +78,7 @@ class ContactViewController: UITableViewController {
if vcard == nil {
refreshVCard();
}
showAddToContacts()
omemoIdentities = DBOMEMOStore.instance.identities(forAccount: account, andName: jid.stringValue);
tableView.contentInset = UIEdgeInsets(top: -1, left: 0, bottom: 0, right: 0);
tableView.reloadData();
@ -92,8 +109,21 @@ class ContactViewController: UITableViewController {
}
}
func showAddToContacts() {
guard let roster = roster else { return }
if !roster.contactExists(account: account, contact: jid) {
addToContacts = true
reloadData()
}
}
func reloadData() {
var sections: [Sections] = [.basic];
if addToContacts {
sections.append(.addToContacts)
}
if chat != nil {
sections.append(.settings);
sections.append(.attachments);
@ -140,7 +170,7 @@ class ContactViewController: UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection sectionNo: Int) -> Int {
switch sections[sectionNo] {
case .basic, .clearHistory, .attachments:
case .basic, .clearHistory, .attachments, .addToContacts:
return 1;
case .settings:
return 3;
@ -174,12 +204,13 @@ class ContactViewController: UITableViewController {
switch sections[indexPath.section] {
case .basic:
let cell = tableView.dequeueReusableCell(withIdentifier: "BasicInfoCell", for: indexPath) as! ContactBasicTableViewCell;
cell.account = account;
cell.jid = jid;
cell.vcard = vcard;
return cell;
case .addToContacts:
let cell = tableView.dequeueReusableCell(withIdentifier: "AddToContactsCell", for: indexPath)
return cell
case .clearHistory:
let cell = tableView.dequeueReusableCell(withIdentifier: "ClearHistoryCell", for: indexPath)
return cell
@ -323,6 +354,14 @@ class ContactViewController: UITableViewController {
switch sections[indexPath.section] {
case .basic:
return;
case .addToContacts:
let navigationController = self.storyboard?.instantiateViewController(withIdentifier: "RosterItemEditNavigationController") as! UINavigationController;
let itemEditController = navigationController.visibleViewController as? RosterItemEditViewController;
itemEditController?.hidesBottomBarWhenPushed = true;
itemEditController?.account = account;
itemEditController?.jid = JID(jid, resource: nil)
navigationController.modalPresentationStyle = .formSheet;
self.present(navigationController, animated: true, completion: nil);
case .clearHistory:
clearChatHistory()
case .settings:
@ -557,6 +596,7 @@ class ContactViewController: UITableViewController {
enum Sections {
case basic
case addToContacts
case settings
case attachments
case encryption
@ -569,6 +609,8 @@ class ContactViewController: UITableViewController {
switch self {
case .basic:
return "";
case .addToContacts:
return ""
case .settings:
return NSLocalizedString("Settings",comment: "")
case .attachments:

View file

@ -16,9 +16,6 @@
/* Class = "UITextField"; placeholder = "Required"; ObjectID = "7KN-S3-8XR"; */
"7KN-S3-8XR.placeholder" = "Required";
/* Class = "UITableViewSection"; headerTitle = "Message Archiving"; ObjectID = "9PW-Rb-rop"; */
"9PW-Rb-rop.headerTitle" = "Message Archiving";
/* Class = "UITableViewController"; title = "OMEMO fingerprints"; ObjectID = "A24-eF-tzh"; */
"A24-eF-tzh.title" = "OMEMO fingerprints";
@ -40,23 +37,20 @@
/* Class = "UITableViewSection"; headerTitle = "Encryption"; ObjectID = "Et5-H6-t7C"; */
"Et5-H6-t7C.headerTitle" = "Encryption";
/* Class = "UITableViewController"; title = "Telephony Provider"; ObjectID = "Ey3-E6-jDR"; */
"Ey3-E6-jDR.title" = "Telephony Provider";
/* Class = "UILabel"; text = "Subtitle"; ObjectID = "FeX-5F-H2m"; */
"FeX-5F-H2m.text" = "Subtitle";
/* Class = "UILabel"; text = "add address"; ObjectID = "fRX-fN-yOj"; */
"fRX-fN-yOj.text" = "add address";
/* Class = "UILabel"; text = "Synchronization"; ObjectID = "GNS-EF-AqY"; */
"GNS-EF-AqY.text" = "Synchronization";
/* Class = "UILabel"; text = "Enabled"; ObjectID = "GTa-Ee-HQT"; */
"GTa-Ee-HQT.text" = "Enabled";
/* Class = "UILabel"; text = "OMEMO fingerprint"; ObjectID = "gUr-GY-P3A"; */
"gUr-GY-P3A.text" = "OMEMO fingerprint";
/* Class = "UILabel"; text = "Delete"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Delete";
/* Class = "UILabel"; text = "Log out"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Log out";
/* Class = "UINavigationItem"; title = "Account settings"; ObjectID = "gzC-dB-kIF"; */
"gzC-dB-kIF.title" = "Account settings";
@ -67,8 +61,8 @@
/* Class = "UIBarButtonItem"; title = "Done"; ObjectID = "ijD-Kb-uyj"; */
"ijD-Kb-uyj.title" = "Done";
/* Class = "UILabel"; text = "Automatic synchronization"; ObjectID = "J9o-cf-vMy"; */
"J9o-cf-vMy.text" = "Automatic synchronization";
/* Class = "UILabel"; text = "Title"; ObjectID = "IUH-ca-fBy"; */
"IUH-ca-fBy.text" = "Title";
/* Class = "UILabel"; text = "add phone"; ObjectID = "jAE-vq-Vfj"; */
"jAE-vq-Vfj.text" = "add phone";
@ -79,6 +73,9 @@
/* Class = "UITextField"; placeholder = "Street"; ObjectID = "KgL-GY-IFp"; */
"KgL-GY-IFp.placeholder" = "Street";
/* Class = "UILabel"; text = "Delete account"; ObjectID = "lGT-wY-qtX"; */
"lGT-wY-qtX.text" = "Delete account";
/* Class = "UITableViewSection"; headerTitle = " Password"; ObjectID = "LO4-Ys-cek"; */
"LO4-Ys-cek.headerTitle" = " Password";
@ -106,6 +103,9 @@
/* Class = "UITableViewSection"; headerTitle = " XMPP ID"; ObjectID = "SKG-bP-NPK"; */
"SKG-bP-NPK.headerTitle" = " XMPP ID";
/* Class = "UILabel"; text = "None"; ObjectID = "StS-4J-0Uu"; */
"StS-4J-0Uu.text" = "None";
/* Class = "UITextField"; placeholder = "Phone number"; ObjectID = "T1r-DU-iqs"; */
"T1r-DU-iqs.placeholder" = "Phone number";
@ -127,15 +127,18 @@
/* Class = "UILabel"; text = "Enabled"; ObjectID = "wbI-5v-vug"; */
"wbI-5v-vug.text" = "Enabled";
/* Class = "UITableViewSection"; headerTitle = "Telephony"; ObjectID = "WFs-jm-Wmk"; */
"WFs-jm-Wmk.headerTitle" = "Telephony";
/* Class = "UITableViewController"; title = "Server Features"; ObjectID = "wW7-3f-Kck"; */
"wW7-3f-Kck.title" = "Server Features";
/* Class = "UILabel"; text = "Last 24 hours"; ObjectID = "XLZ-oR-88H"; */
"XLZ-oR-88H.text" = "Last 24 hours";
/* Class = "UITextField"; placeholder = "Email address"; ObjectID = "ynp-RE-XlY"; */
"ynp-RE-XlY.placeholder" = "Email address";
/* Class = "UILabel"; text = "Provider"; ObjectID = "YYb-TE-za0"; */
"YYb-TE-za0.text" = "Provider";
/* Class = "UILabel"; text = "Re-register push notifications"; ObjectID = "ZHc-Ml-5eX"; */
"ZHc-Ml-5eX.text" = "Enabled";

View file

@ -66,3 +66,6 @@
/* Class = "UILabel"; text = "None"; ObjectID = "YT2-1I-RlM"; */
"YT2-1I-RlM.text" = "None";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "Qmg-sW-dkA"; */
"Qmg-sW-dkA.normalTitle" = "";

View file

@ -4,5 +4,8 @@
/* Class = "UITextView"; text = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n"; ObjectID = "OMw-f8-Hbs"; */
"OMw-f8-Hbs.text" = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n";
/* Class = "UILabel"; text = "Build:"; ObjectID = "R7V-kJ-UYE"; */
"R7V-kJ-UYE.text" = "Build:";
/* Class = "UILabel"; text = "Snikket"; ObjectID = "Z7m-aB-9EO"; */
"Z7m-aB-9EO.text" = "Snikket";

View file

@ -13,9 +13,6 @@
/* Alert dialog title - account is offline */
"Account Offline" = "Account Offline";
/* Account deletion alert title */
"Account Removal" = "Account Removal";
/* Alert title */
"Account Removal Failed" = "Account Removal Failed";
@ -134,9 +131,6 @@
/* No comment provided by engineer. */
"Delete" = "Delete";
/* Account deletion action - destructive */
"Delete account permanently" = "Delete account permanently";
/* Action: Delete all downloaded files */
"Delete all" = "Delete all";
@ -149,6 +143,12 @@
/* No comment provided by engineer. */
"Delete group chat?" = "Delete group chat?";
/* No comment provided by engineer. */
"Delete My Account" = "Delete My Account";
/* No comment provided by engineer. */
"Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@." = "Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@.";
/* No comment provided by engineer. */
"Details" = "Details";
@ -167,9 +167,15 @@
/* No comment provided by engineer. */
"Emails" = "Emails";
/* No comment provided by engineer. */
"Enable %@" = "Enable %@";
/* No comment provided by engineer. */
"Enable automatic message synchronization" = "Enable automatic message synchronization";
/* No comment provided by engineer. */
"Enable telephony provider?" = "Enable telephony provider?";
/* No comment provided by engineer. */
"Encryption" = "Encryption";
@ -272,6 +278,9 @@
/* No comment provided by engineer. */
"List of Messages" = "List of Messages";
/* No comment provided by engineer. */
"Log Out" = "Log Out";
/* No comment provided by engineer. */
"Login and password do not match." = "Login and password do not match.";
@ -362,6 +371,9 @@
/* No comment provided by engineer. */
"Other devices fingerprints" = "Other devices fingerprints";
/* No comment provided by engineer. */
"Permanently Delete Account" = "Permanently Delete Account";
/* No comment provided by engineer. */
"Phones" = "Phones";
@ -402,10 +414,7 @@
"Registration is not supported by this server" = "Registration is not supported by this server";
/* No comment provided by engineer. */
"Remove account from application?" = "Remove account from application?";
/* No comment provided by engineer. */
"Remove from application" = "Remove from application";
"Remove Account Data" = "Remove Account Data";
/* No comment provided by engineer. */
"Rename" = "Rename";
@ -488,9 +497,6 @@
/* No comment provided by engineer. */
"Settings" = "Settings";
/* No comment provided by engineer. */
"Should account be removed from server as well?" = "Should account be removed from server as well?";
/* No comment provided by engineer. */
"Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?" = "Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?";
@ -542,6 +548,9 @@
/* No comment provided by engineer. */
"When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?" = "When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?";
/* No comment provided by engineer. */
"Would you like to use %@ as the default provider for outgoing SMS and calls from %@?" = "Would you like to use %1$@ as the default provider for outgoing SMS and calls from %2$@?";
/* No comment provided by engineer. */
"Yes" = "Yes";
@ -560,6 +569,9 @@
/* No comment provided by engineer. */
"You are not joined to the channel." = "You are not joined to the channel.";
/* No comment provided by engineer. */
"You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone." = "You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone.";
/* No comment provided by engineer. */
"You need to connect to your account before you can update your contact list. Do you wish to connect now?" = "You need to connect to your account before you can update your contact list. Do you wish to connect now?";

View file

@ -31,6 +31,9 @@
/* Class = "UILabel"; text = "Attachments"; ObjectID = "EpU-tc-DIx"; */
"EpU-tc-DIx.text" = "Attachments";
/* Class = "UILabel"; text = "Contact added you to their contact list. Add to contacts?"; ObjectID = "ezh-iK-Sk7"; */
"ezh-iK-Sk7.text" = "Contact added you to their contact list. Add to contacts?";
/* Class = "UILabel"; text = "Receive presence updates"; ObjectID = "ffo-7n-Tn2"; */
"ffo-7n-Tn2.text" = "Receive presence updates";
@ -99,3 +102,9 @@
/* Class = "UIButton"; normalTitle = "Enable"; ObjectID = "yJk-Px-1oo"; */
"yJk-Px-1oo.normalTitle" = "Enable";
/* Class = "UILabel"; text = "Add to contact list"; ObjectID = "zrb-Yr-XQO"; */
"zrb-Yr-XQO.text" = "Add to contact list";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "k9G-H8-3Hb"; */
"k9G-H8-3Hb.normalTitle" = "";

View file

@ -998,7 +998,7 @@ public protocol ChatOptionsProtocol {
public struct RoomOptions: Codable, ChatOptionsProtocol {
var encryption: ChatEncryption?;
public var notifications: ConversationNotification = .mention;
public var notifications: ConversationNotification = .always;
init() {}
@ -1007,7 +1007,7 @@ public struct RoomOptions: Codable, ChatOptionsProtocol {
if let val = try container.decodeIfPresent(String.self, forKey: .encryption) {
encryption = ChatEncryption(rawValue: val);
}
notifications = ConversationNotification(rawValue: try container.decodeIfPresent(String.self, forKey: .notifications) ?? "") ?? .mention;
notifications = ConversationNotification(rawValue: try container.decodeIfPresent(String.self, forKey: .notifications) ?? "") ?? .always;
}
public func encode(to encoder: Encoder) throws {
@ -1015,7 +1015,7 @@ public struct RoomOptions: Codable, ChatOptionsProtocol {
if encryption != nil {
try container.encode(encryption!.rawValue, forKey: .encryption);
}
if notifications != .mention {
if notifications != .always {
try container.encode(notifications.rawValue, forKey: .notifications);
}
}

View file

@ -16,9 +16,6 @@
/* Class = "UITextField"; placeholder = "Required"; ObjectID = "7KN-S3-8XR"; */
"7KN-S3-8XR.placeholder" = "Required";
/* Class = "UITableViewSection"; headerTitle = "Message Archiving"; ObjectID = "9PW-Rb-rop"; */
"9PW-Rb-rop.headerTitle" = "Message Archiving";
/* Class = "UITableViewController"; title = "OMEMO fingerprints"; ObjectID = "A24-eF-tzh"; */
"A24-eF-tzh.title" = "OMEMO fingerprints";
@ -40,23 +37,20 @@
/* Class = "UITableViewSection"; headerTitle = "Encryption"; ObjectID = "Et5-H6-t7C"; */
"Et5-H6-t7C.headerTitle" = "Encryption";
/* Class = "UITableViewController"; title = "Telephony Provider"; ObjectID = "Ey3-E6-jDR"; */
"Ey3-E6-jDR.title" = "Telephony Provider";
/* Class = "UILabel"; text = "Subtitle"; ObjectID = "FeX-5F-H2m"; */
"FeX-5F-H2m.text" = "Subtitle";
/* Class = "UILabel"; text = "add address"; ObjectID = "fRX-fN-yOj"; */
"fRX-fN-yOj.text" = "add address";
/* Class = "UILabel"; text = "Synchronization"; ObjectID = "GNS-EF-AqY"; */
"GNS-EF-AqY.text" = "Synchronization";
/* Class = "UILabel"; text = "Enabled"; ObjectID = "GTa-Ee-HQT"; */
"GTa-Ee-HQT.text" = "Enabled";
/* Class = "UILabel"; text = "OMEMO fingerprint"; ObjectID = "gUr-GY-P3A"; */
"gUr-GY-P3A.text" = "OMEMO fingerprint";
/* Class = "UILabel"; text = "Delete"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Delete";
/* Class = "UILabel"; text = "Log out"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Log out";
/* Class = "UINavigationItem"; title = "Account settings"; ObjectID = "gzC-dB-kIF"; */
"gzC-dB-kIF.title" = "Account settings";
@ -67,8 +61,8 @@
/* Class = "UIBarButtonItem"; title = "Done"; ObjectID = "ijD-Kb-uyj"; */
"ijD-Kb-uyj.title" = "Done";
/* Class = "UILabel"; text = "Automatic synchronization"; ObjectID = "J9o-cf-vMy"; */
"J9o-cf-vMy.text" = "Automatic synchronization";
/* Class = "UILabel"; text = "Title"; ObjectID = "IUH-ca-fBy"; */
"IUH-ca-fBy.text" = "Title";
/* Class = "UILabel"; text = "add phone"; ObjectID = "jAE-vq-Vfj"; */
"jAE-vq-Vfj.text" = "add phone";
@ -79,6 +73,9 @@
/* Class = "UITextField"; placeholder = "Street"; ObjectID = "KgL-GY-IFp"; */
"KgL-GY-IFp.placeholder" = "Street";
/* Class = "UILabel"; text = "Delete account"; ObjectID = "lGT-wY-qtX"; */
"lGT-wY-qtX.text" = "Delete account";
/* Class = "UITableViewSection"; headerTitle = " Password"; ObjectID = "LO4-Ys-cek"; */
"LO4-Ys-cek.headerTitle" = " Password";
@ -106,6 +103,9 @@
/* Class = "UITableViewSection"; headerTitle = " XMPP ID"; ObjectID = "SKG-bP-NPK"; */
"SKG-bP-NPK.headerTitle" = " XMPP ID";
/* Class = "UILabel"; text = "None"; ObjectID = "StS-4J-0Uu"; */
"StS-4J-0Uu.text" = "None";
/* Class = "UITextField"; placeholder = "Phone number"; ObjectID = "T1r-DU-iqs"; */
"T1r-DU-iqs.placeholder" = "Phone number";
@ -127,15 +127,18 @@
/* Class = "UILabel"; text = "Enabled"; ObjectID = "wbI-5v-vug"; */
"wbI-5v-vug.text" = "Enabled";
/* Class = "UITableViewSection"; headerTitle = "Telephony"; ObjectID = "WFs-jm-Wmk"; */
"WFs-jm-Wmk.headerTitle" = "Telephony";
/* Class = "UITableViewController"; title = "Server Features"; ObjectID = "wW7-3f-Kck"; */
"wW7-3f-Kck.title" = "Server Features";
/* Class = "UILabel"; text = "Last 24 hours"; ObjectID = "XLZ-oR-88H"; */
"XLZ-oR-88H.text" = "Last 24 hours";
/* Class = "UITextField"; placeholder = "Email address"; ObjectID = "ynp-RE-XlY"; */
"ynp-RE-XlY.placeholder" = "Email address";
/* Class = "UILabel"; text = "Provider"; ObjectID = "YYb-TE-za0"; */
"YYb-TE-za0.text" = "Provider";
/* Class = "UILabel"; text = "Re-register push notifications"; ObjectID = "ZHc-Ml-5eX"; */
"ZHc-Ml-5eX.text" = "Re-register push notifications";

View file

@ -66,3 +66,6 @@
/* Class = "UILabel"; text = "None"; ObjectID = "YT2-1I-RlM"; */
"YT2-1I-RlM.text" = "None";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "Qmg-sW-dkA"; */
"Qmg-sW-dkA.normalTitle" = "";

View file

@ -4,5 +4,8 @@
/* Class = "UITextView"; text = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n"; ObjectID = "OMw-f8-Hbs"; */
"OMw-f8-Hbs.text" = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n";
/* Class = "UILabel"; text = "Build:"; ObjectID = "R7V-kJ-UYE"; */
"R7V-kJ-UYE.text" = "Build:";
/* Class = "UILabel"; text = "Snikket"; ObjectID = "Z7m-aB-9EO"; */
"Z7m-aB-9EO.text" = "Snikket";

View file

@ -13,9 +13,6 @@
/* Alert dialog title - account is offline */
"Account Offline" = "Account Offline";
/* Account deletion alert title */
"Account Removal" = "Account Removal";
/* Alert title */
"Account Removal Failed" = "Account Removal Failed";
@ -134,9 +131,6 @@
/* No comment provided by engineer. */
"Delete" = "Delete";
/* Account deletion action - destructive */
"Delete account permanently" = "Delete account permanently";
/* Action: Delete all downloaded files */
"Delete all" = "Delete all";
@ -149,6 +143,12 @@
/* No comment provided by engineer. */
"Delete group chat?" = "Delete group chat?";
/* No comment provided by engineer. */
"Delete My Account" = "Delete My Account";
/* No comment provided by engineer. */
"Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@." = "Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@.";
/* No comment provided by engineer. */
"Details" = "Details";
@ -167,9 +167,15 @@
/* No comment provided by engineer. */
"Emails" = "Emails";
/* No comment provided by engineer. */
"Enable %@" = "Enable %@";
/* No comment provided by engineer. */
"Enable automatic message synchronization" = "Enable automatic message synchronization";
/* No comment provided by engineer. */
"Enable telephony provider?" = "Enable telephony provider?";
/* No comment provided by engineer. */
"Encryption" = "Encryption";
@ -272,6 +278,9 @@
/* No comment provided by engineer. */
"List of Messages" = "List of Messages";
/* No comment provided by engineer. */
"Log Out" = "Log Out";
/* No comment provided by engineer. */
"Login and password do not match." = "Login and password do not match.";
@ -362,6 +371,9 @@
/* No comment provided by engineer. */
"Other devices fingerprints" = "Other devices fingerprints";
/* No comment provided by engineer. */
"Permanently Delete Account" = "Permanently Delete Account";
/* No comment provided by engineer. */
"Phones" = "Phones";
@ -402,10 +414,7 @@
"Registration is not supported by this server" = "Registration is not supported by this server";
/* No comment provided by engineer. */
"Remove account from application?" = "Remove account from application?";
/* No comment provided by engineer. */
"Remove from application" = "Remove from application";
"Remove Account Data" = "Remove Account Data";
/* No comment provided by engineer. */
"Rename" = "Rename";
@ -488,9 +497,6 @@
/* No comment provided by engineer. */
"Settings" = "Settings";
/* No comment provided by engineer. */
"Should account be removed from server as well?" = "Should account be removed from server as well?";
/* No comment provided by engineer. */
"Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?" = "Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?";
@ -542,6 +548,9 @@
/* No comment provided by engineer. */
"When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?" = "When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?";
/* No comment provided by engineer. */
"Would you like to use %@ as the default provider for outgoing SMS and calls from %@?" = "Would you like to use %1$@ as the default provider for outgoing SMS and calls from %2$@?";
/* No comment provided by engineer. */
"Yes" = "Yes";
@ -560,6 +569,9 @@
/* No comment provided by engineer. */
"You are not joined to the channel." = "You are not joined to the channel.";
/* No comment provided by engineer. */
"You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone." = "You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone.";
/* No comment provided by engineer. */
"You need to connect to your account before you can update your contact list. Do you wish to connect now?" = "You need to connect to your account before you can update your contact list. Do you wish to connect now?";

View file

@ -31,6 +31,9 @@
/* Class = "UILabel"; text = "Attachments"; ObjectID = "EpU-tc-DIx"; */
"EpU-tc-DIx.text" = "Attachments";
/* Class = "UILabel"; text = "Contact added you to their contact list. Add to contacts?"; ObjectID = "ezh-iK-Sk7"; */
"ezh-iK-Sk7.text" = "Contact added you to their contact list. Add to contacts?";
/* Class = "UILabel"; text = "Receive presence updates"; ObjectID = "ffo-7n-Tn2"; */
"ffo-7n-Tn2.text" = "Receive presence updates";
@ -99,3 +102,9 @@
/* Class = "UIButton"; normalTitle = "Enable"; ObjectID = "yJk-Px-1oo"; */
"yJk-Px-1oo.normalTitle" = "Enable";
/* Class = "UILabel"; text = "Add to contact list"; ObjectID = "zrb-Yr-XQO"; */
"zrb-Yr-XQO.text" = "Add to contact list";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "k9G-H8-3Hb"; */
"k9G-H8-3Hb.normalTitle" = "";

View file

@ -16,9 +16,6 @@
/* Class = "UITextField"; placeholder = "Required"; ObjectID = "7KN-S3-8XR"; */
"7KN-S3-8XR.placeholder" = "Required";
/* Class = "UITableViewSection"; headerTitle = "Message Archiving"; ObjectID = "9PW-Rb-rop"; */
"9PW-Rb-rop.headerTitle" = "Message Archiving";
/* Class = "UITableViewController"; title = "OMEMO fingerprints"; ObjectID = "A24-eF-tzh"; */
"A24-eF-tzh.title" = "OMEMO fingerprints";
@ -40,23 +37,20 @@
/* Class = "UITableViewSection"; headerTitle = "Encryption"; ObjectID = "Et5-H6-t7C"; */
"Et5-H6-t7C.headerTitle" = "Encryption";
/* Class = "UITableViewController"; title = "Telephony Provider"; ObjectID = "Ey3-E6-jDR"; */
"Ey3-E6-jDR.title" = "Telephony Provider";
/* Class = "UILabel"; text = "Subtitle"; ObjectID = "FeX-5F-H2m"; */
"FeX-5F-H2m.text" = "Subtitle";
/* Class = "UILabel"; text = "add address"; ObjectID = "fRX-fN-yOj"; */
"fRX-fN-yOj.text" = "add address";
/* Class = "UILabel"; text = "Synchronization"; ObjectID = "GNS-EF-AqY"; */
"GNS-EF-AqY.text" = "Synchronization";
/* Class = "UILabel"; text = "Enabled"; ObjectID = "GTa-Ee-HQT"; */
"GTa-Ee-HQT.text" = "Enabled";
/* Class = "UILabel"; text = "OMEMO fingerprint"; ObjectID = "gUr-GY-P3A"; */
"gUr-GY-P3A.text" = "OMEMO fingerprint";
/* Class = "UILabel"; text = "Delete"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Delete";
/* Class = "UILabel"; text = "Log out"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Log out";
/* Class = "UINavigationItem"; title = "Account settings"; ObjectID = "gzC-dB-kIF"; */
"gzC-dB-kIF.title" = "Account settings";
@ -67,8 +61,8 @@
/* Class = "UIBarButtonItem"; title = "Done"; ObjectID = "ijD-Kb-uyj"; */
"ijD-Kb-uyj.title" = "Done";
/* Class = "UILabel"; text = "Automatic synchronization"; ObjectID = "J9o-cf-vMy"; */
"J9o-cf-vMy.text" = "Automatic synchronization";
/* Class = "UILabel"; text = "Title"; ObjectID = "IUH-ca-fBy"; */
"IUH-ca-fBy.text" = "Title";
/* Class = "UILabel"; text = "add phone"; ObjectID = "jAE-vq-Vfj"; */
"jAE-vq-Vfj.text" = "add phone";
@ -79,6 +73,9 @@
/* Class = "UITextField"; placeholder = "Street"; ObjectID = "KgL-GY-IFp"; */
"KgL-GY-IFp.placeholder" = "Street";
/* Class = "UILabel"; text = "Delete account"; ObjectID = "lGT-wY-qtX"; */
"lGT-wY-qtX.text" = "Delete account";
/* Class = "UITableViewSection"; headerTitle = " Password"; ObjectID = "LO4-Ys-cek"; */
"LO4-Ys-cek.headerTitle" = " Password";
@ -106,6 +103,9 @@
/* Class = "UITableViewSection"; headerTitle = " XMPP ID"; ObjectID = "SKG-bP-NPK"; */
"SKG-bP-NPK.headerTitle" = " XMPP ID";
/* Class = "UILabel"; text = "None"; ObjectID = "StS-4J-0Uu"; */
"StS-4J-0Uu.text" = "None";
/* Class = "UITextField"; placeholder = "Phone number"; ObjectID = "T1r-DU-iqs"; */
"T1r-DU-iqs.placeholder" = "Phone number";
@ -127,15 +127,18 @@
/* Class = "UILabel"; text = "Enabled"; ObjectID = "wbI-5v-vug"; */
"wbI-5v-vug.text" = "Enabled";
/* Class = "UITableViewSection"; headerTitle = "Telephony"; ObjectID = "WFs-jm-Wmk"; */
"WFs-jm-Wmk.headerTitle" = "Telephony";
/* Class = "UITableViewController"; title = "Server Features"; ObjectID = "wW7-3f-Kck"; */
"wW7-3f-Kck.title" = "Server Features";
/* Class = "UILabel"; text = "Last 24 hours"; ObjectID = "XLZ-oR-88H"; */
"XLZ-oR-88H.text" = "Last 24 hours";
/* Class = "UITextField"; placeholder = "Email address"; ObjectID = "ynp-RE-XlY"; */
"ynp-RE-XlY.placeholder" = "Email address";
/* Class = "UILabel"; text = "Provider"; ObjectID = "YYb-TE-za0"; */
"YYb-TE-za0.text" = "Provider";
/* Class = "UILabel"; text = "Re-register push notifications"; ObjectID = "ZHc-Ml-5eX"; */
"ZHc-Ml-5eX.text" = "Re-register push notifications";

View file

@ -66,3 +66,6 @@
/* Class = "UILabel"; text = "None"; ObjectID = "YT2-1I-RlM"; */
"YT2-1I-RlM.text" = "None";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "Qmg-sW-dkA"; */
"Qmg-sW-dkA.normalTitle" = "";

View file

@ -4,5 +4,8 @@
/* Class = "UITextView"; text = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n"; ObjectID = "OMw-f8-Hbs"; */
"OMw-f8-Hbs.text" = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n";
/* Class = "UILabel"; text = "Build:"; ObjectID = "R7V-kJ-UYE"; */
"R7V-kJ-UYE.text" = "Build:";
/* Class = "UILabel"; text = "Snikket"; ObjectID = "Z7m-aB-9EO"; */
"Z7m-aB-9EO.text" = "Snikket";

View file

@ -13,9 +13,6 @@
/* Alert dialog title - account is offline */
"Account Offline" = "Account Offline";
/* Account deletion alert title */
"Account Removal" = "Account Removal";
/* Alert title */
"Account Removal Failed" = "Account Removal Failed";
@ -134,9 +131,6 @@
/* No comment provided by engineer. */
"Delete" = "Delete";
/* Account deletion action - destructive */
"Delete account permanently" = "Delete account permanently";
/* Action: Delete all downloaded files */
"Delete all" = "Delete all";
@ -149,6 +143,12 @@
/* No comment provided by engineer. */
"Delete group chat?" = "Delete group chat?";
/* No comment provided by engineer. */
"Delete My Account" = "Delete My Account";
/* No comment provided by engineer. */
"Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@." = "Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@.";
/* No comment provided by engineer. */
"Details" = "Details";
@ -167,9 +167,15 @@
/* No comment provided by engineer. */
"Emails" = "Emails";
/* No comment provided by engineer. */
"Enable %@" = "Enable %@";
/* No comment provided by engineer. */
"Enable automatic message synchronization" = "Enable automatic message synchronization";
/* No comment provided by engineer. */
"Enable telephony provider?" = "Enable telephony provider?";
/* No comment provided by engineer. */
"Encryption" = "Encryption";
@ -272,6 +278,9 @@
/* No comment provided by engineer. */
"List of Messages" = "List of Messages";
/* No comment provided by engineer. */
"Log Out" = "Log Out";
/* No comment provided by engineer. */
"Login and password do not match." = "Login and password do not match.";
@ -362,6 +371,9 @@
/* No comment provided by engineer. */
"Other devices fingerprints" = "Other devices fingerprints";
/* No comment provided by engineer. */
"Permanently Delete Account" = "Permanently Delete Account";
/* No comment provided by engineer. */
"Phones" = "Phones";
@ -402,10 +414,7 @@
"Registration is not supported by this server" = "Registration is not supported by this server";
/* No comment provided by engineer. */
"Remove account from application?" = "Remove account from application?";
/* No comment provided by engineer. */
"Remove from application" = "Remove from application";
"Remove Account Data" = "Remove Account Data";
/* No comment provided by engineer. */
"Rename" = "Rename";
@ -488,9 +497,6 @@
/* No comment provided by engineer. */
"Settings" = "Settings";
/* No comment provided by engineer. */
"Should account be removed from server as well?" = "Should account be removed from server as well?";
/* No comment provided by engineer. */
"Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?" = "Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?";
@ -542,6 +548,9 @@
/* No comment provided by engineer. */
"When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?" = "When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?";
/* No comment provided by engineer. */
"Would you like to use %@ as the default provider for outgoing SMS and calls from %@?" = "Would you like to use %1$@ as the default provider for outgoing SMS and calls from %2$@?";
/* No comment provided by engineer. */
"Yes" = "Yes";
@ -560,6 +569,9 @@
/* No comment provided by engineer. */
"You are not joined to the channel." = "You are not joined to the channel.";
/* No comment provided by engineer. */
"You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone." = "You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone.";
/* No comment provided by engineer. */
"You need to connect to your account before you can update your contact list. Do you wish to connect now?" = "You need to connect to your account before you can update your contact list. Do you wish to connect now?";

View file

@ -31,6 +31,9 @@
/* Class = "UILabel"; text = "Attachments"; ObjectID = "EpU-tc-DIx"; */
"EpU-tc-DIx.text" = "Attachments";
/* Class = "UILabel"; text = "Contact added you to their contact list. Add to contacts?"; ObjectID = "ezh-iK-Sk7"; */
"ezh-iK-Sk7.text" = "Contact added you to their contact list. Add to contacts?";
/* Class = "UILabel"; text = "Receive presence updates"; ObjectID = "ffo-7n-Tn2"; */
"ffo-7n-Tn2.text" = "Receive presence updates";
@ -99,3 +102,9 @@
/* Class = "UIButton"; normalTitle = "Enable"; ObjectID = "yJk-Px-1oo"; */
"yJk-Px-1oo.normalTitle" = "Enable";
/* Class = "UILabel"; text = "Add to contact list"; ObjectID = "zrb-Yr-XQO"; */
"zrb-Yr-XQO.text" = "Add to contact list";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "k9G-H8-3Hb"; */
"k9G-H8-3Hb.normalTitle" = "";

View file

@ -16,9 +16,6 @@
/* Class = "UITextField"; placeholder = "Required"; ObjectID = "7KN-S3-8XR"; */
"7KN-S3-8XR.placeholder" = "Required";
/* Class = "UITableViewSection"; headerTitle = "Message Archiving"; ObjectID = "9PW-Rb-rop"; */
"9PW-Rb-rop.headerTitle" = "Message Archiving";
/* Class = "UITableViewController"; title = "OMEMO fingerprints"; ObjectID = "A24-eF-tzh"; */
"A24-eF-tzh.title" = "OMEMO fingerprints";
@ -40,23 +37,20 @@
/* Class = "UITableViewSection"; headerTitle = "Encryption"; ObjectID = "Et5-H6-t7C"; */
"Et5-H6-t7C.headerTitle" = "Encryption";
/* Class = "UITableViewController"; title = "Telephony Provider"; ObjectID = "Ey3-E6-jDR"; */
"Ey3-E6-jDR.title" = "Telephony Provider";
/* Class = "UILabel"; text = "Subtitle"; ObjectID = "FeX-5F-H2m"; */
"FeX-5F-H2m.text" = "Subtitle";
/* Class = "UILabel"; text = "add address"; ObjectID = "fRX-fN-yOj"; */
"fRX-fN-yOj.text" = "add address";
/* Class = "UILabel"; text = "Synchronization"; ObjectID = "GNS-EF-AqY"; */
"GNS-EF-AqY.text" = "Synchronization";
/* Class = "UILabel"; text = "Enabled"; ObjectID = "GTa-Ee-HQT"; */
"GTa-Ee-HQT.text" = "Enabled";
/* Class = "UILabel"; text = "OMEMO fingerprint"; ObjectID = "gUr-GY-P3A"; */
"gUr-GY-P3A.text" = "OMEMO fingerprint";
/* Class = "UILabel"; text = "Delete"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Delete";
/* Class = "UILabel"; text = "Log out"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Log out";
/* Class = "UINavigationItem"; title = "Account settings"; ObjectID = "gzC-dB-kIF"; */
"gzC-dB-kIF.title" = "Account settings";
@ -67,8 +61,8 @@
/* Class = "UIBarButtonItem"; title = "Done"; ObjectID = "ijD-Kb-uyj"; */
"ijD-Kb-uyj.title" = "Done";
/* Class = "UILabel"; text = "Automatic synchronization"; ObjectID = "J9o-cf-vMy"; */
"J9o-cf-vMy.text" = "Automatic synchronization";
/* Class = "UILabel"; text = "Title"; ObjectID = "IUH-ca-fBy"; */
"IUH-ca-fBy.text" = "Title";
/* Class = "UILabel"; text = "add phone"; ObjectID = "jAE-vq-Vfj"; */
"jAE-vq-Vfj.text" = "add phone";
@ -79,6 +73,9 @@
/* Class = "UITextField"; placeholder = "Street"; ObjectID = "KgL-GY-IFp"; */
"KgL-GY-IFp.placeholder" = "Street";
/* Class = "UILabel"; text = "Delete account"; ObjectID = "lGT-wY-qtX"; */
"lGT-wY-qtX.text" = "Delete account";
/* Class = "UITableViewSection"; headerTitle = " Password"; ObjectID = "LO4-Ys-cek"; */
"LO4-Ys-cek.headerTitle" = " Password";
@ -106,6 +103,9 @@
/* Class = "UITableViewSection"; headerTitle = " XMPP ID"; ObjectID = "SKG-bP-NPK"; */
"SKG-bP-NPK.headerTitle" = " XMPP ID";
/* Class = "UILabel"; text = "None"; ObjectID = "StS-4J-0Uu"; */
"StS-4J-0Uu.text" = "None";
/* Class = "UITextField"; placeholder = "Phone number"; ObjectID = "T1r-DU-iqs"; */
"T1r-DU-iqs.placeholder" = "Phone number";
@ -127,15 +127,18 @@
/* Class = "UILabel"; text = "Enabled"; ObjectID = "wbI-5v-vug"; */
"wbI-5v-vug.text" = "Enabled";
/* Class = "UITableViewSection"; headerTitle = "Telephony"; ObjectID = "WFs-jm-Wmk"; */
"WFs-jm-Wmk.headerTitle" = "Telephony";
/* Class = "UITableViewController"; title = "Server Features"; ObjectID = "wW7-3f-Kck"; */
"wW7-3f-Kck.title" = "Server Features";
/* Class = "UILabel"; text = "Last 24 hours"; ObjectID = "XLZ-oR-88H"; */
"XLZ-oR-88H.text" = "Last 24 hours";
/* Class = "UITextField"; placeholder = "Email address"; ObjectID = "ynp-RE-XlY"; */
"ynp-RE-XlY.placeholder" = "Email address";
/* Class = "UILabel"; text = "Provider"; ObjectID = "YYb-TE-za0"; */
"YYb-TE-za0.text" = "Provider";
/* Class = "UILabel"; text = "Re-register push notifications"; ObjectID = "ZHc-Ml-5eX"; */
"ZHc-Ml-5eX.text" = "Re-register for push notifications";

View file

@ -66,3 +66,6 @@
/* Class = "UILabel"; text = "None"; ObjectID = "YT2-1I-RlM"; */
"YT2-1I-RlM.text" = "None";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "Qmg-sW-dkA"; */
"Qmg-sW-dkA.normalTitle" = "";

View file

@ -4,5 +4,8 @@
/* Class = "UITextView"; text = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n"; ObjectID = "OMw-f8-Hbs"; */
"OMw-f8-Hbs.text" = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n";
/* Class = "UILabel"; text = "Build:"; ObjectID = "R7V-kJ-UYE"; */
"R7V-kJ-UYE.text" = "Build:";
/* Class = "UILabel"; text = "Snikket"; ObjectID = "Z7m-aB-9EO"; */
"Z7m-aB-9EO.text" = "Snikket";

View file

@ -13,9 +13,6 @@
/* Alert dialog title - account is offline */
"Account Offline" = "Account Offline";
/* Account deletion alert title */
"Account Removal" = "Account Removal";
/* Alert title */
"Account Removal Failed" = "Account Removal Failed";
@ -134,9 +131,6 @@
/* No comment provided by engineer. */
"Delete" = "Delete";
/* Account deletion action - destructive */
"Delete account permanently" = "Delete account permanently";
/* Action: Delete all downloaded files */
"Delete all" = "Delete all";
@ -149,6 +143,12 @@
/* No comment provided by engineer. */
"Delete group chat?" = "Delete group chat?";
/* No comment provided by engineer. */
"Delete My Account" = "Delete My Account";
/* No comment provided by engineer. */
"Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@." = "Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@.";
/* No comment provided by engineer. */
"Details" = "Details";
@ -167,9 +167,15 @@
/* No comment provided by engineer. */
"Emails" = "Emails";
/* No comment provided by engineer. */
"Enable %@" = "Enable %@";
/* No comment provided by engineer. */
"Enable automatic message synchronization" = "Enable automatic message synchronization";
/* No comment provided by engineer. */
"Enable telephony provider?" = "Enable telephony provider?";
/* No comment provided by engineer. */
"Encryption" = "Encryption";
@ -272,6 +278,9 @@
/* No comment provided by engineer. */
"List of Messages" = "List of Messages";
/* No comment provided by engineer. */
"Log Out" = "Log Out";
/* No comment provided by engineer. */
"Login and password do not match." = "Login and password do not match.";
@ -362,6 +371,9 @@
/* No comment provided by engineer. */
"Other devices fingerprints" = "Other devices fingerprints";
/* No comment provided by engineer. */
"Permanently Delete Account" = "Permanently Delete Account";
/* No comment provided by engineer. */
"Phones" = "Phones";
@ -402,10 +414,7 @@
"Registration is not supported by this server" = "Registration is not supported by this server";
/* No comment provided by engineer. */
"Remove account from application?" = "Remove account from application?";
/* No comment provided by engineer. */
"Remove from application" = "Remove from application";
"Remove Account Data" = "Remove Account Data";
/* No comment provided by engineer. */
"Rename" = "Rename";
@ -488,9 +497,6 @@
/* No comment provided by engineer. */
"Settings" = "Settings";
/* No comment provided by engineer. */
"Should account be removed from server as well?" = "Should account be removed from server as well?";
/* No comment provided by engineer. */
"Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?" = "Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?";
@ -542,6 +548,9 @@
/* No comment provided by engineer. */
"When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?" = "When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?";
/* No comment provided by engineer. */
"Would you like to use %@ as the default provider for outgoing SMS and calls from %@?" = "Would you like to use %1$@ as the default provider for outgoing SMS and calls from %2$@?";
/* No comment provided by engineer. */
"Yes" = "Yes";
@ -560,6 +569,9 @@
/* No comment provided by engineer. */
"You are not joined to the channel." = "You are not joined to the channel.";
/* No comment provided by engineer. */
"You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone." = "You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone.";
/* No comment provided by engineer. */
"You need to connect to your account before you can update your contact list. Do you wish to connect now?" = "You need to connect to your account before you can update your contact list. Do you wish to connect now?";

View file

@ -31,6 +31,9 @@
/* Class = "UILabel"; text = "Attachments"; ObjectID = "EpU-tc-DIx"; */
"EpU-tc-DIx.text" = "Attachments";
/* Class = "UILabel"; text = "Contact added you to their contact list. Add to contacts?"; ObjectID = "ezh-iK-Sk7"; */
"ezh-iK-Sk7.text" = "Contact added you to their contact list. Add to contacts?";
/* Class = "UILabel"; text = "Receive presence updates"; ObjectID = "ffo-7n-Tn2"; */
"ffo-7n-Tn2.text" = "Receive presence updates";
@ -99,3 +102,9 @@
/* Class = "UIButton"; normalTitle = "Enable"; ObjectID = "yJk-Px-1oo"; */
"yJk-Px-1oo.normalTitle" = "Enable";
/* Class = "UILabel"; text = "Add to contact list"; ObjectID = "zrb-Yr-XQO"; */
"zrb-Yr-XQO.text" = "Add to contact list";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "k9G-H8-3Hb"; */
"k9G-H8-3Hb.normalTitle" = "";

View file

@ -16,9 +16,6 @@
/* Class = "UITextField"; placeholder = "Required"; ObjectID = "7KN-S3-8XR"; */
"7KN-S3-8XR.placeholder" = "Required";
/* Class = "UITableViewSection"; headerTitle = "Message Archiving"; ObjectID = "9PW-Rb-rop"; */
"9PW-Rb-rop.headerTitle" = "Message Archiving";
/* Class = "UITableViewController"; title = "OMEMO fingerprints"; ObjectID = "A24-eF-tzh"; */
"A24-eF-tzh.title" = "OMEMO fingerprints";
@ -40,23 +37,20 @@
/* Class = "UITableViewSection"; headerTitle = "Encryption"; ObjectID = "Et5-H6-t7C"; */
"Et5-H6-t7C.headerTitle" = "Encryption";
/* Class = "UITableViewController"; title = "Telephony Provider"; ObjectID = "Ey3-E6-jDR"; */
"Ey3-E6-jDR.title" = "Telephony Provider";
/* Class = "UILabel"; text = "Subtitle"; ObjectID = "FeX-5F-H2m"; */
"FeX-5F-H2m.text" = "Subtitle";
/* Class = "UILabel"; text = "add address"; ObjectID = "fRX-fN-yOj"; */
"fRX-fN-yOj.text" = "add address";
/* Class = "UILabel"; text = "Synchronization"; ObjectID = "GNS-EF-AqY"; */
"GNS-EF-AqY.text" = "Synchronization";
/* Class = "UILabel"; text = "Enabled"; ObjectID = "GTa-Ee-HQT"; */
"GTa-Ee-HQT.text" = "Enabled";
/* Class = "UILabel"; text = "OMEMO fingerprint"; ObjectID = "gUr-GY-P3A"; */
"gUr-GY-P3A.text" = "OMEMO fingerprint";
/* Class = "UILabel"; text = "Delete"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Delete";
/* Class = "UILabel"; text = "Log out"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Log out";
/* Class = "UINavigationItem"; title = "Account settings"; ObjectID = "gzC-dB-kIF"; */
"gzC-dB-kIF.title" = "Account settings";
@ -67,8 +61,8 @@
/* Class = "UIBarButtonItem"; title = "Done"; ObjectID = "ijD-Kb-uyj"; */
"ijD-Kb-uyj.title" = "Done";
/* Class = "UILabel"; text = "Automatic synchronization"; ObjectID = "J9o-cf-vMy"; */
"J9o-cf-vMy.text" = "Automatic synchronization";
/* Class = "UILabel"; text = "Title"; ObjectID = "IUH-ca-fBy"; */
"IUH-ca-fBy.text" = "Title";
/* Class = "UILabel"; text = "add phone"; ObjectID = "jAE-vq-Vfj"; */
"jAE-vq-Vfj.text" = "add phone";
@ -79,6 +73,9 @@
/* Class = "UITextField"; placeholder = "Street"; ObjectID = "KgL-GY-IFp"; */
"KgL-GY-IFp.placeholder" = "Street";
/* Class = "UILabel"; text = "Delete account"; ObjectID = "lGT-wY-qtX"; */
"lGT-wY-qtX.text" = "Delete account";
/* Class = "UITableViewSection"; headerTitle = " Password"; ObjectID = "LO4-Ys-cek"; */
"LO4-Ys-cek.headerTitle" = " Password";
@ -106,6 +103,9 @@
/* Class = "UITableViewSection"; headerTitle = " XMPP ID"; ObjectID = "SKG-bP-NPK"; */
"SKG-bP-NPK.headerTitle" = " XMPP ID";
/* Class = "UILabel"; text = "None"; ObjectID = "StS-4J-0Uu"; */
"StS-4J-0Uu.text" = "None";
/* Class = "UITextField"; placeholder = "Phone number"; ObjectID = "T1r-DU-iqs"; */
"T1r-DU-iqs.placeholder" = "Phone number";
@ -127,15 +127,18 @@
/* Class = "UILabel"; text = "Enabled"; ObjectID = "wbI-5v-vug"; */
"wbI-5v-vug.text" = "Enabled";
/* Class = "UITableViewSection"; headerTitle = "Telephony"; ObjectID = "WFs-jm-Wmk"; */
"WFs-jm-Wmk.headerTitle" = "Telephony";
/* Class = "UITableViewController"; title = "Server Features"; ObjectID = "wW7-3f-Kck"; */
"wW7-3f-Kck.title" = "Server Features";
/* Class = "UILabel"; text = "Last 24 hours"; ObjectID = "XLZ-oR-88H"; */
"XLZ-oR-88H.text" = "Last 24 hours";
/* Class = "UITextField"; placeholder = "Email address"; ObjectID = "ynp-RE-XlY"; */
"ynp-RE-XlY.placeholder" = "Email address";
/* Class = "UILabel"; text = "Provider"; ObjectID = "YYb-TE-za0"; */
"YYb-TE-za0.text" = "Provider";
/* Class = "UILabel"; text = "Re-register push notifications"; ObjectID = "ZHc-Ml-5eX"; */
"ZHc-Ml-5eX.text" = "Re-register push notifications";

View file

@ -66,3 +66,6 @@
/* Class = "UILabel"; text = "None"; ObjectID = "YT2-1I-RlM"; */
"YT2-1I-RlM.text" = "None";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "Qmg-sW-dkA"; */
"Qmg-sW-dkA.normalTitle" = "";

View file

@ -4,5 +4,8 @@
/* Class = "UITextView"; text = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n"; ObjectID = "OMw-f8-Hbs"; */
"OMw-f8-Hbs.text" = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n";
/* Class = "UILabel"; text = "Build:"; ObjectID = "R7V-kJ-UYE"; */
"R7V-kJ-UYE.text" = "Build:";
/* Class = "UILabel"; text = "Snikket"; ObjectID = "Z7m-aB-9EO"; */
"Z7m-aB-9EO.text" = "Snikket";

View file

@ -13,9 +13,6 @@
/* Alert dialog title - account is offline */
"Account Offline" = "Account Offline";
/* Account deletion alert title */
"Account Removal" = "Suppression du compte";
/* Alert title */
"Account Removal Failed" = "Échec de la suppression du compte";
@ -134,9 +131,6 @@
/* No comment provided by engineer. */
"Delete" = "Supprimer";
/* Account deletion action - destructive */
"Delete account permanently" = "Delete account permanently";
/* Action: Delete all downloaded files */
"Delete all" = "Tout supprimer";
@ -149,6 +143,12 @@
/* No comment provided by engineer. */
"Delete group chat?" = "Supprimer ce salon?";
/* No comment provided by engineer. */
"Delete My Account" = "Delete My Account";
/* No comment provided by engineer. */
"Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@." = "Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@.";
/* No comment provided by engineer. */
"Details" = "Details";
@ -167,9 +167,15 @@
/* No comment provided by engineer. */
"Emails" = "E-mails";
/* No comment provided by engineer. */
"Enable %@" = "Enable %@";
/* No comment provided by engineer. */
"Enable automatic message synchronization" = "Activer la synchronisation automatique des messages";
/* No comment provided by engineer. */
"Enable telephony provider?" = "Enable telephony provider?";
/* No comment provided by engineer. */
"Encryption" = "Chiffrement";
@ -272,6 +278,9 @@
/* No comment provided by engineer. */
"List of Messages" = "Liste de messages";
/* No comment provided by engineer. */
"Log Out" = "Log Out";
/* No comment provided by engineer. */
"Login and password do not match." = "Login and password do not match.";
@ -362,6 +371,9 @@
/* No comment provided by engineer. */
"Other devices fingerprints" = "Empreintes des autres clients";
/* No comment provided by engineer. */
"Permanently Delete Account" = "Permanently Delete Account";
/* No comment provided by engineer. */
"Phones" = "Téléphones";
@ -402,10 +414,7 @@
"Registration is not supported by this server" = "Les inscriptions sont désactivées sur ce serveur";
/* No comment provided by engineer. */
"Remove account from application?" = "Supprimer ce compte de lapplication?";
/* Account deletion action */
"Remove from application" = "Supprimer de lapplication";
"Remove Account Data" = "Remove Account Data";
/* No comment provided by engineer. */
"Rename" = "Renommer";
@ -488,9 +497,6 @@
/* No comment provided by engineer. */
"Settings" = "Préférences";
/* No comment provided by engineer. */
"Should account be removed from server as well?" = "Voulez-vous supprimer ce compte du serveur également?";
/* No comment provided by engineer. */
"Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?" = "Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?";
@ -542,6 +548,9 @@
/* No comment provided by engineer. */
"When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?" = "When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?";
/* No comment provided by engineer. */
"Would you like to use %@ as the default provider for outgoing SMS and calls from %@?" = "Would you like to use %1$@ as the default provider for outgoing SMS and calls from %2$@?";
/* No comment provided by engineer. */
"Yes" = "Oui";
@ -560,6 +569,9 @@
/* No comment provided by engineer. */
"You are not joined to the channel." = "Vous nêtes pas dans ce salon.";
/* No comment provided by engineer. */
"You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone." = "You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone.";
/* No comment provided by engineer. */
"You need to connect to your account before you can update your contact list. Do you wish to connect now?" = "You need to connect to your account before you can update your contact list. Do you wish to connect now?";

View file

@ -31,6 +31,9 @@
/* Class = "UILabel"; text = "Attachments"; ObjectID = "EpU-tc-DIx"; */
"EpU-tc-DIx.text" = "Attachments";
/* Class = "UILabel"; text = "Contact added you to their contact list. Add to contacts?"; ObjectID = "ezh-iK-Sk7"; */
"ezh-iK-Sk7.text" = "Contact added you to their contact list. Add to contacts?";
/* Class = "UILabel"; text = "Receive presence updates"; ObjectID = "ffo-7n-Tn2"; */
"ffo-7n-Tn2.text" = "Receive presence updates";
@ -99,3 +102,9 @@
/* Class = "UIButton"; normalTitle = "Enable"; ObjectID = "yJk-Px-1oo"; */
"yJk-Px-1oo.normalTitle" = "Enable";
/* Class = "UILabel"; text = "Add to contact list"; ObjectID = "zrb-Yr-XQO"; */
"zrb-Yr-XQO.text" = "Add to contact list";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "k9G-H8-3Hb"; */
"k9G-H8-3Hb.normalTitle" = "";

View file

@ -22,8 +22,14 @@
import UIKit
import TigaseSwift
import TigaseSwiftOMEMO
import AVKit
class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuAndToolbar {
var cellAudioPlayer: AVAudioPlayer?
var cellAudioTimer: Foundation.Timer?
var cellSliderTimer: Foundation.Timer?
var cellAudioPlayButton: UIButton?
static let MENTION_OCCUPANT = Notification.Name("groupchatMentionOccupant");
@ -41,6 +47,7 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
}
}
@IBOutlet weak var scrollToBottomButton: RoundShadowButton!
let log: Logger = Logger();
override func viewDidLoad() {
@ -65,6 +72,17 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
NotificationCenter.default.addObserver(self, selector: #selector(MucChatViewController.avatarChanged), name: AvatarManager.AVATAR_CHANGED, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(accountStateChanged), name: XmppService.ACCOUNT_STATE_CHANGED, object: nil)
scrollToBottomButton.cornerRadius = 20
scrollToBottomButton.isHidden = true
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let contentOffset = self.conversationLogController?.tableView.contentOffset else { return }
if contentOffset.y > 400 {
scrollToBottomButton.isHidden = false
} else {
scrollToBottomButton.isHidden = true
}
}
override func viewWillAppear(_ animated: Bool) {
@ -83,6 +101,10 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
// Dispose of any resources that can be recreated.
}
@IBAction func scrollToBottomTapped(_ sender: RoundShadowButton) {
self.conversationLogController?.tableView.setContentOffset(.zero, animated: true)
}
func setupGroupName() {
if let name = room?.name {
titleView?.name = name
@ -156,8 +178,8 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
} else {
cell.nicknameView?.text = item.state.direction == .incoming ? "· " + sender : ""
}
cell.set(message: item, maxMessageWidth: self.view.frame.width * 0.60)
cell.cellDelegate = self
cell.set(message: item, maxMessageWidth: self.view.frame.width * 0.60, indexPath: indexPath)
cell.bubbleImageView.isHidden = false
return cell;
}
@ -192,7 +214,8 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
cell.nicknameView?.text = item.state.direction == .incoming ? "· " + sender : ""
}
cell.set(attachment: item, maxImageWidth: self.view.frame.width * 0.60)
cell.cellDelegate = self
cell.set(attachment: item, maxImageWidth: self.view.frame.width * 0.60, indexPath: indexPath)
cell.setNeedsUpdateConstraints();
cell.updateConstraintsIfNeeded();
cell.bubbleImageView.isHidden = false
@ -201,7 +224,7 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
let id = "ChatTableViewLinkPreviewCell";
let cell: LinkPreviewChatTableViewCell = tableView.dequeueReusableCell(withIdentifier: id, for: indexPath) as! LinkPreviewChatTableViewCell;
cell.contentView.transform = dataSource.inverted ? CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0) : CGAffineTransform.identity;
cell.set(linkPreview: item);
cell.set(linkPreview: item, indexPath: indexPath)
return cell;
case let item as SystemMessage:
let cell: ChatTableViewSystemCell = tableView.dequeueReusableCell(withIdentifier: "ChatTableViewSystemCell", for: indexPath) as! ChatTableViewSystemCell;
@ -237,7 +260,7 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
} else {
cell.nicknameView?.text = sender;
}
cell.set(invitation: item);
cell.set(invitation: item, indexPath: indexPath)
return cell;
default:
return tableView.dequeueReusableCell(withIdentifier: "ChatTableViewMessageCell", for: indexPath);
@ -544,3 +567,29 @@ class MucTitleView: UIView {
}
}
}
extension MucChatViewController: CellDelegate {
func didPlayAudio(audioPlayer: AVAudioPlayer, audioTimer: Foundation.Timer, sliderTimer: Foundation.Timer, playButton: UIButton) {
self.cellAudioPlayer?.pause()
self.cellAudioTimer?.invalidate()
self.cellSliderTimer?.invalidate()
if let button = self.cellAudioPlayButton { button.isSelected = false }
self.cellSliderTimer = sliderTimer
self.cellAudioTimer = audioTimer
self.cellAudioPlayer = audioPlayer
self.cellAudioPlayButton = playButton
}
func didStopAudio() {
self.cellSliderTimer = nil
self.cellAudioTimer = nil
self.cellAudioPlayer = nil
self.cellAudioPlayButton = nil
}
func didTapResend(indexPath: IndexPath) {
}
}

View file

@ -51,11 +51,12 @@ class RosterItemEditViewController: UITableViewController, UIPickerViewDataSourc
if let account = account, let jid = jid {
self.jidTextField.isEnabled = false;
self.accountTextField.isEnabled = false;
self.nameTextField.text = PEPDisplayNameModule.getDisplayName(account: account, for: BareJID(jid))
if let sessionObject = xmppService.getClient(forJid: account)?.sessionObject {
let rosterStore: RosterStore = RosterModule.getRosterStore(sessionObject)
if let rosterItem = rosterStore.get(for: jid) {
self.nameTextField.text = PEPDisplayNameModule.getDisplayName(account: account, for: BareJID(jid))
self.sendPresenceUpdatesSwitch.isOn = rosterItem.subscription.isFrom;
self.receivePresenceUpdatesSwitch.isOn = rosterItem.subscription.isTo;
}
@ -129,8 +130,14 @@ class RosterItemEditViewController: UITableViewController, UIPickerViewDataSourc
}
let onSuccess = {(stanza:Stanza)->Void in
self.updateSubscriptions(client: client!);
self.sendSubscriptionRequest(client: client)
DispatchQueue.main.async {
if let account = self.account {
AppDelegate.subscriptionsRequest.removeValue(forKey: account.stringValue)
NotificationCenter.default.post(name: Notification.Name("SUBSCRIPTION_REQUEST"), object: nil)
}
self.dismissView();
}
};
@ -159,6 +166,20 @@ class RosterItemEditViewController: UITableViewController, UIPickerViewDataSourc
return nil;
}
fileprivate func sendSubscriptionRequest(client: XMPPClient?) {
if let client = client, let presenceModule: PresenceModule = client.modulesManager.getModule(PresenceModule.ID), let contactJid = self.jid {
DispatchQueue.main.async {
if self.receivePresenceUpdatesSwitch.isOn {
presenceModule.subscribe(to: contactJid, preauth: self.preauth);
}
if self.sendPresenceUpdatesSwitch.isOn {
presenceModule.subscribed(by: contactJid);
}
}
}
}
fileprivate func updateSubscriptions(client: XMPPClient) {
let rosterModule:RosterModule = client.modulesManager.getModule(RosterModule.ID)!;
if let presenceModule: PresenceModule = client.modulesManager.getModule(PresenceModule.ID) {

View file

@ -40,6 +40,8 @@ protocol RosterProvider {
func queryItems(contains: String?);
func sectionHeader(at: Int) -> String?;
func contactExists(account: BareJID, contact: BareJID) -> Bool
}
public enum RosterSortingOrder: String {
@ -183,6 +185,15 @@ public class RosterProviderAbstract<Item: RosterProviderItem>: RosterProviderAbs
return nil;
}
func contactExists(account: BareJID, contact: BareJID) -> Bool {
for item in self.allItems {
if item.account == account, contact == item.jid.bareJid {
return true
}
}
return false
}
}
public protocol RosterProviderItem {

View file

@ -64,9 +64,6 @@ class RosterViewController: AbstractRosterViewController, UIGestureRecognizerDel
}
private func setColors() {
// navigationController?.navigationBar.barStyle = .black;
// navigationController?.navigationBar.isTranslucent = true;
//searchController.searchBar.barStyle = .black;
self.view.backgroundColor = UIColor(named: "chatslistBackground")!;
self.navigationController?.view.backgroundColor = UIColor(named: "chatslistBackground")!;
if #available(iOS 13.0, *) {
@ -75,12 +72,7 @@ class RosterViewController: AbstractRosterViewController, UIGestureRecognizerDel
self.tableView.backgroundColor = UIColor.white;
}
searchController.searchBar.tintColor = UIColor(named: "tintColor")!;
navigationController?.navigationBar.barTintColor = UIColor(named: "chatslistBackground");//?.withAlphaComponent(0.2);
navigationController?.navigationBar.tintColor = UIColor(named: "tintColor")!;
if #available(iOS 13.0, *) {
// (navigationItem.titleView as? UISegmentedControl)?.selectedSegmentTintColor =
} else {
}
navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {

View file

@ -98,7 +98,7 @@ class PEPDisplayNameModule: AbstractPEPModule {
let rosterModule: RosterModule? = client.modulesManager.getModule(RosterModule.ID);
let rosterItem = rosterModule?.rosterStore.get(for: JID(jid))
var name = rosterItem?.name ?? DBRosterStore.instance.getNickname(jid: jid.stringValue)
name = name == nil ? jid.localPart : name
name = name == nil ? (jid.localPart == nil ? jid.domain : jid.localPart) : name
return name ?? ""
}

View file

@ -0,0 +1,82 @@
//
// TelephonyManager.swift
// Snikket
//
// Created by Muhammad Khalid on 09/10/2021.
// Copyright © 2021 Snikket. All rights reserved.
//
import TigaseSwift
import Foundation
import UIKit
class TelephonyManager: XmppServiceEventHandler {
var events: [Event] = [RosterModule.ItemUpdatedEvent.TYPE]
func handle(event: Event) {
switch event {
case let e as RosterModule.ItemUpdatedEvent:
isTelephonyProvider(event: e)
default:
break
}
}
private func isTelephonyProvider(event: RosterModule.ItemUpdatedEvent) {
guard let account = event.sessionObject.userBareJid, let rosterItem = event.rosterItem else { return }
// No Telephony Currently Configured
guard AccountSettings.telephonyProvider(account).getString() == nil else { return }
// No Local Part
guard rosterItem.jid.localPart == nil else { return }
// Subscribed to User Presence
guard rosterItem.subscription.isFrom else { return }
// Advertises Identity of Category "gateway" and Type "pstn"
verifyService(account: account, jid: rosterItem.jid) { [weak self] success in
if success {
self?.enableProviderAlert(account: account, jid: rosterItem.jid)
}
}
}
func verifyService(account: BareJID, jid: JID, completion: @escaping (Bool)->Void) {
let client = XmppService.instance.getClient(for: account)
if let module: DiscoveryModule = client?.modulesManager.getModule(DiscoveryModule.ID) {
module.getInfo(for: jid, node: nil, completionHandler: { result in
switch result {
case .success(_, let identities, _):
for identity in identities {
if identity.category.lowercased() == "gateway", identity.type.lowercased() == "pstn" {
completion(true)
}
}
case .failure(let errorCondition, _):
print(errorCondition)
}
})
}
}
func enableProviderAlert(account: BareJID, jid: JID) {
let alert = UIAlertController(title: NSLocalizedString("Enable telephony provider?", comment: ""), message: String.localizedStringWithFormat(NSLocalizedString("Would you like to use %@ as the default provider for outgoing SMS and calls from %@?", comment: ""), jid.stringValue, account.stringValue), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: String.localizedStringWithFormat(NSLocalizedString("Enable %@", comment: ""), jid.stringValue), style: .default, handler: {(action) in
AccountSettings.telephonyProvider(account).set(string: jid.stringValue)
}))
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .destructive, handler: nil))
var topController = UIApplication.shared.keyWindow?.rootViewController
while (topController?.presentedViewController != nil) {
topController = topController?.presentedViewController
}
DispatchQueue.main.async {
topController?.present(alert, animated: true, completion: nil)
}
}
}

View file

@ -49,9 +49,9 @@ open class XmppService: Logger, EventHandler {
fileprivate var fetchStart = NSDate();
#if targetEnvironment(simulator)
fileprivate let eventHandlers: [XmppServiceEventHandler] = [NewFeaturesDetector(), MessageEventHandler(), MucEventHandler.instance, PresenceRosterEventHandler(), AvatarEventHandler(), NickChangeEventHandler(), DiscoEventHandler(), PushEventHandler.instance, BlockedEventHandler.instance, MixEventHandler.instance];
fileprivate let eventHandlers: [XmppServiceEventHandler] = [NewFeaturesDetector(), MessageEventHandler(), MucEventHandler.instance, PresenceRosterEventHandler(), TelephonyManager(), AvatarEventHandler(), NickChangeEventHandler(), DiscoEventHandler(), PushEventHandler.instance, BlockedEventHandler.instance, MixEventHandler.instance];
#else
fileprivate let eventHandlers: [XmppServiceEventHandler] = [NewFeaturesDetector(), MessageEventHandler(), MucEventHandler.instance, PresenceRosterEventHandler(), AvatarEventHandler(), NickChangeEventHandler(), DiscoEventHandler(), PushEventHandler.instance, JingleManager.instance, BlockedEventHandler.instance, MixEventHandler.instance];
fileprivate let eventHandlers: [XmppServiceEventHandler] = [NewFeaturesDetector(), MessageEventHandler(), MucEventHandler.instance, PresenceRosterEventHandler(), TelephonyManager(), AvatarEventHandler(), NickChangeEventHandler(), DiscoEventHandler(), PushEventHandler.instance, JingleManager.instance, BlockedEventHandler.instance, MixEventHandler.instance];
#endif
public let dbCapsCache: DBCapabilitiesCache;

View file

@ -35,9 +35,7 @@ class AccountSettingsViewController: UITableViewController {
@IBOutlet var nicknameLabel: UILabel!;
@IBOutlet var pushNotificationsForAwaySwitch: UISwitch!
@IBOutlet var archivingEnabledSwitch: UISwitch!;
@IBOutlet var messageSyncAutomaticSwitch: UISwitch!;
@IBOutlet var messageSyncPeriodLabel: UILabel!;
@IBOutlet weak var telephonyProviderLabel: UILabel!
@IBOutlet var omemoFingerprint: UILabel!;
@ -57,7 +55,6 @@ class AccountSettingsViewController: UITableViewController {
let config = AccountManager.getAccount(for: account);
enabledSwitch.isOn = config?.active ?? false;
nicknameLabel.text = config?.nickname;
messageSyncAutomaticSwitch.isEnabled = false;
pushNotificationsForAwaySwitch.isOn = AccountSettings.PushNotificationsForAway(account).getBool();
updateView();
@ -109,21 +106,6 @@ class AccountSettingsViewController: UITableViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false);
if indexPath.section == 3 && indexPath.row == 2 {
let controller = TablePickerViewController(style: .grouped);
let hoursArr = [0.0, 12.0, 24.0, 3*24.0, 7*24.0, 14 * 24.0, 356 * 24.0];
controller.selected = hoursArr.firstIndex(of: AccountSettings.messageSyncPeriod(account).getDouble()) ?? 0;
controller.items = hoursArr.map({ (it)->TablePickerViewItemsProtocol in
return SyncTimeItem(hours: it);
});
//controller.selected = 1;
controller.onSelectionChange = { (_item) -> Void in
let item = _item as! SyncTimeItem;
print("select sync of last", item.hours, "hours");
AccountSettings.messageSyncPeriod(self.account).set(double: item.hours);
};
self.navigationController?.pushViewController(controller, animated: true);
}
if indexPath.section == 1 && indexPath.row == 3 {
let controller = UIAlertController(title: NSLocalizedString("Nickname",comment: ""), message: NSLocalizedString("Enter default nickname to use in chats",comment: ""), preferredStyle: .alert);
controller.addTextField(configurationHandler: { textField in
@ -139,9 +121,13 @@ class AccountSettingsViewController: UITableViewController {
}))
self.navigationController?.present(controller, animated: true, completion: nil);
}
if indexPath.section == 5 && indexPath.row == 0 {
self.deleteAccount();
self.logOutSheet(indexPath: indexPath)
} else if indexPath.section == 5 && indexPath.row == 1 {
self.deleteAccountSheet(indexPath: indexPath)
}
if indexPath.section == 2 && indexPath.row == 0 {
let client = XmppService.instance.getClient(for: account)
let pushModule: SiskinPushNotificationsModule? = client?.modulesManager.getModule(SiskinPushNotificationsModule.ID)
@ -169,28 +155,7 @@ class AccountSettingsViewController: UITableViewController {
let client = XmppService.instance.getClient(for: account);
let pushModule: SiskinPushNotificationsModule? = client?.modulesManager.getModule(SiskinPushNotificationsModule.ID);
pushNotificationsForAwaySwitch.isEnabled = (pushModule?.isSupported(extension: TigasePushNotificationsModule.PushForAway.self) ?? false);
messageSyncAutomaticSwitch.isOn = AccountSettings.messageSyncAuto(account).getBool();
archivingEnabledSwitch.isEnabled = false;
if (client?.state ?? SocketConnector.State.disconnected == SocketConnector.State.connected), let mamModule: MessageArchiveManagementModule = client?.modulesManager.getModule(MessageArchiveManagementModule.ID) {
mamModule.retrieveSettings(completionHandler: { result in
switch result {
case .success(let defValue, _, _):
DispatchQueue.main.async {
self.archivingEnabledSwitch.isEnabled = true;
self.archivingEnabledSwitch.isOn = defValue == MessageArchiveManagementModule.DefaultValue.always;
self.messageSyncAutomaticSwitch.isEnabled = self.archivingEnabledSwitch.isOn;
}
case .failure(_, _):
DispatchQueue.main.async {
self.archivingEnabledSwitch.isOn = false;
self.archivingEnabledSwitch.isEnabled = false;
self.messageSyncAutomaticSwitch.isEnabled = self.archivingEnabledSwitch.isOn;
}
}
})
}
messageSyncPeriodLabel.text = SyncTimeItem.descriptionFromHours(hours: AccountSettings.messageSyncPeriod(account).getDouble());
telephonyProviderLabel.text = AccountSettings.telephonyProvider(account).getString() ?? "None"
}
@objc func avatarChanged() {
@ -226,27 +191,34 @@ class AccountSettingsViewController: UITableViewController {
case "ManageOMEMOFingerprints":
let destination = segue.destination as! OMEMOFingerprintsController;
destination.account = account;
case "ShowTelephonyProviders":
let destination = segue.destination as! TelephonyProviderViewController
destination.account = account
default:
break;
}
}
@IBAction func enabledSwitchChangedValue(_ sender: AnyObject) {
if let config = AccountManager.getAccount(for: account!) {
config.active = enabledSwitch.isOn;
AccountSettings.LastError(account).set(string: nil);
AccountManager.save(account: config);
}
enabledSwitch.isEnabled = false
if enabledSwitch.isOn {
if let config = AccountManager.getAccount(for: account!) {
config.active = true
AccountSettings.LastError(account).set(string: nil);
AccountManager.save(account: config);
self.enablePushNotifications()
}
} else { disableAccount() }
}
func reRegisterPushNotifications() {
let alert = UIAlertController(title: NSLocalizedString("Push Notifications",comment: ""), message: NSLocalizedString("Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?",comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("Yes",comment: ""), style: .default, handler: self.enablePushNotifications))
alert.addAction(UIAlertAction(title: NSLocalizedString("Yes",comment: ""), style: .default, handler: self.enablePushNotifications(action:)))
alert.addAction(UIAlertAction(title: NSLocalizedString("No",comment: ""), style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
fileprivate func enablePushNotifications(action: UIAlertAction) {
fileprivate func enablePushNotifications(action: UIAlertAction? = nil) {
let accountJid = self.account!;
let onError = { (_ errorCondition: ErrorCondition?) in
DispatchQueue.main.async {
@ -255,14 +227,19 @@ class AccountSettingsViewController: UITableViewController {
userInfo["errorCondition"] = errorCondition;
}
NotificationCenter.default.post(name: Notification.Name("pushNotificationsRegistrationFailed"), object: self, userInfo: userInfo);
self.enabledSwitch.isEnabled = true
}
}
// let's check if push notifications component is accessible
if let pushModule: SiskinPushNotificationsModule = XmppService.instance.getClient(forJid: accountJid)?.modulesManager.getModule(SiskinPushNotificationsModule.ID), let deviceId = PushEventHandler.instance.deviceId {
pushModule.registerDeviceAndEnable(deviceId: deviceId, pushkitDeviceId: PushEventHandler.instance.pushkitDeviceId, completionHandler: { result in
switch result {
case .success(_):
break;
DispatchQueue.main.async {
self.enabledSwitch.isEnabled = true
}
break
case .failure(let errorCondition):
DispatchQueue.main.async {
self.pushNotificationsForAwaySwitch.isOn = false;
@ -324,42 +301,6 @@ class AccountSettingsViewController: UITableViewController {
}
}
@IBAction func archivingSwitchChangedValue(_ sender: Any) {
let client = XmppService.instance.getClient(forJid: account);
if let mamModule: MessageArchiveManagementModule = client?.modulesManager.getModule(MessageArchiveManagementModule.ID) {
let defValue = archivingEnabledSwitch.isOn ? MessageArchiveManagementModule.DefaultValue.always : MessageArchiveManagementModule.DefaultValue.never;
mamModule.retrieveSettings(completionHandler: { result in
switch result {
case .success(let oldDefValue, let always, let never):
mamModule.updateSettings(defaultValue: defValue, always: always, never: never, completionHandler: { result in
switch result {
case .success(let newDefValue, _, _):
DispatchQueue.main.async {
self.archivingEnabledSwitch.isOn = newDefValue == MessageArchiveManagementModule.DefaultValue.always;
self.messageSyncAutomaticSwitch.isEnabled = self.archivingEnabledSwitch.isOn;
}
case .failure(_, _):
DispatchQueue.main.async {
self.archivingEnabledSwitch.isOn = oldDefValue == MessageArchiveManagementModule.DefaultValue.always;
self.messageSyncAutomaticSwitch.isEnabled = self.archivingEnabledSwitch.isOn;
}
}
});
case .failure(_, _):
DispatchQueue.main.async {
self.archivingEnabledSwitch.isOn = !self.archivingEnabledSwitch.isOn;
self.messageSyncAutomaticSwitch.isEnabled = self.archivingEnabledSwitch.isOn;
}
}
});
}
}
@IBAction func messageSyncAutomaticSwitchChangedValue(_ sender: Any) {
AccountSettings.messageSyncAuto(account).set(bool: self.messageSyncAutomaticSwitch.isOn);
}
func update(vcard: VCard?) {
avatarView.image = AvatarManager.instance.avatar(for: account, on: account) ?? AvatarManager.instance.defaultAvatar;
@ -407,108 +348,168 @@ class AccountSettingsViewController: UITableViewController {
}
}
func deleteAccount() {
guard let account = self.account, let config = AccountManager.getAccount(for: account) else {
return;
}
let removeAccount: (BareJID, Bool)->Void = { account, fromServer in
if fromServer {
if let client = XmppService.instance.getClient(forJid: BareJID(account)), client.state == .connected {
let regModule = client.modulesManager.register(InBandRegistrationModule());
regModule.unregister({ (stanza) in
DispatchQueue.main.async() {
_ = AccountManager.deleteAccount(for: account);
self.navigationController?.popViewController(animated: true);
}
})
} else {
DispatchQueue.main.async {
let alert = UIAlertController(title: NSLocalizedString("Account Removal Failed",comment: "Alert title"), message: NSLocalizedString("Could not connect to the service. Check your network connectivity or try again later.",comment: ""), preferredStyle: .alert);
alert.addAction(UIAlertAction(title: NSLocalizedString("OK",comment: ""), style: .default, handler: { _ in
self.tableView.reloadData();
}));
self.present(alert, animated: true, completion: nil);
}
func disableAccount() {
disablePushNotifications() { success in
if success {
if let config = AccountManager.getAccount(for: self.account) {
config.active = false
AccountSettings.LastError(self.account).set(string: nil);
AccountManager.save(account: config);
}
if let client = XmppService.instance.getClient(forJid: self.account) {
client.disconnect()
}
DispatchQueue.main.async {
self.navigationController?.popViewController(animated: true)
}
} else {
DispatchQueue.main.async {
_ = AccountManager.deleteAccount(for: account);
self.navigationController?.popViewController(animated: true);
self.enabledSwitch.isEnabled = true
self.enabledSwitch.isOn = true
}
}
};
}
self.askAboutAccountRemoval(account: account, atRow: IndexPath(row: 0, section: 5), completionHandler: { result in
switch result {
case .success(let removeFromServer):
if let pushSettings = config.pushSettings {
if let client = XmppService.instance.getClient(forJid: BareJID(account)), client.state == .connected, let pushModule: SiskinPushNotificationsModule = client.modulesManager.getModule(SiskinPushNotificationsModule.ID) {
pushModule.unregisterDeviceAndDisable(completionHandler: { result in
switch result {
case .success(_):
// now remove the account...
removeAccount(account, removeFromServer)
break;
case .failure(_):
PushEventHandler.unregisterDevice(from: pushSettings.jid.bareJid, account: account, deviceId: pushSettings.deviceId, completionHandler: { result in
config.pushSettings = nil;
AccountManager.save(account: config);
DispatchQueue.main.async {
switch result {
case .success(_):
removeAccount(account, removeFromServer);
case .failure(let _):
let alert = UIAlertController(title: NSLocalizedString("Account Removal Failed",comment: "Alert title"), message: String.localizedStringWithFormat(NSLocalizedString("Push notifications are enabled for %@. They need to be disabled before account can be removed and it is not possible to at this time. Please try again later.", comment: ""), account.stringValue), preferredStyle: .alert);
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil));
self.present(alert, animated: true, completion: nil);
}
}
})
}
});
} else {
PushEventHandler.unregisterDevice(from: pushSettings.jid.bareJid, account: account, deviceId: pushSettings.deviceId, completionHandler: { result in
DispatchQueue.main.async {
switch result {
case .success(_):
config.pushSettings = nil;
AccountManager.save(account: config);
removeAccount(account, removeFromServer);
case .failure(let _):
let alert = UIAlertController(title: NSLocalizedString("Account Removal Failed", comment: "Alert title"), message: String.localizedStringWithFormat(NSLocalizedString("Push notifications are enabled for %@. They need to be disabled before account can be removed and it is not possible to at this time. Please try again later.", comment: ""), account.stringValue), preferredStyle: .alert);
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil));
self.present(alert, animated: true, completion: nil);
}
}
})
}
} else {
removeAccount(account, removeFromServer);
}
func logOutSheet(indexPath: IndexPath) {
let sheet = UIAlertController(title: NSLocalizedString("Log Out", comment: ""), message: NSLocalizedString("You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone.", comment: ""), preferredStyle: .actionSheet)
let removeDataAction = UIAlertAction(title: NSLocalizedString("Remove Account Data", comment: ""), style: .destructive) { _ in
self.deleteAccountData(fromServer: false) { success in
print("Data Deleted")
}
}
let logOutAction = UIAlertAction(title: NSLocalizedString("Log Out", comment: ""), style: .default) { _ in
self.disableAccount()
}
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil)
sheet.addAction(removeDataAction)
sheet.addAction(logOutAction)
sheet.addAction(cancelAction)
sheet.popoverPresentationController?.sourceView = self.tableView
sheet.popoverPresentationController?.sourceRect = self.tableView.rectForRow(at: indexPath)
self.present(sheet, animated: true, completion: nil)
}
func deleteAccountSheet(indexPath: IndexPath) {
let sheet = UIAlertController(title: NSLocalizedString("Permanently Delete Account", comment: ""), message: String.localizedStringWithFormat(NSLocalizedString("Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@.", comment: ""), account.domain), preferredStyle: .actionSheet)
let deleteAction = UIAlertAction(title: NSLocalizedString("Delete My Account", comment: ""), style: .destructive) { _ in
self.deleteAccountData(fromServer: true) { success in
print("account deleted")
}
}
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil)
sheet.addAction(deleteAction)
sheet.addAction(cancelAction)
sheet.popoverPresentationController?.sourceView = self.tableView
sheet.popoverPresentationController?.sourceRect = self.tableView.rectForRow(at: indexPath)
self.present(sheet, animated: true, completion: nil)
}
func disablePushNotifications(completion: @escaping (Bool) -> Void) {
guard let account = self.account, let config = AccountManager.getAccount(for: account), let pushSettings = config.pushSettings else {
completion(false)
return
}
if let client = XmppService.instance.getClient(forJid: BareJID(account)), client.state == .connected, let pushModule: SiskinPushNotificationsModule = client.modulesManager.getModule(SiskinPushNotificationsModule.ID) {
pushModule.unregisterDeviceAndDisable(completionHandler: { result in
switch result {
case .success(_):
completion(true)
break
case .failure(let error):
print(error)
self.unRegisterDevice(accountConfig: config, account: account, pushSettings: pushSettings, removeAccount: false, fromServer: false, completion: completion)
}
})
} else {
self.unRegisterDevice(accountConfig: config, account: account, pushSettings: pushSettings, removeAccount: false, fromServer: false, completion: completion)
}
}
func unRegisterDevice(accountConfig: AccountManager.Account, account: BareJID, pushSettings: SiskinPushNotificationsModule.PushSettings, removeAccount: Bool, fromServer: Bool, completion: @escaping (Bool) -> Void) {
PushEventHandler.unregisterDevice(from: pushSettings.jid.bareJid, account: account, deviceId: pushSettings.deviceId, completionHandler: { result in
accountConfig.pushSettings = nil
AccountManager.save(account: accountConfig)
DispatchQueue.main.async {
switch result {
case .success(_):
if removeAccount { self.removeAccount(account: account, fromServer: fromServer) }
completion(true)
case .failure( _):
let alert = UIAlertController(title: NSLocalizedString("Account Removal Failed",comment: "Alert title"), message: String.localizedStringWithFormat(NSLocalizedString("Push notifications are enabled for %@. They need to be disabled before account can be removed and it is not possible to at this time. Please try again later.", comment: ""), account.stringValue), preferredStyle: .alert);
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil));
self.present(alert, animated: true, completion: nil);
completion(false)
}
case .failure(_):
break;
}
})
}
func deleteAccountData(fromServer: Bool, completion: @escaping (Bool) -> Void) {
func askAboutAccountRemoval(account: BareJID, atRow indexPath: IndexPath, completionHandler: @escaping (Result<Bool, Error>)->Void) {
let client = XmppService.instance.getClient(forJid: BareJID(account))
let alert = UIAlertController(title: NSLocalizedString("Account Removal", comment: "Account deletion alert title"), message: ( client != nil && client?.state == .connected ) ? NSLocalizedString("Should account be removed from server as well?", comment: "") : NSLocalizedString("Remove account from application?", comment: ""), preferredStyle: .actionSheet);
if client?.state == .connected {
alert.addAction(UIAlertAction(title: NSLocalizedString("Delete account permanently", comment: "Account deletion action - destructive"), style: .destructive, handler: { (action) in
completionHandler(.success(true));
}));
guard let account = self.account, let config = AccountManager.getAccount(for: account) else { return }
if let pushSettings = config.pushSettings {
if let client = XmppService.instance.getClient(forJid: BareJID(account)), client.state == .connected, let pushModule: SiskinPushNotificationsModule = client.modulesManager.getModule(SiskinPushNotificationsModule.ID) {
pushModule.unregisterDeviceAndDisable(completionHandler: { result in
switch result {
case .success(_):
self.removeAccount(account: account, fromServer: fromServer)
break
case .failure(_):
self.unRegisterDevice(accountConfig: config, account: account, pushSettings: pushSettings, removeAccount: true, fromServer: fromServer, completion: completion)
}
})
}
else {
self.unRegisterDevice(accountConfig: config, account: account, pushSettings: pushSettings, removeAccount: true, fromServer: fromServer, completion: completion)
}
}
else {
self.removeAccount(account: account, fromServer: fromServer)
}
}
func removeAccount(account: BareJID, fromServer: Bool) {
if fromServer {
if let client = XmppService.instance.getClient(forJid: BareJID(account)), client.state == .connected {
let regModule = client.modulesManager.register(InBandRegistrationModule())
regModule.unregister({ (stanza) in
DispatchQueue.main.async() {
_ = AccountManager.deleteAccount(for: account)
self.navigationController?.popViewController(animated: true)
}
})
} else {
DispatchQueue.main.async {
let alert = UIAlertController(title: NSLocalizedString("Account Removal Failed",comment: "Alert title"), message: NSLocalizedString("Could not connect to the service. Check your network connectivity or try again later.",comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK",comment: ""), style: .default, handler: { _ in
self.tableView.reloadData()
}))
self.present(alert, animated: true, completion: nil)
}
}
} else {
DispatchQueue.main.async {
_ = AccountManager.deleteAccount(for: account)
self.navigationController?.popViewController(animated: true)
}
}
alert.addAction(UIAlertAction(title: NSLocalizedString("Remove from application", comment: "Account deletion action"), style: .default, handler: { (action) in
completionHandler(.success(false));
}));
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .default, handler: nil));
alert.popoverPresentationController?.sourceView = self.tableView;
alert.popoverPresentationController?.sourceRect = self.tableView.rectForRow(at: indexPath);
self.present(alert, animated: true, completion: nil);
}
class SyncTimeItem: TablePickerViewItemsProtocol {

View file

@ -0,0 +1,99 @@
//
// TelephonyProviderViewController.swift
// Snikket
//
// Created by Muhammad Khalid on 08/10/2021.
// Copyright © 2021 Snikket. All rights reserved.
//
import UIKit
import TigaseSwift
class TelephonyProviderViewController: UITableViewController {
var account: BareJID!
var selected = "None"
var providers: [JID] = []
override func viewDidLoad() {
super.viewDidLoad()
loadProviders()
selected = AccountSettings.telephonyProvider(account).getString() ?? "None"
}
func loadProviders() {
let client = XmppService.instance.getClient(for: account)
let rosterModule: RosterModule? = client?.modulesManager.getModule(RosterModule.ID)
if let module = rosterModule {
let contacts = module.rosterStore.getJids()
for contact in contacts {
if contact.localPart == nil {
verifyService(client: client, jid: contact)
}
}
}
}
func verifyService(client: XMPPClient?, jid: JID) {
if let module: DiscoveryModule = client?.modulesManager.getModule(DiscoveryModule.ID) {
module.getInfo(for: jid, node: nil, completionHandler: { result in
switch result {
case .success(_, let identities, _):
for identity in identities {
if identity.category.lowercased() == "gateway", identity.type.lowercased() == "pstn" {
self.providers.append(jid)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
case .failure(let errorCondition, _):
print(errorCondition)
}
})
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.providers.count + 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "TelephonyProviderCell", for: indexPath)
cell.textLabel?.text = "None"
cell.accessoryType = "None" == selected ? .checkmark : .none
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "TelephonyProviderCell", for: indexPath)
let feature = providers[indexPath.row - 1]
cell.textLabel?.text = feature.stringValue
cell.accessoryType = feature.stringValue == selected ? .checkmark : .none;
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
AccountSettings.telephonyProvider(self.account).set(string: nil)
selected = "None"
} else {
let provider = providers[indexPath.row - 1]
AccountSettings.telephonyProvider(self.account).set(string: provider.stringValue)
selected = provider.stringValue
}
tableView.reloadData()
}
}

View file

@ -16,9 +16,6 @@
/* Class = "UITextField"; placeholder = "Required"; ObjectID = "7KN-S3-8XR"; */
"7KN-S3-8XR.placeholder" = "Måste anges";
/* Class = "UITableViewSection"; headerTitle = "Message Archiving"; ObjectID = "9PW-Rb-rop"; */
"9PW-Rb-rop.headerTitle" = "Meddelandearkivering";
/* Class = "UITableViewController"; title = "OMEMO fingerprints"; ObjectID = "A24-eF-tzh"; */
"A24-eF-tzh.title" = "OMEMO-fingeravtryck";
@ -40,23 +37,20 @@
/* Class = "UITableViewSection"; headerTitle = "Encryption"; ObjectID = "Et5-H6-t7C"; */
"Et5-H6-t7C.headerTitle" = "Kryptering";
/* Class = "UITableViewController"; title = "Telephony Provider"; ObjectID = "Ey3-E6-jDR"; */
"Ey3-E6-jDR.title" = "Telephony Provider";
/* Class = "UILabel"; text = "Subtitle"; ObjectID = "FeX-5F-H2m"; */
"FeX-5F-H2m.text" = "Undertitel";
/* Class = "UILabel"; text = "add address"; ObjectID = "fRX-fN-yOj"; */
"fRX-fN-yOj.text" = "lägg till adress";
/* Class = "UILabel"; text = "Synchronization"; ObjectID = "GNS-EF-AqY"; */
"GNS-EF-AqY.text" = "Synkronisering";
/* Class = "UILabel"; text = "Enabled"; ObjectID = "GTa-Ee-HQT"; */
"GTa-Ee-HQT.text" = "Aktiverad";
/* Class = "UILabel"; text = "OMEMO fingerprint"; ObjectID = "gUr-GY-P3A"; */
"gUr-GY-P3A.text" = "OMEMO-fingeravtryck";
/* Class = "UILabel"; text = "Delete"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Radera";
/* Class = "UILabel"; text = "Log out"; ObjectID = "GWw-NK-6oU"; */
"GWw-NK-6oU.text" = "Log out";
/* Class = "UINavigationItem"; title = "Account settings"; ObjectID = "gzC-dB-kIF"; */
"gzC-dB-kIF.title" = "Kontoinställningar";
@ -67,8 +61,8 @@
/* Class = "UIBarButtonItem"; title = "Done"; ObjectID = "ijD-Kb-uyj"; */
"ijD-Kb-uyj.title" = "Klar";
/* Class = "UILabel"; text = "Automatic synchronization"; ObjectID = "J9o-cf-vMy"; */
"J9o-cf-vMy.text" = "Automatisk synkronisering";
/* Class = "UILabel"; text = "Title"; ObjectID = "IUH-ca-fBy"; */
"IUH-ca-fBy.text" = "Title";
/* Class = "UILabel"; text = "add phone"; ObjectID = "jAE-vq-Vfj"; */
"jAE-vq-Vfj.text" = "lägg till telefon";
@ -79,6 +73,9 @@
/* Class = "UITextField"; placeholder = "Street"; ObjectID = "KgL-GY-IFp"; */
"KgL-GY-IFp.placeholder" = "Gata";
/* Class = "UILabel"; text = "Delete account"; ObjectID = "lGT-wY-qtX"; */
"lGT-wY-qtX.text" = "Delete account";
/* Class = "UITableViewSection"; headerTitle = " Password"; ObjectID = "LO4-Ys-cek"; */
"LO4-Ys-cek.headerTitle" = " Lösenord";
@ -106,6 +103,9 @@
/* Class = "UITableViewSection"; headerTitle = " XMPP ID"; ObjectID = "SKG-bP-NPK"; */
"SKG-bP-NPK.headerTitle" = " XMPP ID";
/* Class = "UILabel"; text = "None"; ObjectID = "StS-4J-0Uu"; */
"StS-4J-0Uu.text" = "None";
/* Class = "UITextField"; placeholder = "Phone number"; ObjectID = "T1r-DU-iqs"; */
"T1r-DU-iqs.placeholder" = "Telefonnummer";
@ -127,15 +127,18 @@
/* Class = "UILabel"; text = "Enabled"; ObjectID = "wbI-5v-vug"; */
"wbI-5v-vug.text" = "Aktiverad";
/* Class = "UITableViewSection"; headerTitle = "Telephony"; ObjectID = "WFs-jm-Wmk"; */
"WFs-jm-Wmk.headerTitle" = "Telephony";
/* Class = "UITableViewController"; title = "Server Features"; ObjectID = "wW7-3f-Kck"; */
"wW7-3f-Kck.title" = "Server Features";
/* Class = "UILabel"; text = "Last 24 hours"; ObjectID = "XLZ-oR-88H"; */
"XLZ-oR-88H.text" = "Senaste 24 timmarna";
/* Class = "UITextField"; placeholder = "Email address"; ObjectID = "ynp-RE-XlY"; */
"ynp-RE-XlY.placeholder" = "Epostadress";
/* Class = "UILabel"; text = "Provider"; ObjectID = "YYb-TE-za0"; */
"YYb-TE-za0.text" = "Provider";
/* Class = "UILabel"; text = "Re-register push notifications"; ObjectID = "ZHc-Ml-5eX"; */
"ZHc-Ml-5eX.text" = "Re-register push notifications";

View file

@ -66,3 +66,6 @@
/* Class = "UILabel"; text = "None"; ObjectID = "YT2-1I-RlM"; */
"YT2-1I-RlM.text" = "None";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "Qmg-sW-dkA"; */
"Qmg-sW-dkA.normalTitle" = "";

View file

@ -4,5 +4,8 @@
/* Class = "UITextView"; text = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n"; ObjectID = "OMw-f8-Hbs"; */
"OMw-f8-Hbs.text" = "============================\nSnikket \n\nbased on SiskinIM by Tigase, Inc.\nCopyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see https://www.gnu.org/licenses/.\n\n============================\nCopyright (c) 2011, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of Google nor the names of its contributors may\n be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n============================\nAdditional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the WebRTC code package.\n\nGoogle hereby grants to you a perpetual, worldwide, non-exclusive,\nno-charge, irrevocable (except as stated in this section) patent\nlicense to make, have made, use, offer to sell, sell, import,\ntransfer, and otherwise run, modify and propagate the contents of this\nimplementation of the WebRTC code package, where such license applies\nonly to those patent claims, both currently owned by Google and\nacquired in the future, licensable by Google that are necessarily\ninfringed by this implementation of the WebRTC code package. This\ngrant does not include claims that would be infringed only as a\nconsequence of further modification of this implementation. If you or\nyour agent or exclusive licensee institute or order or agree to the\ninstitution of patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that this\nimplementation of the WebRTC code package or any code incorporated\nwithin this implementation of the WebRTC code package constitutes\ndirect or contributory patent infringement, or inducement of patent\ninfringement, then any patent rights granted to you under this License\nfor this implementation of the WebRTC code package shall terminate as\nof the date such litigation is filed.\n";
/* Class = "UILabel"; text = "Build:"; ObjectID = "R7V-kJ-UYE"; */
"R7V-kJ-UYE.text" = "Build:";
/* Class = "UILabel"; text = "Snikket"; ObjectID = "Z7m-aB-9EO"; */
"Z7m-aB-9EO.text" = "Snikket";

View file

@ -13,9 +13,6 @@
/* Alert dialog title - account is offline */
"Account Offline" = "Account Offline";
/* Account deletion alert title */
"Account Removal" = "Kontoborttagning";
/* Alert title */
"Account Removal Failed" = "Kontoborttagning Misslyckades";
@ -134,9 +131,6 @@
/* No comment provided by engineer. */
"Delete" = "Delete";
/* Account deletion action - destructive */
"Delete account permanently" = "Radera konto permanent";
/* Action: Delete all downloaded files */
"Delete all" = "Radera alla";
@ -149,6 +143,12 @@
/* No comment provided by engineer. */
"Delete group chat?" = "Radera gruppchatt?";
/* No comment provided by engineer. */
"Delete My Account" = "Delete My Account";
/* No comment provided by engineer. */
"Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@." = "Deleting your account will permanently log out all your devices and delete your account, profile, and associated data on %@.";
/* No comment provided by engineer. */
"Details" = "Details";
@ -167,9 +167,15 @@
/* No comment provided by engineer. */
"Emails" = "Emails";
/* No comment provided by engineer. */
"Enable %@" = "Enable %@";
/* No comment provided by engineer. */
"Enable automatic message synchronization" = "Enable automatic message synchronization";
/* No comment provided by engineer. */
"Enable telephony provider?" = "Enable telephony provider?";
/* No comment provided by engineer. */
"Encryption" = "Kryptering";
@ -272,6 +278,9 @@
/* No comment provided by engineer. */
"List of Messages" = "List of Messages";
/* No comment provided by engineer. */
"Log Out" = "Log Out";
/* No comment provided by engineer. */
"Login and password do not match." = "Login and password do not match.";
@ -362,6 +371,9 @@
/* No comment provided by engineer. */
"Other devices fingerprints" = "Andra enheters fingeravtryck";
/* No comment provided by engineer. */
"Permanently Delete Account" = "Permanently Delete Account";
/* No comment provided by engineer. */
"Phones" = "Telefoner";
@ -402,10 +414,7 @@
"Registration is not supported by this server" = "Registrering stöds inte på denna server";
/* No comment provided by engineer. */
"Remove account from application?" = "Ta bort konto från applikation?";
/* Account deletion action */
"Remove from application" = "Ta bort från applikationen";
"Remove Account Data" = "Remove Account Data";
/* No comment provided by engineer. */
"Rename" = "Byt namn";
@ -488,9 +497,6 @@
/* No comment provided by engineer. */
"Settings" = "Inställningar";
/* No comment provided by engineer. */
"Should account be removed from server as well?" = "Ska kontot tas bort från servern också?";
/* No comment provided by engineer. */
"Snikket can be automatically notified by compatible XMPP servers about new messages when it is in background or stopped.\nIf enabled, notifications about new messages will be forwarded to our push component and delivered to the device. These notifications may contain message senders jid and part of a message.\nDo you want to enable push notifications?" = "Snikket kan automatiskt bli notifierad av kompatibla XMPP-servrar om nya meddelanden när den är i bakgrunden eller stoppad.\nOm aktiverat så skickas notifikationer om nya meddelanden till vår push-komponent and levereras till enheten. Dessa notifikationer kan innehålla meddelandets avsändare och del av meddelandet.\nVill du aktivera push-notiser?";
@ -542,6 +548,9 @@
/* No comment provided by engineer. */
"When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?" = "When you share files using HTTP, they are uploaded to HTTP server with unique URL. Anyone who knows the unique URL to the file is able to download it.\nDo you wish to enable?";
/* No comment provided by engineer. */
"Would you like to use %@ as the default provider for outgoing SMS and calls from %@?" = "Would you like to use %1$@ as the default provider for outgoing SMS and calls from %2$@?";
/* No comment provided by engineer. */
"Yes" = "Ja";
@ -560,6 +569,9 @@
/* No comment provided by engineer. */
"You are not joined to the channel." = "Du är inte med i kanalen.";
/* No comment provided by engineer. */
"You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone." = "You can log out of this account temporarily, or permanently remove all account data from this device (including chats). Account removal cannot be undone.";
/* No comment provided by engineer. */
"You need to connect to your account before you can update your contact list. Do you wish to connect now?" = "You need to connect to your account before you can update your contact list. Do you wish to connect now?";

View file

@ -31,6 +31,9 @@
/* Class = "UILabel"; text = "Attachments"; ObjectID = "EpU-tc-DIx"; */
"EpU-tc-DIx.text" = "Bilagor";
/* Class = "UILabel"; text = "Contact added you to their contact list. Add to contacts?"; ObjectID = "ezh-iK-Sk7"; */
"ezh-iK-Sk7.text" = "Contact added you to their contact list. Add to contacts?";
/* Class = "UILabel"; text = "Receive presence updates"; ObjectID = "ffo-7n-Tn2"; */
"ffo-7n-Tn2.text" = "Receive presence updates";
@ -99,3 +102,9 @@
/* Class = "UIButton"; normalTitle = "Enable"; ObjectID = "yJk-Px-1oo"; */
"yJk-Px-1oo.normalTitle" = "Aktivera";
/* Class = "UILabel"; text = "Add to contact list"; ObjectID = "zrb-Yr-XQO"; */
"zrb-Yr-XQO.text" = "Add to contact list";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "k9G-H8-3Hb"; */
"k9G-H8-3Hb.normalTitle" = "";

View file

@ -24,12 +24,14 @@ import UIKit
class AboutController: UIViewController {
@IBOutlet weak var buildLabel: UILabel!
@IBOutlet var nameLabel: UILabel!
@IBOutlet var versionLabel: UILabel!;
@IBOutlet var copyrightTextView: UITextView!;
override func viewDidLoad() {
versionLabel.text = "Version: \(Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String ?? "Unknown")";
versionLabel.text = "Version: \(Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String ?? "Unknown")";
buildLabel.text = "Build: \(Bundle.main.infoDictionary!["CFBundleVersion"] as? String ?? "Unknown")"
}
override func viewWillAppear(_ animated: Bool) {

View file

@ -33,7 +33,7 @@ class MainTabBarController: CustomTabBarController, UITabBarControllerDelegate {
self.delegate = self;
NotificationCenter.default.addObserver(self, selector: #selector(updateMoreBadge), name: XmppService.ACCOUNT_STATE_CHANGED, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(updateMoreBadge), name: XmppService.ACCOUNT_STATE_CHANGED, object: nil);
}
@objc func updateMoreBadge(notification: Notification) {

View file

@ -35,3 +35,33 @@ class RoundButton: UIButton {
layer.cornerRadius = self.frame.height / 2;
}
}
final class RoundShadowButton: UIButton {
private var shadowLayer: CAShapeLayer!
public var cornerRadius: CGFloat = 0 {
didSet {
setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
if shadowLayer == nil {
shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.darkGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = .zero
shadowLayer.shadowOpacity = 0.6
shadowLayer.shadowRadius = 8
self.layer.cornerRadius = cornerRadius
layer.insertSublayer(shadowLayer, at: 0)
}
}
}

View file

@ -99,16 +99,47 @@ class WelcomeController: UIViewController, QRScannerViewDelegate {
}
func found(code: String) {
print("found code: \(code)");
guard let url = URL(string: code), let xmppUri = AppDelegate.XmppUri(url: url), (xmppUri.action == .register || xmppUri.action == .roster) else {
let alert = UIAlertController(title: NSLocalizedString("Error", comment: ""), message: NSLocalizedString("Scanned QR code is not valid for Snikket.", comment: ""), preferredStyle: .alert);
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil));
self.present(alert, animated: true, completion: nil);
return;
guard let url = URL(string: code) else { return }
if let xmppUri = AppDelegate.XmppUri(url: url), (xmppUri.action == .register || xmppUri.action == .roster) {
_ = (UIApplication.shared.delegate as? AppDelegate)?.application(UIApplication.shared, open: url)
return
} else {
let session = URLSession(configuration: .default)
session.dataTask(with: url) { data, response, error in
guard error == nil else { return }
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
self.showWrongQRScan()
return
}
if let link = response.allHeaderFields["Link"] as? String, var xmppComponent = link.components(separatedBy: " ").first {
xmppComponent = xmppComponent.replacingOccurrences(of: "<", with: "")
xmppComponent = xmppComponent.replacingOccurrences(of: ">", with: "")
if let url = URL(string: xmppComponent), let xmppUri = AppDelegate.XmppUri(url: url), (xmppUri.action == .register || xmppUri.action == .roster) {
DispatchQueue.main.async {
_ = (UIApplication.shared.delegate as? AppDelegate)?.application(UIApplication.shared, open: url)
}
return
} else {
self.showWrongQRScan()
}
} else {
self.showWrongQRScan()
}
}.resume()
}
}
func showWrongQRScan() {
let alert = UIAlertController(title: NSLocalizedString("Error", comment: ""), message: NSLocalizedString("Scanned QR code is not valid for Snikket.", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
DispatchQueue.main.async {
self.present(alert, animated: true, completion: nil)
}
(UIApplication.shared.delegate as? AppDelegate)?.application(UIApplication.shared, open: url);
}
func scanningDidStop() {

View file

@ -249,6 +249,7 @@ public enum AccountSettings {
case reconnectionLocation(BareJID)
case pushHash(BareJID)
case displayName(BareJID)
case telephonyProvider(BareJID)
public var account: BareJID {
switch self {
@ -272,6 +273,8 @@ public enum AccountSettings {
return account;
case .displayName(let account):
return account
case .telephonyProvider(let account):
return account
}
}
@ -297,6 +300,8 @@ public enum AccountSettings {
return "pushHash";
case .displayName(_):
return "displayName"
case .telephonyProvider(_):
return "TelephonyProvider"
}
}
@ -452,6 +457,44 @@ public enum AccountSettings {
AccountSettings.displayName(account).set(string: currentAccount.nickname ?? currentAccount.name.stringValue)
}
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 5) {
setDefaultSettings()
}
}
private static func setDefaultSettings() {
let accounts = AccountManager.getActiveAccounts()
for account in accounts {
AccountSettings.messageSyncAuto(account).set(bool: true)
// Checking for Message Archiving
let client = XmppService.instance.getClient(forJid: account)
if let mamModule: MessageArchiveManagementModule = client?.modulesManager.getModule(MessageArchiveManagementModule.ID) {
mamModule.retrieveSettings { result in
switch result {
case .success(let oldValue, let always, let never):
if oldValue == .never { // turn on meesage synchronization if it was previously disabled
mamModule.updateSettings(defaultValue: .always, always: always, never: never) { result in
AccountSettings.messageSyncAuto(account).set(bool: true)
}
}
case .failure(let error, let response):
print("\(error)\(response)")
}
}
}
// Message Archiving Period
let last14Days: Double = 14 * 24
let period = AccountSettings.messageSyncPeriod(account).getDouble()
if period != last14Days { // 14 Days
AccountSettings.messageSyncPeriod(account).set(double: last14Days)
}
}
}
}

View file

@ -114,22 +114,19 @@ class CallManager: NSObject, CXProviderDelegate {
}
func reportIncomingCall(_ call: Call, completionHandler: @escaping(Result<Void,Error>)->Void) {
// guard self.currentCall == nil else {
// if let curCall = self.currentCall, curCall.account == call.account && curCall.sid == call.sid && curCall.jid == call.jid {
// return;
// }
// completionHandler(.failure(ErrorCondition.conflict));
// return;
// }
let update = CXCallUpdate();
update.remoteHandle = CXHandle(type: .generic, value: call.jid.stringValue);
let rosterModule: RosterModule? = XmppService.instance.getClient(for: call.account)?.modulesManager.getModule(RosterModule.ID);
let name = rosterModule?.rosterStore.get(for: JID(call.jid))?.name ?? call.jid.stringValue;
update.localizedCallerName = name;
update.hasVideo = AVCaptureDevice.authorizationStatus(for: .video) == .authorized && call.media.contains(.video);
print("reporting incoming call: \(call.uuid)")
update.supportsGrouping = false
update.supportsUngrouping = false
update.supportsHolding = false
provider.configuration.supportsVideo = call.media.contains(.video)
update.hasVideo = AVCaptureDevice.authorizationStatus(for: .video) == .authorized && call.media.contains(.video)
update.remoteHandle = call.media.contains(.video) ? CXHandle(type: .generic, value: call.jid.stringValue) : nil
configureAudioSession()
provider.reportNewIncomingCall(with: call.uuid, update: update, completion: { err in
guard let error = err else {
print("error of the incoming call..");
@ -425,17 +422,6 @@ class CallManager: NSObject, CXProviderDelegate {
}
currentConnection = VideoCallController.initiatePeerConnection(iceServers: iceServers, withDelegate: self);
if currentConnection != nil {
let avsession = AVAudioSession.sharedInstance()
do {
try avsession.setCategory(.playAndRecord, mode: .videoChat)
try avsession.setPreferredIOBufferDuration(0.005)
try avsession.setPreferredSampleRate(4_410)
} catch {
fatalError(error.localizedDescription)
}
self.localAudioTrack = VideoCallController.peerConnectionFactory.audioTrack(withTrackId: "audio-" + UUID().uuidString);
if let localAudioTrack = self.localAudioTrack {
@ -471,6 +457,18 @@ class CallManager: NSObject, CXProviderDelegate {
}
}
func configureAudioSession() {
RTCDispatcher.dispatchAsync(on: RTCDispatcherQueueType.typeAudioSession) {
let audioSession = RTCAudioSession.sharedInstance()
audioSession.lockForConfiguration()
let configuration = RTCAudioSessionConfiguration.webRTC()
configuration.categoryOptions = [.allowBluetoothA2DP,.duckOthers,
.allowBluetooth]
try? audioSession.setConfiguration(configuration)
audioSession.unlockForConfiguration()
}
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
guard let call = currentCall else {
action.fail();
@ -484,6 +482,7 @@ class CallManager: NSObject, CXProviderDelegate {
return;
}
self.changeCallState(.connecting)
// here we should wait till XMPPClient is connected..
@ -499,7 +498,6 @@ class CallManager: NSObject, CXProviderDelegate {
switch result {
case .success(_):
action.fulfill();
session.delegate = self;
session.accept();
case .failure(_):
@ -523,7 +521,6 @@ class CallManager: NSObject, CXProviderDelegate {
}
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
print("ending call from provider: \(self.currentCall?.uuid), \(self.currentConnection != nil)");
let wasAccepted = self.currentConnection != nil;
let session = self.session;
self.reset();
@ -952,8 +949,12 @@ extension CallManager: PKPushRegistryDelegate {
}
}
let uuid = UUID();
let update = CXCallUpdate();
update.supportsGrouping = false
update.supportsUngrouping = false
update.supportsHolding = false
update.remoteHandle = CXHandle(type: .generic, value: "Unknown");
provider.reportNewIncomingCall(with: uuid, update: update, completion: { error in
if error == nil {

View file

@ -160,7 +160,7 @@ open class SiskinPushNotificationsModule: TigasePushNotificationsModule {
}
open func reenable(pushSettings: PushSettings, completionHandler: @escaping (Result<PushSettings,ErrorCondition>)->Void) {
self.enable(serviceJid: pushSettings.jid, node: pushSettings.node, deviceId: pushSettings.deviceId, features: pushSettings.encryption ? [TigasePushNotificationsModule.Encryption.XMLNS] : [], maxSize: pushSettings.maxSize, completionHandler: completionHandler);
self.enable(serviceJid: pushSettings.jid, node: pushSettings.node, deviceId: pushSettings.deviceId, pushkitDeviceId: pushSettings.pushkitDeviceId, features: pushSettings.encryption ? [TigasePushNotificationsModule.Encryption.XMLNS] : [], maxSize: pushSettings.maxSize, completionHandler: completionHandler);
}
private func hash(extensions: [PushNotificationsModuleExtension]) -> Int {