mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '2485d6d99787b50e9d8b6fef0ca2768c930a9076' into experimental-2
This commit is contained in:
commit
8119f8c234
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.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**";
|
||||
|
@ -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',
|
||||
|
@ -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:
|
||||
|
@ -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: [])
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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>()
|
||||
|
@ -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) {
|
||||
|
@ -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 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.
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user