mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
b740522e3c
139
.github/workflows/build.yml
vendored
Normal file
139
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macos-10.15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
fetch-depth: '0'
|
||||||
|
|
||||||
|
- name: Set active Xcode path
|
||||||
|
run: sudo xcode-select -s /Applications/Xcode_12.3.app/Contents/Developer
|
||||||
|
|
||||||
|
- name: Create canonical source directory
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
sudo mkdir /Users/telegram
|
||||||
|
sudo chown -R $(whoami) /Users/telegram
|
||||||
|
cp -R $GITHUB_WORKSPACE /Users/telegram/
|
||||||
|
mv /Users/telegram/$(basename $GITHUB_WORKSPACE) /Users/telegram/telegram-ios
|
||||||
|
|
||||||
|
- name: Build the App
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# source code paths are included in the final binary, so we need to make them stable across builds
|
||||||
|
SOURCE_DIR=/Users/telegram/telegram-ios
|
||||||
|
|
||||||
|
# use canonical bazel root
|
||||||
|
BAZEL_USER_ROOT="/private/var/tmp/_bazel_telegram"
|
||||||
|
|
||||||
|
# download bazel
|
||||||
|
mkdir -p $HOME/bazel-dist
|
||||||
|
pushd $HOME/bazel-dist
|
||||||
|
curl -O -L https://github.com/bazelbuild/bazel/releases/download/3.7.0/bazel-3.7.0-darwin-x86_64
|
||||||
|
mv bazel-3.7.0* bazel
|
||||||
|
chmod +x bazel
|
||||||
|
./bazel --version
|
||||||
|
popd
|
||||||
|
|
||||||
|
cd $SOURCE_DIR
|
||||||
|
|
||||||
|
export APP_VERSION=$(cat versions.json | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["app"]);')
|
||||||
|
export COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||||
|
export COMMIT_COUNT="$(($COMMIT_COUNT+2000))"
|
||||||
|
export BUILD_NUMBER="$COMMIT_COUNT"
|
||||||
|
echo "BUILD_NUMBER=$(echo $BUILD_NUMBER)" >> $GITHUB_ENV
|
||||||
|
echo "APP_VERSION=$(echo $APP_VERSION)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# prepare temporary keychain
|
||||||
|
export MY_KEYCHAIN="temp.keychain"
|
||||||
|
export MY_KEYCHAIN_PASSWORD="secret"
|
||||||
|
security create-keychain -p "$MY_KEYCHAIN_PASSWORD" "$MY_KEYCHAIN"
|
||||||
|
security list-keychains -d user -s "$MY_KEYCHAIN" $(security list-keychains -d user | sed s/\"//g)
|
||||||
|
security set-keychain-settings "$MY_KEYCHAIN"
|
||||||
|
security unlock-keychain -p "$MY_KEYCHAIN_PASSWORD" "$MY_KEYCHAIN"
|
||||||
|
|
||||||
|
# install fake certificates
|
||||||
|
export CERTS_PATH="build-system/fake-codesigning/certs/distribution"
|
||||||
|
for f in "$CERTS_PATH"/*.p12; do
|
||||||
|
security import "$f" -k "$MY_KEYCHAIN" -P "" -T /usr/bin/codesign -T /usr/bin/security
|
||||||
|
done
|
||||||
|
# fake certificates are self-signed, so we need to manually mark them as trusted (otherwise bazel will not pick them up)
|
||||||
|
for f in "$CERTS_PATH"/*.cer; do
|
||||||
|
sudo security add-trusted-cert -d -r trustRoot -p codeSign -k "$MY_KEYCHAIN" "$f"
|
||||||
|
done
|
||||||
|
security set-key-partition-list -S apple-tool:,apple: -k "$MY_KEYCHAIN_PASSWORD" "$MY_KEYCHAIN"
|
||||||
|
|
||||||
|
# use the official release configuration
|
||||||
|
rm -rf $HOME/telegram-configuration
|
||||||
|
mkdir -p $HOME/telegram-configuration
|
||||||
|
cp -R build-system/example-configuration/* $HOME/telegram-configuration/
|
||||||
|
|
||||||
|
# build the app
|
||||||
|
python3 build-system/Make/Make.py \
|
||||||
|
--bazel="$HOME/bazel-dist/bazel" \
|
||||||
|
--bazelUserRoot="$BAZEL_USER_ROOT" \
|
||||||
|
build \
|
||||||
|
--configurationPath="$HOME/telegram-configuration" \
|
||||||
|
--buildNumber=$BUILD_NUMBER \
|
||||||
|
--configuration=release_universal
|
||||||
|
|
||||||
|
# collect ipa
|
||||||
|
OUTPUT_PATH="build/artifacts"
|
||||||
|
rm -rf "$OUTPUT_PATH"
|
||||||
|
mkdir -p "$OUTPUT_PATH"
|
||||||
|
for f in bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/Telegram.ipa; do
|
||||||
|
cp "$f" $OUTPUT_PATH/
|
||||||
|
done
|
||||||
|
|
||||||
|
# collect dsym
|
||||||
|
mkdir -p build/DSYMs
|
||||||
|
for f in bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/*.dSYM; do
|
||||||
|
cp -R "$f" build/DSYMs/
|
||||||
|
done
|
||||||
|
zip -r "./$OUTPUT_PATH/Telegram.DSYMs.zip" build/DSYMs 1>/dev/null
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: build-${{ env.BUILD_NUMBER }}
|
||||||
|
release_name: Telegram ${{ env.APP_VERSION }} (${{ env.BUILD_NUMBER }})
|
||||||
|
body: |
|
||||||
|
An unsigned build of Telegram for iOS ${{ env.APP_VERSION }} (${{ env.BUILD_NUMBER }})
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Upload Release IPA
|
||||||
|
id: upload-release-ipa
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: /Users/telegram/telegram-ios/build/artifacts/Telegram.ipa
|
||||||
|
asset_name: Telegram.ipa
|
||||||
|
asset_content_type: application/zip
|
||||||
|
|
||||||
|
- name: Upload Release DSYM
|
||||||
|
id: upload-release-dsym
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: /Users/telegram/telegram-ios/build/artifacts/Telegram.DSYMs.zip
|
||||||
|
asset_name: Telegram.DSYMs.zip
|
||||||
|
asset_content_type: application/zip
|
@ -5959,6 +5959,7 @@ Sorry for the inconvenience.";
|
|||||||
"ChatImportActivity.ErrorInvalidChatType" = "Wrong type of chat for the messages you are trying to import.";
|
"ChatImportActivity.ErrorInvalidChatType" = "Wrong type of chat for the messages you are trying to import.";
|
||||||
"ChatImportActivity.ErrorUserBlocked" = "Unable to import messages due to privacy settings.";
|
"ChatImportActivity.ErrorUserBlocked" = "Unable to import messages due to privacy settings.";
|
||||||
"ChatImportActivity.ErrorGeneric" = "An error occurred.";
|
"ChatImportActivity.ErrorGeneric" = "An error occurred.";
|
||||||
|
"ChatImportActivity.ErrorLimitExceeded" = "Daily maximum reached,\nplease come back tomorrow.";
|
||||||
"ChatImportActivity.Success" = "Chat imported\nsuccessfully.";
|
"ChatImportActivity.Success" = "Chat imported\nsuccessfully.";
|
||||||
|
|
||||||
"VoiceOver.Chat.GoToOriginalMessage" = "Go to message";
|
"VoiceOver.Chat.GoToOriginalMessage" = "Go to message";
|
||||||
|
@ -12,7 +12,7 @@ from ProjectGeneration import generate
|
|||||||
|
|
||||||
|
|
||||||
class BazelCommandLine:
|
class BazelCommandLine:
|
||||||
def __init__(self, bazel_path, bazel_x86_64_path, override_bazel_version, override_xcode_version):
|
def __init__(self, bazel_path, bazel_x86_64_path, override_bazel_version, override_xcode_version, bazel_user_root):
|
||||||
self.build_environment = BuildEnvironment(
|
self.build_environment = BuildEnvironment(
|
||||||
base_path=os.getcwd(),
|
base_path=os.getcwd(),
|
||||||
bazel_path=bazel_path,
|
bazel_path=bazel_path,
|
||||||
@ -20,6 +20,7 @@ class BazelCommandLine:
|
|||||||
override_bazel_version=override_bazel_version,
|
override_bazel_version=override_bazel_version,
|
||||||
override_xcode_version=override_xcode_version
|
override_xcode_version=override_xcode_version
|
||||||
)
|
)
|
||||||
|
self.bazel_user_root = bazel_user_root
|
||||||
self.remote_cache = None
|
self.remote_cache = None
|
||||||
self.cache_dir = None
|
self.cache_dir = None
|
||||||
self.additional_args = None
|
self.additional_args = None
|
||||||
@ -173,9 +174,18 @@ class BazelCommandLine:
|
|||||||
else:
|
else:
|
||||||
raise Exception('Unknown configuration {}'.format(configuration))
|
raise Exception('Unknown configuration {}'.format(configuration))
|
||||||
|
|
||||||
|
def get_startup_bazel_arguments(self):
|
||||||
|
combined_arguments = []
|
||||||
|
if self.bazel_user_root is not None:
|
||||||
|
combined_arguments += ['--output_user_root={}'.format(self.bazel_user_root)]
|
||||||
|
return combined_arguments
|
||||||
|
|
||||||
def invoke_clean(self):
|
def invoke_clean(self):
|
||||||
combined_arguments = [
|
combined_arguments = [
|
||||||
self.build_environment.bazel_path,
|
self.build_environment.bazel_path
|
||||||
|
]
|
||||||
|
combined_arguments += self.get_startup_bazel_arguments()
|
||||||
|
combined_arguments += [
|
||||||
'clean',
|
'clean',
|
||||||
'--expunge'
|
'--expunge'
|
||||||
]
|
]
|
||||||
@ -209,7 +219,10 @@ class BazelCommandLine:
|
|||||||
|
|
||||||
def invoke_build(self):
|
def invoke_build(self):
|
||||||
combined_arguments = [
|
combined_arguments = [
|
||||||
self.build_environment.bazel_path,
|
self.build_environment.bazel_path
|
||||||
|
]
|
||||||
|
combined_arguments += self.get_startup_bazel_arguments()
|
||||||
|
combined_arguments += [
|
||||||
'build',
|
'build',
|
||||||
'Telegram/Telegram'
|
'Telegram/Telegram'
|
||||||
]
|
]
|
||||||
@ -247,7 +260,8 @@ def clean(arguments):
|
|||||||
bazel_path=arguments.bazel,
|
bazel_path=arguments.bazel,
|
||||||
bazel_x86_64_path=None,
|
bazel_x86_64_path=None,
|
||||||
override_bazel_version=arguments.overrideBazelVersion,
|
override_bazel_version=arguments.overrideBazelVersion,
|
||||||
override_xcode_version=arguments.overrideXcodeVersion
|
override_xcode_version=arguments.overrideXcodeVersion,
|
||||||
|
bazel_user_root=arguments.bazelUserRoot
|
||||||
)
|
)
|
||||||
|
|
||||||
bazel_command_line.invoke_clean()
|
bazel_command_line.invoke_clean()
|
||||||
@ -292,7 +306,8 @@ def generate_project(arguments):
|
|||||||
bazel_path=arguments.bazel,
|
bazel_path=arguments.bazel,
|
||||||
bazel_x86_64_path=bazel_x86_64_path,
|
bazel_x86_64_path=bazel_x86_64_path,
|
||||||
override_bazel_version=arguments.overrideBazelVersion,
|
override_bazel_version=arguments.overrideBazelVersion,
|
||||||
override_xcode_version=arguments.overrideXcodeVersion
|
override_xcode_version=arguments.overrideXcodeVersion,
|
||||||
|
bazel_user_root=arguments.bazelUserRoot
|
||||||
)
|
)
|
||||||
|
|
||||||
if arguments.cacheDir is not None:
|
if arguments.cacheDir is not None:
|
||||||
@ -326,7 +341,8 @@ def build(arguments):
|
|||||||
bazel_path=arguments.bazel,
|
bazel_path=arguments.bazel,
|
||||||
bazel_x86_64_path=None,
|
bazel_x86_64_path=None,
|
||||||
override_bazel_version=arguments.overrideBazelVersion,
|
override_bazel_version=arguments.overrideBazelVersion,
|
||||||
override_xcode_version=arguments.overrideXcodeVersion
|
override_xcode_version=arguments.overrideXcodeVersion,
|
||||||
|
bazel_user_root=arguments.bazelUserRoot
|
||||||
)
|
)
|
||||||
|
|
||||||
if arguments.cacheDir is not None:
|
if arguments.cacheDir is not None:
|
||||||
@ -383,6 +399,13 @@ if __name__ == '__main__':
|
|||||||
metavar='path'
|
metavar='path'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--bazelUserRoot',
|
||||||
|
required=False,
|
||||||
|
help='Use custom bazel user root (useful when reproducing a build)',
|
||||||
|
metavar='path'
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--overrideBazelVersion',
|
'--overrideBazelVersion',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
@ -57,6 +57,7 @@ private final class ImportManager {
|
|||||||
case chatAdminRequired
|
case chatAdminRequired
|
||||||
case invalidChatType
|
case invalidChatType
|
||||||
case userBlocked
|
case userBlocked
|
||||||
|
case limitExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
@ -108,6 +109,11 @@ private final class ImportManager {
|
|||||||
|
|
||||||
self.stateValue = .progress(totalBytes: self.totalBytes, totalUploadedBytes: 0, totalMediaBytes: self.totalMediaBytes, totalUploadedMediaBytes: 0)
|
self.stateValue = .progress(totalBytes: self.totalBytes, totalUploadedBytes: 0, totalMediaBytes: self.totalMediaBytes, totalUploadedMediaBytes: 0)
|
||||||
|
|
||||||
|
Logger.shared.log("ChatImportScreen", "Requesting import session for \(peerId), media count: \(entries.count) with pending entries:")
|
||||||
|
for entry in entries {
|
||||||
|
Logger.shared.log("ChatImportScreen", " \(entry.1)")
|
||||||
|
}
|
||||||
|
|
||||||
self.disposable.set((ChatHistoryImport.initSession(account: self.account, peerId: peerId, file: mainFile, mediaCount: Int32(entries.count))
|
self.disposable.set((ChatHistoryImport.initSession(account: self.account, peerId: peerId, file: mainFile, mediaCount: Int32(entries.count))
|
||||||
|> mapError { error -> ImportError in
|
|> mapError { error -> ImportError in
|
||||||
switch error {
|
switch error {
|
||||||
@ -119,6 +125,8 @@ private final class ImportManager {
|
|||||||
return .generic
|
return .generic
|
||||||
case .userBlocked:
|
case .userBlocked:
|
||||||
return .userBlocked
|
return .userBlocked
|
||||||
|
case .limitExceeded:
|
||||||
|
return .limitExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] session in
|
|> deliverOnMainQueue).start(next: { [weak self] session in
|
||||||
@ -188,28 +196,37 @@ private final class ImportManager {
|
|||||||
|
|
||||||
private func updateState() {
|
private func updateState() {
|
||||||
guard let session = self.session else {
|
guard let session = self.session else {
|
||||||
|
Logger.shared.log("ChatImportScreen", "updateState called with no session, ignoring")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.pendingEntries.isEmpty && self.activeEntries.isEmpty {
|
if self.pendingEntries.isEmpty && self.activeEntries.isEmpty {
|
||||||
|
Logger.shared.log("ChatImportScreen", "updateState called with no pending and no active entries, completing")
|
||||||
self.complete()
|
self.complete()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if case .error = self.stateValue {
|
if case .error = self.stateValue {
|
||||||
|
Logger.shared.log("ChatImportScreen", "updateState called after error, ignoring")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let archivePath = self.archivePath else {
|
guard let archivePath = self.archivePath else {
|
||||||
|
Logger.shared.log("ChatImportScreen", "updateState called with empty arhivePath, ignoring")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
if self.activeEntries.count >= 3 {
|
if self.activeEntries.count >= 3 {
|
||||||
|
Logger.shared.log("ChatImportScreen", "updateState concurrent processing limit reached, stop searching")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if self.pendingEntries.isEmpty {
|
if self.pendingEntries.isEmpty {
|
||||||
|
Logger.shared.log("ChatImportScreen", "updateState no more pending entries, stop searching")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = self.pendingEntries.removeFirst()
|
let entry = self.pendingEntries.removeFirst()
|
||||||
|
|
||||||
|
Logger.shared.log("ChatImportScreen", "updateState take pending entry \(entry.1)")
|
||||||
|
|
||||||
let unpackedFile = Signal<TempBoxFile, ImportError> { subscriber in
|
let unpackedFile = Signal<TempBoxFile, ImportError> { subscriber in
|
||||||
let tempFile = TempBox.shared.tempFile(fileName: entry.0.path)
|
let tempFile = TempBox.shared.tempFile(fileName: entry.0.path)
|
||||||
Logger.shared.log("ChatImportScreen", "Extracting \(entry.0.path) to \(tempFile.path)...")
|
Logger.shared.log("ChatImportScreen", "Extracting \(entry.0.path) to \(tempFile.path)...")
|
||||||
@ -266,6 +283,7 @@ private final class ImportManager {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Logger.shared.log("ChatImportScreen", "updateState entry \(entry.1) has completed upload")
|
||||||
strongSelf.activeEntries.removeValue(forKey: entry.0.path)
|
strongSelf.activeEntries.removeValue(forKey: entry.0.path)
|
||||||
strongSelf.updateState()
|
strongSelf.updateState()
|
||||||
}))
|
}))
|
||||||
@ -533,6 +551,8 @@ public final class ChatImportActivityScreen: ViewController {
|
|||||||
errorText = self.presentationData.strings.ChatImportActivity_ErrorGeneric
|
errorText = self.presentationData.strings.ChatImportActivity_ErrorGeneric
|
||||||
case .userBlocked:
|
case .userBlocked:
|
||||||
errorText = self.presentationData.strings.ChatImportActivity_ErrorUserBlocked
|
errorText = self.presentationData.strings.ChatImportActivity_ErrorUserBlocked
|
||||||
|
case .limitExceeded:
|
||||||
|
errorText = self.presentationData.strings.ChatImportActivity_ErrorLimitExceeded
|
||||||
}
|
}
|
||||||
self.statusText.attributedText = NSAttributedString(string: errorText, font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemDestructiveColor)
|
self.statusText.attributedText = NSAttributedString(string: errorText, font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemDestructiveColor)
|
||||||
case .done:
|
case .done:
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
objc_library(
|
swift_library(
|
||||||
name = "Reachability",
|
name = "Reachability",
|
||||||
enable_modules = True,
|
|
||||||
module_name = "Reachability",
|
module_name = "Reachability",
|
||||||
srcs = glob([
|
srcs = glob([
|
||||||
"Sources/*.m",
|
"Sources/**/*.swift",
|
||||||
]),
|
]),
|
||||||
hdrs = glob([
|
deps = [
|
||||||
"PublicHeaders/**/*.h",
|
"//submodules/Reachability/LegacyReachability:LegacyReachability",
|
||||||
]),
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
includes = [
|
|
||||||
"PublicHeaders",
|
|
||||||
],
|
|
||||||
sdk_frameworks = [
|
|
||||||
"Foundation",
|
|
||||||
"SystemConfiguration",
|
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
22
submodules/Reachability/LegacyReachability/BUILD
Normal file
22
submodules/Reachability/LegacyReachability/BUILD
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
objc_library(
|
||||||
|
name = "LegacyReachability",
|
||||||
|
enable_modules = True,
|
||||||
|
module_name = "LegacyReachability",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/*.m",
|
||||||
|
]),
|
||||||
|
hdrs = glob([
|
||||||
|
"PublicHeaders/**/*.h",
|
||||||
|
]),
|
||||||
|
includes = [
|
||||||
|
"PublicHeaders",
|
||||||
|
],
|
||||||
|
sdk_frameworks = [
|
||||||
|
"Foundation",
|
||||||
|
"SystemConfiguration",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -24,7 +24,7 @@ typedef enum : NSInteger {
|
|||||||
extern NSString *kReachabilityChangedNotification;
|
extern NSString *kReachabilityChangedNotification;
|
||||||
|
|
||||||
|
|
||||||
@interface Reachability : NSObject
|
@interface LegacyReachability : NSObject
|
||||||
|
|
||||||
@property (nonatomic, copy) void (^reachabilityChanged)(NetworkStatus status);
|
@property (nonatomic, copy) void (^reachabilityChanged)(NetworkStatus status);
|
||||||
|
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#import <CoreFoundation/CoreFoundation.h>
|
#import <CoreFoundation/CoreFoundation.h>
|
||||||
|
|
||||||
#import <Reachability/Reachability.h>
|
#import <LegacyReachability/LegacyReachability.h>
|
||||||
|
|
||||||
#import <pthread.h>
|
#import <pthread.h>
|
||||||
#import <libkern/OSAtomic.h>
|
#import <libkern/OSAtomic.h>
|
||||||
@ -126,14 +126,14 @@ static ReachabilityAtomic *contexts() {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void withContext(int32_t key, void (^f)(Reachability *)) {
|
static void withContext(int32_t key, void (^f)(LegacyReachability *)) {
|
||||||
Reachability *reachability = [contexts() with:^id(NSDictionary *dict) {
|
LegacyReachability *reachability = [contexts() with:^id(NSDictionary *dict) {
|
||||||
return dict[@(key)];
|
return dict[@(key)];
|
||||||
}];
|
}];
|
||||||
f(reachability);
|
f(reachability);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t addContext(Reachability *context) {
|
static int32_t addContext(LegacyReachability *context) {
|
||||||
int32_t key = OSAtomicIncrement32(&nextKey);
|
int32_t key = OSAtomicIncrement32(&nextKey);
|
||||||
[contexts() modify:^id(NSMutableDictionary *dict) {
|
[contexts() modify:^id(NSMutableDictionary *dict) {
|
||||||
NSMutableDictionary *updatedDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
|
NSMutableDictionary *updatedDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
|
||||||
@ -155,19 +155,19 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
|||||||
{
|
{
|
||||||
#pragma unused (target, flags)
|
#pragma unused (target, flags)
|
||||||
//NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
|
//NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
|
||||||
//NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
|
//NSCAssert([(__bridge NSObject*) info isKindOfClass: [LegacyReachability class]], @"info was wrong class in ReachabilityCallback");
|
||||||
|
|
||||||
int32_t key = (int32_t)((intptr_t)info);
|
int32_t key = (int32_t)((intptr_t)info);
|
||||||
withContext(key, ^(Reachability *context) {
|
withContext(key, ^(LegacyReachability *context) {
|
||||||
if ([context isKindOfClass:[Reachability class]] && context.reachabilityChanged != nil)
|
if ([context isKindOfClass:[LegacyReachability class]] && context.reachabilityChanged != nil)
|
||||||
context.reachabilityChanged(context.currentReachabilityStatus);
|
context.reachabilityChanged(context.currentReachabilityStatus);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Reachability implementation
|
#pragma mark - LegacyReachability implementation
|
||||||
|
|
||||||
@implementation Reachability
|
@implementation LegacyReachability
|
||||||
{
|
{
|
||||||
int32_t _key;
|
int32_t _key;
|
||||||
SCNetworkReachabilityRef _reachabilityRef;
|
SCNetworkReachabilityRef _reachabilityRef;
|
||||||
@ -175,7 +175,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
|||||||
|
|
||||||
+ (instancetype)reachabilityWithHostName:(NSString *)hostName
|
+ (instancetype)reachabilityWithHostName:(NSString *)hostName
|
||||||
{
|
{
|
||||||
Reachability* returnValue = NULL;
|
LegacyReachability* returnValue = NULL;
|
||||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
|
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
|
||||||
if (reachability != NULL)
|
if (reachability != NULL)
|
||||||
{
|
{
|
||||||
@ -199,7 +199,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
|||||||
{
|
{
|
||||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress);
|
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress);
|
||||||
|
|
||||||
Reachability* returnValue = NULL;
|
LegacyReachability* returnValue = NULL;
|
||||||
|
|
||||||
if (reachability != NULL)
|
if (reachability != NULL)
|
||||||
{
|
{
|
180
submodules/Reachability/Sources/Reachability.swift
Normal file
180
submodules/Reachability/Sources/Reachability.swift
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
|
import LegacyReachability
|
||||||
|
import Network
|
||||||
|
|
||||||
|
private final class WrappedLegacyReachability: NSObject {
|
||||||
|
@objc private static func threadImpl() {
|
||||||
|
while true {
|
||||||
|
RunLoop.current.run(until: .distantFuture)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static let thread: Thread = {
|
||||||
|
let thread = Thread(target: Reachability.self, selector: #selector(WrappedLegacyReachability.threadImpl), object: nil)
|
||||||
|
thread.start()
|
||||||
|
return thread
|
||||||
|
}()
|
||||||
|
|
||||||
|
@objc private static func dispatchOnThreadImpl(_ f: @escaping () -> Void) {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func dispatchOnThread(_ f: @escaping @convention(block) () -> Void) {
|
||||||
|
WrappedLegacyReachability.perform(#selector(WrappedLegacyReachability.dispatchOnThreadImpl(_:)), on: WrappedLegacyReachability.thread, with: f, waitUntilDone: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private let reachability: LegacyReachability
|
||||||
|
|
||||||
|
let value: ValuePromise<Reachability.NetworkType>
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
assert(Thread.current === WrappedLegacyReachability.thread)
|
||||||
|
self.reachability = LegacyReachability.forInternetConnection()
|
||||||
|
let type: Reachability.NetworkType
|
||||||
|
switch self.reachability.currentReachabilityStatus() {
|
||||||
|
case NotReachable:
|
||||||
|
type = .none
|
||||||
|
case ReachableViaWiFi:
|
||||||
|
type = .wifi
|
||||||
|
case ReachableViaWWAN:
|
||||||
|
type = .cellular
|
||||||
|
default:
|
||||||
|
type = .none
|
||||||
|
}
|
||||||
|
self.value = ValuePromise<Reachability.NetworkType>(type)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.reachability.reachabilityChanged = { [weak self] status in
|
||||||
|
WrappedLegacyReachability.dispatchOnThread {
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let internalNetworkType: Reachability.NetworkType
|
||||||
|
switch status {
|
||||||
|
case NotReachable:
|
||||||
|
internalNetworkType = .none
|
||||||
|
case ReachableViaWiFi:
|
||||||
|
internalNetworkType = .wifi
|
||||||
|
case ReachableViaWWAN:
|
||||||
|
internalNetworkType = .cellular
|
||||||
|
default:
|
||||||
|
internalNetworkType = .none
|
||||||
|
}
|
||||||
|
strongSelf.value.set(internalNetworkType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.reachability.startNotifier()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var valueRef: Unmanaged<WrappedLegacyReachability>?
|
||||||
|
|
||||||
|
static func withInstance(_ f: @escaping (WrappedLegacyReachability) -> Void) {
|
||||||
|
WrappedLegacyReachability.dispatchOnThread {
|
||||||
|
if self.valueRef == nil {
|
||||||
|
self.valueRef = Unmanaged.passRetained(WrappedLegacyReachability())
|
||||||
|
}
|
||||||
|
if let valueRef = self.valueRef {
|
||||||
|
let value = valueRef.takeUnretainedValue()
|
||||||
|
f(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOSApplicationExtension 12.0, iOS 12.0, OSX 10.14, *)
|
||||||
|
private final class PathMonitor {
|
||||||
|
private let queue: Queue
|
||||||
|
private let monitor: NWPathMonitor
|
||||||
|
|
||||||
|
let networkType = Promise<Reachability.NetworkType>()
|
||||||
|
|
||||||
|
init(queue: Queue) {
|
||||||
|
self.queue = queue
|
||||||
|
self.monitor = NWPathMonitor()
|
||||||
|
|
||||||
|
self.monitor.pathUpdateHandler = { [weak self] path in
|
||||||
|
queue.async {
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let networkType: Reachability.NetworkType
|
||||||
|
if path.status == .satisfied {
|
||||||
|
if path.usesInterfaceType(.cellular) {
|
||||||
|
networkType = .cellular
|
||||||
|
} else {
|
||||||
|
networkType = .wifi
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
networkType = .none
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.networkType.set(.single(networkType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.monitor.start(queue: self.queue.queue)
|
||||||
|
|
||||||
|
let networkType: Reachability.NetworkType
|
||||||
|
let path = self.monitor.currentPath
|
||||||
|
if path.status == .satisfied {
|
||||||
|
if path.usesInterfaceType(.cellular) {
|
||||||
|
networkType = .cellular
|
||||||
|
} else {
|
||||||
|
networkType = .wifi
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
networkType = .none
|
||||||
|
}
|
||||||
|
|
||||||
|
self.networkType.set(.single(networkType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOSApplicationExtension 12.0, iOS 12.0, OSX 10.14, *)
|
||||||
|
private final class SharedPathMonitor {
|
||||||
|
static let queue = Queue()
|
||||||
|
static let impl = QueueLocalObject<PathMonitor>(queue: queue, generate: {
|
||||||
|
return PathMonitor(queue: queue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Reachability {
|
||||||
|
public enum NetworkType: Equatable {
|
||||||
|
case none
|
||||||
|
case wifi
|
||||||
|
case cellular
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var networkType: Signal<NetworkType, NoError> {
|
||||||
|
if #available(iOSApplicationExtension 12.0, iOS 12.0, OSX 10.14, *) {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
SharedPathMonitor.impl.with { impl in
|
||||||
|
disposable.set(impl.networkType.get().start(next: { value in
|
||||||
|
subscriber.putNext(value)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
} else {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
WrappedLegacyReachability.withInstance({ impl in
|
||||||
|
disposable.set(impl.value.get().start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -57,7 +57,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
|||||||
groupCall: nil
|
groupCall: nil
|
||||||
)))
|
)))
|
||||||
|
|
||||||
self.disposable = (getGroupCallParticipants(account: account, callId: call.id, accessHash: call.accessHash, offset: "", peerIds: [], limit: 100)
|
self.disposable = (getGroupCallParticipants(account: account, callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100)
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<GroupCallParticipantsContext.State?, NoError> in
|
|> `catch` { _ -> Signal<GroupCallParticipantsContext.State?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -228,13 +228,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
private let silentTimeout: Int32 = 2
|
private let silentTimeout: Int32 = 2
|
||||||
|
|
||||||
struct Participant {
|
struct Participant {
|
||||||
|
let ssrc: UInt32
|
||||||
let timestamp: Int32
|
let timestamp: Int32
|
||||||
let level: Float
|
let level: Float
|
||||||
}
|
}
|
||||||
|
|
||||||
private var participants: [PeerId: Participant] = [:]
|
private var participants: [PeerId: Participant] = [:]
|
||||||
private let speakingParticipantsPromise = ValuePromise<Set<PeerId>>()
|
private let speakingParticipantsPromise = ValuePromise<[PeerId: UInt32]>()
|
||||||
private var speakingParticipants = Set<PeerId>() {
|
private var speakingParticipants = [PeerId: UInt32]() {
|
||||||
didSet {
|
didSet {
|
||||||
self.speakingParticipantsPromise.set(self.speakingParticipants)
|
self.speakingParticipantsPromise.set(self.speakingParticipants)
|
||||||
}
|
}
|
||||||
@ -245,17 +246,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
init() {
|
init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(levels: [(PeerId, Float, Bool)]) {
|
func update(levels: [(PeerId, UInt32, Float, Bool)]) {
|
||||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent())
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent())
|
||||||
let currentParticipants: [PeerId: Participant] = self.participants
|
let currentParticipants: [PeerId: Participant] = self.participants
|
||||||
|
|
||||||
var validSpeakers: [PeerId: Participant] = [:]
|
var validSpeakers: [PeerId: Participant] = [:]
|
||||||
var silentParticipants = Set<PeerId>()
|
var silentParticipants = Set<PeerId>()
|
||||||
var speakingParticipants = Set<PeerId>()
|
var speakingParticipants = [PeerId: UInt32]()
|
||||||
for (peerId, level, hasVoice) in levels {
|
for (peerId, ssrc, level, hasVoice) in levels {
|
||||||
if level > speakingLevelThreshold && hasVoice {
|
if level > speakingLevelThreshold && hasVoice {
|
||||||
validSpeakers[peerId] = Participant(timestamp: timestamp, level: level)
|
validSpeakers[peerId] = Participant(ssrc: ssrc, timestamp: timestamp, level: level)
|
||||||
speakingParticipants.insert(peerId)
|
speakingParticipants[peerId] = ssrc
|
||||||
} else {
|
} else {
|
||||||
silentParticipants.insert(peerId)
|
silentParticipants.insert(peerId)
|
||||||
}
|
}
|
||||||
@ -268,17 +269,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
if silentParticipants.contains(peerId) {
|
if silentParticipants.contains(peerId) {
|
||||||
if delta < silentTimeout {
|
if delta < silentTimeout {
|
||||||
validSpeakers[peerId] = participant
|
validSpeakers[peerId] = participant
|
||||||
speakingParticipants.insert(peerId)
|
speakingParticipants[peerId] = participant.ssrc
|
||||||
}
|
}
|
||||||
} else if delta < cutoffTimeout {
|
} else if delta < cutoffTimeout {
|
||||||
validSpeakers[peerId] = participant
|
validSpeakers[peerId] = participant
|
||||||
speakingParticipants.insert(peerId)
|
speakingParticipants[peerId] = participant.ssrc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var audioLevels: [(PeerId, Float, Bool)] = []
|
var audioLevels: [(PeerId, Float, Bool)] = []
|
||||||
for (peerId, level, hasVoice) in levels {
|
for (peerId, _, level, hasVoice) in levels {
|
||||||
if level > 0.001 {
|
if level > 0.001 {
|
||||||
audioLevels.append((peerId, level, hasVoice))
|
audioLevels.append((peerId, level, hasVoice))
|
||||||
}
|
}
|
||||||
@ -289,8 +290,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.audioLevelsPromise.set(.single(audioLevels))
|
self.audioLevelsPromise.set(.single(audioLevels))
|
||||||
}
|
}
|
||||||
|
|
||||||
func get() -> Signal<Set<PeerId>, NoError> {
|
func get() -> Signal<[PeerId: UInt32], NoError> {
|
||||||
return self.speakingParticipantsPromise.get() |> distinctUntilChanged
|
return self.speakingParticipantsPromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAudioLevels() -> Signal<[(PeerId, Float, Bool)], NoError> {
|
func getAudioLevels() -> Signal<[(PeerId, Float, Bool)], NoError> {
|
||||||
@ -887,16 +888,20 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var result: [(PeerId, Float, Bool)] = []
|
var result: [(PeerId, UInt32, Float, Bool)] = []
|
||||||
var myLevel: Float = 0.0
|
var myLevel: Float = 0.0
|
||||||
var myLevelHasVoice: Bool = false
|
var myLevelHasVoice: Bool = false
|
||||||
|
var missingSsrcs = Set<UInt32>()
|
||||||
for (ssrcKey, level, hasVoice) in levels {
|
for (ssrcKey, level, hasVoice) in levels {
|
||||||
var peerId: PeerId?
|
var peerId: PeerId?
|
||||||
|
let ssrcValue: UInt32
|
||||||
switch ssrcKey {
|
switch ssrcKey {
|
||||||
case .local:
|
case .local:
|
||||||
peerId = strongSelf.accountContext.account.peerId
|
peerId = strongSelf.accountContext.account.peerId
|
||||||
|
ssrcValue = 0
|
||||||
case let .source(ssrc):
|
case let .source(ssrc):
|
||||||
peerId = strongSelf.ssrcMapping[ssrc]
|
peerId = strongSelf.ssrcMapping[ssrc]
|
||||||
|
ssrcValue = ssrc
|
||||||
}
|
}
|
||||||
if let peerId = peerId {
|
if let peerId = peerId {
|
||||||
if case .local = ssrcKey {
|
if case .local = ssrcKey {
|
||||||
@ -905,7 +910,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
myLevelHasVoice = hasVoice
|
myLevelHasVoice = hasVoice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.append((peerId, level, hasVoice))
|
result.append((peerId, ssrcValue, level, hasVoice))
|
||||||
|
} else if ssrcValue != 0 {
|
||||||
|
missingSsrcs.insert(ssrcValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -914,6 +921,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
let mappedLevel = myLevel * 1.5
|
let mappedLevel = myLevel * 1.5
|
||||||
strongSelf.myAudioLevelPipe.putNext(mappedLevel)
|
strongSelf.myAudioLevelPipe.putNext(mappedLevel)
|
||||||
strongSelf.processMyAudioLevel(level: mappedLevel, hasVoice: myLevelHasVoice)
|
strongSelf.processMyAudioLevel(level: mappedLevel, hasVoice: myLevelHasVoice)
|
||||||
|
|
||||||
|
if !missingSsrcs.isEmpty {
|
||||||
|
strongSelf.participantsContext?.ensureHaveParticipants(ssrcs: missingSsrcs)
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -985,9 +996,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
var topParticipants: [GroupCallParticipantsContext.Participant] = []
|
var topParticipants: [GroupCallParticipantsContext.Participant] = []
|
||||||
|
|
||||||
var reportSpeakingParticipants: [PeerId] = []
|
var reportSpeakingParticipants: [PeerId: UInt32] = [:]
|
||||||
let timestamp = CACurrentMediaTime()
|
let timestamp = CACurrentMediaTime()
|
||||||
for peerId in speakingParticipants {
|
for (peerId, ssrc) in speakingParticipants {
|
||||||
let shouldReport: Bool
|
let shouldReport: Bool
|
||||||
if let previousTimestamp = strongSelf.speakingParticipantsReportTimestamp[peerId] {
|
if let previousTimestamp = strongSelf.speakingParticipantsReportTimestamp[peerId] {
|
||||||
shouldReport = previousTimestamp + 1.0 < timestamp
|
shouldReport = previousTimestamp + 1.0 < timestamp
|
||||||
@ -996,7 +1007,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
if shouldReport {
|
if shouldReport {
|
||||||
strongSelf.speakingParticipantsReportTimestamp[peerId] = timestamp
|
strongSelf.speakingParticipantsReportTimestamp[peerId] = timestamp
|
||||||
reportSpeakingParticipants.append(peerId)
|
reportSpeakingParticipants[peerId] = ssrc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1008,7 +1019,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
var members = PresentationGroupCallMembers(
|
var members = PresentationGroupCallMembers(
|
||||||
participants: [],
|
participants: [],
|
||||||
speakingParticipants: speakingParticipants,
|
speakingParticipants: Set(speakingParticipants.keys),
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
loadMoreToken: nil
|
loadMoreToken: nil
|
||||||
)
|
)
|
||||||
|
@ -1229,7 +1229,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if case let .known(value) = offset, value < 100.0 {
|
if case let .known(value) = offset, value < 200.0 {
|
||||||
if let loadMoreToken = strongSelf.currentCallMembers?.1 {
|
if let loadMoreToken = strongSelf.currentCallMembers?.1 {
|
||||||
strongSelf.currentLoadToken = loadMoreToken
|
strongSelf.currentLoadToken = loadMoreToken
|
||||||
strongSelf.call.loadMoreMembers(token: loadMoreToken)
|
strongSelf.call.loadMoreMembers(token: loadMoreToken)
|
||||||
|
@ -165,7 +165,10 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
|
|||||||
case .broadcast:
|
case .broadcast:
|
||||||
break
|
break
|
||||||
case .group:
|
case .group:
|
||||||
let infoFlags = TelegramChannelGroupFlags()
|
var infoFlags = TelegramChannelGroupFlags()
|
||||||
|
if (flags & Int32(1 << 22)) != 0 {
|
||||||
|
infoFlags.insert(.slowModeEnabled)
|
||||||
|
}
|
||||||
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
|
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ public enum ChatHistoryImport {
|
|||||||
case chatAdminRequired
|
case chatAdminRequired
|
||||||
case invalidChatType
|
case invalidChatType
|
||||||
case userBlocked
|
case userBlocked
|
||||||
|
case limitExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ParsedInfo {
|
public enum ParsedInfo {
|
||||||
@ -64,16 +65,17 @@ public enum ChatHistoryImport {
|
|||||||
guard let inputPeer = inputPeer else {
|
guard let inputPeer = inputPeer else {
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
return account.network.request(Api.functions.messages.initHistoryImport(peer: inputPeer, file: inputFile, mediaCount: mediaCount))
|
return account.network.request(Api.functions.messages.initHistoryImport(peer: inputPeer, file: inputFile, mediaCount: mediaCount), automaticFloodWait: false)
|
||||||
|> mapError { error -> InitImportError in
|
|> mapError { error -> InitImportError in
|
||||||
switch error.errorDescription {
|
if error.errorDescription == "CHAT_ADMIN_REQUIRED" {
|
||||||
case "CHAT_ADMIN_REQUIRED":
|
|
||||||
return .chatAdminRequired
|
return .chatAdminRequired
|
||||||
case "IMPORT_PEER_TYPE_INVALID":
|
} else if error.errorDescription == "IMPORT_PEER_TYPE_INVALID" {
|
||||||
return .invalidChatType
|
return .invalidChatType
|
||||||
case "USER_IS_BLOCKED":
|
} else if error.errorDescription == "USER_IS_BLOCKED" {
|
||||||
return .userBlocked
|
return .userBlocked
|
||||||
default:
|
} else if error.errorDescription == "FLOOD_WAIT" {
|
||||||
|
return .limitExceeded
|
||||||
|
} else {
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,7 +109,10 @@ public enum ChatHistoryImport {
|
|||||||
|
|
||||||
public static func uploadMedia(account: Account, session: Session, file: TempBoxFile, fileName: String, mimeType: String, type: MediaType) -> Signal<Float, UploadMediaError> {
|
public static func uploadMedia(account: Account, session: Session, file: TempBoxFile, fileName: String, mimeType: String, type: MediaType) -> Signal<Float, UploadMediaError> {
|
||||||
var forceNoBigParts = true
|
var forceNoBigParts = true
|
||||||
if let size = fileSize(file.path), size >= 30 * 1024 * 1024 {
|
guard let size = fileSize(file.path), size != 0 else {
|
||||||
|
return .single(1.0)
|
||||||
|
}
|
||||||
|
if size >= 30 * 1024 * 1024 {
|
||||||
forceNoBigParts = false
|
forceNoBigParts = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,8 +187,8 @@ public enum GetGroupCallParticipantsError {
|
|||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, peerIds: [PeerId], limit: Int32) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
public func getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, ssrcs: [UInt32], limit: Int32) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
||||||
return account.network.request(Api.functions.phone.getGroupParticipants(call: .inputGroupCall(id: callId, accessHash: accessHash), ids: peerIds.map { $0.id }, sources: [], offset: offset, limit: limit))
|
return account.network.request(Api.functions.phone.getGroupParticipants(call: .inputGroupCall(id: callId, accessHash: accessHash), ids: [], sources: ssrcs.map { Int32(bitPattern: $0) }, offset: offset, limit: limit))
|
||||||
|> mapError { _ -> GetGroupCallParticipantsError in
|
|> mapError { _ -> GetGroupCallParticipantsError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
@ -368,7 +368,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
|
|||||||
|> mapError { _ -> JoinGroupCallError in
|
|> mapError { _ -> JoinGroupCallError in
|
||||||
return .generic
|
return .generic
|
||||||
},
|
},
|
||||||
getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", peerIds: [], limit: 100)
|
getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", ssrcs: [], limit: 100)
|
||||||
|> mapError { _ -> JoinGroupCallError in
|
|> mapError { _ -> JoinGroupCallError in
|
||||||
return .generic
|
return .generic
|
||||||
},
|
},
|
||||||
@ -772,7 +772,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
private var isLoadingMore: Bool = false
|
private var isLoadingMore: Bool = false
|
||||||
private var shouldResetStateFromServer: Bool = false
|
private var shouldResetStateFromServer: Bool = false
|
||||||
private var missingPeerIds = Set<PeerId>()
|
private var missingSsrcs = Set<UInt32>()
|
||||||
|
|
||||||
private let updateDefaultMuteDisposable = MetaDisposable()
|
private let updateDefaultMuteDisposable = MetaDisposable()
|
||||||
|
|
||||||
@ -811,8 +811,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
})
|
})
|
||||||
strongSelf.activeSpeakersValue = peerIds
|
strongSelf.activeSpeakersValue = peerIds
|
||||||
|
|
||||||
strongSelf.ensureHaveParticipants(peerIds: peerIds)
|
|
||||||
|
|
||||||
if !strongSelf.hasReceivedSpeakingParticipantsReport {
|
if !strongSelf.hasReceivedSpeakingParticipantsReport {
|
||||||
var updatedParticipants = strongSelf.stateValue.state.participants
|
var updatedParticipants = strongSelf.stateValue.state.participants
|
||||||
var indexMap: [PeerId: Int] = [:]
|
var indexMap: [PeerId: Int] = [:]
|
||||||
@ -890,7 +888,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reportSpeakingParticipants(ids: [PeerId]) {
|
public func reportSpeakingParticipants(ids: [PeerId: UInt32]) {
|
||||||
if !ids.isEmpty {
|
if !ids.isEmpty {
|
||||||
self.hasReceivedSpeakingParticipantsReport = true
|
self.hasReceivedSpeakingParticipantsReport = true
|
||||||
}
|
}
|
||||||
@ -906,7 +904,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||||
|
|
||||||
for activityPeerId in ids {
|
for (activityPeerId, _) in ids {
|
||||||
if let index = indexMap[activityPeerId] {
|
if let index = indexMap[activityPeerId] {
|
||||||
var updateTimestamp = false
|
var updateTimestamp = false
|
||||||
if let activityTimestamp = updatedParticipants[index].activityTimestamp {
|
if let activityTimestamp = updatedParticipants[index].activityTimestamp {
|
||||||
@ -947,30 +945,32 @@ public final class GroupCallParticipantsContext {
|
|||||||
overlayState: strongSelf.stateValue.overlayState
|
overlayState: strongSelf.stateValue.overlayState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.ensureHaveParticipants(ssrcs: Set(ids.map { $0.1 }))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func ensureHaveParticipants(peerIds: Set<PeerId>) {
|
public func ensureHaveParticipants(ssrcs: Set<UInt32>) {
|
||||||
var missingPeerIds = Set<PeerId>()
|
var missingSsrcs = Set<UInt32>()
|
||||||
|
|
||||||
var existingParticipantIds = Set<PeerId>()
|
var existingSsrcs = Set<UInt32>()
|
||||||
for participant in self.stateValue.state.participants {
|
for participant in self.stateValue.state.participants {
|
||||||
existingParticipantIds.insert(participant.peer.id)
|
existingSsrcs.insert(participant.ssrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
for peerId in peerIds {
|
for ssrc in ssrcs {
|
||||||
if !existingParticipantIds.contains(peerId) {
|
if !existingSsrcs.contains(ssrc) {
|
||||||
missingPeerIds.insert(peerId)
|
missingSsrcs.insert(ssrc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !missingPeerIds.isEmpty {
|
if !missingSsrcs.isEmpty {
|
||||||
self.missingPeerIds.formUnion(missingPeerIds)
|
self.missingSsrcs.formUnion(missingSsrcs)
|
||||||
self.loadMissingPeers()
|
self.loadMissingSsrcs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadMissingPeers() {
|
private func loadMissingSsrcs() {
|
||||||
if self.missingPeerIds.isEmpty {
|
if self.missingSsrcs.isEmpty {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.isLoadingMore {
|
if self.isLoadingMore {
|
||||||
@ -978,16 +978,16 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
self.isLoadingMore = true
|
self.isLoadingMore = true
|
||||||
|
|
||||||
let peerIds = self.missingPeerIds
|
let ssrcs = self.missingSsrcs
|
||||||
|
|
||||||
self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", peerIds: Array(peerIds), limit: 100)
|
self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", ssrcs: Array(ssrcs), limit: 100)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.isLoadingMore = false
|
strongSelf.isLoadingMore = false
|
||||||
|
|
||||||
strongSelf.missingPeerIds.subtract(peerIds)
|
strongSelf.missingSsrcs.subtract(ssrcs)
|
||||||
|
|
||||||
var updatedState = strongSelf.stateValue.state
|
var updatedState = strongSelf.stateValue.state
|
||||||
|
|
||||||
@ -1013,7 +1013,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
if strongSelf.shouldResetStateFromServer {
|
if strongSelf.shouldResetStateFromServer {
|
||||||
strongSelf.resetStateFromServer()
|
strongSelf.resetStateFromServer()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.loadMissingPeers()
|
strongSelf.loadMissingSsrcs()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -1165,7 +1165,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
self.updateQueue.removeAll()
|
self.updateQueue.removeAll()
|
||||||
|
|
||||||
self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", peerIds: [], limit: 100)
|
self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", ssrcs: [], limit: 100)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1285,7 +1285,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
self.isLoadingMore = true
|
self.isLoadingMore = true
|
||||||
|
|
||||||
self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: token, peerIds: [], limit: 100)
|
self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: token, ssrcs: [], limit: 100)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
@ -34,12 +34,6 @@ extension CellularNetworkType {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum InternalNetworkType: Equatable {
|
|
||||||
case none
|
|
||||||
case wifi
|
|
||||||
case cellular
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum NetworkType: Equatable {
|
public enum NetworkType: Equatable {
|
||||||
case none
|
case none
|
||||||
case wifi
|
case wifi
|
||||||
@ -50,7 +44,7 @@ public enum NetworkType: Equatable {
|
|||||||
|
|
||||||
extension NetworkType {
|
extension NetworkType {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
init(internalType: InternalNetworkType, cellularType: CellularNetworkType) {
|
init(internalType: Reachability.NetworkType, cellularType: CellularNetworkType) {
|
||||||
switch internalType {
|
switch internalType {
|
||||||
case .none:
|
case .none:
|
||||||
self = .none
|
self = .none
|
||||||
@ -61,7 +55,7 @@ extension NetworkType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
init(internalType: InternalNetworkType) {
|
init(internalType: Reachability.NetworkType) {
|
||||||
switch internalType {
|
switch internalType {
|
||||||
case .none:
|
case .none:
|
||||||
self = .none
|
self = .none
|
||||||
@ -72,91 +66,11 @@ extension NetworkType {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class WrappedReachability: NSObject {
|
|
||||||
@objc private static func threadImpl() {
|
|
||||||
while true {
|
|
||||||
RunLoop.current.run(until: .distantFuture)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static let thread: Thread = {
|
|
||||||
let thread = Thread(target: WrappedReachability.self, selector: #selector(WrappedReachability.threadImpl), object: nil)
|
|
||||||
thread.start()
|
|
||||||
return thread
|
|
||||||
}()
|
|
||||||
|
|
||||||
@objc private static func dispatchOnThreadImpl(_ f: @escaping () -> Void) {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func dispatchOnThread(_ f: @escaping @convention(block) () -> Void) {
|
|
||||||
WrappedReachability.perform(#selector(WrappedReachability.dispatchOnThreadImpl(_:)), on: WrappedReachability.thread, with: f, waitUntilDone: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private let reachability: Reachability
|
|
||||||
|
|
||||||
let value: ValuePromise<InternalNetworkType>
|
|
||||||
|
|
||||||
override init() {
|
|
||||||
assert(Thread.current === WrappedReachability.thread)
|
|
||||||
self.reachability = Reachability.forInternetConnection()
|
|
||||||
let type: InternalNetworkType
|
|
||||||
switch self.reachability.currentReachabilityStatus() {
|
|
||||||
case NotReachable:
|
|
||||||
type = .none
|
|
||||||
case ReachableViaWiFi:
|
|
||||||
type = .wifi
|
|
||||||
case ReachableViaWWAN:
|
|
||||||
type = .cellular
|
|
||||||
default:
|
|
||||||
type = .none
|
|
||||||
}
|
|
||||||
self.value = ValuePromise<InternalNetworkType>(type)
|
|
||||||
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
self.reachability.reachabilityChanged = { [weak self] status in
|
|
||||||
WrappedReachability.dispatchOnThread {
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let internalNetworkType: InternalNetworkType
|
|
||||||
switch status {
|
|
||||||
case NotReachable:
|
|
||||||
internalNetworkType = .none
|
|
||||||
case ReachableViaWiFi:
|
|
||||||
internalNetworkType = .wifi
|
|
||||||
case ReachableViaWWAN:
|
|
||||||
internalNetworkType = .cellular
|
|
||||||
default:
|
|
||||||
internalNetworkType = .none
|
|
||||||
}
|
|
||||||
strongSelf.value.set(internalNetworkType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.reachability.startNotifier()
|
|
||||||
}
|
|
||||||
|
|
||||||
static var valueRef: Unmanaged<WrappedReachability>?
|
|
||||||
|
|
||||||
static func withInstance(_ f: @escaping (WrappedReachability) -> Void) {
|
|
||||||
WrappedReachability.dispatchOnThread {
|
|
||||||
if self.valueRef == nil {
|
|
||||||
self.valueRef = Unmanaged.passRetained(WrappedReachability())
|
|
||||||
}
|
|
||||||
if let valueRef = self.valueRef {
|
|
||||||
let value = valueRef.takeUnretainedValue()
|
|
||||||
f(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class NetworkTypeManagerImpl {
|
private final class NetworkTypeManagerImpl {
|
||||||
let queue: Queue
|
let queue: Queue
|
||||||
let updated: (NetworkType) -> Void
|
let updated: (NetworkType) -> Void
|
||||||
var networkTypeDisposable: Disposable?
|
var networkTypeDisposable: Disposable?
|
||||||
var currentNetworkType: InternalNetworkType?
|
var currentNetworkType: Reachability.NetworkType?
|
||||||
var networkType: NetworkType?
|
var networkType: NetworkType?
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
var currentCellularType: CellularNetworkType
|
var currentCellularType: CellularNetworkType
|
||||||
@ -196,29 +110,27 @@ private final class NetworkTypeManagerImpl {
|
|||||||
|
|
||||||
let networkTypeDisposable = MetaDisposable()
|
let networkTypeDisposable = MetaDisposable()
|
||||||
self.networkTypeDisposable = networkTypeDisposable
|
self.networkTypeDisposable = networkTypeDisposable
|
||||||
|
|
||||||
WrappedReachability.withInstance({ [weak self] impl in
|
networkTypeDisposable.set((Reachability.networkType
|
||||||
networkTypeDisposable.set((impl.value.get()
|
|> deliverOn(queue)).start(next: { [weak self] networkStatus in
|
||||||
|> deliverOn(queue)).start(next: { networkStatus in
|
guard let strongSelf = self else {
|
||||||
guard let strongSelf = self else {
|
return
|
||||||
return
|
}
|
||||||
|
if strongSelf.currentNetworkType != networkStatus {
|
||||||
|
strongSelf.currentNetworkType = networkStatus
|
||||||
|
|
||||||
|
let networkType: NetworkType
|
||||||
|
#if os(iOS)
|
||||||
|
networkType = NetworkType(internalType: networkStatus, cellularType: strongSelf.currentCellularType)
|
||||||
|
#else
|
||||||
|
networkType = NetworkType(internalType: networkStatus)
|
||||||
|
#endif
|
||||||
|
if strongSelf.networkType != networkType {
|
||||||
|
strongSelf.networkType = networkType
|
||||||
|
updated(networkType)
|
||||||
}
|
}
|
||||||
if strongSelf.currentNetworkType != networkStatus {
|
}
|
||||||
strongSelf.currentNetworkType = networkStatus
|
}))
|
||||||
|
|
||||||
let networkType: NetworkType
|
|
||||||
#if os(iOS)
|
|
||||||
networkType = NetworkType(internalType: networkStatus, cellularType: strongSelf.currentCellularType)
|
|
||||||
#else
|
|
||||||
networkType = NetworkType(internalType: networkStatus)
|
|
||||||
#endif
|
|
||||||
if strongSelf.networkType != networkType {
|
|
||||||
strongSelf.networkType = networkType
|
|
||||||
updated(networkType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -73,6 +73,32 @@ public struct ShareRootControllerInitializationData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func extractTextFileHeader(path: String) -> String? {
|
||||||
|
guard let file = ManagedFile(queue: nil, path: path, mode: .read) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let size = file.getSize() else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let limit = 3000
|
||||||
|
|
||||||
|
var data = file.readData(count: min(size, limit))
|
||||||
|
let additionalCapacity = min(10, max(0, size - data.count))
|
||||||
|
|
||||||
|
for alignment in 0 ... additionalCapacity {
|
||||||
|
if alignment != 0 {
|
||||||
|
data.append(file.readData(count: 1))
|
||||||
|
}
|
||||||
|
if let text = String(data: data, encoding: .utf8) {
|
||||||
|
return text
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
public class ShareRootControllerImpl {
|
public class ShareRootControllerImpl {
|
||||||
private let initializationData: ShareRootControllerInitializationData
|
private let initializationData: ShareRootControllerInitializationData
|
||||||
private let getExtensionContext: () -> NSExtensionContext?
|
private let getExtensionContext: () -> NSExtensionContext?
|
||||||
@ -523,11 +549,11 @@ public class ShareRootControllerImpl {
|
|||||||
return try? NSRegularExpression(pattern: string)
|
return try? NSRegularExpression(pattern: string)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let mainFileText = try? String(contentsOf: URL(fileURLWithPath: url.path)) {
|
if let mainFileTextHeader = extractTextFileHeader(path: url.path) {
|
||||||
let fullRange = NSRange(mainFileText.startIndex ..< mainFileText.endIndex, in: mainFileText)
|
let fullRange = NSRange(mainFileTextHeader.startIndex ..< mainFileTextHeader.endIndex, in: mainFileTextHeader)
|
||||||
var foundMatch = false
|
var foundMatch = false
|
||||||
for pattern in filePatterns {
|
for pattern in filePatterns {
|
||||||
if pattern.firstMatch(in: mainFileText, options: [], range: fullRange) != nil {
|
if pattern.firstMatch(in: mainFileTextHeader, options: [], range: fullRange) != nil {
|
||||||
foundMatch = true
|
foundMatch = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -550,14 +576,7 @@ public class ShareRootControllerImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let mainFile = mainFile, let mainFileText = try? String(contentsOf: URL(fileURLWithPath: mainFile.path)) {
|
if let mainFile = mainFile, let mainFileHeader = extractTextFileHeader(path :mainFile.path) {
|
||||||
let mainFileHeader: String
|
|
||||||
if mainFileText.count < 2000 {
|
|
||||||
mainFileHeader = mainFileText
|
|
||||||
} else {
|
|
||||||
mainFileHeader = String(mainFileText[mainFileText.startIndex ..< mainFileText.index(mainFileText.startIndex, offsetBy: 2000)])
|
|
||||||
}
|
|
||||||
|
|
||||||
final class TempController: ViewController {
|
final class TempController: ViewController {
|
||||||
override public var _presentedInModal: Bool {
|
override public var _presentedInModal: Bool {
|
||||||
get {
|
get {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user