mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-01 07:57:01 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
b78817526e
@ -11,25 +11,26 @@ def is_apple_silicon():
|
||||
return False
|
||||
|
||||
|
||||
def get_clean_env():
|
||||
def get_clean_env(use_clean_env=True):
|
||||
clean_env = os.environ.copy()
|
||||
if use_clean_env:
|
||||
clean_env['PATH'] = '/usr/bin:/bin:/usr/sbin:/sbin'
|
||||
return clean_env
|
||||
|
||||
|
||||
def resolve_executable(program):
|
||||
def resolve_executable(program, use_clean_env=True):
|
||||
def is_executable(fpath):
|
||||
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
||||
|
||||
for path in get_clean_env()["PATH"].split(os.pathsep):
|
||||
for path in get_clean_env(use_clean_env=use_clean_env)["PATH"].split(os.pathsep):
|
||||
executable_file = os.path.join(path, program)
|
||||
if is_executable(executable_file):
|
||||
return executable_file
|
||||
return None
|
||||
|
||||
|
||||
def run_executable_with_output(path, arguments, decode=True, input=None, stderr_to_stdout=True, print_command=False, check_result=False):
|
||||
executable_path = resolve_executable(path)
|
||||
def run_executable_with_output(path, arguments, use_clean_env=True, decode=True, input=None, stderr_to_stdout=True, print_command=False, check_result=False):
|
||||
executable_path = resolve_executable(path, use_clean_env=use_clean_env)
|
||||
if executable_path is None:
|
||||
raise Exception('Could not resolve {} to a valid executable file'.format(path))
|
||||
|
||||
@ -45,7 +46,7 @@ def run_executable_with_output(path, arguments, decode=True, input=None, stderr_
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=stderr_assignment,
|
||||
stdin=subprocess.PIPE,
|
||||
env=get_clean_env()
|
||||
env=get_clean_env(use_clean_env=use_clean_env)
|
||||
)
|
||||
if input is not None:
|
||||
output_data, _ = process.communicate(input=input)
|
||||
|
@ -2,8 +2,9 @@ import os
|
||||
import sys
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
|
||||
from BuildEnvironment import check_run_system
|
||||
from BuildEnvironment import run_executable_with_output
|
||||
|
||||
def deploy_to_firebase(args):
|
||||
if not os.path.exists(args.configuration):
|
||||
@ -27,17 +28,25 @@ def deploy_to_firebase(args):
|
||||
print('Configuration at {} does not contain {}'.format(args.configuration, key))
|
||||
sys.exit(1)
|
||||
|
||||
debug_flag = "--debug" if args.debug else ""
|
||||
command = 'firebase appdistribution:distribute --app {app_id} --groups "{group}" {debug_flag}'.format(
|
||||
app_id=configuration_dict['app_id'],
|
||||
group=configuration_dict['group'],
|
||||
debug_flag=debug_flag
|
||||
firebase_arguments = [
|
||||
'appdistribution:distribute',
|
||||
'--app', configuration_dict['app_id'],
|
||||
'--groups', configuration_dict['group'],
|
||||
args.ipa
|
||||
]
|
||||
|
||||
output = run_executable_with_output(
|
||||
'firebase',
|
||||
firebase_arguments,
|
||||
use_clean_env=False,
|
||||
check_result=True
|
||||
)
|
||||
|
||||
command += ' "{ipa_path}"'.format(ipa_path=args.ipa)
|
||||
|
||||
check_run_system(command)
|
||||
|
||||
sharing_link_match = re.search(r'Share this release with testers who have access: (https://\S+)', output)
|
||||
if sharing_link_match:
|
||||
print(f"Sharing link: {sharing_link_match.group(1)}")
|
||||
else:
|
||||
print("No sharing link found in the output.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(prog='deploy-firebase')
|
||||
|
@ -423,6 +423,7 @@ public protocol PresentationGroupCall: AnyObject {
|
||||
var internalId: CallSessionInternalId { get }
|
||||
var peerId: EnginePeer.Id? { get }
|
||||
var callId: Int64? { get }
|
||||
var currentReference: InternalGroupCallReference? { get }
|
||||
|
||||
var hasVideo: Bool { get }
|
||||
var hasScreencast: Bool { get }
|
||||
|
@ -138,16 +138,7 @@ class CallListCallItem: ListViewItem {
|
||||
|
||||
func selected(listView: ListView) {
|
||||
listView.clearHighlightAnimated(true)
|
||||
var isVideo = false
|
||||
for media in self.topMessage.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
||||
isVideo = isVideoValue
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
self.interaction.call(self.topMessage.id.peerId, isVideo)
|
||||
self.interaction.call(self.topMessage)
|
||||
}
|
||||
|
||||
static func mergeType(item: CallListCallItem, previousItem: ListViewItem?, nextItem: ListViewItem?) -> (first: Bool, last: Bool, firstWithHeader: Bool) {
|
||||
@ -262,15 +253,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
guard let item = self?.layoutParams?.0 else {
|
||||
return false
|
||||
}
|
||||
var isVideo = false
|
||||
for media in item.topMessage.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
||||
isVideo = isVideoValue
|
||||
}
|
||||
}
|
||||
}
|
||||
item.interaction.call(item.topMessage.id.peerId, isVideo)
|
||||
item.interaction.call(item.topMessage)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -390,6 +373,9 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
var hadDuration = false
|
||||
var callDuration: Int32?
|
||||
|
||||
var isConference = false
|
||||
var conferenceIsDeclined = false
|
||||
|
||||
for message in item.messages {
|
||||
inner: for media in message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
@ -411,6 +397,36 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
callDuration = nil
|
||||
}
|
||||
} else if case let .conferenceCall(conferenceCall) = action.action {
|
||||
isConference = true
|
||||
|
||||
isVideo = conferenceCall.flags.contains(.isVideo)
|
||||
if message.flags.contains(.Incoming) {
|
||||
hasIncoming = true
|
||||
//TODO:localize
|
||||
let missedTimeout: Int32
|
||||
#if DEBUG
|
||||
missedTimeout = 5
|
||||
#else
|
||||
missedTimeout = 30
|
||||
#endif
|
||||
let currentTime = Int32(Date().timeIntervalSince1970)
|
||||
if conferenceCall.flags.contains(.isMissed) {
|
||||
titleColor = item.presentationData.theme.list.itemDestructiveColor
|
||||
conferenceIsDeclined = true
|
||||
} else if message.timestamp < currentTime - missedTimeout {
|
||||
titleColor = item.presentationData.theme.list.itemDestructiveColor
|
||||
hasMissed = true
|
||||
}
|
||||
} else {
|
||||
hasOutgoing = true
|
||||
}
|
||||
if callDuration == nil && !hadDuration {
|
||||
hadDuration = true
|
||||
callDuration = conferenceCall.duration
|
||||
} else {
|
||||
callDuration = nil
|
||||
}
|
||||
}
|
||||
break inner
|
||||
}
|
||||
@ -441,7 +457,18 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
titleAttributedString = NSAttributedString(string: channel.title, font: titleFont, textColor: titleColor)
|
||||
}
|
||||
|
||||
if hasMissed {
|
||||
if isConference {
|
||||
//TODO:localize
|
||||
if conferenceIsDeclined {
|
||||
statusAttributedString = NSAttributedString(string: "Declined Group Call", font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
} else if hasMissed {
|
||||
statusAttributedString = NSAttributedString(string: "Missed Group Call", font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
} else {
|
||||
statusAttributedString = NSAttributedString(string: "Group call", font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
}
|
||||
|
||||
statusAccessibilityString = statusAttributedString?.string ?? ""
|
||||
} else if hasMissed {
|
||||
statusAttributedString = NSAttributedString(string: item.presentationData.strings.Notification_CallMissedShort, font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
statusAccessibilityString = isVideo ? item.presentationData.strings.Call_VoiceOver_VideoCallMissed : item.presentationData.strings.Call_VoiceOver_VoiceCallMissed
|
||||
} else if hasIncoming && hasOutgoing {
|
||||
|
@ -92,6 +92,7 @@ public final class CallListController: TelegramBaseController {
|
||||
|
||||
private let createActionDisposable = MetaDisposable()
|
||||
private let clearDisposable = MetaDisposable()
|
||||
private var createConferenceCallDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, mode: CallListControllerMode) {
|
||||
self.context = context
|
||||
@ -163,6 +164,7 @@ public final class CallListController: TelegramBaseController {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
self.peerViewDisposable.dispose()
|
||||
self.clearDisposable.dispose()
|
||||
self.createConferenceCallDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func updateThemeAndStrings() {
|
||||
@ -210,11 +212,16 @@ public final class CallListController: TelegramBaseController {
|
||||
guard !self.presentAccountFrozenInfoIfNeeded() else {
|
||||
return
|
||||
}
|
||||
let _ = (self.context.engine.calls.createConferenceCall()
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] call in
|
||||
if self.createConferenceCallDisposable != nil {
|
||||
return
|
||||
}
|
||||
self.createConferenceCallDisposable = (self.context.engine.calls.createConferenceCall()
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] call in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.createConferenceCallDisposable?.dispose()
|
||||
self.createConferenceCallDisposable = nil
|
||||
|
||||
let openCall: () -> Void = { [weak self] in
|
||||
guard let self else {
|
||||
@ -235,7 +242,13 @@ public final class CallListController: TelegramBaseController {
|
||||
)
|
||||
}
|
||||
|
||||
let controller = InviteLinkInviteController(context: self.context, updatedPresentationData: nil, mode: .groupCall(link: call.link, isRecentlyCreated: true), parentNavigationController: self.navigationController as? NavigationController, completed: { [weak self] result in
|
||||
let controller = InviteLinkInviteController(
|
||||
context: self.context,
|
||||
updatedPresentationData: nil,
|
||||
mode: .groupCall(InviteLinkInviteController.Mode.GroupCall(callId: call.callInfo.id, accessHash: call.callInfo.accessHash, isRecentlyCreated: true, canRevoke: true)),
|
||||
initialInvite: .link(link: call.link, title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: self.context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil),
|
||||
parentNavigationController: self.navigationController as? NavigationController,
|
||||
completed: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -254,15 +267,26 @@ public final class CallListController: TelegramBaseController {
|
||||
openCall()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
self.present(controller, in: .window(.root), with: nil)
|
||||
})
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = CallListControllerNode(controller: self, context: self.context, mode: self.mode, presentationData: self.presentationData, call: { [weak self] peerId, isVideo in
|
||||
if let strongSelf = self {
|
||||
strongSelf.call(peerId, isVideo: isVideo)
|
||||
self.displayNode = CallListControllerNode(controller: self, context: self.context, mode: self.mode, presentationData: self.presentationData, call: { [weak self] message in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if case let .phoneCall(_, _, _, isVideo) = action.action {
|
||||
self.call(message.id.peerId, isVideo: isVideo)
|
||||
} else if case .conferenceCall = action.action {
|
||||
self.openGroupCall(message: message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, joinGroupCall: { [weak self] peerId, activeCall in
|
||||
if let self {
|
||||
@ -573,6 +597,48 @@ public final class CallListController: TelegramBaseController {
|
||||
}))
|
||||
}
|
||||
|
||||
private func openGroupCall(message: EngineMessage) {
|
||||
var action: TelegramMediaAction?
|
||||
for media in message.media {
|
||||
if let media = media as? TelegramMediaAction {
|
||||
action = media
|
||||
break
|
||||
}
|
||||
}
|
||||
guard case let .conferenceCall(conferenceCall) = action?.action else {
|
||||
return
|
||||
}
|
||||
if conferenceCall.duration != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if let currentGroupCallController = self.context.sharedContext as? VoiceChatController, case let .group(groupCall) = currentGroupCallController.call, let currentCallId = groupCall.callId, currentCallId == conferenceCall.callId {
|
||||
self.context.sharedContext.navigateToCurrentCall()
|
||||
return
|
||||
}
|
||||
|
||||
let signal = self.context.engine.peers.joinCallInvitationInformation(messageId: message.id)
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] resolvedCallLink in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.context.sharedContext.callManager?.joinConferenceCall(
|
||||
accountContext: self.context,
|
||||
initialCall: EngineGroupCallDescription(
|
||||
id: resolvedCallLink.id,
|
||||
accessHash: resolvedCallLink.accessHash,
|
||||
title: nil,
|
||||
scheduleTimestamp: nil,
|
||||
subscribedToScheduled: false,
|
||||
isStream: false
|
||||
),
|
||||
reference: .message(id: message.id),
|
||||
beginWithVideo: conferenceCall.flags.contains(.isVideo)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) {
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Calls_StartNewCall, icon: { theme in
|
||||
|
@ -62,14 +62,14 @@ private extension EngineCallList.Item {
|
||||
|
||||
final class CallListNodeInteraction {
|
||||
let setMessageIdWithRevealedOptions: (EngineMessage.Id?, EngineMessage.Id?) -> Void
|
||||
let call: (EnginePeer.Id, Bool) -> Void
|
||||
let call: (EngineMessage) -> Void
|
||||
let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void
|
||||
let delete: ([EngineMessage.Id]) -> Void
|
||||
let updateShowCallsTab: (Bool) -> Void
|
||||
let openGroupCall: (EnginePeer.Id) -> Void
|
||||
let createGroupCall: () -> Void
|
||||
|
||||
init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EnginePeer.Id, Bool) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void, createGroupCall: @escaping () -> Void) {
|
||||
init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EngineMessage) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void, createGroupCall: @escaping () -> Void) {
|
||||
self.setMessageIdWithRevealedOptions = setMessageIdWithRevealedOptions
|
||||
self.call = call
|
||||
self.openInfo = openInfo
|
||||
@ -222,7 +222,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
private let emptyButtonIconNode: ASImageNode
|
||||
private let emptyButtonTextNode: ImmediateTextNode
|
||||
|
||||
private let call: (EnginePeer.Id, Bool) -> Void
|
||||
private let call: (EngineMessage) -> Void
|
||||
private let joinGroupCall: (EnginePeer.Id, EngineGroupCallDescription) -> Void
|
||||
private let createGroupCall: () -> Void
|
||||
private let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void
|
||||
@ -234,7 +234,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
|
||||
private var previousContentOffset: ListViewVisibleContentOffset?
|
||||
|
||||
init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EnginePeer.Id, Bool) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, createGroupCall: @escaping () -> Void) {
|
||||
init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EngineMessage) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, createGroupCall: @escaping () -> Void) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
@ -333,8 +333,8 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, call: { [weak self] peerId, isVideo in
|
||||
self?.call(peerId, isVideo)
|
||||
}, call: { [weak self] message in
|
||||
self?.call(message)
|
||||
}, openInfo: { [weak self] peerId, messages in
|
||||
self?.openInfo(peerId, messages)
|
||||
}, delete: { [weak self] messageIds in
|
||||
@ -519,10 +519,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
|
||||
let canCreateGroupCall = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App())
|
||||
|> map { configuration -> Bool in
|
||||
var isConferencePossible = false
|
||||
if context.sharedContext.immediateExperimentalUISettings.conferenceDebug {
|
||||
isConferencePossible = true
|
||||
}
|
||||
var isConferencePossible = true
|
||||
if let data = configuration.data, let value = data["ios_enable_conference"] as? Double {
|
||||
isConferencePossible = value != 0.0
|
||||
}
|
||||
|
@ -100,7 +100,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case enableReactionOverrides(Bool)
|
||||
case compressedEmojiCache(Bool)
|
||||
case storiesJpegExperiment(Bool)
|
||||
case conferenceDebug(Bool)
|
||||
case checkSerializedData(Bool)
|
||||
case enableQuickReactionSwitch(Bool)
|
||||
case disableReloginTokens(Bool)
|
||||
@ -134,7 +133,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.web.rawValue
|
||||
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .conferenceDebug, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .fakeAds, .enableLocalTranslation:
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .fakeAds, .enableLocalTranslation:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .logTranslationRecognition, .resetTranslationStates:
|
||||
return DebugControllerSection.translation.rawValue
|
||||
@ -243,8 +242,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 47
|
||||
case .disableReloginTokens:
|
||||
return 48
|
||||
case .conferenceDebug:
|
||||
return 49
|
||||
case .checkSerializedData:
|
||||
return 50
|
||||
case .enableQuickReactionSwitch:
|
||||
@ -1311,16 +1308,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .conferenceDebug(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Conference Debug", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
settings.conferenceDebug = value
|
||||
return PreferencesEntry(settings)
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .checkSerializedData(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Check Serialized Data", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
@ -1552,7 +1539,6 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.storiesJpegExperiment(experimentalSettings.storiesJpegExperiment))
|
||||
entries.append(.disableReloginTokens(experimentalSettings.disableReloginTokens))
|
||||
|
||||
entries.append(.conferenceDebug(experimentalSettings.conferenceDebug))
|
||||
entries.append(.checkSerializedData(experimentalSettings.checkSerializedData))
|
||||
entries.append(.enableQuickReactionSwitch(!experimentalSettings.disableQuickReaction))
|
||||
entries.append(.liveStreamV2(experimentalSettings.liveStreamV2))
|
||||
|
@ -154,14 +154,32 @@ private func preparedTransition(from fromEntries: [InviteLinkInviteEntry], to to
|
||||
return InviteLinkInviteTransaction(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading)
|
||||
}
|
||||
|
||||
private func getBackgroundColor(theme: PresentationTheme) -> UIColor {
|
||||
return theme.actionSheet.opaqueItemBackgroundColor
|
||||
}
|
||||
|
||||
public final class InviteLinkInviteController: ViewController {
|
||||
private var controllerNode: Node {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
public struct GroupCall {
|
||||
public let callId: Int64
|
||||
public let accessHash: Int64
|
||||
public let isRecentlyCreated: Bool
|
||||
public let canRevoke: Bool
|
||||
|
||||
public init(callId: Int64, accessHash: Int64, isRecentlyCreated: Bool, canRevoke: Bool) {
|
||||
self.callId = callId
|
||||
self.accessHash = accessHash
|
||||
self.isRecentlyCreated = isRecentlyCreated
|
||||
self.canRevoke = canRevoke
|
||||
}
|
||||
}
|
||||
|
||||
case groupOrChannel(peerId: EnginePeer.Id)
|
||||
case groupCall(link: String, isRecentlyCreated: Bool)
|
||||
case groupCall(GroupCall)
|
||||
}
|
||||
|
||||
public enum CompletionResult {
|
||||
@ -173,6 +191,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
|
||||
private let context: AccountContext
|
||||
private let mode: Mode
|
||||
private let initialInvite: ExportedInvitation?
|
||||
private weak var parentNavigationController: NavigationController?
|
||||
|
||||
private var presentationData: PresentationData
|
||||
@ -180,9 +199,10 @@ public final class InviteLinkInviteController: ViewController {
|
||||
|
||||
fileprivate let completed: ((CompletionResult?) -> Void)?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: Mode, parentNavigationController: NavigationController?, completed: ((CompletionResult?) -> Void)? = nil) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: Mode, initialInvite: ExportedInvitation?, parentNavigationController: NavigationController?, completed: ((CompletionResult?) -> Void)? = nil) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.initialInvite = initialInvite
|
||||
self.parentNavigationController = parentNavigationController
|
||||
self.completed = completed
|
||||
|
||||
@ -215,7 +235,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = Node(context: self.context, presentationData: self.presentationData, mode: self.mode, controller: self)
|
||||
self.displayNode = Node(context: self.context, presentationData: self.presentationData, mode: self.mode, controller: self, initialInvite: self.initialInvite)
|
||||
}
|
||||
|
||||
private var didAppearOnce: Bool = false
|
||||
@ -296,7 +316,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
|
||||
private var revokeDisposable = MetaDisposable()
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, mode: InviteLinkInviteController.Mode, controller: InviteLinkInviteController) {
|
||||
init(context: AccountContext, presentationData: PresentationData, mode: InviteLinkInviteController.Mode, controller: InviteLinkInviteController, initialInvite: ExportedInvitation?) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
|
||||
@ -319,7 +339,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self.headerNode.clipsToBounds = false
|
||||
|
||||
self.headerBackgroundNode = ASDisplayNode()
|
||||
self.headerBackgroundNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.headerBackgroundNode.backgroundColor = getBackgroundColor(theme: self.presentationData.theme)
|
||||
self.headerBackgroundNode.cornerRadius = 16.0
|
||||
self.headerBackgroundNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
@ -338,7 +358,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
|
||||
self.historyBackgroundContentNode = ASDisplayNode()
|
||||
self.historyBackgroundContentNode.isLayerBacked = true
|
||||
self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.historyBackgroundContentNode.backgroundColor = getBackgroundColor(theme: self.presentationData.theme)
|
||||
|
||||
self.historyBackgroundNode.addSubnode(self.historyBackgroundContentNode)
|
||||
|
||||
@ -354,7 +374,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self.backgroundColor = nil
|
||||
self.isOpaque = false
|
||||
|
||||
let mainInvitePromise = ValuePromise<ExportedInvitation?>(nil)
|
||||
let mainInvitePromise = ValuePromise<ExportedInvitation?>(initialInvite)
|
||||
|
||||
self.interaction = InviteLinkInviteInteraction(context: context, mainLinkContextAction: { [weak self] invite, node, gesture in
|
||||
guard let self else {
|
||||
@ -363,7 +383,6 @@ public final class InviteLinkInviteController: ViewController {
|
||||
guard let node = node as? ContextReferenceContentNode else {
|
||||
return
|
||||
}
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextCopy, icon: { theme in
|
||||
@ -381,7 +400,6 @@ public final class InviteLinkInviteController: ViewController {
|
||||
}
|
||||
})))
|
||||
|
||||
if case let .groupOrChannel(peerId) = self.mode {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
@ -391,7 +409,8 @@ public final class InviteLinkInviteController: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
if let invite = invite {
|
||||
if let invite {
|
||||
if case let .groupOrChannel(peerId) = self.mode {
|
||||
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let strongSelf = self else {
|
||||
@ -404,12 +423,17 @@ public final class InviteLinkInviteController: ViewController {
|
||||
isGroup = true
|
||||
}
|
||||
let updatedPresentationData = (strongSelf.presentationData, strongSelf.presentationDataPromise.get())
|
||||
let controller = QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup))
|
||||
let controller = QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel))
|
||||
strongSelf.controller?.present(controller, in: .window(.root))
|
||||
})
|
||||
} else if case .groupCall = self.mode {
|
||||
let controller = QrCodeScreen(context: context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), subject: .invite(invite: invite, type: .channel))
|
||||
self.controller?.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
})))
|
||||
|
||||
if case let .groupOrChannel(peerId) = self.mode {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { [ weak self] _, f in
|
||||
@ -454,7 +478,45 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self?.controller?.present(controller, in: .window(.root))
|
||||
})
|
||||
})))
|
||||
} else if case let .groupCall(groupCall) = self.mode, groupCall.canRevoke {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { [ weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
//TODO:localize
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: "Revoke Link"),
|
||||
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { [weak self] in
|
||||
dismissAction()
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let inviteLink = invite?.link {
|
||||
let _ = (context.engine.calls.revokeConferenceInviteLink(reference: .id(id: groupCall.callId, accessHash: groupCall.accessHash), link: inviteLink) |> deliverOnMainQueue).start(next: { result in
|
||||
mainInvitePromise.set(.link(link: result.listenerLink, title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil))
|
||||
})
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: presentationData.strings.InviteLink_InviteLinkRevoked), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||
}
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
self.controller?.present(controller, in: .window(.root))
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
@ -597,15 +659,16 @@ public final class InviteLinkInviteController: ViewController {
|
||||
strongSelf.enqueueTransition(transition)
|
||||
}
|
||||
})
|
||||
case let .groupCall(link, isRecentlyCreated):
|
||||
//TODO:release
|
||||
let tempInfo: Signal<Void, NoError> = .single(Void()) |> delay(0.0, queue: .mainQueue())
|
||||
case let .groupCall(groupCall):
|
||||
// A workaround to skip the first run of the event cycle
|
||||
let delayOfZero = Signal<Void, NoError>.single(()) |> delay(0.0, queue: .mainQueue())
|
||||
|
||||
self.disposable = (combineLatest(queue: .mainQueue(),
|
||||
self.presentationDataPromise.get(),
|
||||
tempInfo
|
||||
mainInvitePromise.get(),
|
||||
delayOfZero
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, _ in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, mainInvite, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -615,9 +678,9 @@ public final class InviteLinkInviteController: ViewController {
|
||||
let helpText: String = "Anyone on Telegram can join your call by following the link below."
|
||||
entries.append(.header(title: "Call Link", text: helpText))
|
||||
|
||||
let mainInvite: ExportedInvitation = .link(link: link, title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: self.context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil)
|
||||
let mainInvite: ExportedInvitation = .link(link: mainInvite?.link ?? "", title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: self.context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil)
|
||||
|
||||
entries.append(.mainLink(invitation: mainInvite, isCall: true, isRecentlyCreated: isRecentlyCreated))
|
||||
entries.append(.mainLink(invitation: mainInvite, isCall: true, isRecentlyCreated: groupCall.isRecentlyCreated))
|
||||
|
||||
let previousEntries = previousEntries.swap(entries)
|
||||
|
||||
@ -675,8 +738,8 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self.presentationData = presentationData
|
||||
self.presentationDataPromise.set(.single(presentationData))
|
||||
|
||||
self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.headerBackgroundNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.historyBackgroundContentNode.backgroundColor = getBackgroundColor(theme: self.presentationData.theme)
|
||||
self.headerBackgroundNode.backgroundColor = getBackgroundColor(theme: self.presentationData.theme)
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.InviteLink_InviteLink, font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
|
||||
|
||||
self.doneButtonIconNode.image = generateCloseButtonImage(backgroundColor: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05), foregroundColor: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.4))!
|
||||
|
@ -530,7 +530,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
} else {
|
||||
isGroup = true
|
||||
}
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), nil)
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel)), nil)
|
||||
})
|
||||
})))
|
||||
|
||||
@ -719,7 +719,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
isGroup = true
|
||||
}
|
||||
Queue.mainQueue().after(0.2) {
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), nil)
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel)), nil)
|
||||
}
|
||||
})
|
||||
})))
|
||||
|
@ -755,7 +755,7 @@ public final class InviteLinkViewController: ViewController {
|
||||
isGroup = true
|
||||
}
|
||||
let updatedPresentationData = (strongSelf.presentationData, parentController.presentationDataPromise.get())
|
||||
strongSelf.controller?.present(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), in: .window(.root))
|
||||
strongSelf.controller?.present(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel)), in: .window(.root))
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
||||
}, inviteViaLink: {
|
||||
if let controller = getControllerImpl?() {
|
||||
dismissInputImpl?()
|
||||
presentControllerImpl?(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: peerId), parentNavigationController: controller.navigationController as? NavigationController), nil)
|
||||
presentControllerImpl?(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: peerId), initialInvite: nil, parentNavigationController: controller.navigationController as? NavigationController), nil)
|
||||
}
|
||||
}, updateHideMembers: { value in
|
||||
let _ = context.engine.peers.updateChannelMembersHidden(peerId: peerId, value: value).start()
|
||||
|
@ -1609,7 +1609,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
} else {
|
||||
isGroup = true
|
||||
}
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), nil)
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel)), nil)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -35,9 +35,15 @@ private func shareQrCode(context: AccountContext, link: String, ecl: String, vie
|
||||
}
|
||||
|
||||
public final class QrCodeScreen: ViewController {
|
||||
public enum SubjectType {
|
||||
case group
|
||||
case channel
|
||||
case groupCall
|
||||
}
|
||||
|
||||
public enum Subject {
|
||||
case peer(peer: EnginePeer)
|
||||
case invite(invite: ExportedInvitation, isGroup: Bool)
|
||||
case invite(invite: ExportedInvitation, type: SubjectType)
|
||||
case chatFolder(slug: String)
|
||||
|
||||
var link: String {
|
||||
@ -239,9 +245,17 @@ public final class QrCodeScreen: ViewController {
|
||||
let title: String
|
||||
let text: String
|
||||
switch subject {
|
||||
case let .invite(_, isGroup):
|
||||
case let .invite(_, type):
|
||||
title = self.presentationData.strings.InviteLink_QRCode_Title
|
||||
text = isGroup ? self.presentationData.strings.InviteLink_QRCode_Info : self.presentationData.strings.InviteLink_QRCode_InfoChannel
|
||||
switch type {
|
||||
case .group:
|
||||
text = self.presentationData.strings.InviteLink_QRCode_Info
|
||||
case .channel:
|
||||
text = self.presentationData.strings.InviteLink_QRCode_InfoChannel
|
||||
case .groupCall:
|
||||
//TODO:localize
|
||||
text = "Everyone on Telegram can scan this code to join your group call."
|
||||
}
|
||||
case .chatFolder:
|
||||
title = self.presentationData.strings.InviteLink_QRCodeFolder_Title
|
||||
text = self.presentationData.strings.InviteLink_QRCodeFolder_Text
|
||||
|
@ -167,10 +167,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
||||
self.conferenceAddParticipant?()
|
||||
}
|
||||
|
||||
var isConferencePossible = false
|
||||
if self.call.context.sharedContext.immediateExperimentalUISettings.conferenceDebug {
|
||||
isConferencePossible = true
|
||||
}
|
||||
var isConferencePossible = true
|
||||
if let data = self.call.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_enable_conference"] as? Double {
|
||||
isConferencePossible = value != 0.0
|
||||
}
|
||||
|
@ -161,7 +161,8 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: state.isStream
|
||||
isStream: state.isStream,
|
||||
isCreator: state.isCreator
|
||||
),
|
||||
topParticipants: topParticipants,
|
||||
participantCount: state.totalCount,
|
||||
@ -888,6 +889,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void)
|
||||
|
||||
private(set) var initialCall: (description: EngineGroupCallDescription, reference: InternalGroupCallReference)?
|
||||
public var currentReference: InternalGroupCallReference?
|
||||
public let internalId: CallSessionInternalId
|
||||
public let peerId: EnginePeer.Id?
|
||||
private let isChannel: Bool
|
||||
@ -1208,6 +1210,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.getDeviceAccessData = getDeviceAccessData
|
||||
|
||||
self.initialCall = initialCall
|
||||
self.currentReference = initialCall?.reference
|
||||
self.callId = initialCall?.description.id
|
||||
|
||||
self.internalId = internalId
|
||||
@ -1963,7 +1966,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: callInfo.isVideoEnabled,
|
||||
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
isStream: callInfo.isStream,
|
||||
isCreator: callInfo.isCreator
|
||||
)), audioSessionControl: self.audioSessionControl)
|
||||
} else {
|
||||
self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
|
||||
@ -1979,7 +1983,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
isStream: callInfo.isStream,
|
||||
isCreator: callInfo.isCreator
|
||||
))))
|
||||
|
||||
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||
@ -2301,6 +2306,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.currentReference = .id(id: joinCallResult.callInfo.id, accessHash: joinCallResult.callInfo.accessHash)
|
||||
|
||||
let clientParams = joinCallResult.jsonParams
|
||||
if let data = clientParams.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] {
|
||||
if let video = dict["video"] as? [String: Any] {
|
||||
@ -2910,7 +2918,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
isStream: callInfo.isStream,
|
||||
isCreator: callInfo.isCreator
|
||||
))))
|
||||
|
||||
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||
@ -2988,6 +2997,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
result.append(OngoingGroupCallContext.MediaChannelDescription(
|
||||
kind: .audio,
|
||||
peerId: participant.peer.id.id._internalGetInt64Value(),
|
||||
audioSsrc: audioSsrc,
|
||||
videoDescription: nil
|
||||
))
|
||||
@ -2999,6 +3009,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
result.append(OngoingGroupCallContext.MediaChannelDescription(
|
||||
kind: .audio,
|
||||
peerId: participant.peer.id.id._internalGetInt64Value(),
|
||||
audioSsrc: screencastSsrc,
|
||||
videoDescription: nil
|
||||
))
|
||||
|
@ -25,6 +25,7 @@ import LegacyComponents
|
||||
import TooltipUI
|
||||
import BlurredBackgroundComponent
|
||||
import CallsEmoji
|
||||
import InviteLinksUI
|
||||
|
||||
extension VideoChatCall {
|
||||
var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> {
|
||||
@ -653,6 +654,51 @@ final class VideoChatScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
if groupCall.isConference {
|
||||
guard let navigationController = self.environment?.controller()?.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
guard let currentReference = groupCall.currentReference, case let .id(callId, accessHash) = currentReference else {
|
||||
return
|
||||
}
|
||||
guard let callState = self.callState else {
|
||||
return
|
||||
}
|
||||
var presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
let controller = InviteLinkInviteController(
|
||||
context: groupCall.accountContext,
|
||||
updatedPresentationData: (initial: presentationData, signal: .single(presentationData)),
|
||||
mode: .groupCall(InviteLinkInviteController.Mode.GroupCall(
|
||||
callId: callId,
|
||||
accessHash: accessHash,
|
||||
isRecentlyCreated: false,
|
||||
canRevoke: callState.canManageCall
|
||||
)),
|
||||
initialInvite: .link(link: inviteLinks.listenerLink, title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: groupCall.accountContext.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil),
|
||||
parentNavigationController: navigationController,
|
||||
completed: { [weak self] result in
|
||||
guard let self, case let .group(groupCall) = self.currentCall else {
|
||||
return
|
||||
}
|
||||
if let result {
|
||||
switch result {
|
||||
case .linkCopied:
|
||||
//TODO:localize
|
||||
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
return false
|
||||
}), in: .current)
|
||||
case .openCall:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.environment?.controller()?.present(controller, in: .window(.root), with: nil)
|
||||
return
|
||||
}
|
||||
|
||||
let formatSendTitle: (String) -> String = { string in
|
||||
var string = string
|
||||
if string.contains("[") && string.contains("]") {
|
||||
|
@ -16,31 +16,6 @@ extension VideoChatScreenComponent.View {
|
||||
return
|
||||
}
|
||||
|
||||
/*if groupCall.accountContext.sharedContext.immediateExperimentalUISettings.conferenceDebug {
|
||||
guard let navigationController = self.environment?.controller()?.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
var presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
let controller = InviteLinkInviteController(context: groupCall.accountContext, updatedPresentationData: (initial: presentationData, signal: .single(presentationData)), mode: .groupCall(link: "https://t.me/call/+abbfbffll123", isRecentlyCreated: false), parentNavigationController: navigationController, completed: { [weak self] result in
|
||||
guard let self, case let .group(groupCall) = self.currentCall else {
|
||||
return
|
||||
}
|
||||
if let result {
|
||||
switch result {
|
||||
case .linkCopied:
|
||||
//TODO:localize
|
||||
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
return false
|
||||
}), in: .current)
|
||||
}
|
||||
}
|
||||
})
|
||||
self.environment?.controller()?.present(controller, in: .window(.root), with: nil)
|
||||
return
|
||||
}*/
|
||||
|
||||
if groupCall.isConference {
|
||||
var disablePeerIds: [EnginePeer.Id] = []
|
||||
disablePeerIds.append(groupCall.accountContext.account.peerId)
|
||||
|
@ -98,6 +98,11 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||
if incoming, let discardReason = discardReason, case .missed = discardReason {
|
||||
globalTags.insert(.MissedCalls)
|
||||
}
|
||||
case let .conferenceCall(conferenceCall):
|
||||
globalTags.insert(.Calls)
|
||||
if incoming, conferenceCall.flags.contains(.isMissed) {
|
||||
globalTags.insert(.MissedCalls)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -118,9 +123,6 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||
}
|
||||
}
|
||||
|
||||
if !incoming {
|
||||
assert(true)
|
||||
}
|
||||
return (tags, globalTags)
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,7 @@ public struct GroupCallInfo: Equatable {
|
||||
public var isVideoEnabled: Bool
|
||||
public var unmutedVideoLimit: Int
|
||||
public var isStream: Bool
|
||||
public var isCreator: Bool
|
||||
|
||||
public init(
|
||||
id: Int64,
|
||||
@ -110,7 +111,8 @@ public struct GroupCallInfo: Equatable {
|
||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?,
|
||||
isVideoEnabled: Bool,
|
||||
unmutedVideoLimit: Int,
|
||||
isStream: Bool
|
||||
isStream: Bool,
|
||||
isCreator: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
@ -125,6 +127,7 @@ public struct GroupCallInfo: Equatable {
|
||||
self.isVideoEnabled = isVideoEnabled
|
||||
self.unmutedVideoLimit = unmutedVideoLimit
|
||||
self.isStream = isStream
|
||||
self.isCreator = isCreator
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +153,8 @@ extension GroupCallInfo {
|
||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: (flags & (1 << 1)) != 0, canChange: (flags & (1 << 2)) != 0),
|
||||
isVideoEnabled: (flags & (1 << 9)) != 0,
|
||||
unmutedVideoLimit: Int(unmutedVideoLimit),
|
||||
isStream: (flags & (1 << 12)) != 0
|
||||
isStream: (flags & (1 << 12)) != 0,
|
||||
isCreator: (flags & (1 << 15)) != 0
|
||||
)
|
||||
case .groupCallDiscarded:
|
||||
return nil
|
||||
@ -454,17 +458,17 @@ public enum GetGroupCallParticipantsError {
|
||||
func _internal_getGroupCallParticipants(account: Account, reference: InternalGroupCallReference, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
||||
let accountPeerId = account.peerId
|
||||
|
||||
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError>
|
||||
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool, Bool), GetGroupCallParticipantsError>
|
||||
|
||||
sortAscendingValue = _internal_getCurrentGroupCall(account: account, reference: reference)
|
||||
|> mapError { _ -> GetGroupCallParticipantsError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError> in
|
||||
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool, Bool), GetGroupCallParticipantsError> in
|
||||
guard let result = result else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return .single((sortAscending ?? result.info.sortAscending, result.info.scheduleTimestamp, result.info.subscribedToScheduled, result.info.defaultParticipantsAreMuted, result.info.isVideoEnabled, result.info.unmutedVideoLimit, result.info.isStream))
|
||||
return .single((sortAscending ?? result.info.sortAscending, result.info.scheduleTimestamp, result.info.subscribedToScheduled, result.info.defaultParticipantsAreMuted, result.info.isVideoEnabled, result.info.unmutedVideoLimit, result.info.isStream, result.info.isCreator))
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
@ -481,7 +485,7 @@ func _internal_getGroupCallParticipants(account: Account, reference: InternalGro
|
||||
let version: Int32
|
||||
let nextParticipantsFetchOffset: String?
|
||||
|
||||
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream) = sortAscendingAndScheduleTimestamp
|
||||
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream, isCreator) = sortAscendingAndScheduleTimestamp
|
||||
|
||||
switch result {
|
||||
case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion):
|
||||
@ -506,7 +510,7 @@ func _internal_getGroupCallParticipants(account: Account, reference: InternalGro
|
||||
participants: parsedParticipants,
|
||||
nextParticipantsFetchOffset: nextParticipantsFetchOffset,
|
||||
adminIds: Set(),
|
||||
isCreator: false,
|
||||
isCreator: isCreator,
|
||||
defaultParticipantsAreMuted: defaultParticipantsAreMuted ?? GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false),
|
||||
sortAscending: sortAscendingValue,
|
||||
recordingStartTimestamp: nil,
|
||||
@ -2959,6 +2963,29 @@ func _internal_createConferenceCall(postbox: Postbox, network: Network, accountP
|
||||
}
|
||||
}
|
||||
|
||||
public enum RevokeConferenceInviteLinkError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func _internal_revokeConferenceInviteLink(account: Account, reference: InternalGroupCallReference, link: String) -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> {
|
||||
return account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 1, call: reference.apiInputGroupCall, joinMuted: .boolFalse))
|
||||
|> mapError { _ -> RevokeConferenceInviteLinkError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> in
|
||||
account.stateManager.addUpdates(result)
|
||||
|
||||
return _internal_groupCallInviteLinks(account: account, reference: reference, isConference: true)
|
||||
|> castError(RevokeConferenceInviteLinkError.self)
|
||||
|> mapToSignal { result -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> in
|
||||
guard let result = result else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConfirmAddConferenceParticipantError {
|
||||
case generic
|
||||
}
|
||||
|
@ -97,6 +97,10 @@ public extension TelegramEngine {
|
||||
return _internal_createConferenceCall(postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId)
|
||||
}
|
||||
|
||||
public func revokeConferenceInviteLink(reference: InternalGroupCallReference, link: String) -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> {
|
||||
return _internal_revokeConferenceInviteLink(account: self.account, reference: reference, link: link)
|
||||
}
|
||||
|
||||
public func pollConferenceCallBlockchain(reference: InternalGroupCallReference, subChainId: Int, offset: Int, limit: Int) -> Signal<(blocks: [Data], nextOffset: Int)?, NoError> {
|
||||
return _internal_pollConferenceCallBlockchain(network: self.account.network, reference: reference, subChainId: subChainId, offset: offset, limit: limit)
|
||||
}
|
||||
|
@ -14124,7 +14124,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
|
||||
|
||||
createInviteLinkImpl = { [weak contactsController] in
|
||||
contactsController?.view.window?.endEditing(true)
|
||||
contactsController?.present(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: groupPeer.id), parentNavigationController: contactsController?.navigationController as? NavigationController), in: .window(.root))
|
||||
contactsController?.present(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: groupPeer.id), initialInvite: nil, parentNavigationController: contactsController?.navigationController as? NavigationController), in: .window(.root))
|
||||
}
|
||||
|
||||
parentController?.push(contactsController)
|
||||
|
@ -402,6 +402,15 @@ func openResolvedUrlImpl(
|
||||
members: resolvedCallLink.members,
|
||||
totalMemberCount: resolvedCallLink.totalMemberCount
|
||||
))))
|
||||
}, error: { _ in
|
||||
var elevatedLayout = true
|
||||
if case .chat = urlContext {
|
||||
elevatedLayout = false
|
||||
}
|
||||
//TODO:localize
|
||||
present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: "This link is no longer active"), elevatedLayout: elevatedLayout, animateInAsReplacement: false, action: { _ in
|
||||
return true
|
||||
}), nil)
|
||||
})
|
||||
case let .localization(identifier):
|
||||
dismissInput()
|
||||
@ -788,6 +797,7 @@ func openResolvedUrlImpl(
|
||||
}
|
||||
if let currentState = starsContext.currentState, currentState.balance >= StarsAmount(value: amount, nanos: 0) {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
let controller = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .universal(
|
||||
|
@ -270,11 +270,13 @@ public final class OngoingGroupCallContext {
|
||||
}
|
||||
|
||||
public var kind: Kind
|
||||
public var peerId: Int64
|
||||
public var audioSsrc: UInt32
|
||||
public var videoDescription: String?
|
||||
|
||||
public init(kind: Kind, audioSsrc: UInt32, videoDescription: String?) {
|
||||
public init(kind: Kind, peerId: Int64, audioSsrc: UInt32, videoDescription: String?) {
|
||||
self.kind = kind
|
||||
self.peerId = peerId
|
||||
self.audioSsrc = audioSsrc
|
||||
self.videoDescription = videoDescription
|
||||
}
|
||||
@ -575,6 +577,7 @@ public final class OngoingGroupCallContext {
|
||||
}
|
||||
return OngoingGroupCallMediaChannelDescription(
|
||||
type: mappedType,
|
||||
peerId: channel.peerId,
|
||||
audioSsrc: channel.audioSsrc,
|
||||
videoDescription: channel.videoDescription
|
||||
)
|
||||
@ -688,6 +691,7 @@ public final class OngoingGroupCallContext {
|
||||
}
|
||||
return OngoingGroupCallMediaChannelDescription(
|
||||
type: mappedType,
|
||||
peerId: channel.peerId,
|
||||
audioSsrc: channel.audioSsrc,
|
||||
videoDescription: channel.videoDescription
|
||||
)
|
||||
|
@ -332,10 +332,12 @@ typedef NS_ENUM(int32_t, OngoingGroupCallMediaChannelType) {
|
||||
@interface OngoingGroupCallMediaChannelDescription : NSObject
|
||||
|
||||
@property (nonatomic, readonly) OngoingGroupCallMediaChannelType type;
|
||||
@property (nonatomic, readonly) uint64_t peerId;
|
||||
@property (nonatomic, readonly) uint32_t audioSsrc;
|
||||
@property (nonatomic, strong, readonly) NSString * _Nullable videoDescription;
|
||||
|
||||
- (instancetype _Nonnull)initWithType:(OngoingGroupCallMediaChannelType)type
|
||||
peerId:(int64_t)peerId
|
||||
audioSsrc:(uint32_t)audioSsrc
|
||||
videoDescription:(NSString * _Nullable)videoDescription;
|
||||
|
||||
|
@ -3029,11 +3029,13 @@ encryptDecrypt:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull, bool))encryp
|
||||
@implementation OngoingGroupCallMediaChannelDescription
|
||||
|
||||
- (instancetype _Nonnull)initWithType:(OngoingGroupCallMediaChannelType)type
|
||||
peerId:(int64_t)peerId
|
||||
audioSsrc:(uint32_t)audioSsrc
|
||||
videoDescription:(NSString * _Nullable)videoDescription {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_type = type;
|
||||
_peerId = peerId;
|
||||
_audioSsrc = audioSsrc;
|
||||
_videoDescription = videoDescription;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user