Merge commit '2485d6d99787b50e9d8b6fef0ca2768c930a9076' into experimental-2

This commit is contained in:
Ali 2021-02-01 15:39:02 +04:00
commit 8119f8c234
29 changed files with 4715 additions and 4321 deletions

139
.github/workflows/build.yml vendored Normal file
View 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

View File

@ -5959,6 +5959,7 @@ Sorry for the inconvenience.";
"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.ErrorGeneric" = "An error occurred.";
"ChatImportActivity.ErrorLimitExceeded" = "Daily maximum reached,\nplease come back tomorrow.";
"ChatImportActivity.Success" = "Chat imported\nsuccessfully.";
"VoiceOver.Chat.GoToOriginalMessage" = "Go to message";
@ -5978,3 +5979,13 @@ Sorry for the inconvenience.";
"VoiceOver.Chat.Profile" = "Profile";
"VoiceOver.Chat.GroupInfo" = "Group Info";
"VoiceOver.Chat.ChannelInfo" = "Channel Info";
"Conversation.ForwardTooltip.Chat.One" = "Message forwarded to **%@**";
"Conversation.ForwardTooltip.Chat.Many" = "Messages forwarded to **%@**";
"Conversation.ForwardTooltip.TwoChats.One" = "Message forwarded to **%@** and **%@**";
"Conversation.ForwardTooltip.TwoChats.Many" = "Messages forwarded to **%@** and **%@**";
"Conversation.ForwardTooltip.ManyChats.One" = "Message forwarded to **%@** and %@ others";
"Conversation.ForwardTooltip.ManyChats.Many" = "Messages forwarded to **%@** and %@ others";
"Conversation.ForwardTooltip.SavedMessages.One" = "Message forwarded to **Saved Messages**";
"Conversation.ForwardTooltip.SavedMessages.Many" = "Messages forwarded to **Saved Messages**";

View File

