mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
dfc5077946
commit
b4415c251b
@ -12546,3 +12546,9 @@ Sorry for the inconvenience.";
|
||||
"Stars.Intro.StarsSent_1" = "%@ Star sent.";
|
||||
"Stars.Intro.StarsSent_any" = "%@ Stars sent.";
|
||||
"Stars.Intro.StarsSent.ViewChat" = "View Chat";
|
||||
|
||||
"Stars.Gift.Received.Title" = "Received Gift";
|
||||
"Stars.Gift.Received.Text" = "Use Stars to unlock content and services on Telegram. [See Examples >]()";
|
||||
|
||||
"Stars.Gift.Sent.Title" = "Sent Gift";
|
||||
"Stars.Gift.Sent.Text" = "With Stars, %@ will be able to unlock content and services on Telegram. [See Examples >]()";
|
||||
|
@ -2274,7 +2274,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
safeInset += layout.safeInsets.right + 16.0
|
||||
}
|
||||
let navigationHeight = navigationLayout(layout: layout).navigationFrame.height
|
||||
self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: self.view.bounds.width - 54.0 - selectedSize.width - safeInset, y: floorToScreenPixels((navigationHeight - selectedSize.height) / 2.0) + UIScreenPixel), size: selectedSize)
|
||||
self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: self.view.bounds.width - 54.0 - selectedSize.width - safeInset, y: floorToScreenPixels((navigationHeight - selectedSize.height) / 2.0) + 1.0), size: selectedSize)
|
||||
|
||||
let isSelectionButtonVisible = count > 0 && self.controllerNode.currentDisplayMode == .all
|
||||
transition.updateAlpha(node: self.selectedButtonNode, alpha: isSelectionButtonVisible ? 1.0 : 0.0)
|
||||
@ -2677,7 +2677,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
safeInset += layout.safeInsets.right + 16.0
|
||||
}
|
||||
let navigationHeight = navigationLayout(layout: layout).navigationFrame.height
|
||||
self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: self.view.bounds.width - 54.0 - self.selectedButtonNode.frame.width - safeInset, y: floorToScreenPixels((navigationHeight - self.selectedButtonNode.frame.height) / 2.0) + UIScreenPixel), size: self.selectedButtonNode.frame.size)
|
||||
self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: self.view.bounds.width - 54.0 - self.selectedButtonNode.frame.width - safeInset, y: floorToScreenPixels((navigationHeight - self.selectedButtonNode.frame.height) / 2.0) + 1.0), size: self.selectedButtonNode.frame.size)
|
||||
}
|
||||
|
||||
public var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||
|
@ -2720,7 +2720,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
if url.hasPrefix("https://apps.apple.com/account/subscriptions") {
|
||||
controller.context.sharedContext.applicationBindings.openSubscriptions()
|
||||
} else if url.hasPrefix("https://") || url.hasPrefix("tg://") {
|
||||
controller.context.sharedContext.openExternalUrl(context: controller.context, urlContext: .generic, url: url, forceExternal: !url.hasPrefix("tg://") && !url.contains("?start="), presentationData: controller.context.sharedContext.currentPresentationData.with({$0}), navigationController: navigationController, dismissInput: {})
|
||||
controller.context.sharedContext.openExternalUrl(context: controller.context, urlContext: .generic, url: url, forceExternal: false, presentationData: controller.context.sharedContext.currentPresentationData.with({$0}), navigationController: navigationController, dismissInput: {})
|
||||
} else {
|
||||
let context = controller.context
|
||||
let signal: Signal<ResolvedUrl, NoError>?
|
||||
|
@ -1198,6 +1198,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
|
||||
let displayTopButtons = !(self.inputPanelExternalState.isEditing || isEditingTextEntity || component.isDisplayingTool != nil)
|
||||
|
||||
var inputPanelSize: CGSize = .zero
|
||||
if case .storyEditor = controller.mode {
|
||||
let nextInputMode: MessageInputPanelComponent.InputMode
|
||||
switch self.currentInputMode {
|
||||
@ -1217,7 +1218,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
|
||||
self.inputPanel.parentState = state
|
||||
let inputPanelSize = self.inputPanel.update(
|
||||
inputPanelSize = self.inputPanel.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MessageInputPanelComponent(
|
||||
externalState: self.inputPanelExternalState,
|
||||
@ -1429,192 +1430,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
transition.setFrame(view: inputPanelView, frame: inputPanelFrame)
|
||||
transition.setAlpha(view: inputPanelView, alpha: isEditingTextEntity || component.isDisplayingTool != nil || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
if let playerState = state.playerState {
|
||||
let scrubberInset: CGFloat = 9.0
|
||||
|
||||
let minDuration: Double
|
||||
let maxDuration: Double
|
||||
if playerState.isAudioOnly {
|
||||
minDuration = 5.0
|
||||
maxDuration = 15.0
|
||||
} else {
|
||||
minDuration = 1.0
|
||||
maxDuration = storyMaxVideoDuration
|
||||
}
|
||||
|
||||
let previousTrackCount = self.currentVisibleTracks?.count
|
||||
let visibleTracks = playerState.tracks.filter { $0.visibleInTimeline }.map { MediaScrubberComponent.Track($0) }
|
||||
self.currentVisibleTracks = visibleTracks
|
||||
|
||||
var scrubberTransition = transition
|
||||
if let previousTrackCount, previousTrackCount != visibleTracks.count {
|
||||
scrubberTransition = .easeInOut(duration: 0.2)
|
||||
}
|
||||
|
||||
let isAudioOnly = playerState.isAudioOnly
|
||||
let hasMainVideoTrack = playerState.tracks.contains(where: { $0.id == 0 })
|
||||
|
||||
let scrubber: ComponentView<Empty>
|
||||
if let current = self.scrubber {
|
||||
scrubber = current
|
||||
} else {
|
||||
scrubber = ComponentView<Empty>()
|
||||
self.scrubber = scrubber
|
||||
}
|
||||
|
||||
let scrubberSize = scrubber.update(
|
||||
transition: scrubberTransition,
|
||||
component: AnyComponent(MediaScrubberComponent(
|
||||
context: component.context,
|
||||
style: .editor,
|
||||
theme: environment.theme,
|
||||
generationTimestamp: playerState.generationTimestamp,
|
||||
position: playerState.position,
|
||||
minDuration: minDuration,
|
||||
maxDuration: maxDuration,
|
||||
isPlaying: playerState.isPlaying,
|
||||
tracks: visibleTracks,
|
||||
positionUpdated: { [weak mediaEditor] position, apply in
|
||||
if let mediaEditor {
|
||||
mediaEditor.seek(position, andPlay: apply)
|
||||
}
|
||||
},
|
||||
trackTrimUpdated: { [weak mediaEditor] trackId, start, end, updatedEnd, apply in
|
||||
guard let mediaEditor else {
|
||||
return
|
||||
}
|
||||
let trimRange = start..<end
|
||||
if trackId == 2 {
|
||||
mediaEditor.setAudioTrackTrimRange(trimRange, apply: apply)
|
||||
if isAudioOnly {
|
||||
let offset = (mediaEditor.values.audioTrackOffset ?? 0.0)
|
||||
if apply {
|
||||
mediaEditor.seek(offset + start, andPlay: true)
|
||||
} else {
|
||||
mediaEditor.seek(offset + start, andPlay: false)
|
||||
mediaEditor.stop()
|
||||
}
|
||||
} else {
|
||||
if apply {
|
||||
mediaEditor.play()
|
||||
} else {
|
||||
mediaEditor.stop()
|
||||
}
|
||||
}
|
||||
} else if trackId == 1 {
|
||||
mediaEditor.setAdditionalVideoTrimRange(trimRange, apply: apply)
|
||||
if hasMainVideoTrack {
|
||||
if apply {
|
||||
mediaEditor.play()
|
||||
} else {
|
||||
mediaEditor.stop()
|
||||
}
|
||||
} else {
|
||||
if apply {
|
||||
mediaEditor.seek(start, andPlay: true)
|
||||
} else {
|
||||
mediaEditor.seek(updatedEnd ? end : start, andPlay: false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mediaEditor.setVideoTrimRange(trimRange, apply: apply)
|
||||
if apply {
|
||||
mediaEditor.seek(start, andPlay: true)
|
||||
} else {
|
||||
mediaEditor.seek(updatedEnd ? end : start, andPlay: false)
|
||||
}
|
||||
}
|
||||
},
|
||||
trackOffsetUpdated: { trackId, offset, apply in
|
||||
guard let mediaEditor else {
|
||||
return
|
||||
}
|
||||
if trackId == 2 {
|
||||
mediaEditor.setAudioTrackOffset(offset, apply: apply)
|
||||
if isAudioOnly {
|
||||
let offset = (mediaEditor.values.audioTrackOffset ?? 0.0)
|
||||
let start = (mediaEditor.values.audioTrackTrimRange?.lowerBound ?? 0.0)
|
||||
if apply {
|
||||
mediaEditor.seek(offset + start, andPlay: true)
|
||||
} else {
|
||||
mediaEditor.seek(offset + start, andPlay: false)
|
||||
mediaEditor.stop()
|
||||
}
|
||||
} else {
|
||||
if apply {
|
||||
let audioStart = mediaEditor.values.audioTrackTrimRange?.lowerBound ?? 0.0
|
||||
let audioOffset = min(0.0, mediaEditor.values.audioTrackOffset ?? 0.0)
|
||||
|
||||
var start = -audioOffset + audioStart
|
||||
if let duration = mediaEditor.duration {
|
||||
let lowerBound = mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0
|
||||
let upperBound = mediaEditor.values.videoTrimRange?.upperBound ?? duration
|
||||
if start >= upperBound {
|
||||
start = lowerBound
|
||||
} else if start < lowerBound {
|
||||
start = lowerBound
|
||||
}
|
||||
}
|
||||
|
||||
mediaEditor.seek(start, andPlay: true)
|
||||
mediaEditor.play()
|
||||
} else {
|
||||
mediaEditor.stop()
|
||||
}
|
||||
}
|
||||
} else if trackId == 1 {
|
||||
mediaEditor.setAdditionalVideoOffset(offset, apply: apply)
|
||||
}
|
||||
},
|
||||
trackLongPressed: { [weak controller] trackId, sourceView in
|
||||
guard let controller else {
|
||||
return
|
||||
}
|
||||
controller.node.presentTrackOptions(trackId: trackId, sourceView: sourceView)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: previewSize.width - scrubberInset * 2.0, height: availableSize.height)
|
||||
)
|
||||
|
||||
let scrubberFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height + controlsBottomInset - inputPanelSize.height + 3.0), size: scrubberSize)
|
||||
if let scrubberView = scrubber.view {
|
||||
var animateIn = false
|
||||
if scrubberView.superview == nil {
|
||||
animateIn = true
|
||||
if let inputPanelBackgroundView = self.inputPanelBackground.view, inputPanelBackgroundView.superview != nil {
|
||||
self.insertSubview(scrubberView, belowSubview: inputPanelBackgroundView)
|
||||
} else {
|
||||
self.addSubview(scrubberView)
|
||||
}
|
||||
}
|
||||
if animateIn {
|
||||
scrubberView.frame = scrubberFrame
|
||||
} else {
|
||||
scrubberTransition.setFrame(view: scrubberView, frame: scrubberFrame)
|
||||
}
|
||||
if !self.animatingButtons && !(!hasMainVideoTrack && animateIn) {
|
||||
transition.setAlpha(view: scrubberView, alpha: component.isDisplayingTool != nil || component.isDismissing || component.isInteractingWithEntities || isEditingCaption || isRecordingAdditionalVideo || isEditingTextEntity ? 0.0 : 1.0)
|
||||
} else if animateIn {
|
||||
scrubberView.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: .zero, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
scrubberView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
scrubberView.layer.animateScale(from: 0.6, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let scrubber = self.scrubber {
|
||||
self.scrubber = nil
|
||||
if let scrubberView = scrubber.view {
|
||||
scrubberView.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 44.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||
scrubberView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
scrubberView.removeFromSuperview()
|
||||
})
|
||||
scrubberView.layer.animateScale(from: 1.0, to: 0.6, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let saveContentComponent: AnyComponentWithIdentity<Empty>
|
||||
if component.hasAppeared {
|
||||
saveContentComponent = AnyComponentWithIdentity(
|
||||
@ -1971,8 +1787,199 @@ final class MediaEditorScreenComponent: Component {
|
||||
transition.setScale(view: switchCameraButtonView, scale: isRecordingAdditionalVideo ? 1.0 : 0.01)
|
||||
transition.setAlpha(view: switchCameraButtonView, alpha: isRecordingAdditionalVideo ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
} else {
|
||||
inputPanelSize = CGSize(width: 0.0, height: 12.0)
|
||||
}
|
||||
|
||||
if case .stickerEditor = controller.mode {
|
||||
|
||||
} else {
|
||||
if let playerState = state.playerState {
|
||||
let scrubberInset: CGFloat = 9.0
|
||||
|
||||
let minDuration: Double
|
||||
let maxDuration: Double
|
||||
if playerState.isAudioOnly {
|
||||
minDuration = 5.0
|
||||
maxDuration = 15.0
|
||||
} else {
|
||||
minDuration = 1.0
|
||||
maxDuration = storyMaxVideoDuration
|
||||
}
|
||||
|
||||
let previousTrackCount = self.currentVisibleTracks?.count
|
||||
let visibleTracks = playerState.tracks.filter { $0.visibleInTimeline }.map { MediaScrubberComponent.Track($0) }
|
||||
self.currentVisibleTracks = visibleTracks
|
||||
|
||||
var scrubberTransition = transition
|
||||
if let previousTrackCount, previousTrackCount != visibleTracks.count {
|
||||
scrubberTransition = .easeInOut(duration: 0.2)
|
||||
}
|
||||
|
||||
let isAudioOnly = playerState.isAudioOnly
|
||||
let hasMainVideoTrack = playerState.tracks.contains(where: { $0.id == 0 })
|
||||
|
||||
let scrubber: ComponentView<Empty>
|
||||
if let current = self.scrubber {
|
||||
scrubber = current
|
||||
} else {
|
||||
scrubber = ComponentView<Empty>()
|
||||
self.scrubber = scrubber
|
||||
}
|
||||
|
||||
let scrubberSize = scrubber.update(
|
||||
transition: scrubberTransition,
|
||||
component: AnyComponent(MediaScrubberComponent(
|
||||
context: component.context,
|
||||
style: .editor,
|
||||
theme: environment.theme,
|
||||
generationTimestamp: playerState.generationTimestamp,
|
||||
position: playerState.position,
|
||||
minDuration: minDuration,
|
||||
maxDuration: maxDuration,
|
||||
isPlaying: playerState.isPlaying,
|
||||
tracks: visibleTracks,
|
||||
positionUpdated: { [weak mediaEditor] position, apply in
|
||||
if let mediaEditor {
|
||||
mediaEditor.seek(position, andPlay: apply)
|
||||
}
|
||||
},
|
||||
trackTrimUpdated: { [weak mediaEditor] trackId, start, end, updatedEnd, apply in
|
||||
guard let mediaEditor else {
|
||||
return
|
||||
}
|
||||
let trimRange = start..<end
|
||||
if trackId == 2 {
|
||||
mediaEditor.setAudioTrackTrimRange(trimRange, apply: apply)
|
||||
if isAudioOnly {
|
||||
let offset = (mediaEditor.values.audioTrackOffset ?? 0.0)
|
||||
if apply {
|
||||
mediaEditor.seek(offset + start, andPlay: true)
|
||||
} else {
|
||||
mediaEditor.seek(offset + start, andPlay: false)
|
||||
mediaEditor.stop()
|
||||
}
|
||||
} else {
|
||||
if apply {
|
||||
mediaEditor.play()
|
||||
} else {
|
||||
mediaEditor.stop()
|
||||
}
|
||||
}
|
||||
} else if trackId == 1 {
|
||||
mediaEditor.setAdditionalVideoTrimRange(trimRange, apply: apply)
|
||||
if hasMainVideoTrack {
|
||||
if apply {
|
||||
mediaEditor.play()
|
||||
} else {
|
||||
mediaEditor.stop()
|
||||
}
|
||||
} else {
|
||||
if apply {
|
||||
mediaEditor.seek(start, andPlay: true)
|
||||
} else {
|
||||
mediaEditor.seek(updatedEnd ? end : start, andPlay: false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mediaEditor.setVideoTrimRange(trimRange, apply: apply)
|
||||
if apply {
|
||||
mediaEditor.seek(start, andPlay: true)
|
||||
} else {
|
||||
mediaEditor.seek(updatedEnd ? end : start, andPlay: false)
|
||||
}
|
||||
}
|
||||
},
|
||||
trackOffsetUpdated: { trackId, offset, apply in
|
||||
guard let mediaEditor else {
|
||||
return
|
||||
}
|
||||
if trackId == 2 {
|
||||
mediaEditor.setAudioTrackOffset(offset, apply: apply)
|
||||
if isAudioOnly {
|
||||
let offset = (mediaEditor.values.audioTrackOffset ?? 0.0)
|
||||
let start = (mediaEditor.values.audioTrackTrimRange?.lowerBound ?? 0.0)
|
||||
if apply {
|
||||
mediaEditor.seek(offset + start, andPlay: true)
|
||||
} else {
|
||||
mediaEditor.seek(offset + start, andPlay: false)
|
||||
mediaEditor.stop()
|
||||
}
|
||||
} else {
|
||||
if apply {
|
||||
let audioStart = mediaEditor.values.audioTrackTrimRange?.lowerBound ?? 0.0
|
||||
let audioOffset = min(0.0, mediaEditor.values.audioTrackOffset ?? 0.0)
|
||||
|
||||
var start = -audioOffset + audioStart
|
||||
if let duration = mediaEditor.duration {
|
||||
let lowerBound = mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0
|
||||
let upperBound = mediaEditor.values.videoTrimRange?.upperBound ?? duration
|
||||
if start >= upperBound {
|
||||
start = lowerBound
|
||||
} else if start < lowerBound {
|
||||
start = lowerBound
|
||||
}
|
||||
}
|
||||
|
||||
mediaEditor.seek(start, andPlay: true)
|
||||
mediaEditor.play()
|
||||
} else {
|
||||
mediaEditor.stop()
|
||||
}
|
||||
}
|
||||
} else if trackId == 1 {
|
||||
mediaEditor.setAdditionalVideoOffset(offset, apply: apply)
|
||||
}
|
||||
},
|
||||
trackLongPressed: { [weak controller] trackId, sourceView in
|
||||
guard let controller else {
|
||||
return
|
||||
}
|
||||
controller.node.presentTrackOptions(trackId: trackId, sourceView: sourceView)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: previewSize.width - scrubberInset * 2.0, height: availableSize.height)
|
||||
)
|
||||
|
||||
let scrubberFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height + controlsBottomInset - inputPanelSize.height + 3.0), size: scrubberSize)
|
||||
if let scrubberView = scrubber.view {
|
||||
var animateIn = false
|
||||
if scrubberView.superview == nil {
|
||||
animateIn = true
|
||||
if let inputPanelBackgroundView = self.inputPanelBackground.view, inputPanelBackgroundView.superview != nil {
|
||||
self.insertSubview(scrubberView, belowSubview: inputPanelBackgroundView)
|
||||
} else {
|
||||
self.addSubview(scrubberView)
|
||||
}
|
||||
}
|
||||
if animateIn {
|
||||
scrubberView.frame = scrubberFrame
|
||||
} else {
|
||||
scrubberTransition.setFrame(view: scrubberView, frame: scrubberFrame)
|
||||
}
|
||||
if !self.animatingButtons && !(!hasMainVideoTrack && animateIn) {
|
||||
transition.setAlpha(view: scrubberView, alpha: component.isDisplayingTool != nil || component.isDismissing || component.isInteractingWithEntities || isEditingCaption || isRecordingAdditionalVideo || isEditingTextEntity ? 0.0 : 1.0)
|
||||
} else if animateIn {
|
||||
scrubberView.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: .zero, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
scrubberView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
scrubberView.layer.animateScale(from: 0.6, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let scrubber = self.scrubber {
|
||||
self.scrubber = nil
|
||||
if let scrubberView = scrubber.view {
|
||||
scrubberView.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 44.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||
scrubberView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
scrubberView.removeFromSuperview()
|
||||
})
|
||||
scrubberView.layer.animateScale(from: 1.0, to: 0.6, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if case .stickerEditor = controller.mode {
|
||||
var stickerButtonsHidden = buttonsAreHidden
|
||||
if let displayingTool = component.isDisplayingTool, [.cutoutErase, .cutoutRestore].contains(displayingTool) {
|
||||
|
@ -5,6 +5,7 @@ import TelegramCore
|
||||
import StickerPickerScreen
|
||||
import AccountContext
|
||||
import DeviceLocationManager
|
||||
import DeviceAccess
|
||||
|
||||
struct StoryWeather {
|
||||
let emoji: String
|
||||
@ -50,33 +51,44 @@ func getWeather(context: AccountContext) -> Signal<StickerPickerScreen.Weather,
|
||||
guard let locationManager = context.sharedContext.locationManager else {
|
||||
return .single(.none)
|
||||
}
|
||||
return .single(.fetching)
|
||||
|> then(
|
||||
currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0)
|
||||
|> mapToSignal { location in
|
||||
if let location {
|
||||
return getWeatherData(context: context, location: location)
|
||||
|> mapToSignal { weather in
|
||||
if let weather {
|
||||
let effectiveEmoji = emojiFor(for: weather.emoji.strippedEmoji, date: Date(), location: location)
|
||||
if let match = context.animatedEmojiStickersValue[effectiveEmoji]?.first {
|
||||
return .single(.loaded(StickerPickerScreen.Weather.LoadedWeather(
|
||||
emoji: effectiveEmoji,
|
||||
emojiFile: match.file,
|
||||
temperature: weather.temperature
|
||||
)))
|
||||
} else {
|
||||
return .single(.none)
|
||||
|
||||
return DeviceAccess.authorizationStatus(subject: .location(.send))
|
||||
|> mapToSignal { status in
|
||||
switch status {
|
||||
case .notDetermined:
|
||||
return .single(.notDetermined)
|
||||
case .denied, .restricted, .unreachable:
|
||||
return .single(.notAllowed)
|
||||
case .allowed:
|
||||
return .single(.fetching)
|
||||
|> then(
|
||||
currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0)
|
||||
|> mapToSignal { location in
|
||||
if let location {
|
||||
return getWeatherData(context: context, location: location)
|
||||
|> mapToSignal { weather in
|
||||
if let weather {
|
||||
let effectiveEmoji = emojiFor(for: weather.emoji.strippedEmoji, date: Date(), location: location)
|
||||
if let match = context.animatedEmojiStickersValue[effectiveEmoji]?.first {
|
||||
return .single(.loaded(StickerPickerScreen.Weather.LoadedWeather(
|
||||
emoji: effectiveEmoji,
|
||||
emojiFile: match.file,
|
||||
temperature: weather.temperature
|
||||
)))
|
||||
} else {
|
||||
return .single(.none)
|
||||
}
|
||||
} else {
|
||||
return .single(.none)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.none)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.none)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private struct WeatherBotConfiguration {
|
||||
|
@ -200,8 +200,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
switch subject {
|
||||
case let .transaction(transaction, parentPeer):
|
||||
if transaction.flags.contains(.isGift) {
|
||||
titleText = "Received Gift"
|
||||
descriptionText = "Use Stars to unlock content and services on Telegram. [See Examples >]()"
|
||||
titleText = strings.Stars_Gift_Received_Title
|
||||
descriptionText = strings.Stars_Gift_Received_Text
|
||||
count = transaction.count
|
||||
countOnTop = true
|
||||
transactionId = transaction.id
|
||||
@ -218,7 +218,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
photo = nil
|
||||
isRefund = false
|
||||
isGift = true
|
||||
delayedCloseOnOpenPeer = false
|
||||
} else {
|
||||
switch transaction.peer {
|
||||
case let .peer(peer):
|
||||
@ -320,9 +319,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
delayedCloseOnOpenPeer = false
|
||||
case let .gift(message):
|
||||
let incoming = message.flags.contains(.Incoming)
|
||||
titleText = incoming ? "Received Gift" : "Sent Gift"
|
||||
titleText = incoming ? strings.Stars_Gift_Received_Title : strings.Stars_Gift_Sent_Title
|
||||
let peerName = state.peerMap[message.id.peerId]?.compactDisplayTitle ?? ""
|
||||
descriptionText = incoming ? "Use Stars to unlock content and services on Telegram. [See Examples >]()" : "With Stars, \(peerName) will be able to unlock content and services on Telegram. [See Examples >]()"
|
||||
descriptionText = incoming ? strings.Stars_Gift_Received_Text : strings.Stars_Gift_Sent_Text(peerName).string
|
||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .giftStars(_, _, countValue, _, _, _) = action.action {
|
||||
count = countValue
|
||||
if !incoming {
|
||||
@ -346,7 +345,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
photo = nil
|
||||
isRefund = false
|
||||
isGift = true
|
||||
delayedCloseOnOpenPeer = false
|
||||
}
|
||||
if let spaceRegex {
|
||||
let nsRange = NSRange(descriptionText.startIndex..., in: descriptionText)
|
||||
@ -484,10 +482,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
)
|
||||
),
|
||||
action: {
|
||||
if toPeer.id.namespace == Namespaces.Peer.CloudUser && toPeer.id.id._internalGetInt64Value() == 777000 {
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_Transaction_FragmentUnknown_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
} else if delayedCloseOnOpenPeer {
|
||||
if delayedCloseOnOpenPeer {
|
||||
component.openPeer(toPeer)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
component.cancel(false)
|
||||
@ -607,8 +602,11 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
}
|
||||
},
|
||||
tapAction: { attributes, _ in
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_Transaction_Terms_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
if let controller = controller() as? StarsTransactionScreen, let navigationController = controller.navigationController as? NavigationController {
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_Transaction_Terms_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||
component.cancel(true)
|
||||
}
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
|
@ -466,8 +466,10 @@ final class StarsStatisticsScreenComponent: Component {
|
||||
return nil
|
||||
}
|
||||
},
|
||||
tapAction: { attributes, _ in
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_BotRevenue_Withdraw_Info_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
tapAction: { [weak self] attributes, _ in
|
||||
if let controller = self?.controller?() as? StarsStatisticsScreen, let navigationController = controller.navigationController as? NavigationController {
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_BotRevenue_Withdraw_Info_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||
}
|
||||
}
|
||||
)),
|
||||
items: [AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||
|
@ -66,6 +66,8 @@ private final class SheetContent: CombinedComponent {
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
|
||||
let controller = environment.controller
|
||||
|
||||
let theme = environment.theme.withModalBlocksBackground()
|
||||
let strings = environment.strings
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -229,7 +231,9 @@ private final class SheetContent: CombinedComponent {
|
||||
}
|
||||
},
|
||||
tapAction: { attributes, _ in
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_PaidContent_AmountInfo_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
if let controller = controller() as? StarsWithdrawScreen, let navigationController = controller.navigationController as? NavigationController {
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_PaidContent_AmountInfo_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||
}
|
||||
}
|
||||
))
|
||||
case let .reaction(starsToTop):
|
||||
@ -307,7 +311,6 @@ private final class SheetContent: CombinedComponent {
|
||||
buttonAttributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: buttonAttributedString.string))
|
||||
}
|
||||
|
||||
let controller = environment.controller
|
||||
let button = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
|
@ -2065,6 +2065,8 @@ public class StickerPickerScreen: ViewController {
|
||||
}
|
||||
|
||||
case none
|
||||
case notDetermined
|
||||
case notAllowed
|
||||
case fetching
|
||||
case loaded(StickerPickerScreen.Weather.LoadedWeather)
|
||||
}
|
||||
@ -2722,66 +2724,64 @@ final class StoryStickersContentView: UIView, EmojiCustomContentView {
|
||||
|
||||
} else {
|
||||
maxHorizontalItems = 3
|
||||
|
||||
|
||||
let weatherButtonContent: AnyComponent<Empty>
|
||||
switch self.weather {
|
||||
case .notAllowed, .notDetermined:
|
||||
weatherButtonContent = AnyComponent(
|
||||
InteractiveStickerButtonContent(
|
||||
context: self.context,
|
||||
theme: theme,
|
||||
title: stringForTemperature(24),
|
||||
iconName: "☀️",
|
||||
iconFile: self.context.animatedEmojiStickersValue["☀️"]?.first?.file,
|
||||
useOpaqueTheme: useOpaqueTheme,
|
||||
tintContainerView: self.tintContainerView
|
||||
)
|
||||
)
|
||||
case let .loaded(weather):
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "weather",
|
||||
component: AnyComponent(
|
||||
CameraButton(
|
||||
content: AnyComponentWithIdentity(
|
||||
id: "weather",
|
||||
component: AnyComponent(
|
||||
InteractiveStickerButtonContent(
|
||||
context: self.context,
|
||||
theme: theme,
|
||||
title: stringForTemperature(weather.temperature),
|
||||
iconName: weather.emoji,
|
||||
iconFile: weather.emojiFile,
|
||||
useOpaqueTheme: useOpaqueTheme,
|
||||
tintContainerView: self.tintContainerView
|
||||
)
|
||||
)
|
||||
),
|
||||
action: { [weak self] in
|
||||
if let self {
|
||||
self.weatherAction()
|
||||
}
|
||||
})
|
||||
)
|
||||
weatherButtonContent = AnyComponent(
|
||||
InteractiveStickerButtonContent(
|
||||
context: self.context,
|
||||
theme: theme,
|
||||
title: stringForTemperature(weather.temperature),
|
||||
iconName: weather.emoji,
|
||||
iconFile: weather.emojiFile,
|
||||
useOpaqueTheme: useOpaqueTheme,
|
||||
tintContainerView: self.tintContainerView
|
||||
)
|
||||
)
|
||||
case .fetching:
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "weather",
|
||||
component: AnyComponent(
|
||||
CameraButton(
|
||||
content: AnyComponentWithIdentity(
|
||||
id: "weather",
|
||||
component: AnyComponent(
|
||||
InteractiveStickerButtonContent(
|
||||
context: self.context,
|
||||
theme: theme,
|
||||
title: nil,
|
||||
iconName: nil,
|
||||
useOpaqueTheme: useOpaqueTheme,
|
||||
tintContainerView: self.tintContainerView
|
||||
)
|
||||
)
|
||||
),
|
||||
action: { [weak self] in
|
||||
if let self {
|
||||
self.weatherAction()
|
||||
}
|
||||
})
|
||||
)
|
||||
weatherButtonContent = AnyComponent(
|
||||
InteractiveStickerButtonContent(
|
||||
context: self.context,
|
||||
theme: theme,
|
||||
title: nil,
|
||||
iconName: nil,
|
||||
useOpaqueTheme: useOpaqueTheme,
|
||||
tintContainerView: self.tintContainerView
|
||||
)
|
||||
)
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "weather",
|
||||
component: AnyComponent(
|
||||
CameraButton(
|
||||
content: AnyComponentWithIdentity(
|
||||
id: "weather",
|
||||
component: weatherButtonContent
|
||||
),
|
||||
action: { [weak self] in
|
||||
if let self {
|
||||
self.weatherAction()
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
items.append(
|
||||
|
Loading…
x
Reference in New Issue
Block a user