mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Combo update
This commit is contained in:
parent
af59d15412
commit
ef99bb604f
@ -1820,7 +1820,8 @@ ios_application(
|
||||
],
|
||||
)
|
||||
|
||||
# a temporary target used to simplify webrtc build tests
|
||||
# Temporary targets used to simplify webrtc build tests
|
||||
|
||||
ios_application(
|
||||
name = "webrtc_build_test",
|
||||
bundle_id = "{telegram_bundle_id}".format(
|
||||
@ -1843,3 +1844,26 @@ ios_application(
|
||||
"//third-party/webrtc:webrtc_lib",
|
||||
],
|
||||
)
|
||||
|
||||
ios_application(
|
||||
name = "libvpx_build_test",
|
||||
bundle_id = "{telegram_bundle_id}".format(
|
||||
telegram_bundle_id = telegram_bundle_id,
|
||||
),
|
||||
families = ["iphone", "ipad"],
|
||||
minimum_os_version = "9.0",
|
||||
provisioning_profile = select({
|
||||
":disableProvisioningProfilesSetting": None,
|
||||
"//conditions:default": "@build_configuration//provisioning:Telegram.mobileprovision",
|
||||
}),
|
||||
entitlements = ":TelegramEntitlements.entitlements",
|
||||
infoplists = [
|
||||
":TelegramInfoPlist",
|
||||
":BuildNumberInfoPlist",
|
||||
":VersionInfoPlist",
|
||||
":UrlTypesInfoPlist",
|
||||
],
|
||||
deps = [
|
||||
"//third-party/libvpx:vpx",
|
||||
],
|
||||
)
|
||||
|
||||
@ -154,9 +154,9 @@ public struct ChatAvailableMessageActions {
|
||||
}
|
||||
|
||||
public enum WallpaperUrlParameter {
|
||||
case slug(String, WallpaperPresentationOptions, UIColor?, UIColor?, Int32?, Int32?)
|
||||
case slug(String, WallpaperPresentationOptions, [UInt32], Int32?, Int32?)
|
||||
case color(UIColor)
|
||||
case gradient(UIColor, UIColor, Int32?)
|
||||
case gradient([UInt32], Int32?)
|
||||
}
|
||||
|
||||
public enum ResolvedUrlSettingsSection {
|
||||
|
||||
@ -109,6 +109,7 @@ public final class PresentationCallVideoView {
|
||||
public let getAspect: () -> CGFloat
|
||||
public let setOnOrientationUpdated: (((Orientation, CGFloat) -> Void)?) -> Void
|
||||
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
||||
public let updateIsEnabled: (Bool) -> Void
|
||||
|
||||
public init(
|
||||
holder: AnyObject,
|
||||
@ -117,7 +118,8 @@ public final class PresentationCallVideoView {
|
||||
getOrientation: @escaping () -> Orientation,
|
||||
getAspect: @escaping () -> CGFloat,
|
||||
setOnOrientationUpdated: @escaping (((Orientation, CGFloat) -> Void)?) -> Void,
|
||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void,
|
||||
updateIsEnabled: @escaping (Bool) -> Void
|
||||
) {
|
||||
self.holder = holder
|
||||
self.view = view
|
||||
@ -126,6 +128,7 @@ public final class PresentationCallVideoView {
|
||||
self.getAspect = getAspect
|
||||
self.setOnOrientationUpdated = setOnOrientationUpdated
|
||||
self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
|
||||
self.updateIsEnabled = updateIsEnabled
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1132,12 +1132,18 @@ open class NavigationBar: ASDisplayNode {
|
||||
if let titleView = self.titleView {
|
||||
let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)
|
||||
transition.updateFrame(view: titleView, frame: titleFrame)
|
||||
var titleViewTransition = transition
|
||||
if titleView.frame.isEmpty {
|
||||
titleViewTransition = .immediate
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
|
||||
titleViewTransition.updateFrame(view: titleView, frame: titleFrame)
|
||||
|
||||
if let titleView = titleView as? NavigationBarTitleView {
|
||||
let titleWidth = size.width - (leftTitleInset > 0.0 ? leftTitleInset : rightTitleInset) - (rightTitleInset > 0.0 ? rightTitleInset : leftTitleInset)
|
||||
|
||||
titleView.updateLayout(size: titleFrame.size, clearBounds: CGRect(origin: CGPoint(x: leftTitleInset - titleFrame.minX, y: 0.0), size: CGSize(width: titleWidth, height: titleFrame.height)), transition: transition)
|
||||
titleView.updateLayout(size: titleFrame.size, clearBounds: CGRect(origin: CGPoint(x: leftTitleInset - titleFrame.minX, y: 0.0), size: CGSize(width: titleWidth, height: titleFrame.height)), transition: titleViewTransition)
|
||||
}
|
||||
|
||||
if let transitionState = self.transitionState, let otherNavigationBar = transitionState.navigationBar {
|
||||
|
||||
@ -275,6 +275,7 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
|
||||
}
|
||||
|
||||
self.pager.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: layout.size)
|
||||
|
||||
self.pager.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
}
|
||||
|
||||
|
||||
@ -25,8 +25,8 @@ public enum WallpaperListType {
|
||||
|
||||
public enum WallpaperListSource {
|
||||
case list(wallpapers: [TelegramWallpaper], central: TelegramWallpaper, type: WallpaperListType)
|
||||
case wallpaper(TelegramWallpaper, WallpaperPresentationOptions?, UIColor?, UIColor?, Int32?, Int32?, Message?)
|
||||
case slug(String, TelegramMediaFile?, WallpaperPresentationOptions?, UIColor?, UIColor?, Int32?, Int32?, Message?)
|
||||
case wallpaper(TelegramWallpaper, WallpaperPresentationOptions?, [UInt32], Int32?, Int32?, Message?)
|
||||
case slug(String, TelegramMediaFile?, WallpaperPresentationOptions?, [UInt32], Int32?, Int32?, Message?)
|
||||
case asset(PHAsset)
|
||||
case contextResult(ChatContextResult)
|
||||
case customColor(UInt32?)
|
||||
@ -131,42 +131,30 @@ class WallpaperGalleryControllerNode: GalleryControllerNode {
|
||||
}
|
||||
}
|
||||
|
||||
private func updatedFileWallpaper(wallpaper: TelegramWallpaper, firstColor: UIColor?, secondColor: UIColor?, intensity: Int32?, rotation: Int32?) -> TelegramWallpaper {
|
||||
private func updatedFileWallpaper(wallpaper: TelegramWallpaper, colors: [UInt32], intensity: Int32?, rotation: Int32?) -> TelegramWallpaper {
|
||||
if case let .file(file) = wallpaper {
|
||||
return updatedFileWallpaper(id: file.id, accessHash: file.accessHash, slug: file.slug, file: file.file, firstColor: firstColor, secondColor: secondColor, intensity: intensity, rotation: rotation)
|
||||
return updatedFileWallpaper(id: file.id, accessHash: file.accessHash, slug: file.slug, file: file.file, colors: colors, intensity: intensity, rotation: rotation)
|
||||
} else {
|
||||
return wallpaper
|
||||
}
|
||||
}
|
||||
|
||||
private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, slug: String, file: TelegramMediaFile, firstColor: UIColor?, secondColor: UIColor?, intensity: Int32?, rotation: Int32?) -> TelegramWallpaper {
|
||||
private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, slug: String, file: TelegramMediaFile, colors: [UInt32], intensity: Int32?, rotation: Int32?) -> TelegramWallpaper {
|
||||
var isPattern = ["image/png", "image/svg+xml", "application/x-tgwallpattern"].contains(file.mimeType)
|
||||
if let fileName = file.fileName, fileName.hasSuffix(".svgbg") {
|
||||
isPattern = true
|
||||
}
|
||||
var firstColorValue: UInt32?
|
||||
var secondColorValue: UInt32?
|
||||
var colorValues: [UInt32] = []
|
||||
var intensityValue: Int32?
|
||||
if let firstColor = firstColor {
|
||||
firstColorValue = firstColor.argb
|
||||
intensityValue = intensity
|
||||
} else if isPattern {
|
||||
firstColorValue = 0xd6e2ee
|
||||
if !colors.isEmpty {
|
||||
colorValues = colors
|
||||
intensityValue = intensity ?? 50
|
||||
} else {
|
||||
colorValues = [0xd6e2ee]
|
||||
intensityValue = 50
|
||||
}
|
||||
if let secondColor = secondColor {
|
||||
secondColorValue = secondColor.argb
|
||||
}
|
||||
|
||||
var colors: [UInt32] = []
|
||||
if let firstColorValue = firstColorValue {
|
||||
colors.append(firstColorValue)
|
||||
}
|
||||
if let secondColorValue = secondColorValue {
|
||||
colors.append(secondColorValue)
|
||||
}
|
||||
|
||||
return .file(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, isDark: false, slug: slug, file: file, settings: WallpaperSettings(colors: colors, intensity: intensityValue, rotation: rotation))
|
||||
return .file(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, isDark: false, slug: slug, file: file, settings: WallpaperSettings(colors: colorValues, intensity: intensityValue, rotation: rotation))
|
||||
}
|
||||
|
||||
public class WallpaperGalleryController: ViewController {
|
||||
@ -234,15 +222,15 @@ public class WallpaperGalleryController: ViewController {
|
||||
if case let .wallpapers(wallpaperOptions) = type, let options = wallpaperOptions {
|
||||
self.initialOptions = options
|
||||
}
|
||||
case let .slug(slug, file, options, firstColor, secondColor, intensity, rotation, message):
|
||||
case let .slug(slug, file, options, colors, intensity, rotation, message):
|
||||
if let file = file {
|
||||
let wallpaper = updatedFileWallpaper(slug: slug, file: file, firstColor: firstColor, secondColor: secondColor, intensity: intensity, rotation: rotation)
|
||||
let wallpaper = updatedFileWallpaper(slug: slug, file: file, colors: colors, intensity: intensity, rotation: rotation)
|
||||
entries = [.wallpaper(wallpaper, message)]
|
||||
centralEntryIndex = 0
|
||||
self.initialOptions = options
|
||||
}
|
||||
case let .wallpaper(wallpaper, options, firstColor, secondColor, intensity, rotation, message):
|
||||
let wallpaper = updatedFileWallpaper(wallpaper: wallpaper, firstColor: firstColor, secondColor: secondColor, intensity: intensity, rotation: rotation)
|
||||
case let .wallpaper(wallpaper, options, colors, intensity, rotation, message):
|
||||
let wallpaper = updatedFileWallpaper(wallpaper: wallpaper, colors: colors, intensity: intensity, rotation: rotation)
|
||||
entries = [.wallpaper(wallpaper, message)]
|
||||
centralEntryIndex = 0
|
||||
self.initialOptions = options
|
||||
@ -327,7 +315,7 @@ public class WallpaperGalleryController: ViewController {
|
||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
self.toolbarNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
self.patternPanelNode?.updateTheme(self.presentationData.theme)
|
||||
self.patternPanelNode?.backgroundColors = self.presentationData.theme.overallDarkAppearance ? ([self.presentationData.theme.list.blocksBackgroundColor.rgb], nil) : nil
|
||||
//self.patternPanelNode?.backgroundColors = self.presentationData.theme.overallDarkAppearance ? ([self.presentationData.theme.list.blocksBackgroundColor.rgb], nil) : nil
|
||||
|
||||
self.colorsPanelNode?.updateTheme(self.presentationData.theme)
|
||||
}
|
||||
@ -452,7 +440,7 @@ public class WallpaperGalleryController: ViewController {
|
||||
|
||||
let completion: (TelegramWallpaper) -> Void = { wallpaper in
|
||||
let baseSettings = wallpaper.settings
|
||||
let updatedSettings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), colors: baseSettings?.colors ?? [], intensity: baseSettings?.intensity)
|
||||
let updatedSettings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), colors: baseSettings?.colors ?? [], intensity: baseSettings?.intensity, rotation: baseSettings?.rotation)
|
||||
let wallpaper = wallpaper.withUpdatedSettings(updatedSettings)
|
||||
|
||||
let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered
|
||||
@ -613,14 +601,34 @@ public class WallpaperGalleryController: ViewController {
|
||||
strongSelf.colorsPanelEnabled = false
|
||||
|
||||
strongSelf.patternInitialWallpaper = enabled ? initialWallpaper : nil
|
||||
switch initialWallpaper {
|
||||
case let .color(color):
|
||||
strongSelf.patternPanelNode?.backgroundColors = ([color], nil)
|
||||
case let .gradient(colors, settings):
|
||||
strongSelf.patternPanelNode?.backgroundColors = (colors, settings.rotation)
|
||||
case let .file(file) where file.isPattern:
|
||||
strongSelf.patternPanelNode?.backgroundColors = (file.settings.colors, file.settings.rotation)
|
||||
default:
|
||||
break
|
||||
}
|
||||
strongSelf.patternPanelNode?.serviceBackgroundColor = serviceColor(for: (initialWallpaper, nil))
|
||||
strongSelf.patternPanelEnabled = enabled
|
||||
strongSelf.galleryNode.scrollView.isScrollEnabled = !enabled
|
||||
if enabled {
|
||||
strongSelf.patternPanelNode?.updateWallpapers()
|
||||
strongSelf.patternPanelNode?.didAppear()
|
||||
} else {
|
||||
switch initialWallpaper {
|
||||
case .color:
|
||||
strongSelf.updateEntries(pattern: .color(0), preview: false)
|
||||
case .color, .gradient:
|
||||
strongSelf.updateEntries(wallpaper: initialWallpaper)
|
||||
case let .file(file):
|
||||
if !file.settings.colors.isEmpty {
|
||||
if file.settings.colors.count >= 2 {
|
||||
strongSelf.updateEntries(wallpaper: .gradient(file.settings.colors, WallpaperSettings(rotation: file.settings.rotation)))
|
||||
} else {
|
||||
strongSelf.updateEntries(wallpaper: .color(file.settings.colors[0]))
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -652,6 +660,15 @@ public class WallpaperGalleryController: ViewController {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
node.requestRotateGradient = { [weak self] angle in
|
||||
guard let strongSelf = self, let _ = strongSelf.validLayout, let entry = strongSelf.currentEntry(), case let .wallpaper(wallpaper, _) = entry else {
|
||||
return
|
||||
}
|
||||
var settings = wallpaper.settings ?? WallpaperSettings()
|
||||
settings.rotation = angle
|
||||
strongSelf.updateEntries(wallpaper: wallpaper.withUpdatedSettings(settings))
|
||||
}
|
||||
|
||||
if let entry = self.currentEntry(), case let .wallpaper(wallpaper, _) = entry, case let .file(_, _, _, _, true, _, _, _ , settings) = wallpaper, !settings.colors.isEmpty {
|
||||
if self.patternPanelNode?.backgroundColors != nil, let snapshotView = self.patternPanelNode?.scrollNode.view.snapshotContentTree() {
|
||||
@ -660,7 +677,7 @@ public class WallpaperGalleryController: ViewController {
|
||||
snapshotView?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
self.patternPanelNode?.backgroundColors = ([settings.colors[0]], nil)
|
||||
//self.patternPanelNode?.backgroundColors = ([settings.colors[0]], nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -711,23 +728,30 @@ public class WallpaperGalleryController: ViewController {
|
||||
private func updateEntries(pattern: TelegramWallpaper?, intensity: Int32? = nil, preview: Bool = false) {
|
||||
var updatedEntries: [WallpaperGalleryEntry] = []
|
||||
for entry in self.entries {
|
||||
var entryColor: UInt32?
|
||||
var entryColors: [UInt32] = []
|
||||
if case let .wallpaper(wallpaper, _) = entry {
|
||||
if case let .color(color) = wallpaper {
|
||||
entryColor = color
|
||||
} else if case let .file(file) = wallpaper {
|
||||
entryColor = file.settings.colors.first
|
||||
entryColors = [color]
|
||||
} else if case let .file(file) = wallpaper, file.isPattern {
|
||||
entryColors = file.settings.colors
|
||||
} else if case let .gradient(colors, _) = wallpaper {
|
||||
entryColors = colors
|
||||
}
|
||||
}
|
||||
|
||||
if let entryColor = entryColor {
|
||||
if !entryColors.isEmpty {
|
||||
if let pattern = pattern, case let .file(file) = pattern {
|
||||
let newSettings = WallpaperSettings(blur: file.settings.blur, motion: file.settings.motion, colors: [entryColor], intensity: intensity)
|
||||
let newSettings = WallpaperSettings(blur: file.settings.blur, motion: file.settings.motion, colors: entryColors, intensity: intensity)
|
||||
let newWallpaper = TelegramWallpaper.file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: pattern.isPattern, isDark: file.isDark, slug: file.slug, file: file.file, settings: newSettings)
|
||||
updatedEntries.append(.wallpaper(newWallpaper, nil))
|
||||
} else {
|
||||
let newWallpaper = TelegramWallpaper.color(entryColor)
|
||||
updatedEntries.append(.wallpaper(newWallpaper, nil))
|
||||
if entryColors.count == 1 {
|
||||
let newWallpaper = TelegramWallpaper.color(entryColors[0])
|
||||
updatedEntries.append(.wallpaper(newWallpaper, nil))
|
||||
} else {
|
||||
let newWallpaper = TelegramWallpaper.gradient(entryColors, WallpaperSettings(rotation: nil))
|
||||
updatedEntries.append(.wallpaper(newWallpaper, nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -742,9 +766,17 @@ public class WallpaperGalleryController: ViewController {
|
||||
let hadLayout = self.validLayout != nil
|
||||
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
let panelHeight: CGFloat = 235.0
|
||||
|
||||
var pagerLayout = layout
|
||||
if self.patternPanelEnabled || self.colorsPanelEnabled {
|
||||
pagerLayout.intrinsicInsets.bottom += panelHeight
|
||||
}
|
||||
pagerLayout.intrinsicInsets.bottom = max(pagerLayout.intrinsicInsets.bottom, layout.inputHeight ?? 0.0)
|
||||
|
||||
self.galleryNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.galleryNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
self.galleryNode.containerLayoutUpdated(pagerLayout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
self.overlayNode?.frame = self.galleryNode.bounds
|
||||
|
||||
transition.updateFrame(node: self.toolbarNode!, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - 49.0 - layout.intrinsicInsets.bottom), size: CGSize(width: layout.size.width, height: 49.0 + layout.intrinsicInsets.bottom)))
|
||||
@ -760,7 +792,7 @@ public class WallpaperGalleryController: ViewController {
|
||||
patternPanelNode.patternChanged = { [weak self] pattern, intensity, preview in
|
||||
if let strongSelf = self, strongSelf.validLayout != nil, let patternInitialWallpaper = strongSelf.patternInitialWallpaper {
|
||||
switch patternInitialWallpaper {
|
||||
case .color:
|
||||
case .color, .file, .gradient:
|
||||
strongSelf.updateEntries(pattern: pattern, intensity: intensity, preview: preview)
|
||||
default:
|
||||
break
|
||||
@ -799,12 +831,10 @@ public class WallpaperGalleryController: ViewController {
|
||||
strongSelf.updateEntries(wallpaper: wallpaper)
|
||||
}
|
||||
}
|
||||
|
||||
let panelHeight: CGFloat = 235.0
|
||||
|
||||
var patternPanelFrame = CGRect(x: 0.0, y: layout.size.height, width: layout.size.width, height: panelHeight)
|
||||
if self.patternPanelEnabled {
|
||||
patternPanelFrame.origin = CGPoint(x: 0.0, y: layout.size.height - bottomInset - panelHeight)
|
||||
patternPanelFrame.origin = CGPoint(x: 0.0, y: layout.size.height - max((layout.inputHeight ?? 0.0) - panelHeight + 44.0, bottomInset) - panelHeight)
|
||||
bottomInset += panelHeight
|
||||
}
|
||||
|
||||
@ -813,7 +843,7 @@ public class WallpaperGalleryController: ViewController {
|
||||
|
||||
var colorsPanelFrame = CGRect(x: 0.0, y: layout.size.height, width: layout.size.width, height: panelHeight)
|
||||
if self.colorsPanelEnabled {
|
||||
colorsPanelFrame.origin = CGPoint(x: 0.0, y: layout.size.height - bottomInset - panelHeight)
|
||||
colorsPanelFrame.origin = CGPoint(x: 0.0, y: layout.size.height - max((layout.inputHeight ?? 0.0) - panelHeight + 44.0, bottomInset) - panelHeight)
|
||||
bottomInset += panelHeight
|
||||
}
|
||||
|
||||
@ -879,8 +909,17 @@ public class WallpaperGalleryController: ViewController {
|
||||
case let .file(_, _, _, _, isPattern, _, slug, _, settings):
|
||||
if isPattern {
|
||||
if !settings.colors.isEmpty {
|
||||
if settings.colors.count >= 2 {
|
||||
if settings.colors.count == 2 {
|
||||
options.append("bg_color=\(UIColor(rgb: settings.colors[0]).hexString)-\(UIColor(rgb: settings.colors[1]).hexString)")
|
||||
} else if settings.colors.count >= 3 {
|
||||
var colorsString = ""
|
||||
for color in settings.colors {
|
||||
if !colorsString.isEmpty {
|
||||
colorsString.append("~")
|
||||
}
|
||||
colorsString.append(UIColor(rgb: color).hexString)
|
||||
}
|
||||
options.append("bg_color=\(colorsString)")
|
||||
} else {
|
||||
options.append("bg_color=\(UIColor(rgb: settings.colors[0]).hexString)")
|
||||
}
|
||||
@ -902,7 +941,20 @@ public class WallpaperGalleryController: ViewController {
|
||||
case let .color(color):
|
||||
controller = ShareController(context: context, subject: .url("https://t.me/bg/\(UIColor(rgb: color).hexString)"))
|
||||
case let .gradient(colors, _):
|
||||
controller = ShareController(context: context, subject:. url("https://t.me/bg/\(UIColor(rgb: colors[0]).hexString)-\(UIColor(rgb: colors[1]).hexString)"))
|
||||
var colorsString = ""
|
||||
|
||||
for color in colors {
|
||||
if !colorsString.isEmpty {
|
||||
if colors.count >= 3 {
|
||||
colorsString.append("~")
|
||||
} else {
|
||||
colorsString.append("-")
|
||||
}
|
||||
}
|
||||
colorsString.append(UIColor(rgb: color).hexString)
|
||||
}
|
||||
|
||||
controller = ShareController(context: context, subject:. url("https://t.me/bg/\(colorsString)"))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@ -116,11 +116,15 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
var action: (() -> Void)?
|
||||
var requestPatternPanel: ((Bool, TelegramWallpaper) -> Void)?
|
||||
var requestColorsPanel: (([UIColor]?) -> Void)?
|
||||
var requestRotateGradient: ((Int32) -> Void)?
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
private var validOffset: CGFloat?
|
||||
|
||||
private var initialWallpaper: TelegramWallpaper?
|
||||
|
||||
private let playButtonPlayImage: UIImage?
|
||||
private let playButtonRotateImage: UIImage?
|
||||
|
||||
init(context: AccountContext) {
|
||||
self.context = context
|
||||
@ -151,7 +155,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
self.colorsButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_WallpaperColors, value: .colors(false, [.clear]))
|
||||
self.playButtonNode = HighlightableButtonNode()
|
||||
|
||||
self.playButtonNode.setImage(generateImage(CGSize(width: 48.0, height: 48.0), rotatedContext: { size, context in
|
||||
self.playButtonPlayImage = generateImage(CGSize(width: 48.0, height: 48.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
|
||||
@ -174,7 +178,11 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||
}
|
||||
context.translateBy(x: -(diameter - size.width) / 2.0 - 1.5, y: -(diameter - size.height) / 2.0)
|
||||
}), for: [])
|
||||
})
|
||||
|
||||
self.playButtonRotateImage = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorRotateIcon"), color: .white)
|
||||
|
||||
self.playButtonNode.setImage(self.playButtonPlayImage, for: [])
|
||||
|
||||
super.init()
|
||||
|
||||
@ -250,8 +258,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
if previousEntry != entry {
|
||||
self.preparePatternEditing()
|
||||
}
|
||||
|
||||
self.patternButtonNode.isSelected = self.arguments.patternEnabled
|
||||
|
||||
self.colorsButtonNode.colors = self.calculateGradientColors() ?? defaultBuiltinWallpaperGradientColors
|
||||
|
||||
let imagePromise = Promise<UIImage?>()
|
||||
|
||||
@ -274,14 +282,35 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
switch entry {
|
||||
case let .wallpaper(wallpaper, _):
|
||||
if case let .file(_, _, _, _, isPattern, _, _, _, settings) = wallpaper, isPattern, settings.colors.count >= 3 {
|
||||
if case let .file(_, _, _, _, isPattern, _, _, _, settings) = wallpaper, isPattern {
|
||||
self.nativeNode.isHidden = false
|
||||
self.nativeNode.update(wallpaper: wallpaper)
|
||||
self.patternButtonNode.isSelected = isPattern
|
||||
|
||||
if isPattern && settings.colors.count >= 3 {
|
||||
self.playButtonNode.setImage(self.playButtonPlayImage, for: [])
|
||||
} else {
|
||||
self.playButtonNode.setImage(self.playButtonRotateImage, for: [])
|
||||
}
|
||||
} else if case let .gradient(colors, _) = wallpaper {
|
||||
self.nativeNode.isHidden = false
|
||||
self.nativeNode.update(wallpaper: wallpaper)
|
||||
self.patternButtonNode.isSelected = false
|
||||
|
||||
if colors.count >= 3 {
|
||||
self.playButtonNode.setImage(self.playButtonPlayImage, for: [])
|
||||
} else {
|
||||
self.playButtonNode.setImage(self.playButtonRotateImage, for: [])
|
||||
}
|
||||
} else {
|
||||
self.nativeNode.isHidden = true
|
||||
self.patternButtonNode.isSelected = false
|
||||
self.playButtonNode.setImage(self.playButtonRotateImage, for: [])
|
||||
}
|
||||
default:
|
||||
self.nativeNode.isHidden = true
|
||||
self.patternButtonNode.isSelected = false
|
||||
self.playButtonNode.setImage(self.playButtonRotateImage, for: [])
|
||||
}
|
||||
|
||||
switch entry {
|
||||
@ -311,7 +340,11 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
case let .gradient(colors, settings):
|
||||
displaySize = CGSize(width: 1.0, height: 1.0)
|
||||
contentSize = displaySize
|
||||
signal = gradientImage([UIColor(rgb: colors[0]), UIColor(rgb: colors[1])], rotation: settings.rotation)
|
||||
if colors.count >= 2 {
|
||||
signal = gradientImage([UIColor(rgb: colors[0]), UIColor(rgb: colors[1])], rotation: settings.rotation)
|
||||
} else {
|
||||
signal = solidColorImage(UIColor(rgb: colors[0]))
|
||||
}
|
||||
fetchSignal = .complete()
|
||||
statusSignal = .single(.Local)
|
||||
subtitleSignal = .single(nil)
|
||||
@ -319,10 +352,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox)
|
||||
isBlurrable = false
|
||||
case let .file(file):
|
||||
let gradientColors = self.calculateGradientColors() ?? defaultBuiltinWallpaperGradientColors
|
||||
|
||||
self.colorsButtonNode.colors = gradientColors
|
||||
|
||||
let dimensions = file.file.dimensions ?? PixelDimensions(width: 2000, height: 4000)
|
||||
contentSize = dimensions.cgSize
|
||||
displaySize = dimensions.cgSize.dividedByScreenScale().integralFloor
|
||||
@ -738,12 +767,20 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
switch entry {
|
||||
case let .wallpaper(wallpaper, _):
|
||||
switch wallpaper {
|
||||
case let .file(_, _, _, _, _, _, _, _, settings):
|
||||
if settings.colors.count >= 3 {
|
||||
return settings.colors.map(UIColor.init(rgb:))
|
||||
case let .file(_, _, _, _, isPattern, _, _, _, settings):
|
||||
if isPattern {
|
||||
if settings.colors.isEmpty {
|
||||
return nil
|
||||
} else {
|
||||
return settings.colors.map(UIColor.init(rgb:))
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case let .gradient(colors, _):
|
||||
return colors.map(UIColor.init(rgb:))
|
||||
case let .color(color):
|
||||
return [UIColor(rgb: color)]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -763,7 +800,29 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
}
|
||||
|
||||
@objc private func togglePlay() {
|
||||
self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring))
|
||||
guard let entry = self.entry, case let .wallpaper(wallpaper, _) = entry else {
|
||||
return
|
||||
}
|
||||
switch wallpaper {
|
||||
case let .gradient(colors, settings):
|
||||
if colors.count >= 3 {
|
||||
self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring))
|
||||
} else {
|
||||
let rotation = settings.rotation ?? 0
|
||||
self.requestRotateGradient?((rotation + 90) % 360)
|
||||
}
|
||||
case let .file(file):
|
||||
if file.isPattern {
|
||||
if file.settings.colors.count >= 3 {
|
||||
self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring))
|
||||
} else {
|
||||
let rotation = file.settings.rotation ?? 0
|
||||
self.requestRotateGradient?((rotation + 90) % 360)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func preparePatternEditing() {
|
||||
@ -837,12 +896,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
let buttonSize = CGSize(width: maxButtonWidth, height: 30.0)
|
||||
let alpha = 1.0 - min(1.0, max(0.0, abs(offset.y) / 50.0))
|
||||
|
||||
var additionalYOffset: CGFloat = 0.0
|
||||
if self.patternButtonNode.isSelected {
|
||||
let additionalYOffset: CGFloat = 0.0
|
||||
/*if self.patternButtonNode.isSelected {
|
||||
additionalYOffset = -235.0
|
||||
} else if self.colorsButtonNode.isSelected {
|
||||
additionalYOffset = -235.0
|
||||
}
|
||||
}*/
|
||||
|
||||
let leftButtonFrame = CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0 - buttonSize.width - 10.0) + offset.x, y: layout.size.height - 49.0 - layout.intrinsicInsets.bottom - 54.0 + offset.y + additionalYOffset), size: buttonSize)
|
||||
let centerButtonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0) + offset.x, y: layout.size.height - 49.0 - layout.intrinsicInsets.bottom - 54.0 + offset.y + additionalYOffset), size: buttonSize)
|
||||
@ -883,23 +942,30 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
motionAlpha = 1.0
|
||||
motionFrame = centerButtonFrame
|
||||
case .color:
|
||||
motionAlpha = 0.0
|
||||
patternAlpha = 1.0
|
||||
if self.patternButtonNode.isSelected {
|
||||
patternFrame = leftButtonFrame
|
||||
motionAlpha = 1.0
|
||||
motionFrame = rightButtonFrame
|
||||
}
|
||||
|
||||
patternFrame = leftButtonFrame.offsetBy(dx: -centerOffset, dy: 0.0)
|
||||
colorsFrame = colorsFrame.offsetBy(dx: centerOffset, dy: 0.0)
|
||||
playAlpha = 1.0
|
||||
|
||||
colorsAlpha = 1.0
|
||||
case .image:
|
||||
blurAlpha = 1.0
|
||||
blurFrame = leftButtonFrame
|
||||
motionAlpha = 1.0
|
||||
motionFrame = rightButtonFrame
|
||||
case .gradient:
|
||||
motionAlpha = 1.0
|
||||
case let .file(file):
|
||||
if file.settings.colors.count >= 3 {
|
||||
self.colorsButtonNode.colors = self.calculateGradientColors()
|
||||
motionAlpha = 0.0
|
||||
patternAlpha = 1.0
|
||||
|
||||
patternFrame = leftButtonFrame.offsetBy(dx: -centerOffset, dy: 0.0)
|
||||
colorsFrame = colorsFrame.offsetBy(dx: centerOffset, dy: 0.0)
|
||||
playAlpha = 1.0
|
||||
|
||||
colorsAlpha = 1.0
|
||||
case let .file(file):
|
||||
if file.isPattern {
|
||||
motionAlpha = 0.0
|
||||
patternAlpha = 1.0
|
||||
|
||||
@ -948,8 +1014,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
private func updateMessagesLayout(layout: ContainerViewLayout, offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
var bottomInset: CGFloat = 115.0
|
||||
|
||||
if self.patternButtonNode.isSelected || self.colorsButtonNode.isSelected {
|
||||
bottomInset = 350.0
|
||||
//bottomInset = 350.0
|
||||
}
|
||||
|
||||
var items: [ListViewItem] = []
|
||||
@ -1058,10 +1125,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
self.blurredNode.frame = self.imageNode.bounds
|
||||
}
|
||||
|
||||
var additionalYOffset: CGFloat = 0.0
|
||||
if self.patternButtonNode.isSelected || self.colorsButtonNode.isSelected {
|
||||
additionalYOffset = -190.0
|
||||
}
|
||||
let additionalYOffset: CGFloat = 0.0
|
||||
|
||||
self.statusNode.frame = CGRect(x: layout.safeInsets.left + floorToScreenPixels((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - progressDiameter) / 2.0), y: floorToScreenPixels((layout.size.height + additionalYOffset - progressDiameter) / 2.0), width: progressDiameter, height: progressDiameter)
|
||||
|
||||
|
||||
@ -15,13 +15,21 @@ enum WallpaperOptionButtonValue {
|
||||
private func generateColorsImage(diameter: CGFloat, colors: [UIColor]) -> UIImage? {
|
||||
return generateImage(CGSize(width: diameter, height: diameter), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.addEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
context.clip()
|
||||
for i in 0 ..< min(colors.count, 4) {
|
||||
let x = i % 2
|
||||
let y = i / 2
|
||||
context.setFillColor(colors[i].cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(x: CGFloat(x) * size.width / 2.0, y: CGFloat(y) * size.height / 2.0), size: CGSize(width: size.width / 2.0, height: size.height / 2.0)))
|
||||
|
||||
if !colors.isEmpty {
|
||||
let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
|
||||
var startAngle = -CGFloat.pi * 0.5
|
||||
for i in 0 ..< colors.count {
|
||||
context.setFillColor(colors[i].cgColor)
|
||||
|
||||
let endAngle = startAngle + 2.0 * CGFloat.pi * (1.0 / CGFloat(colors.count))
|
||||
|
||||
context.move(to: center)
|
||||
context.addArc(center: center, radius: size.width / 2.0, startAngle: startAngle, endAngle: endAngle, clockwise: false)
|
||||
context.fillPath()
|
||||
|
||||
startAngle = endAngle
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -100,6 +100,10 @@ final class GroupVideoNode: ASDisplayNode {
|
||||
|
||||
self.containerNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
}
|
||||
|
||||
func updateIsEnabled(_ isEnabled: Bool) {
|
||||
self.videoView.updateIsEnabled(isEnabled)
|
||||
}
|
||||
|
||||
func updateIsBlurred(isBlurred: Bool, light: Bool = false, animated: Bool = true) {
|
||||
if self.isBlurred == isBlurred {
|
||||
|
||||
@ -927,6 +927,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
let updateIsEnabled = view.updateIsEnabled
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
@ -978,6 +979,9 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { value in
|
||||
updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
@ -997,6 +1001,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
let updateIsEnabled = view.updateIsEnabled
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
@ -1048,6 +1053,9 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { value in
|
||||
updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
|
||||
@ -1942,9 +1942,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
} else if participant.muteState?.mutedByYou == true {
|
||||
strongSelf.genericCallContext?.setVolume(ssrc: ssrc, volume: 0.0)
|
||||
}
|
||||
if participant.videoJsonDescription == nil {
|
||||
strongSelf.genericCallContext?.removeIncomingVideoSource(ssrc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2426,6 +2423,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
let updateIsEnabled = view.updateIsEnabled
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
@ -2477,6 +2475,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { value in
|
||||
updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
@ -2915,6 +2916,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
let updateIsEnabled = view.updateIsEnabled
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
@ -2966,6 +2968,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { value in
|
||||
updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
|
||||
@ -3844,6 +3844,9 @@ public final class VoiceChatController: ViewController {
|
||||
self.actionButton.update(size: centralButtonSize, buttonSize: CGSize(width: 112.0, height: 112.0), state: actionButtonState, title: actionButtonTitle, subtitle: actionButtonSubtitle, dark: self.isFullscreen, small: smallButtons, animated: true)
|
||||
|
||||
var hasCameraButton = self.callState?.isVideoEnabled ?? false
|
||||
#if DEBUG
|
||||
hasCameraButton = true
|
||||
#endif
|
||||
switch actionButtonState {
|
||||
case let .active(state):
|
||||
switch state {
|
||||
|
||||
@ -259,6 +259,8 @@ final class VoiceChatTileItemNode: ASDisplayNode {
|
||||
if let shimmerNode = self.shimmerNode {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||
}
|
||||
let isVisible = rect.maxY >= 0.0 && rect.minY <= containerSize.height
|
||||
self.videoNode?.updateIsEnabled(isVisible)
|
||||
}
|
||||
|
||||
func update(size: CGSize, availableWidth: CGFloat, item: VoiceChatTileItem, transition: ContainedViewLayoutTransition) {
|
||||
|
||||
@ -596,7 +596,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData, presentationData.dateTimeFormat, message, attributes, wallpaper, imageDateAndStatus, .full, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
|
||||
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
||||
refineContentImageLayout = refineLayout
|
||||
if case let .file(_, _, _, _, isTheme, _) = wallpaper.content, isTheme {
|
||||
if case let .file(_, _, _, isTheme, _) = wallpaper.content, isTheme {
|
||||
skipStandardStatus = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -419,7 +419,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
unboundSize = CGSize(width: floor(dimensions.cgSize.width * 0.5), height: floor(dimensions.cgSize.height * 0.5))
|
||||
} else if let wallpaper = media as? WallpaperPreviewMedia {
|
||||
switch wallpaper.content {
|
||||
case let .file(file, _, _, _, isTheme, isSupported):
|
||||
case let .file(file, _, _, isTheme, isSupported):
|
||||
if let thumbnail = file.previewRepresentations.first, var dimensions = file.dimensions {
|
||||
let dimensionsVertical = dimensions.width < dimensions.height
|
||||
let thumbnailVertical = thumbnail.dimensions.width < thumbnail.dimensions.height
|
||||
@ -570,11 +570,12 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
emptyColor = message.effectivelyIncoming(context.account.peerId) ? presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
|
||||
}
|
||||
if let wallpaper = media as? WallpaperPreviewMedia {
|
||||
if case let .file(_, patternColor, patternBottomColor, rotation, _, _) = wallpaper.content {
|
||||
if case let .file(_, patternColors, rotation, _, _) = wallpaper.content {
|
||||
var colors: [UIColor] = []
|
||||
colors.append(patternColor ?? UIColor(rgb: 0xd6e2ee, alpha: 0.5))
|
||||
if let patternBottomColor = patternBottomColor {
|
||||
colors.append(patternBottomColor)
|
||||
if patternColors.isEmpty {
|
||||
colors.append(UIColor(rgb: 0xd6e2ee, alpha: 0.5))
|
||||
} else {
|
||||
colors.append(contentsOf: patternColors.map(UIColor.init(rgb:)))
|
||||
}
|
||||
patternArguments = PatternWallpaperArguments(colors: colors, rotation: rotation)
|
||||
}
|
||||
@ -714,7 +715,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
} else if let wallpaper = media as? WallpaperPreviewMedia {
|
||||
updateImageSignal = { synchronousLoad, _ in
|
||||
switch wallpaper.content {
|
||||
case let .file(file, _, _, _, isTheme, _):
|
||||
case let .file(file, _, _, isTheme, _):
|
||||
if isTheme {
|
||||
return themeImage(account: context.account, accountManager: context.sharedContext.accountManager, source: .file(FileMediaReference.message(message: MessageReference(message), media: file)))
|
||||
} else {
|
||||
@ -732,12 +733,12 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
return themeImage(account: context.account, accountManager: context.sharedContext.accountManager, source: .settings(settings))
|
||||
case let .color(color):
|
||||
return solidColorImage(color)
|
||||
case let .gradient(topColor, bottomColor, rotation):
|
||||
return gradientImage([topColor, bottomColor], rotation: rotation ?? 0)
|
||||
case let .gradient(colors, rotation):
|
||||
return gradientImage(colors.map(UIColor.init(rgb:)), rotation: rotation ?? 0)
|
||||
}
|
||||
}
|
||||
|
||||
if case let .file(file, _, _, _, _, _) = wallpaper.content {
|
||||
if case let .file(file, _, _, _, _) = wallpaper.content {
|
||||
updatedFetchControls = FetchControls(fetch: { manual in
|
||||
if let strongSelf = self {
|
||||
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: manual).start())
|
||||
@ -782,7 +783,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
} else if let wallpaper = media as? WallpaperPreviewMedia {
|
||||
switch wallpaper.content {
|
||||
case let .file(file, _, _, _, _, _):
|
||||
case let .file(file, _, _, _, _):
|
||||
updatedStatusSignal = messageMediaFileStatus(context: context, messageId: message.id, file: file)
|
||||
|> map { resourceStatus -> (MediaResourceStatus, MediaResourceStatus?) in
|
||||
return (resourceStatus, nil)
|
||||
|
||||
@ -171,15 +171,13 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
mediaAndFlags = (webpage.image ?? file, [.preferMediaBeforeText])
|
||||
}
|
||||
} else if webpage.type == "telegram_background" {
|
||||
var topColor: UIColor?
|
||||
var bottomColor: UIColor?
|
||||
var colors: [UInt32] = []
|
||||
var rotation: Int32?
|
||||
if let wallpaper = parseWallpaperUrl(webpage.url), case let .slug(_, _, firstColor, secondColor, intensity, rotationValue) = wallpaper {
|
||||
topColor = firstColor?.withAlphaComponent(CGFloat(intensity ?? 50) / 100.0)
|
||||
bottomColor = secondColor?.withAlphaComponent(CGFloat(intensity ?? 50) / 100.0)
|
||||
if let wallpaper = parseWallpaperUrl(webpage.url), case let .slug(_, _, colorsValue, intensity, rotationValue) = wallpaper {
|
||||
colors = colorsValue
|
||||
rotation = rotationValue
|
||||
}
|
||||
let media = WallpaperPreviewMedia(content: .file(file, topColor, bottomColor, rotation, false, false))
|
||||
let media = WallpaperPreviewMedia(content: .file(file, colors, rotation, false, false))
|
||||
mediaAndFlags = (media, [.preferMediaAspectFilled])
|
||||
if let fileSize = file.size {
|
||||
badge = dataSizeString(fileSize, formatting: DataSizeStringFormatting(chatPresentationData: item.presentationData))
|
||||
@ -207,25 +205,24 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
} else if let type = webpage.type {
|
||||
if type == "telegram_background" {
|
||||
var topColor: UIColor?
|
||||
var colors: [UInt32] = []
|
||||
var bottomColor: UIColor?
|
||||
var rotation: Int32?
|
||||
if let wallpaper = parseWallpaperUrl(webpage.url) {
|
||||
if case let .color(color) = wallpaper {
|
||||
topColor = color
|
||||
} else if case let .gradient(topColorValue, bottomColorValue, rotationValue) = wallpaper {
|
||||
topColor = topColorValue
|
||||
bottomColor = bottomColorValue
|
||||
colors = [color.rgb]
|
||||
} else if case let .gradient(colorsValue, rotationValue) = wallpaper {
|
||||
colors = colorsValue
|
||||
rotation = rotationValue
|
||||
}
|
||||
}
|
||||
|
||||
var content: WallpaperPreviewMediaContent?
|
||||
if let topColor = topColor {
|
||||
if let bottomColor = bottomColor {
|
||||
content = .gradient(topColor, bottomColor, rotation)
|
||||
if !colors.isEmpty {
|
||||
if colors.count >= 2 {
|
||||
content = .gradient(colors, rotation)
|
||||
} else {
|
||||
content = .color(topColor)
|
||||
content = .color(UIColor(rgb: colors[0]))
|
||||
}
|
||||
}
|
||||
if let content = content {
|
||||
@ -254,7 +251,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
file = contentFile
|
||||
}
|
||||
if let file = file {
|
||||
let media = WallpaperPreviewMedia(content: .file(file, nil, nil, nil, true, isSupported))
|
||||
let media = WallpaperPreviewMedia(content: .file(file, [], nil, true, isSupported))
|
||||
mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
|
||||
} else if let settings = settings {
|
||||
let media = WallpaperPreviewMedia(content: .themeSettings(settings))
|
||||
|
||||
@ -258,12 +258,12 @@ func openChatWallpaper(context: AccountContext, message: Message, present: @esca
|
||||
if case let .wallpaper(parameter) = resolvedUrl {
|
||||
let source: WallpaperListSource
|
||||
switch parameter {
|
||||
case let .slug(slug, options, firstColor, secondColor, intensity, rotation):
|
||||
source = .slug(slug, content.file, options, firstColor, secondColor, intensity, rotation, message)
|
||||
case let .slug(slug, options, colors, intensity, rotation):
|
||||
source = .slug(slug, content.file, options, colors, intensity, rotation, message)
|
||||
case let .color(color):
|
||||
source = .wallpaper(.color(color.argb), nil, nil, nil, nil, nil, message)
|
||||
case let .gradient(topColor, bottomColor, rotation):
|
||||
source = .wallpaper(.gradient([topColor.argb, bottomColor.argb], WallpaperSettings(rotation: rotation)), nil, nil, nil, nil, rotation, message)
|
||||
source = .wallpaper(.color(color.argb), nil, [], nil, nil, message)
|
||||
case let .gradient(colors, rotation):
|
||||
source = .wallpaper(.gradient(colors, WallpaperSettings(rotation: rotation)), nil, [], nil, rotation, message)
|
||||
}
|
||||
|
||||
let controller = WallpaperGalleryController(context: context, source: source)
|
||||
|
||||
@ -294,30 +294,28 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
|
||||
let signal: Signal<TelegramWallpaper, GetWallpaperError>
|
||||
var options: WallpaperPresentationOptions?
|
||||
var topColor: UIColor?
|
||||
var bottomColor: UIColor?
|
||||
var colors: [UInt32] = []
|
||||
var intensity: Int32?
|
||||
var rotation: Int32?
|
||||
switch parameter {
|
||||
case let .slug(slug, wallpaperOptions, firstColor, secondColor, intensityValue, rotationValue):
|
||||
case let .slug(slug, wallpaperOptions, colorsValue, intensityValue, rotationValue):
|
||||
signal = getWallpaper(network: context.account.network, slug: slug)
|
||||
options = wallpaperOptions
|
||||
topColor = firstColor
|
||||
bottomColor = secondColor
|
||||
colors = colorsValue
|
||||
intensity = intensityValue
|
||||
rotation = rotationValue
|
||||
controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
||||
present(controller!, nil)
|
||||
case let .color(color):
|
||||
signal = .single(.color(color.argb))
|
||||
case let .gradient(topColor, bottomColor, rotation):
|
||||
signal = .single(.gradient([topColor.argb, bottomColor.argb], WallpaperSettings(rotation: rotation)))
|
||||
case let .gradient(colors, rotation):
|
||||
signal = .single(.gradient(colors, WallpaperSettings(rotation: rotation)))
|
||||
}
|
||||
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).start(next: { [weak controller] wallpaper in
|
||||
controller?.dismiss()
|
||||
let galleryController = WallpaperGalleryController(context: context, source: .wallpaper(wallpaper, options, topColor, bottomColor, intensity, rotation, nil))
|
||||
let galleryController = WallpaperGalleryController(context: context, source: .wallpaper(wallpaper, options, colors, intensity, rotation, nil))
|
||||
present(galleryController, nil)
|
||||
}, error: { [weak controller] error in
|
||||
controller?.dismiss()
|
||||
|
||||
@ -5,9 +5,9 @@ import TelegramCore
|
||||
import SyncCore
|
||||
|
||||
enum WallpaperPreviewMediaContent: Equatable {
|
||||
case file(TelegramMediaFile, UIColor?, UIColor?, Int32?, Bool, Bool)
|
||||
case file(TelegramMediaFile, [UInt32], Int32?, Bool, Bool)
|
||||
case color(UIColor)
|
||||
case gradient(UIColor, UIColor, Int32?)
|
||||
case gradient([UInt32], Int32?)
|
||||
case themeSettings(TelegramThemeSettings)
|
||||
}
|
||||
|
||||
|
||||
@ -499,6 +499,9 @@ public final class OngoingGroupCallContext {
|
||||
view?.setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { [weak view] value in
|
||||
view?.updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
#else
|
||||
|
||||
@ -376,6 +376,9 @@ public final class OngoingCallVideoCapturer {
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak view] f in
|
||||
view?.setOnIsMirroredUpdated(f)
|
||||
},
|
||||
updateIsEnabled: { [weak view] value in
|
||||
view?.updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
@ -508,6 +511,7 @@ public final class OngoingCallContextPresentationCallVideoView {
|
||||
public let getAspect: () -> CGFloat
|
||||
public let setOnOrientationUpdated: (((OngoingCallVideoOrientation, CGFloat) -> Void)?) -> Void
|
||||
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
||||
public let updateIsEnabled: (Bool) -> Void
|
||||
|
||||
public init(
|
||||
view: UIView,
|
||||
@ -515,7 +519,8 @@ public final class OngoingCallContextPresentationCallVideoView {
|
||||
getOrientation: @escaping () -> OngoingCallVideoOrientation,
|
||||
getAspect: @escaping () -> CGFloat,
|
||||
setOnOrientationUpdated: @escaping (((OngoingCallVideoOrientation, CGFloat) -> Void)?) -> Void,
|
||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void,
|
||||
updateIsEnabled: @escaping (Bool) -> Void
|
||||
) {
|
||||
self.view = view
|
||||
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
||||
@ -523,6 +528,7 @@ public final class OngoingCallContextPresentationCallVideoView {
|
||||
self.getAspect = getAspect
|
||||
self.setOnOrientationUpdated = setOnOrientationUpdated
|
||||
self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
|
||||
self.updateIsEnabled = updateIsEnabled
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,6 +924,9 @@ public final class OngoingCallContext {
|
||||
view?.setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { [weak view] value in
|
||||
view?.updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
|
||||
@ -19,6 +19,7 @@ objc_library(
|
||||
"tgcalls/tgcalls/platform/uwp/**",
|
||||
"tgcalls/tgcalls/platform/darwin/VideoCameraCapturerMac.*",
|
||||
"tgcalls/tgcalls/platform/darwin/VideoMetalViewMac.*",
|
||||
"tgcalls/tgcalls/platform/darwin/VideoSampleBufferViewMac.*",
|
||||
"tgcalls/tgcalls/platform/darwin/GLVideoViewMac.*",
|
||||
"tgcalls/tgcalls/platform/darwin/ScreenCapturer.*",
|
||||
"tgcalls/tgcalls/platform/darwin/DesktopSharingCapturer.*",
|
||||
|
||||
@ -101,6 +101,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
||||
- (void)setOnFirstFrameReceived:(void (^ _Nullable)(float))onFirstFrameReceived;
|
||||
- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc, CGFloat))onOrientationUpdated;
|
||||
- (void)setOnIsMirroredUpdated:(void (^ _Nullable)(bool))onIsMirroredUpdated;
|
||||
- (void)updateIsEnabled:(bool)isEnabled;
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
- (void)setVideoContentMode:(CALayerContentsGravity _Nonnull )mode;
|
||||
- (void)setForceMirrored:(bool)forceMirrored;
|
||||
|
||||
@ -101,6 +101,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateIsEnabled:(bool)isEnabled {
|
||||
[self setEnabled:isEnabled];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GLVideoView (VideoViewImpl) <OngoingCallThreadLocalContextWebrtcVideoView, OngoingCallThreadLocalContextWebrtcVideoViewImpl>
|
||||
@ -144,6 +148,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateIsEnabled:(bool)__unused isEnabled {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface VideoSampleBufferView (VideoViewImpl) <OngoingCallThreadLocalContextWebrtcVideoView, OngoingCallThreadLocalContextWebrtcVideoViewImpl>
|
||||
@ -187,6 +194,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateIsEnabled:(bool)isEnabled {
|
||||
[self setEnabled:isEnabled];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface OngoingCallThreadLocalContextVideoCapturer () {
|
||||
@ -1271,7 +1282,21 @@ private:
|
||||
__weak GroupCallThreadLocalContext *weakSelf = self;
|
||||
id<OngoingCallThreadLocalContextQueueWebrtc> queue = _queue;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([VideoMetalView isSupported]) {
|
||||
if (true) {
|
||||
VideoSampleBufferView *remoteRenderer = [[VideoSampleBufferView alloc] initWithFrame:CGRectZero];
|
||||
remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill;
|
||||
|
||||
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
|
||||
|
||||
[queue dispatch:^{
|
||||
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
||||
if (strongSelf && strongSelf->_instance) {
|
||||
strongSelf->_instance->addIncomingVideoOutput(endpointId.UTF8String, sink);
|
||||
}
|
||||
}];
|
||||
|
||||
completion(remoteRenderer);
|
||||
} else if ([VideoMetalView isSupported]) {
|
||||
VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
|
||||
#if TARGET_OS_IPHONE
|
||||
remoteRenderer.videoContentMode = UIViewContentModeScaleToFill;
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 66186febe0e20d7bc39053e627f93fd95a5ecac9
|
||||
Subproject commit 197878f551449341bc3fa74e117d011a9d0e8120
|
||||
@ -186,7 +186,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
let parameter: WallpaperUrlParameter
|
||||
if [6, 8].contains(component.count), component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil, let color = UIColor(hexString: component) {
|
||||
parameter = .color(color)
|
||||
} else if [13, 15, 17].contains(component.count), component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF-").inverted) == nil {
|
||||
} else if [13, 15, 17].contains(component.count), component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF-~").inverted) == nil {
|
||||
var rotation: Int32?
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
@ -197,17 +197,33 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
}
|
||||
}
|
||||
}
|
||||
let components = component.components(separatedBy: "-")
|
||||
if components.count == 2, let topColor = UIColor(hexString: components[0]), let bottomColor = UIColor(hexString: components[1]) {
|
||||
parameter = .gradient(topColor, bottomColor, rotation)
|
||||
if component.contains("~") {
|
||||
let components = component.components(separatedBy: "~")
|
||||
|
||||
var colors: [UInt32] = []
|
||||
if components.count >= 2 && components.count <= 4 {
|
||||
colors = components.compactMap { component in
|
||||
return UIColor(hexString: component)?.rgb
|
||||
}
|
||||
}
|
||||
|
||||
if !colors.isEmpty {
|
||||
parameter = .gradient(colors, rotation)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
let components = component.components(separatedBy: "-")
|
||||
if components.count == 2, let topColor = UIColor(hexString: components[0]), let bottomColor = UIColor(hexString: components[1]) {
|
||||
parameter = .gradient([topColor.rgb, bottomColor.rgb], rotation)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var options: WallpaperPresentationOptions = []
|
||||
var intensity: Int32?
|
||||
var topColor: UIColor?
|
||||
var bottomColor: UIColor?
|
||||
var colors: [UInt32] = []
|
||||
var rotation: Int32?
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
@ -225,12 +241,18 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
}
|
||||
} else if queryItem.name == "bg_color" {
|
||||
if [6, 8].contains(value.count), value.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil, let color = UIColor(hexString: value) {
|
||||
topColor = color
|
||||
colors = [color.rgb]
|
||||
} else if [13, 15, 17].contains(value.count), value.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF-").inverted) == nil {
|
||||
let components = value.components(separatedBy: "-")
|
||||
if components.count == 2, let topColorValue = UIColor(hexString: components[0]), let bottomColorValue = UIColor(hexString: components[1]) {
|
||||
topColor = topColorValue
|
||||
bottomColor = bottomColorValue
|
||||
colors = [topColorValue.rgb, bottomColorValue.rgb]
|
||||
}
|
||||
} else if value.contains("~") {
|
||||
let components = value.components(separatedBy: "~")
|
||||
if components.count >= 2 && components.count <= 4 {
|
||||
colors = components.compactMap { component in
|
||||
return UIColor(hexString: component)?.rgb
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if queryItem.name == "intensity" {
|
||||
@ -241,7 +263,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter = .slug(component, options, topColor, bottomColor, intensity, rotation)
|
||||
parameter = .slug(component, options, colors, intensity, rotation)
|
||||
}
|
||||
return .wallpaper(parameter)
|
||||
} else if pathComponents[0] == "addtheme" {
|
||||
|
||||
@ -1136,7 +1136,20 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
||||
outgoingColor = (bubbleColor, bubbleColor)
|
||||
}
|
||||
} else {
|
||||
topBackgroundColor = UIColor(rgb: 0xd6e2ee)
|
||||
if let wallpaper = wallpaper, case let .file(file) = wallpaper {
|
||||
topBackgroundColor = file.settings.colors.first.flatMap { UIColor(rgb: $0) } ?? UIColor(rgb: 0xd6e2ee)
|
||||
if file.settings.colors.count >= 2 {
|
||||
bottomBackgroundColor = UIColor(rgb: file.settings.colors[1])
|
||||
}
|
||||
} else if let wallpaper = wallpaper, case let .gradient(colors, _) = wallpaper {
|
||||
topBackgroundColor = colors.first.flatMap { UIColor(rgb: $0) } ?? UIColor(rgb: 0xd6e2ee)
|
||||
if colors.count >= 2 {
|
||||
bottomBackgroundColor = UIColor(rgb: colors[1])
|
||||
}
|
||||
} else {
|
||||
topBackgroundColor = defaultBuiltinWallpaperGradientColors[0]
|
||||
bottomBackgroundColor = defaultBuiltinWallpaperGradientColors[1]
|
||||
}
|
||||
outgoingColor = (UIColor(rgb: 0xe1ffc7), UIColor(rgb: 0xe1ffc7))
|
||||
}
|
||||
case .day:
|
||||
@ -1190,6 +1203,8 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
||||
colors = [0xd6e2ee]
|
||||
topBackgroundColor = UIColor(rgb: 0xd6e2ee)
|
||||
}
|
||||
} else {
|
||||
colors = defaultBuiltinWallpaperGradientColors.map(\.rgb)
|
||||
}
|
||||
|
||||
colorsSignal = .single(((topBackgroundColor, bottomBackgroundColor, colors), (incomingColor, incomingColor), outgoingColor, nil, rotation))
|
||||
|
||||
2
third-party/rnnoise/BUILD
vendored
2
third-party/rnnoise/BUILD
vendored
@ -41,6 +41,8 @@ objc_library(
|
||||
],
|
||||
copts = [
|
||||
"-D{name}=rnnoise_{name}".format(name = name) for name in replace_symbol_list
|
||||
] + [
|
||||
"-Os",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
2
third-party/webrtc/BUILD
vendored
2
third-party/webrtc/BUILD
vendored
@ -9,7 +9,7 @@ config_setting(
|
||||
)
|
||||
|
||||
optimization_flags = select({
|
||||
#":debug_build": ["-Os"],
|
||||
":debug_build": ["-Os"],
|
||||
"//conditions:default": [],
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user