@ -12,7 +12,7 @@ from ProjectGeneration import generate
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(
base_path=os.getcwd(),
bazel_path=bazel_path,
@ -20,6 +20,7 @@ class BazelCommandLine:
override_bazel_version=override_bazel_version,
override_xcode_version=override_xcode_version
)
self.bazel_user_root = bazel_user_root
self.remote_cache = None
self.cache_dir = None
self.additional_args = None
@ -173,9 +174,18 @@ class BazelCommandLine:
else:
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):
combined_arguments = [
self.build_environment.bazel_path,
self.build_environment.bazel_path
]
combined_arguments += self.get_startup_bazel_arguments()
combined_arguments += [
'clean',
'--expunge'
]
@ -209,7 +219,10 @@ class BazelCommandLine:
def invoke_build(self):
combined_arguments = [
self.build_environment.bazel_path,
self.build_environment.bazel_path
]
combined_arguments += self.get_startup_bazel_arguments()
combined_arguments += [
'build',
'Telegram/Telegram'
]
@ -247,7 +260,8 @@ def clean(arguments):
bazel_path=arguments.bazel,
bazel_x86_64_path=None,
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()
@ -292,7 +306,8 @@ def generate_project(arguments):
bazel_path=arguments.bazel,
bazel_x86_64_path=bazel_x86_64_path,
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:
@ -326,7 +341,8 @@ def build(arguments):
bazel_path=arguments.bazel,
bazel_x86_64_path=None,
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:
@ -383,6 +399,13 @@ if __name__ == '__main__':
metavar='path'
)
parser.add_argument(
'--bazelUserRoot',
required=False,
help='Use custom bazel user root (useful when reproducing a build)',
metavar='path'
)
parser.add_argument(
'--overrideBazelVersion',
action='store_true',

View File

@ -57,6 +57,7 @@ private final class ImportManager {
case chatAdminRequired
case invalidChatType
case userBlocked
case limitExceeded
}
enum State {
@ -108,6 +109,11 @@ private final class ImportManager {
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))
|> mapError { error -> ImportError in
switch error {
@ -119,6 +125,8 @@ private final class ImportManager {
return .generic
case .userBlocked:
return .userBlocked
case .limitExceeded:
return .limitExceeded
}
}
|> deliverOnMainQueue).start(next: { [weak self] session in
@ -188,28 +196,37 @@ private final class ImportManager {
private func updateState() {
guard let session = self.session else {
Logger.shared.log("ChatImportScreen", "updateState called with no session, ignoring")
return
}
if self.pendingEntries.isEmpty && self.activeEntries.isEmpty {
Logger.shared.log("ChatImportScreen", "updateState called with no pending and no active entries, completing")
self.complete()
return
}
if case .error = self.stateValue {
Logger.shared.log("ChatImportScreen", "updateState called after error, ignoring")
return
}
guard let archivePath = self.archivePath else {
Logger.shared.log("ChatImportScreen", "updateState called with empty arhivePath, ignoring")
return
}
while true {
if self.activeEntries.count >= 3 {
Logger.shared.log("ChatImportScreen", "updateState concurrent processing limit reached, stop searching")
break
}
if self.pendingEntries.isEmpty {
Logger.shared.log("ChatImportScreen", "updateState no more pending entries, stop searching")
break
}
let entry = self.pendingEntries.removeFirst()
Logger.shared.log("ChatImportScreen", "updateState take pending entry \(entry.1)")
let unpackedFile = Signal<TempBoxFile, ImportError> { subscriber in
let tempFile = TempBox.shared.tempFile(fileName: entry.0.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 {
return
}
Logger.shared.log("ChatImportScreen", "updateState entry \(entry.1) has completed upload")
strongSelf.activeEntries.removeValue(forKey: entry.0.path)
strongSelf.updateState()
}))
@ -533,6 +551,8 @@ public final class ChatImportActivityScreen: ViewController {
errorText = self.presentationData.strings.ChatImportActivity_ErrorGeneric
case .userBlocked:
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)
case .done:

View File

@ -29,6 +29,7 @@ import GalleryData
import InstantPageUI
import ChatInterfaceState
import ShareController
import UndoUI
private enum ChatListTokenId: Int32 {
case filter
@ -899,6 +900,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let peerId = peer.id
if let strongSelf = self, let _ = peerSelectionController {
if peerId == strongSelf.context.account.peerId {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
(strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in
return .forward(source: id, grouping: .auto, attributes: [])
})

View File

@ -7,6 +7,22 @@ struct KeyboardSurface {
let host: UIView
}
public extension UIResponder {
private struct Static {
static weak var responder: UIResponder?
}
static func currentFirst() -> UIResponder? {
Static.responder = nil
UIApplication.shared.sendAction(#selector(UIResponder._trap), to: nil, from: nil, for: nil)
return Static.responder
}
@objc private func _trap() {
Static.responder = self
}
}
private func getFirstResponder(_ view: UIView) -> UIView? {
if view.isFirstResponder {
return view

View File

@ -218,11 +218,7 @@ public final class InviteLinkInviteController: ViewController {
override public func loadDisplayNode() {
self.displayNode = Node(context: self.context, peerId: self.peerId, controller: self)
}
override public func loadView() {
super.loadView()
}
private var didAppearOnce: Bool = false
private var isDismissed: Bool = false
public override func viewDidAppear(_ animated: Bool) {

View File

@ -850,7 +850,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
let revokeLinkDisposable = MetaDisposable()
actionsDisposable.add(revokeLinkDisposable)
let arguments = ChannelVisibilityControllerArguments(context: context, updateCurrentType: { type in
updateState { state in
return state.withUpdatedSelectedType(type)

View File

@ -12,7 +12,7 @@ private final class WrappedLegacyReachability: NSObject {
}
private static let thread: Thread = {
let thread = Thread(target: Reachability.self, selector: #selector(WrappedLegacyReachability.threadImpl), object: nil)
let thread = Thread(target: WrappedLegacyReachability.self, selector: #selector(WrappedLegacyReachability.threadImpl), object: nil)
thread.start()
return thread
}()
@ -84,7 +84,7 @@ private final class WrappedLegacyReachability: NSObject {
}
}
@available(iOSApplicationExtension 12.0, iOS 12.0, *)
@available(iOSApplicationExtension 12.0, iOS 12.0, OSX 10.14, *)
private final class PathMonitor {
private let queue: Queue
private let monitor: NWPathMonitor
@ -133,7 +133,7 @@ private final class PathMonitor {
}
}
@available(iOSApplicationExtension 12.0, iOS 12.0, *)
@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: {
@ -149,16 +149,16 @@ public enum Reachability {
}
public static var networkType: Signal<NetworkType, NoError> {
if #available(iOSApplicationExtension 12.0, iOS 12.0, *) {
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

View File

@ -305,6 +305,13 @@ public final class ShareController: ViewController {
private var defaultAction: ShareControllerAction?
public var dismissed: ((Bool) -> Void)?
public var completed: (([PeerId]) -> Void)? {
didSet {
if self.isNodeLoaded {
self.controllerNode.completed = completed
}
}
}
public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, openStats: (() -> Void)? = nil, fromForeignApp: Bool = false, shares: Int? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, forcedTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil) {
self.init(sharedContext: context.sharedContext, currentContext: context, subject: subject, presetText: presetText, preferredAction: preferredAction, showInChat: showInChat, openStats: openStats, fromForeignApp: fromForeignApp, shares: shares, externalShare: externalShare, immediateExternalShare: immediateExternalShare, switchableAccounts: switchableAccounts, immediatePeerId: immediatePeerId, forcedTheme: forcedTheme, forcedActionTitle: forcedActionTitle)
@ -451,6 +458,7 @@ public final class ShareController: ViewController {
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, shares: self.shares, fromForeignApp: self.fromForeignApp, forcedTheme: self.forcedTheme)
self.controllerNode.completed = completed
self.controllerNode.dismiss = { [weak self] shared in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
self?.dismissed?(shared)
@ -466,6 +474,7 @@ public final class ShareController: ViewController {
guard let strongSelf = self else {
return .complete()
}
var shareSignals: [Signal<[MessageId?], NoError>] = []
switch strongSelf.subject {
case let .url(url):

View File

@ -65,6 +65,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
var shareExternal: (() -> Signal<ShareExternalState, NoError>)?
var switchToAnotherAccount: (() -> Void)?
var openStats: (() -> Void)?
var completed: (([PeerId]) -> Void)?
let ready = Promise<Bool>()
private var didSetReady = false
@ -585,6 +586,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
Queue.mainQueue().after(delay, {
self?.animateOut(shared: true, completion: {
self?.dismiss?(true)
self?.completed?(peerIds)
})
})
}

View File

@ -977,6 +977,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
var result: [(PeerId, UInt32, Float, Bool)] = []
var myLevel: Float = 0.0
var myLevelHasVoice: Bool = false
var missingSsrcs = Set<UInt32>()
for (ssrcKey, level, hasVoice) in levels {
var peerId: PeerId?
let ssrcValue: UInt32
@ -996,6 +997,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
result.append((peerId, ssrcValue, level, hasVoice))
} else if ssrcValue != 0 {
missingSsrcs.insert(ssrcValue)
}
}
@ -1004,6 +1007,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let mappedLevel = myLevel * 1.5
strongSelf.myAudioLevelPipe.putNext(mappedLevel)
strongSelf.processMyAudioLevel(level: mappedLevel, hasVoice: myLevelHasVoice)
if !missingSsrcs.isEmpty {
strongSelf.participantsContext?.ensureHaveParticipants(ssrcs: missingSsrcs)
}
}))
}
}

View File

@ -1299,7 +1299,7 @@ public final class VoiceChatController: ViewController {
if !strongSelf.didSetDataReady {
strongSelf.accountPeer = accountPeer
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? ([], nil), invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set())
strongSelf.didSetDataReady = true
strongSelf.controller?.dataReady.set(true)
}
@ -1490,7 +1490,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else {
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 {
strongSelf.currentLoadToken = loadMoreToken
strongSelf.call.loadMoreMembers(token: loadMoreToken)

View File

@ -167,7 +167,10 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
case .broadcast:
break
case .group:
let infoFlags = TelegramChannelGroupFlags()
var infoFlags = TelegramChannelGroupFlags()
if (flags & Int32(1 << 22)) != 0 {
infoFlags.insert(.slowModeEnabled)
}
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
}

View File

@ -16,6 +16,7 @@ public enum ChatHistoryImport {
case chatAdminRequired
case invalidChatType
case userBlocked
case limitExceeded
}
public enum ParsedInfo {
@ -64,16 +65,17 @@ public enum ChatHistoryImport {
guard let inputPeer = inputPeer else {
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
switch error.errorDescription {
case "CHAT_ADMIN_REQUIRED":
if error.errorDescription == "CHAT_ADMIN_REQUIRED" {
return .chatAdminRequired
case "IMPORT_PEER_TYPE_INVALID":
} else if error.errorDescription == "IMPORT_PEER_TYPE_INVALID" {
return .invalidChatType
case "USER_IS_BLOCKED":
} else if error.errorDescription == "USER_IS_BLOCKED" {
return .userBlocked
default:
} else if error.errorDescription == "FLOOD_WAIT" {
return .limitExceeded
} else {
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> {
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
}

View File

@ -989,7 +989,7 @@ public final class GroupCallParticipantsContext {
self.ensureHaveParticipants(ssrcs: Set(ids.map { $0.1 }))
}
private func ensureHaveParticipants(ssrcs: Set<UInt32>) {
public func ensureHaveParticipants(ssrcs: Set<UInt32>) {
var missingSsrcs = Set<UInt32>()
var existingSsrcs = Set<UInt32>()

View File

@ -6,6 +6,66 @@ import MtProtoKit
import SyncCore
public func ensuredExistingPeerExportedInvitation(account: Account, peerId: PeerId, revokeExisted: Bool = false) -> Signal<ExportedInvitation?, NoError> {
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, NoError> in
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
if let _ = peer as? TelegramChannel {
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, cachedData.exportedInvitation != nil && !revokeExisted {
return .single(cachedData.exportedInvitation)
} else {
return account.network.request(Api.functions.messages.exportChatInvite(peer: inputPeer))
|> retryRequest
|> mapToSignal { result -> Signal<ExportedInvitation?, NoError> in
return account.postbox.transaction { transaction -> ExportedInvitation? in
if let invitation = ExportedInvitation(apiExportedInvite: result) {
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedChannelData {
return current.withUpdatedExportedInvitation(invitation)
} else {
return CachedChannelData().withUpdatedExportedInvitation(invitation)
}
})
return invitation
} else {
return nil
}
}
}
}
} else if let _ = peer as? TelegramGroup {
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData, cachedData.exportedInvitation != nil && !revokeExisted {
return .single(cachedData.exportedInvitation)
} else {
return account.network.request(Api.functions.messages.exportChatInvite(peer: inputPeer))
|> retryRequest
|> mapToSignal { result -> Signal<ExportedInvitation?, NoError> in
return account.postbox.transaction { transaction -> ExportedInvitation? in
if let invitation = ExportedInvitation(apiExportedInvite: result) {
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedGroupData {
return current.withUpdatedExportedInvitation(invitation)
} else {
return current
}
})
return invitation
} else {
return nil
}
}
}
}
} else {
return .complete()
}
} else {
return .complete()
}
} |> switchToLatest
}
public func revokePersistentPeerExportedInvitation(account: Account, peerId: PeerId) -> Signal<ExportedInvitation?, NoError> {
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, NoError> in
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {

View File

@ -84,7 +84,7 @@ public extension TelegramChannel {
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banAddMembers) {
return false
}
return false
return true
}
case .editAllMessages:
if let adminRights = self.adminRights, adminRights.flags.contains(.canEditMessages) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1413,11 +1413,51 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, openMessageShareMenu: { [weak self] id in
if let strongSelf = self, let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(id), let _ = messages.first {
let shareController = ShareController(context: strongSelf.context, subject: .messages(messages))
shareController.dismissed = { shared in
shareController.dismissed = { [weak self] shared in
if shared {
self?.commitPurposefulAction()
}
}
shareController.completed = { [weak self] peerIds in
if let strongSelf = self {
let _ = (strongSelf.context.account.postbox.transaction { transaction -> [Peer] in
var peers: [Peer] = []
for peerId in peerIds {
if let peer = transaction.getPeer(peerId) {
peers.append(peer)
}
}
return peers
} |> deliverOnMainQueue).start(next: { [weak self] peers in
if let strongSelf = self {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many
savedMessages = true
} else {
if peers.count == 1, let peer = peers.first {
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).0 : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).0
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).0 : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).0
} else if let peer = peers.first {
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").0 : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").0
} else {
text = ""
}
}
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
})
}
}
strongSelf.chatDisplayNode.dismissInput()
strongSelf.present(shareController, in: .window(.root), blockInteraction: true)
}
@ -10041,6 +10081,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.searchResultsController = nil
strongController.dismiss()
} else if peerId == strongSelf.context.account.peerId {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messages.map { message -> EnqueueMessage in
return .forward(source: message.id, grouping: .auto, attributes: [])
})
@ -11262,7 +11305,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
]
} else {
} else if UIResponder.currentFirst() == nil {
inputShortcuts = [
KeyShortcut(title: strings.KeyCommand_FocusOnInputField, input: "\r", action: { [weak self] in
if let strongSelf = self {
@ -11316,6 +11359,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
]
} else {
inputShortcuts = []
}
var canEdit = false

View File

@ -54,6 +54,7 @@ import GalleryData
import ChatInterfaceState
import TelegramVoip
import InviteLinksUI
import UndoUI
protocol PeerInfoScreenItem: class {
var id: AnyHashable { get }
@ -4689,6 +4690,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if let strongSelf = self, let _ = peerSelectionController {
if peerId == strongSelf.context.account.peerId {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in

View File

@ -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 {
private let initializationData: ShareRootControllerInitializationData
private let getExtensionContext: () -> NSExtensionContext?
@ -523,11 +549,11 @@ public class ShareRootControllerImpl {
return try? NSRegularExpression(pattern: string)
}
if let mainFileText = try? String(contentsOf: URL(fileURLWithPath: url.path)) {
let fullRange = NSRange(mainFileText.startIndex ..< mainFileText.endIndex, in: mainFileText)
if let mainFileTextHeader = extractTextFileHeader(path: url.path) {
let fullRange = NSRange(mainFileTextHeader.startIndex ..< mainFileTextHeader.endIndex, in: mainFileTextHeader)
var foundMatch = false
for pattern in filePatterns {
if pattern.firstMatch(in: mainFileText, options: [], range: fullRange) != nil {
if pattern.firstMatch(in: mainFileTextHeader, options: [], range: fullRange) != nil {
foundMatch = true
break
}
@ -550,14 +576,7 @@ public class ShareRootControllerImpl {
}
}
if let mainFile = mainFile, let mainFileText = try? String(contentsOf: URL(fileURLWithPath: mainFile.path)) {
let mainFileHeader: String
if mainFileText.count < 2000 {
mainFileHeader = mainFileText
} else {
mainFileHeader = String(mainFileText[mainFileText.startIndex ..< mainFileText.index(mainFileText.startIndex, offsetBy: 2000)])
}
if let mainFile = mainFile, let mainFileHeader = extractTextFileHeader(path :mainFile.path) {
final class TempController: ViewController {
override public var _presentedInModal: Bool {
get {

View File

@ -128,14 +128,6 @@ public final class TelegramRootController: NavigationController {
self.accountSettingsController = accountSettingsController
self.rootTabController = tabBarController
self.pushViewController(tabBarController, animated: false)
Queue.mainQueue().after(1.0) {
let datePicker = DatePickerNode(theme: DatePickerTheme(backgroundColor: .white, textColor: .black, secondaryTextColor: .gray, accentColor: .blue, disabledColor: .lightGray, selectionColor: .blue, selectedCurrentTextColor: .white, secondarySelectionColor: .cyan), strings: self.context.sharedContext.currentPresentationData.with { $0 }.strings)
let frame = CGRect(origin: CGPoint(x: 50.0, y: 100.0), size: CGSize(width: 300.0, height: 300.0))
datePicker.updateLayout(size: frame.size, transition: .immediate)
self.rootTabController?.displayNode.addSubnode(datePicker)
}
}
public func updateRootControllers(showCallsTab: Bool) {

View File

@ -174,7 +174,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
updateStatus(self.status)
let html = String(format: htmlTemplate, paramsJson)
webView.loadHTMLString(html, baseURL: URL(string: "https://messenger.telegram.org"))
webView.loadHTMLString(html, baseURL: URL(string: "https://telegram.youtube.com"))
// webView.isUserInteractionEnabled = false
userContentController.addUserScript(WKUserScript(source: userScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false))

View File

@ -28,6 +28,7 @@ public enum UndoOverlayContent {
case banned(text: String)
case importedMessage(text: String)
case audioRate(slowdown: Bool, text: String)
case forward(savedMessages: Bool, text: String)
}
public enum UndoOverlayAction {

View File

@ -492,6 +492,21 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2
displayUndo = false
self.originalRemainingSeconds = 3
case let .forward(savedMessages, text):
self.avatarNode = nil
self.iconNode = nil
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: savedMessages ? "anim_savedmessages" : "anim_forward", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2
displayUndo = false
self.originalRemainingSeconds = 3
}
@ -522,7 +537,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
switch content {
case .removedChat:
self.panelWrapperNode.addSubnode(self.timerTextNode)
case .archivedChat, .hidArchive, .revealedArchive, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate:
case .archivedChat, .hidArchive, .revealedArchive, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward:
break
case .dice:
self.panelWrapperNode.clipsToBounds = true