mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-30 03:44:18 +00:00
Various improvements
This commit is contained in:
parent
c6acd92aed
commit
29b24d9d57
@ -15299,3 +15299,58 @@ Error: %8$@";
|
|||||||
|
|
||||||
"Camera.LiveStream.Change" = "change";
|
"Camera.LiveStream.Change" = "change";
|
||||||
"Camera.LiveStream.StartLiveStream" = "Start Live Stream";
|
"Camera.LiveStream.StartLiveStream" = "Start Live Stream";
|
||||||
|
|
||||||
|
"Camera.LiveStream.End" = "End";
|
||||||
|
"Camera.LiveStream.End.Title" = "End Live Stream";
|
||||||
|
"Camera.LiveStream.End.Text" = "Are you sure you want to end this live stream?";
|
||||||
|
"Camera.LiveStream.End.End" = "End";
|
||||||
|
"Camera.LiveStream.End.Leave" = "Leave";
|
||||||
|
|
||||||
|
"Message.RepeatAt" = "%1$@ at %2$@";
|
||||||
|
"Message.RepeatPeriod.Daily" = "daily";
|
||||||
|
"Message.RepeatPeriod.Weekly" = "weekly";
|
||||||
|
"Message.RepeatPeriod.Biweekly" = "biweekly";
|
||||||
|
"Message.RepeatPeriod.Monthly" = "monthly";
|
||||||
|
"Message.RepeatPeriod.3Months" = "every 3 months";
|
||||||
|
"Message.RepeatPeriod.6Months" = "every 6 months";
|
||||||
|
"Message.RepeatPeriod.Yearly" = "yearly";
|
||||||
|
"Message.Approximate" = "appx. %@";
|
||||||
|
|
||||||
|
"LiveStreamSettings.Title" = "Live Settings";
|
||||||
|
"LiveStreamSettings.TitleEdit" = "Live Stream";
|
||||||
|
"LiveStreamSettings.StartLiveAs" = "START LIVE AS";
|
||||||
|
"LiveStreamSettings.WhoCanView" = "WHO CAN VIEW THIS LIVE";
|
||||||
|
"LiveStreamSettings.WhoCanViewInfo" = "[Select people]() who won't see your live.";
|
||||||
|
"LiveStreamSettings.ConnectStream" = "Connect Stream";
|
||||||
|
"LiveStreamSettings.ConnectStreamInfo" = "Stream with a different app.";
|
||||||
|
"LiveStreamSettings.AllowComments" = "Allow Comments";
|
||||||
|
"LiveStreamSettings.AllowScreenshots" = "Allow Screenshots";
|
||||||
|
"LiveStreamSettings.PricePerComment" = "PRICE PER COMMENT";
|
||||||
|
"LiveStreamSettings.PricePerCommentInfo" = "The price a viewer must pay to send a comment.";
|
||||||
|
"LiveStreamSettings.PricePerComment.Free" = "Free";
|
||||||
|
"LiveStreamSettings.PricePerComment.Stars_1" = "%@ Star";
|
||||||
|
"LiveStreamSettings.PricePerComment.Stars_any" = "%@ Stars";
|
||||||
|
"LiveStreamSettings.SaveSettings" = "Save Settings";
|
||||||
|
|
||||||
|
"AddContact.Title" = "New Contact";
|
||||||
|
"AddContact.PhoneNumber.IsContact"= "This phone number is already in your contacts. [View >]()";
|
||||||
|
"AddContact.PhoneNumber.Registered"= "This phone number is on Telegram.";
|
||||||
|
"AddContact.PhoneNumber.NotRegistered"= "This phone number is not on Telegram. [Invite >]()";
|
||||||
|
"AddContact.SyncToPhone" = "Sync Contact to Phone";
|
||||||
|
"AddContact.NotePlaceholder" = "Add notes only visible to you";
|
||||||
|
"AddContact.AddQR" = "Add via QR Code";
|
||||||
|
|
||||||
|
"ScheduleMessage.Time" = "Time";
|
||||||
|
"ScheduleMessage.Repeat" = "Repeat";
|
||||||
|
"ScheduleMessage.RepeatPeriod.Never" = "Never";
|
||||||
|
"ScheduleMessage.RepeatPeriod.Daily" = "Daily";
|
||||||
|
"ScheduleMessage.RepeatPeriod.Weekly" = "Weekly";
|
||||||
|
"ScheduleMessage.RepeatPeriod.Biweekly" = "Biweekly";
|
||||||
|
"ScheduleMessage.RepeatPeriod.Monthly" = "Monthly";
|
||||||
|
"ScheduleMessage.RepeatPeriod.3Months" = "Every 3 Months";
|
||||||
|
"ScheduleMessage.RepeatPeriod.6Months" = "Every 6 Months";
|
||||||
|
"ScheduleMessage.RepeatPeriod.Yearly" = "Yearly";
|
||||||
|
|
||||||
|
"ScheduleMessage.PremiumRequired.Title" = "Premium Required";
|
||||||
|
"ScheduleMessage.PremiumRequired.Text" = "Subscribe to **Telegram Premium** to schedule repeating messages.";
|
||||||
|
"ScheduleMessage.PremiumRequired.Add" = "Add";
|
||||||
|
|||||||
@ -2575,6 +2575,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupLoginEmail.id).startStandalone()
|
let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupLoginEmail.id).startStandalone()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if let layout = strongSelf.validLayout, layout.metrics.isTablet {
|
||||||
|
controller.navigationPresentation = .standaloneFlatModal
|
||||||
|
} else {
|
||||||
|
controller.navigationPresentation = .flatModal
|
||||||
|
}
|
||||||
navigationController.pushViewController(controller)
|
navigationController.pushViewController(controller)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -75,6 +75,12 @@ struct CameraState: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum StreamingMode {
|
||||||
|
case none
|
||||||
|
case camera
|
||||||
|
case rtmp
|
||||||
|
}
|
||||||
|
|
||||||
let mode: CameraMode
|
let mode: CameraMode
|
||||||
let position: Camera.Position
|
let position: Camera.Position
|
||||||
let flashMode: Camera.FlashMode
|
let flashMode: Camera.FlashMode
|
||||||
@ -87,7 +93,7 @@ struct CameraState: Equatable {
|
|||||||
let isCollageEnabled: Bool
|
let isCollageEnabled: Bool
|
||||||
let collageGrid: Camera.CollageGrid
|
let collageGrid: Camera.CollageGrid
|
||||||
let collageProgress: Float
|
let collageProgress: Float
|
||||||
let isStreaming: Bool
|
let isStreaming: StreamingMode
|
||||||
let isWaitingForStream: Bool
|
let isWaitingForStream: Bool
|
||||||
|
|
||||||
func updatedMode(_ mode: CameraMode) -> CameraState {
|
func updatedMode(_ mode: CameraMode) -> CameraState {
|
||||||
@ -134,7 +140,7 @@ struct CameraState: Equatable {
|
|||||||
return CameraState(mode: self.mode, position: self.position, flashMode: self.flashMode, flashModeDidChange: self.flashModeDidChange, flashTint: self.flashTint, flashTintSize: self.flashTintSize, recording: self.recording, duration: self.duration, isDualCameraEnabled: self.isDualCameraEnabled, isCollageEnabled: self.isCollageEnabled, collageGrid: self.collageGrid, collageProgress: collageProgress, isStreaming: self.isStreaming, isWaitingForStream: self.isWaitingForStream)
|
return CameraState(mode: self.mode, position: self.position, flashMode: self.flashMode, flashModeDidChange: self.flashModeDidChange, flashTint: self.flashTint, flashTintSize: self.flashTintSize, recording: self.recording, duration: self.duration, isDualCameraEnabled: self.isDualCameraEnabled, isCollageEnabled: self.isCollageEnabled, collageGrid: self.collageGrid, collageProgress: collageProgress, isStreaming: self.isStreaming, isWaitingForStream: self.isWaitingForStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatedIsStreaming(_ isStreaming: Bool) -> CameraState {
|
func updatedIsStreaming(_ isStreaming: StreamingMode) -> CameraState {
|
||||||
return CameraState(mode: self.mode, position: self.position, flashMode: self.flashMode, flashModeDidChange: self.flashModeDidChange, flashTint: self.flashTint, flashTintSize: self.flashTintSize, recording: self.recording, duration: self.duration, isDualCameraEnabled: self.isDualCameraEnabled, isCollageEnabled: self.isCollageEnabled, collageGrid: self.collageGrid, collageProgress: self.collageProgress, isStreaming: isStreaming, isWaitingForStream: self.isWaitingForStream)
|
return CameraState(mode: self.mode, position: self.position, flashMode: self.flashMode, flashModeDidChange: self.flashModeDidChange, flashTint: self.flashTint, flashTintSize: self.flashTintSize, recording: self.recording, duration: self.duration, isDualCameraEnabled: self.isDualCameraEnabled, isCollageEnabled: self.isCollageEnabled, collageGrid: self.collageGrid, collageProgress: self.collageProgress, isStreaming: isStreaming, isWaitingForStream: self.isWaitingForStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1133,7 +1139,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
self.liveStreamStory = story
|
self.liveStreamStory = story
|
||||||
|
|
||||||
controller?.updateCameraState({ $0.updatedIsStreaming(true).updatedIsWaitingForStream(false) }, transition: .spring(duration: 0.4))
|
controller?.updateCameraState({ $0.updatedIsStreaming(rtmp ? .rtmp : .camera).updatedIsWaitingForStream(false) }, transition: .spring(duration: 0.4))
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1155,9 +1161,9 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for item in state.items.reversed() {
|
for item in state.items.reversed() {
|
||||||
if case let .item(item) = item, case .liveStream = item.media {
|
if case let .item(item) = item, case let .liveStream(liveStream) = item.media {
|
||||||
self.liveStreamStory = item
|
self.liveStreamStory = item
|
||||||
controller?.updateCameraState({ $0.updatedIsStreaming(true) }, transition: .spring(duration: 0.4))
|
controller?.updateCameraState({ $0.updatedIsStreaming(liveStream.kind == .rtmp ? .rtmp : .camera) }, transition: .spring(duration: 0.4))
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1180,17 +1186,17 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
let alertController = textAlertController(
|
let alertController = textAlertController(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
forceTheme: defaultDarkColorPresentationTheme,
|
forceTheme: defaultDarkColorPresentationTheme,
|
||||||
title: "End Live Stream",
|
title: presentationData.strings.Camera_LiveStream_End_Title,
|
||||||
text: "Are you sure you want to end this live stream?",
|
text: presentationData.strings.Camera_LiveStream_End_Text,
|
||||||
actions: [
|
actions: [
|
||||||
TextAlertAction(type: .destructiveAction, title: "End", action: { [weak self, weak controller] in
|
TextAlertAction(type: .destructiveAction, title: presentationData.strings.Camera_LiveStream_End_End, action: { [weak self, weak controller] in
|
||||||
guard let self, let controller else {
|
guard let self, let controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = self.liveStreamCall?.leave(terminateIfPossible: true).startStandalone()
|
let _ = self.liveStreamCall?.leave(terminateIfPossible: true).startStandalone()
|
||||||
controller.requestDismiss(animated: true)
|
controller.requestDismiss(animated: true)
|
||||||
}),
|
}),
|
||||||
TextAlertAction(type: .genericAction, title: "Leave", action: { [weak controller] in
|
TextAlertAction(type: .genericAction, title: presentationData.strings.Camera_LiveStream_End_Leave, action: { [weak controller] in
|
||||||
guard let controller else {
|
guard let controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1206,11 +1212,11 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
let alertController = textAlertController(
|
let alertController = textAlertController(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
forceTheme: defaultDarkColorPresentationTheme,
|
forceTheme: defaultDarkColorPresentationTheme,
|
||||||
title: "End Live Stream",
|
title: presentationData.strings.Camera_LiveStream_End_Title,
|
||||||
text: "Are you sure you want to end this live stream?",
|
text: presentationData.strings.Camera_LiveStream_End_Text,
|
||||||
actions: [
|
actions: [
|
||||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}),
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||||
TextAlertAction(type: .destructiveAction, title: "End", action: { [weak self, weak controller] in
|
TextAlertAction(type: .destructiveAction, title: presentationData.strings.Camera_LiveStream_End_End, action: { [weak self, weak controller] in
|
||||||
guard let self, let controller else {
|
guard let self, let controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1435,7 +1441,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
case .video:
|
case .video:
|
||||||
shutterState = .video
|
shutterState = .video
|
||||||
case .live:
|
case .live:
|
||||||
shutterState = .live(active: component.cameraState.isStreaming, progress: component.cameraState.isWaitingForStream)
|
shutterState = .live(active: component.cameraState.isStreaming != .none, progress: component.cameraState.isWaitingForStream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1560,7 +1566,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if component.cameraState.mode == .live, component.cameraState.isStreaming {
|
if component.cameraState.mode == .live, component.cameraState.isStreaming != .none {
|
||||||
let liveStream = liveStream.update(
|
let liveStream = liveStream.update(
|
||||||
component: CameraLiveStreamComponent(
|
component: CameraLiveStreamComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
@ -1607,7 +1613,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
let topControlVerticalInset: CGFloat = 12.0
|
let topControlVerticalInset: CGFloat = 12.0
|
||||||
let topButtonSpacing: CGFloat = 15.0
|
let topButtonSpacing: CGFloat = 15.0
|
||||||
|
|
||||||
if component.cameraState.mode == .live && !component.cameraState.isStreaming {
|
if component.cameraState.mode == .live && component.cameraState.isStreaming == .none {
|
||||||
let streamAsButton = streamAsButton.update(
|
let streamAsButton = streamAsButton.update(
|
||||||
component: PlainButtonComponent(
|
component: PlainButtonComponent(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
@ -1647,7 +1653,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if case .none = component.cameraState.recording, !state.isTransitioning {
|
if case .none = component.cameraState.recording, !state.isTransitioning {
|
||||||
if !state.displayingCollageSelection && !component.cameraState.isStreaming {
|
if !state.displayingCollageSelection && component.cameraState.isStreaming == .none {
|
||||||
let cancelButton = cancelButton.update(
|
let cancelButton = cancelButton.update(
|
||||||
component: CameraButton(
|
component: CameraButton(
|
||||||
content: AnyComponentWithIdentity(
|
content: AnyComponentWithIdentity(
|
||||||
@ -1728,22 +1734,23 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasAllRequiredAccess {
|
if hasAllRequiredAccess {
|
||||||
let isStreaming = component.cameraState.mode == .live && component.cameraState.isStreaming
|
let isStreaming = component.cameraState.mode == .live && component.cameraState.isStreaming != .none
|
||||||
|
var endStreamButtonWidth: CGFloat = 56.0
|
||||||
if isStreaming {
|
if isStreaming {
|
||||||
let endStreamButton = endStreamButton.update(
|
let endStreamButton = endStreamButton.update(
|
||||||
component: GlassBarButtonComponent(
|
component: GlassBarButtonComponent(
|
||||||
size: CGSize(width: 56.0, height: 40.0),
|
size: nil,
|
||||||
backgroundColor: UIColor.black.withAlphaComponent(0.5),
|
backgroundColor: UIColor.black.withAlphaComponent(0.5),
|
||||||
isDark: true,
|
isDark: true,
|
||||||
state: .glass,
|
state: .glass,
|
||||||
component: AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: "End", font: Font.semibold(17.0), color: .white))),
|
component: AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: environment.strings.Camera_LiveStream_End, font: Font.semibold(17.0), color: .white))),
|
||||||
action: { [weak state] _ in
|
action: { [weak state] _ in
|
||||||
if let state {
|
if let state {
|
||||||
state.endLiveStream()
|
state.endLiveStream()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: 40.0, height: 40.0),
|
availableSize: CGSize(width: 120.0, height: 40.0),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
context.add(endStreamButton
|
context.add(endStreamButton
|
||||||
@ -1751,12 +1758,13 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
.appear(.default(scale: true))
|
.appear(.default(scale: true))
|
||||||
.disappear(.default(scale: true))
|
.disappear(.default(scale: true))
|
||||||
)
|
)
|
||||||
|
endStreamButtonWidth = endStreamButton.size.width
|
||||||
}
|
}
|
||||||
|
|
||||||
let rightMostButtonWidth: CGFloat
|
let rightMostButtonWidth: CGFloat
|
||||||
if component.cameraState.mode == .live {
|
if component.cameraState.mode == .live {
|
||||||
if component.cameraState.isStreaming {
|
if component.cameraState.isStreaming != .none {
|
||||||
rightMostButtonWidth = -25.0
|
rightMostButtonWidth = -25.0 + (endStreamButtonWidth - 56.0) * 2.0
|
||||||
} else {
|
} else {
|
||||||
rightMostButtonWidth = -55.0
|
rightMostButtonWidth = -55.0
|
||||||
}
|
}
|
||||||
@ -1824,7 +1832,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
|
|
||||||
if !isSticker && !isAvatar && !isTablet {
|
if !isSticker && !isAvatar && !isTablet {
|
||||||
var nextButtonX = availableSize.width - topControlSideInset - rightMostButtonWidth / 2.0 - 100.0
|
var nextButtonX = availableSize.width - topControlSideInset - rightMostButtonWidth / 2.0 - 100.0
|
||||||
if Camera.isDualCameraSupported(forRoundVideo: false) && !component.cameraState.isCollageEnabled {
|
if Camera.isDualCameraSupported(forRoundVideo: false) && !component.cameraState.isCollageEnabled && component.cameraState.isStreaming == .none {
|
||||||
let dualButton = dualButton.update(
|
let dualButton = dualButton.update(
|
||||||
component: CameraButton(
|
component: CameraButton(
|
||||||
content: AnyComponentWithIdentity(
|
content: AnyComponentWithIdentity(
|
||||||
@ -1856,7 +1864,35 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if component.cameraState.mode == .live {
|
if component.cameraState.mode == .live {
|
||||||
|
if component.cameraState.isStreaming == .camera {
|
||||||
|
let flipButton = flipButton.update(
|
||||||
|
component: CameraButton(
|
||||||
|
content: AnyComponentWithIdentity(
|
||||||
|
id: "flip",
|
||||||
|
component: AnyComponent(
|
||||||
|
FlipButtonContentComponent(
|
||||||
|
action: animateFlipAction,
|
||||||
|
maskFrame: .zero,
|
||||||
|
tintColor: controlsTintColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
minSize: CGSize(width: 44.0, height: 44.0),
|
||||||
|
action: { [weak state] in
|
||||||
|
if let state {
|
||||||
|
state.togglePosition(animateFlipAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
availableSize: availableSize,
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
context.add(flipButton
|
||||||
|
.position(CGPoint(x: nextButtonX, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlVerticalInset) + flipButton.size.height / 2.0 - 4.0))
|
||||||
|
.appear(.default(scale: true))
|
||||||
|
.disappear(.default(scale: true))
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let collageButton = collageButton.update(
|
let collageButton = collageButton.update(
|
||||||
component: CameraButton(
|
component: CameraButton(
|
||||||
@ -2026,7 +2062,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isSticker, case .none = component.cameraState.recording, !component.cameraState.isStreaming && !state.isTransitioning && hasAllRequiredAccess && component.cameraState.collageProgress < 1.0 - .ulpOfOne {
|
if !isSticker, case .none = component.cameraState.recording, component.cameraState.isStreaming == .none && !state.isTransitioning && hasAllRequiredAccess && component.cameraState.collageProgress < 1.0 - .ulpOfOne {
|
||||||
let availableModeControlSize: CGSize
|
let availableModeControlSize: CGSize
|
||||||
if isTablet {
|
if isTablet {
|
||||||
availableModeControlSize = CGSize(width: panelWidth, height: 120.0)
|
availableModeControlSize = CGSize(width: panelWidth, height: 120.0)
|
||||||
@ -2474,7 +2510,7 @@ public class CameraScreenImpl: ViewController, CameraScreen {
|
|||||||
isCollageEnabled: false,
|
isCollageEnabled: false,
|
||||||
collageGrid: collageGrids[6],
|
collageGrid: collageGrids[6],
|
||||||
collageProgress: 0.0,
|
collageProgress: 0.0,
|
||||||
isStreaming: false,
|
isStreaming: .none,
|
||||||
isWaitingForStream: false
|
isWaitingForStream: false
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2900,7 +2936,7 @@ public class CameraScreenImpl: ViewController, CameraScreen {
|
|||||||
case .began:
|
case .began:
|
||||||
break
|
break
|
||||||
case .changed:
|
case .changed:
|
||||||
if case .none = self.cameraState.recording, !self.cameraState.isStreaming && !self.cameraState.isWaitingForStream {
|
if case .none = self.cameraState.recording, self.cameraState.isStreaming == .none && !self.cameraState.isWaitingForStream {
|
||||||
if case .compact = layout.metrics.widthClass {
|
if case .compact = layout.metrics.widthClass {
|
||||||
switch controller.mode {
|
switch controller.mode {
|
||||||
case .story:
|
case .story:
|
||||||
|
|||||||
@ -96,7 +96,6 @@ public func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Mess
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let repeatPeriod = message.scheduleRepeatPeriod {
|
if let repeatPeriod = message.scheduleRepeatPeriod {
|
||||||
//TODO:localize
|
|
||||||
let repeatString: String
|
let repeatString: String
|
||||||
switch repeatPeriod {
|
switch repeatPeriod {
|
||||||
case 60:
|
case 60:
|
||||||
@ -104,27 +103,27 @@ public func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Mess
|
|||||||
case 300:
|
case 300:
|
||||||
repeatString = "5 min"
|
repeatString = "5 min"
|
||||||
case 86400:
|
case 86400:
|
||||||
repeatString = "daily"
|
repeatString = strings.Message_RepeatPeriod_Daily
|
||||||
case 7 * 86400:
|
case 7 * 86400:
|
||||||
repeatString = "weekly"
|
repeatString = strings.Message_RepeatPeriod_Weekly
|
||||||
case 14 * 86400:
|
case 14 * 86400:
|
||||||
repeatString = "biweekly"
|
repeatString = strings.Message_RepeatPeriod_Biweekly
|
||||||
case 30 * 86400:
|
case 30 * 86400:
|
||||||
repeatString = "monthly"
|
repeatString = strings.Message_RepeatPeriod_Monthly
|
||||||
case 91 * 86400:
|
case 91 * 86400:
|
||||||
repeatString = "every 3 months"
|
repeatString = strings.Message_RepeatPeriod_3Months
|
||||||
case 182 * 86400:
|
case 182 * 86400:
|
||||||
repeatString = "every 6 months"
|
repeatString = strings.Message_RepeatPeriod_6Months
|
||||||
case 365 * 86400:
|
case 365 * 86400:
|
||||||
repeatString = "yearly"
|
repeatString = strings.Message_RepeatPeriod_Yearly
|
||||||
default:
|
default:
|
||||||
repeatString = ""
|
repeatString = "\(repeatPeriod)s"
|
||||||
}
|
}
|
||||||
dateText = "\(repeatString) at \(dateText)"
|
dateText = strings.Message_RepeatAt(repeatString, dateText).string
|
||||||
}
|
}
|
||||||
|
|
||||||
if message.id.namespace == Namespaces.Message.ScheduledCloud, let _ = message.pendingProcessingAttribute {
|
if message.id.namespace == Namespaces.Message.ScheduledCloud, let _ = message.pendingProcessingAttribute {
|
||||||
return "appx. \(dateText)"
|
return strings.Message_Approximate(dateText).string
|
||||||
}
|
}
|
||||||
|
|
||||||
if displayFullDate {
|
if displayFullDate {
|
||||||
|
|||||||
@ -159,6 +159,8 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
|
let strings = environment.strings
|
||||||
|
|
||||||
let sideInset: CGFloat = 39.0
|
let sideInset: CGFloat = 39.0
|
||||||
|
|
||||||
var contentHeight: CGFloat = 0.0
|
var contentHeight: CGFloat = 0.0
|
||||||
@ -201,9 +203,9 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
let title: String
|
let title: String
|
||||||
switch component.mode {
|
switch component.mode {
|
||||||
case .scheduledMessages:
|
case .scheduledMessages:
|
||||||
title = environment.strings.Conversation_ScheduleMessage_Title
|
title = strings.Conversation_ScheduleMessage_Title
|
||||||
case .reminders:
|
case .reminders:
|
||||||
title = environment.strings.Conversation_SetReminder_Title
|
title = strings.Conversation_SetReminder_Title
|
||||||
}
|
}
|
||||||
let titleSize = self.title.update(
|
let titleSize = self.title.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
@ -232,7 +234,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
datePicker = DatePickerNode(
|
datePicker = DatePickerNode(
|
||||||
theme: DatePickerTheme(theme: environment.theme),
|
theme: DatePickerTheme(theme: environment.theme),
|
||||||
strings: environment.strings,
|
strings: strings,
|
||||||
dateTimeFormat: environment.dateTimeFormat,
|
dateTimeFormat: environment.dateTimeFormat,
|
||||||
hasValueRow: false
|
hasValueRow: false
|
||||||
)
|
)
|
||||||
@ -295,7 +297,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
let timeTitleSize = self.timeTitle.update(
|
let timeTitleSize = self.timeTitle.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
Text(text: "Time", font: Font.regular(17.0), color: environment.theme.actionSheet.primaryTextColor)
|
Text(text: strings.ScheduleMessage_Time, font: Font.regular(17.0), color: environment.theme.actionSheet.primaryTextColor)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: availableSize
|
containerSize: availableSize
|
||||||
@ -364,7 +366,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
let repeatTitleSize = self.repeatTitle.update(
|
let repeatTitleSize = self.repeatTitle.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
Text(text: "Repeat", font: Font.regular(17.0), color: environment.theme.actionSheet.primaryTextColor)
|
Text(text: strings.ScheduleMessage_Repeat, font: Font.regular(17.0), color: environment.theme.actionSheet.primaryTextColor)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: availableSize
|
containerSize: availableSize
|
||||||
@ -380,29 +382,25 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
let repeatString: String
|
let repeatString: String
|
||||||
if let repeatPeriod = self.repeatPeriod {
|
if let repeatPeriod = self.repeatPeriod {
|
||||||
switch repeatPeriod {
|
switch repeatPeriod {
|
||||||
case 60:
|
|
||||||
repeatString = "Every Minute"
|
|
||||||
case 300:
|
|
||||||
repeatString = "Every 5 Minutes"
|
|
||||||
case 86400:
|
case 86400:
|
||||||
repeatString = "Daily"
|
repeatString = strings.ScheduleMessage_RepeatPeriod_Daily
|
||||||
case 7 * 86400:
|
case 7 * 86400:
|
||||||
repeatString = "Weekly"
|
repeatString = strings.ScheduleMessage_RepeatPeriod_Weekly
|
||||||
case 14 * 86400:
|
case 14 * 86400:
|
||||||
repeatString = "Biweekly"
|
repeatString = strings.ScheduleMessage_RepeatPeriod_Biweekly
|
||||||
case 30 * 86400:
|
case 30 * 86400:
|
||||||
repeatString = "Monthly"
|
repeatString = strings.ScheduleMessage_RepeatPeriod_Monthly
|
||||||
case 91 * 86400:
|
case 91 * 86400:
|
||||||
repeatString = "Every 3 Months"
|
repeatString = strings.ScheduleMessage_RepeatPeriod_3Months
|
||||||
case 182 * 86400:
|
case 182 * 86400:
|
||||||
repeatString = "Every 6 Months"
|
repeatString = strings.ScheduleMessage_RepeatPeriod_6Months
|
||||||
case 365 * 86400:
|
case 365 * 86400:
|
||||||
repeatString = "Yearly"
|
repeatString = strings.ScheduleMessage_RepeatPeriod_Yearly
|
||||||
default:
|
default:
|
||||||
repeatString = "\(repeatPeriod)"
|
repeatString = "\(repeatPeriod)s"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
repeatString = "Never"
|
repeatString = strings.ScheduleMessage_RepeatPeriod_Never
|
||||||
}
|
}
|
||||||
|
|
||||||
let repeatValueSize = self.repeatValue.update(
|
let repeatValueSize = self.repeatValue.update(
|
||||||
@ -448,19 +446,19 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
switch component.mode {
|
switch component.mode {
|
||||||
case .scheduledMessages:
|
case .scheduledMessages:
|
||||||
if calendar.isDateInToday(date) {
|
if calendar.isDateInToday(date) {
|
||||||
buttonTitle = environment.strings.Conversation_ScheduleMessage_SendToday(time).string
|
buttonTitle = strings.Conversation_ScheduleMessage_SendToday(time).string
|
||||||
} else if calendar.isDateInTomorrow(date) {
|
} else if calendar.isDateInTomorrow(date) {
|
||||||
buttonTitle = environment.strings.Conversation_ScheduleMessage_SendTomorrow(time).string
|
buttonTitle = strings.Conversation_ScheduleMessage_SendTomorrow(time).string
|
||||||
} else {
|
} else {
|
||||||
buttonTitle = environment.strings.Conversation_ScheduleMessage_SendOn(self.dateFormatter.string(from: date), time).string
|
buttonTitle = strings.Conversation_ScheduleMessage_SendOn(self.dateFormatter.string(from: date), time).string
|
||||||
}
|
}
|
||||||
case .reminders:
|
case .reminders:
|
||||||
if calendar.isDateInToday(date) {
|
if calendar.isDateInToday(date) {
|
||||||
buttonTitle = environment.strings.Conversation_SetReminder_RemindToday(time).string
|
buttonTitle = strings.Conversation_SetReminder_RemindToday(time).string
|
||||||
} else if calendar.isDateInTomorrow(date) {
|
} else if calendar.isDateInTomorrow(date) {
|
||||||
buttonTitle = environment.strings.Conversation_SetReminder_RemindTomorrow(time).string
|
buttonTitle = strings.Conversation_SetReminder_RemindTomorrow(time).string
|
||||||
} else {
|
} else {
|
||||||
buttonTitle = environment.strings.Conversation_SetReminder_RemindOn(self.dateFormatter.string(from: date), time).string
|
buttonTitle = strings.Conversation_SetReminder_RemindOn(self.dateFormatter.string(from: date), time).string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,7 +515,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8),
|
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8),
|
||||||
),
|
),
|
||||||
content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(
|
content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(
|
||||||
Text(text: environment.strings.Conversation_ScheduleMessage_SendWhenOnline, font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.fillColor)
|
Text(text: strings.Conversation_ScheduleMessage_SendWhenOnline, font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.fillColor)
|
||||||
)),
|
)),
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
displaysProgress: false,
|
displaysProgress: false,
|
||||||
@ -608,6 +606,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
sourceFrame: repeatValueFrame,
|
sourceFrame: repeatValueFrame,
|
||||||
component: AnyComponent(RepeatMenuComponent(
|
component: AnyComponent(RepeatMenuComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
|
strings: strings,
|
||||||
value: self.repeatPeriod,
|
value: self.repeatPeriod,
|
||||||
valueUpdated: { [weak self] value in
|
valueUpdated: { [weak self] value in
|
||||||
guard let self, let component = self.component, let environment = self.environment else {
|
guard let self, let component = self.component, let environment = self.environment else {
|
||||||
@ -620,9 +619,9 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||||||
let toastController = UndoOverlayController(
|
let toastController = UndoOverlayController(
|
||||||
presentationData: component.context.sharedContext.currentPresentationData.with { $0 },
|
presentationData: component.context.sharedContext.currentPresentationData.with { $0 },
|
||||||
content: .premiumPaywall(
|
content: .premiumPaywall(
|
||||||
title: "Premium Required",
|
title: strings.ScheduleMessage_PremiumRequired_Title,
|
||||||
text: "Subscribe to **Telegram Premium** to schedule repeating messages.",
|
text: strings.ScheduleMessage_PremiumRequired_Text,
|
||||||
customUndoText: "Add",
|
customUndoText: strings.ScheduleMessage_PremiumRequired_Add,
|
||||||
timeout: nil,
|
timeout: nil,
|
||||||
linkAction: nil
|
linkAction: nil
|
||||||
),
|
),
|
||||||
@ -1169,15 +1168,18 @@ private final class MenuComponent: Component {
|
|||||||
|
|
||||||
private final class RepeatMenuComponent: Component {
|
private final class RepeatMenuComponent: Component {
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
let strings: PresentationStrings
|
||||||
let value: Int32?
|
let value: Int32?
|
||||||
let valueUpdated: (Int32?) -> Void
|
let valueUpdated: (Int32?) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
|
strings: PresentationStrings,
|
||||||
value: Int32?,
|
value: Int32?,
|
||||||
valueUpdated: @escaping (Int32?) -> Void
|
valueUpdated: @escaping (Int32?) -> Void
|
||||||
) {
|
) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
self.value = value
|
self.value = value
|
||||||
self.valueUpdated = valueUpdated
|
self.valueUpdated = valueUpdated
|
||||||
}
|
}
|
||||||
@ -1186,6 +1188,9 @@ private final class RepeatMenuComponent: Component {
|
|||||||
if lhs.theme !== rhs.theme {
|
if lhs.theme !== rhs.theme {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.strings !== rhs.strings {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.value != rhs.value {
|
if lhs.value != rhs.value {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1240,7 +1245,7 @@ private final class RepeatMenuComponent: Component {
|
|||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
PlainButtonComponent(
|
PlainButtonComponent(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
Text(text: "Never", font: Font.regular(17.0), color: textColor)
|
Text(text: component.strings.ScheduleMessage_RepeatPeriod_Never, font: Font.regular(17.0), color: textColor)
|
||||||
),
|
),
|
||||||
action: { [weak self] in
|
action: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -1278,26 +1283,22 @@ private final class RepeatMenuComponent: Component {
|
|||||||
|
|
||||||
let repeatString: String
|
let repeatString: String
|
||||||
switch value {
|
switch value {
|
||||||
case 60:
|
|
||||||
repeatString = "Every Minute"
|
|
||||||
case 300:
|
|
||||||
repeatString = "Every 5 Minutes"
|
|
||||||
case 86400:
|
case 86400:
|
||||||
repeatString = "Daily"
|
repeatString = component.strings.ScheduleMessage_RepeatPeriod_Daily
|
||||||
case 7 * 86400:
|
case 7 * 86400:
|
||||||
repeatString = "Weekly"
|
repeatString = component.strings.ScheduleMessage_RepeatPeriod_Weekly
|
||||||
case 14 * 86400:
|
case 14 * 86400:
|
||||||
repeatString = "Biweekly"
|
repeatString = component.strings.ScheduleMessage_RepeatPeriod_Biweekly
|
||||||
case 30 * 86400:
|
case 30 * 86400:
|
||||||
repeatString = "Monthly"
|
repeatString = component.strings.ScheduleMessage_RepeatPeriod_Monthly
|
||||||
case 91 * 86400:
|
case 91 * 86400:
|
||||||
repeatString = "Every 3 Months"
|
repeatString = component.strings.ScheduleMessage_RepeatPeriod_3Months
|
||||||
case 182 * 86400:
|
case 182 * 86400:
|
||||||
repeatString = "Every 6 Months"
|
repeatString = component.strings.ScheduleMessage_RepeatPeriod_6Months
|
||||||
case 365 * 86400:
|
case 365 * 86400:
|
||||||
repeatString = "Yearly"
|
repeatString = component.strings.ScheduleMessage_RepeatPeriod_Yearly
|
||||||
default:
|
default:
|
||||||
repeatString = "\(value)"
|
repeatString = "\(value)s"
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemSize = itemView.update(
|
let itemSize = itemView.update(
|
||||||
|
|||||||
@ -253,6 +253,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
self.environment = environment
|
self.environment = environment
|
||||||
|
|
||||||
let theme = environment.theme
|
let theme = environment.theme
|
||||||
|
let strings = environment.strings
|
||||||
|
|
||||||
var initialCountryCode: Int32?
|
var initialCountryCode: Int32?
|
||||||
var updateFocusTag: Any?
|
var updateFocusTag: Any?
|
||||||
@ -326,7 +327,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
theme: theme,
|
theme: theme,
|
||||||
initialText: component.initialData.firstName ?? "",
|
initialText: component.initialData.firstName ?? "",
|
||||||
resetText: nil,
|
resetText: nil,
|
||||||
placeholder: "First Name",
|
placeholder: strings.UserInfo_FirstNamePlaceholder,
|
||||||
autocapitalizationType: .sentences,
|
autocapitalizationType: .sentences,
|
||||||
autocorrectionType: .default,
|
autocorrectionType: .default,
|
||||||
returnKeyType: .next,
|
returnKeyType: .next,
|
||||||
@ -347,7 +348,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
theme: theme,
|
theme: theme,
|
||||||
initialText: component.initialData.lastName ?? "",
|
initialText: component.initialData.lastName ?? "",
|
||||||
resetText: nil,
|
resetText: nil,
|
||||||
placeholder: "Last Name",
|
placeholder: strings.UserInfo_LastNamePlaceholder,
|
||||||
autocapitalizationType: .sentences,
|
autocapitalizationType: .sentences,
|
||||||
autocorrectionType: .default,
|
autocorrectionType: .default,
|
||||||
returnKeyType: .next,
|
returnKeyType: .next,
|
||||||
@ -437,7 +438,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
style: .glass,
|
style: .glass,
|
||||||
title: AnyComponent(VStack([
|
title: AnyComponent(VStack([
|
||||||
AnyComponentWithIdentity(id: "title", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "mobile", font: Font.regular(14.0), textColor: theme.list.itemPrimaryTextColor))))),
|
AnyComponentWithIdentity(id: "title", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "mobile", font: Font.regular(14.0), textColor: theme.list.itemPrimaryTextColor))))),
|
||||||
AnyComponentWithIdentity(id: "value", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.ContactInfo_PhoneNumberHidden, font: Font.regular(17.0), textColor: theme.list.itemAccentColor)))))
|
AnyComponentWithIdentity(id: "value", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.ContactInfo_PhoneNumberHidden, font: Font.regular(17.0), textColor: theme.list.itemAccentColor)))))
|
||||||
], alignment: .left, spacing: 4.0)),
|
], alignment: .left, spacing: 4.0)),
|
||||||
contentInsets: UIEdgeInsets(top: 15.0, left: 0.0, bottom: 15.0, right: 0.0),
|
contentInsets: UIEdgeInsets(top: 15.0, left: 0.0, bottom: 15.0, right: 0.0),
|
||||||
accessory: nil,
|
accessory: nil,
|
||||||
@ -445,7 +446,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
)))
|
)))
|
||||||
)
|
)
|
||||||
phoneFooterComponent = AnyComponent(MultilineTextComponent(
|
phoneFooterComponent = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(string: environment.strings.AddContact_ContactWillBeSharedAfterMutual(peer.compactDisplayTitle).string, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)),
|
text: .plain(NSAttributedString(string: strings.AddContact_ContactWillBeSharedAfterMutual(peer.compactDisplayTitle).string, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -454,14 +455,14 @@ final class NewContactScreenComponent: Component {
|
|||||||
ListItemComponentAdaptor(
|
ListItemComponentAdaptor(
|
||||||
itemGenerator: PhoneInputItem(
|
itemGenerator: PhoneInputItem(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
strings: environment.strings,
|
strings: strings,
|
||||||
value: (initialCountryCode, nil, ""),
|
value: (initialCountryCode, nil, ""),
|
||||||
accessory: phoneAccesory,
|
accessory: phoneAccesory,
|
||||||
selectCountryCode: { [weak self] in
|
selectCountryCode: { [weak self] in
|
||||||
guard let self, let environment = self.environment, let controller = environment.controller() else {
|
guard let self, let environment = self.environment, let controller = environment.controller() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let countryController = AuthorizationSequenceCountrySelectionController(strings: environment.strings, theme: environment.theme, glass: true)
|
let countryController = AuthorizationSequenceCountrySelectionController(strings: strings, theme: environment.theme, glass: true)
|
||||||
countryController.completeWithCountryCode = { [weak self] code, name in
|
countryController.completeWithCountryCode = { [weak self] code, name in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -550,12 +551,12 @@ final class NewContactScreenComponent: Component {
|
|||||||
phoneFooterRawText = ""
|
phoneFooterRawText = ""
|
||||||
case let .peer(_, isContact):
|
case let .peer(_, isContact):
|
||||||
if isContact {
|
if isContact {
|
||||||
phoneFooterRawText = "This phone number is already in your contacts. [View >]()"
|
phoneFooterRawText = strings.AddContact_PhoneNumber_IsContact
|
||||||
} else {
|
} else {
|
||||||
phoneFooterRawText = "This phone number is on Telegram."
|
phoneFooterRawText = strings.AddContact_PhoneNumber_Registered
|
||||||
}
|
}
|
||||||
case .notFound:
|
case .notFound:
|
||||||
phoneFooterRawText = "This phone number is not on Telegram. [Invite >]()"
|
phoneFooterRawText = strings.AddContact_PhoneNumber_NotRegistered
|
||||||
}
|
}
|
||||||
let phoneFooterText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(phoneFooterRawText, attributes: footerAttributes))
|
let phoneFooterText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(phoneFooterRawText, attributes: footerAttributes))
|
||||||
if let range = phoneFooterText.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 {
|
if let range = phoneFooterText.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 {
|
||||||
@ -625,7 +626,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
style: .glass,
|
style: .glass,
|
||||||
title: AnyComponent(MultilineTextComponent(
|
title: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Sync Contact to Phone",
|
string: strings.AddContact_SyncToPhone,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: theme.list.itemPrimaryTextColor
|
textColor: theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -649,7 +650,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
style: .glass,
|
style: .glass,
|
||||||
title: AnyComponent(MultilineTextComponent(
|
title: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: environment.strings.AddContact_SharedContactException,
|
string: strings.AddContact_SharedContactException,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: theme.list.itemPrimaryTextColor
|
textColor: theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -666,7 +667,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
)))
|
)))
|
||||||
)
|
)
|
||||||
optionsFooterComponent = AnyComponent(MultilineTextComponent(
|
optionsFooterComponent = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(string: environment.strings.AddContact_SharedContactExceptionInfo(peer.compactDisplayTitle).string, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)),
|
text: .plain(NSAttributedString(string: strings.AddContact_SharedContactExceptionInfo(peer.compactDisplayTitle).string, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -714,8 +715,8 @@ final class NewContactScreenComponent: Component {
|
|||||||
context: component.context,
|
context: component.context,
|
||||||
style: .glass,
|
style: .glass,
|
||||||
theme: theme,
|
theme: theme,
|
||||||
strings: environment.strings,
|
strings: strings,
|
||||||
placeholder: NSAttributedString(string: "Add notes only visible to you", font: Font.regular(17.0), textColor: theme.list.itemPlaceholderTextColor),
|
placeholder: NSAttributedString(string: strings.AddContact_NotePlaceholder, font: Font.regular(17.0), textColor: theme.list.itemPlaceholderTextColor),
|
||||||
characterLimit: characterLimit,
|
characterLimit: characterLimit,
|
||||||
emptyLineHandling: .allowed,
|
emptyLineHandling: .allowed,
|
||||||
returnKeyAction: nil,
|
returnKeyAction: nil,
|
||||||
@ -771,7 +772,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
title: AnyComponent(VStack([
|
title: AnyComponent(VStack([
|
||||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Add via QR Code",
|
string: strings.AddContact_AddQR,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: theme.list.itemAccentColor
|
textColor: theme.list.itemAccentColor
|
||||||
)),
|
)),
|
||||||
@ -867,7 +868,7 @@ final class NewContactScreenComponent: Component {
|
|||||||
MultilineTextComponent(
|
MultilineTextComponent(
|
||||||
text: .plain(
|
text: .plain(
|
||||||
NSAttributedString(
|
NSAttributedString(
|
||||||
string: "New Contact",
|
string: strings.AddContact_Title,
|
||||||
font: Font.semibold(17.0),
|
font: Font.semibold(17.0),
|
||||||
textColor: environment.theme.rootController.navigationBar.primaryTextColor
|
textColor: environment.theme.rootController.navigationBar.primaryTextColor
|
||||||
)
|
)
|
||||||
|
|||||||
@ -12,7 +12,7 @@ public final class GlassBarButtonComponent: Component {
|
|||||||
case tintedGlass
|
case tintedGlass
|
||||||
}
|
}
|
||||||
|
|
||||||
public let size: CGSize
|
public let size: CGSize?
|
||||||
public let backgroundColor: UIColor
|
public let backgroundColor: UIColor
|
||||||
public let isDark: Bool
|
public let isDark: Bool
|
||||||
public let state: DisplayState?
|
public let state: DisplayState?
|
||||||
@ -21,7 +21,7 @@ public final class GlassBarButtonComponent: Component {
|
|||||||
public let action: ((UIView) -> Void)?
|
public let action: ((UIView) -> Void)?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
size: CGSize,
|
size: CGSize?,
|
||||||
backgroundColor: UIColor,
|
backgroundColor: UIColor,
|
||||||
isDark: Bool,
|
isDark: Bool,
|
||||||
state: DisplayState? = nil,
|
state: DisplayState? = nil,
|
||||||
@ -149,9 +149,17 @@ public final class GlassBarButtonComponent: Component {
|
|||||||
transition: componentTransition,
|
transition: componentTransition,
|
||||||
component: component.component.component,
|
component: component.component.component,
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: component.size
|
containerSize: component.size ?? availableSize
|
||||||
)
|
)
|
||||||
let componentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((component.size.width - componentSize.width) / 2.0), y: floorToScreenPixels((component.size.height - componentSize.height) / 2.0)), size: componentSize)
|
|
||||||
|
let containerSize: CGSize
|
||||||
|
if let size = component.size {
|
||||||
|
containerSize = size
|
||||||
|
} else {
|
||||||
|
containerSize = CGSize(width: componentSize.width + 25.0, height: max(availableSize.height, componentSize.height + 19.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
let componentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((containerSize.width - componentSize.width) / 2.0), y: floorToScreenPixels((containerSize.height - componentSize.height) / 2.0)), size: componentSize)
|
||||||
if let view = componentView.view {
|
if let view = componentView.view {
|
||||||
if view.superview == nil {
|
if view.superview == nil {
|
||||||
self.containerView.addSubview(view)
|
self.containerView.addSubview(view)
|
||||||
@ -175,11 +183,11 @@ public final class GlassBarButtonComponent: Component {
|
|||||||
genericAlpha = 0.0
|
genericAlpha = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let cornerRadius = component.size.height * 0.5
|
let cornerRadius = containerSize.height * 0.5
|
||||||
self.genericBackgroundView.update(size: component.size, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: .custom, color: component.backgroundColor), transition: transition)
|
self.genericBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: .custom, color: component.backgroundColor), transition: transition)
|
||||||
self.glassBackgroundView.update(size: component.size, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: effectiveState == .tintedGlass ? .custom : .panel , color: component.backgroundColor.withMultipliedAlpha(effectiveState == .tintedGlass ? 1.0 : 0.7)), transition: transition)
|
self.glassBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: effectiveState == .tintedGlass ? .custom : .panel , color: component.backgroundColor.withMultipliedAlpha(effectiveState == .tintedGlass ? 1.0 : 0.7)), transition: transition)
|
||||||
|
|
||||||
let bounds = CGRect(origin: .zero, size: component.size)
|
let bounds = CGRect(origin: .zero, size: containerSize)
|
||||||
transition.setFrame(view: self.containerView, frame: bounds)
|
transition.setFrame(view: self.containerView, frame: bounds)
|
||||||
|
|
||||||
transition.setAlpha(view: self.genericContainerView, alpha: genericAlpha)
|
transition.setAlpha(view: self.genericContainerView, alpha: genericAlpha)
|
||||||
@ -191,7 +199,7 @@ public final class GlassBarButtonComponent: Component {
|
|||||||
transition.setFrame(view: self.genericBackgroundView, frame: bounds)
|
transition.setFrame(view: self.genericBackgroundView, frame: bounds)
|
||||||
transition.setFrame(view: self.glassBackgroundView, frame: bounds)
|
transition.setFrame(view: self.glassBackgroundView, frame: bounds)
|
||||||
|
|
||||||
return component.size
|
return containerSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -239,6 +239,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
|
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let theme = environment.theme.withModalBlocksBackground()
|
let theme = environment.theme.withModalBlocksBackground()
|
||||||
|
let strings = environment.strings
|
||||||
|
|
||||||
guard let screenState = component.stateContext.stateValue else {
|
guard let screenState = component.stateContext.stateValue else {
|
||||||
return CGSize()
|
return CGSize()
|
||||||
@ -318,7 +319,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
|
|
||||||
let streamAsSectionHeader = AnyComponent(MultilineTextComponent(
|
let streamAsSectionHeader = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "START LIVE AS",
|
string: strings.LiveStreamSettings_StartLiveAs,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: theme.list.freeTextColor
|
textColor: theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -587,7 +588,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
|
|
||||||
let privacySectionHeader = AnyComponent(MultilineTextComponent(
|
let privacySectionHeader = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "WHO CAN VIEW THIS LIVE",
|
string: strings.LiveStreamSettings_WhoCanView,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: theme.list.freeTextColor
|
textColor: theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -596,7 +597,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
|
|
||||||
let privacySectionFooter = AnyComponent(MultilineTextComponent(
|
let privacySectionFooter = AnyComponent(MultilineTextComponent(
|
||||||
text: .markdown(
|
text: .markdown(
|
||||||
text: "[Select people]() who won't see your live.",
|
text: strings.LiveStreamSettings_WhoCanViewInfo,
|
||||||
attributes: MarkdownAttributes(
|
attributes: MarkdownAttributes(
|
||||||
body: MarkdownAttributeSet(font: footerTextFont, textColor: footerTextColor),
|
body: MarkdownAttributeSet(font: footerTextFont, textColor: footerTextColor),
|
||||||
bold: MarkdownAttributeSet(font: footerBoldTextFont, textColor: footerTextColor),
|
bold: MarkdownAttributeSet(font: footerBoldTextFont, textColor: footerTextColor),
|
||||||
@ -666,13 +667,12 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if !screenState.isEdit || (screenState.call != nil && screenState.isEdit && screenState.callIsStream) {
|
if !screenState.isEdit || (screenState.call != nil && screenState.isEdit && screenState.callIsStream) {
|
||||||
let externalStreamSectionItems = [AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
let externalStreamSectionItems = [AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||||
ListActionItemComponent(
|
ListActionItemComponent(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
style: .glass,
|
style: .glass,
|
||||||
title: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "Connect Stream", font: Font.regular(presentationData.listsFontSize.baseDisplaySize), textColor: theme.list.itemPrimaryTextColor)))),
|
title: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.LiveStreamSettings_ConnectStream, font: Font.regular(presentationData.listsFontSize.baseDisplaySize), textColor: theme.list.itemPrimaryTextColor)))),
|
||||||
action: { [weak self] _ in
|
action: { [weak self] _ in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -682,7 +682,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
)
|
)
|
||||||
))]
|
))]
|
||||||
let externalStreamFooterComponent = AnyComponent(MultilineTextComponent(
|
let externalStreamFooterComponent = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(string: "Stream with a different app.", font: footerTextFont, textColor: footerTextColor)),
|
text: .plain(NSAttributedString(string: strings.LiveStreamSettings_ConnectStreamInfo, font: footerTextFont, textColor: footerTextColor)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -710,15 +710,13 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
contentHeight += sectionSpacing
|
contentHeight += sectionSpacing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
var settingsSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
var settingsSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||||
settingsSectionItems.append(AnyComponentWithIdentity(id: "comments", component: AnyComponent(ListActionItemComponent(
|
settingsSectionItems.append(AnyComponentWithIdentity(id: "comments", component: AnyComponent(ListActionItemComponent(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
style: .glass,
|
style: .glass,
|
||||||
title: AnyComponent(MultilineTextComponent(
|
title: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Allow Comments",
|
string: strings.LiveStreamSettings_AllowComments,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: theme.list.itemPrimaryTextColor
|
textColor: theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -735,13 +733,12 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
))))
|
))))
|
||||||
|
|
||||||
if !(screenState.call != nil && screenState.isEdit) {
|
if !(screenState.call != nil && screenState.isEdit) {
|
||||||
//TODO:localize
|
|
||||||
settingsSectionItems.append(AnyComponentWithIdentity(id: "screenshots", component: AnyComponent(ListActionItemComponent(
|
settingsSectionItems.append(AnyComponentWithIdentity(id: "screenshots", component: AnyComponent(ListActionItemComponent(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
style: .glass,
|
style: .glass,
|
||||||
title: AnyComponent(MultilineTextComponent(
|
title: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Allow Screenshots",
|
string: strings.LiveStreamSettings_AllowScreenshots,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: theme.list.itemPrimaryTextColor
|
textColor: theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -789,7 +786,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
minValue: 0,
|
minValue: 0,
|
||||||
lowerBoundTitle: "0",
|
lowerBoundTitle: "0",
|
||||||
upperBoundTitle: "\(presentationStringsFormattedNumber(Int32(clamping: screenState.maxPaidMessageStars), environment.dateTimeFormat.groupingSeparator))",
|
upperBoundTitle: "\(presentationStringsFormattedNumber(Int32(clamping: screenState.maxPaidMessageStars), environment.dateTimeFormat.groupingSeparator))",
|
||||||
title: screenState.paidMessageStars == 0 ? "Free" : "\(screenState.paidMessageStars) Stars",
|
title: screenState.paidMessageStars == 0 ? strings.LiveStreamSettings_PricePerComment_Free : strings.LiveStreamSettings_PricePerComment_Stars(Int32(clamping: screenState.paidMessageStars)),
|
||||||
valueUpdated: { [weak self] value in
|
valueUpdated: { [weak self] value in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
@ -805,7 +802,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
if screenState.allowComments {
|
if screenState.allowComments {
|
||||||
let paidMessageSectionHeader = AnyComponent(MultilineTextComponent(
|
let paidMessageSectionHeader = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "PRICE PER COMMENT",
|
string: strings.LiveStreamSettings_PricePerComment,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: theme.list.freeTextColor
|
textColor: theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -813,7 +810,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
))
|
))
|
||||||
|
|
||||||
let paidMessageSectionFooterComponent = AnyComponent(MultilineTextComponent(
|
let paidMessageSectionFooterComponent = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(string: "The price a viewer must pay to send a comment.", font: footerTextFont, textColor: footerTextColor)),
|
text: .plain(NSAttributedString(string: strings.LiveStreamSettings_PricePerCommentInfo, font: footerTextFont, textColor: footerTextColor)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -856,7 +853,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
transition.setFrame(view: self.bottomEdgeEffectView, frame: bottomEdgeEffectFrame)
|
transition.setFrame(view: self.bottomEdgeEffectView, frame: bottomEdgeEffectFrame)
|
||||||
self.bottomEdgeEffectView.update(content: theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: bottomEdgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: transition)
|
self.bottomEdgeEffectView.update(content: theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: bottomEdgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: transition)
|
||||||
|
|
||||||
let title: String = screenState.isEdit ? "Live Stream" : "Live Settings"
|
let title: String = screenState.isEdit ? strings.LiveStreamSettings_TitleEdit : strings.LiveStreamSettings_Title
|
||||||
let titleSize = self.title.update(
|
let titleSize = self.title.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
@ -961,7 +958,7 @@ final class LiveStreamSettingsScreenComponent: Component {
|
|||||||
content: AnyComponentWithIdentity(
|
content: AnyComponentWithIdentity(
|
||||||
id: "label",
|
id: "label",
|
||||||
component: AnyComponent(ButtonTextContentComponent(
|
component: AnyComponent(ButtonTextContentComponent(
|
||||||
text: "Save Settings",
|
text: strings.LiveStreamSettings_SaveSettings,
|
||||||
badge: 0,
|
badge: 0,
|
||||||
textColor: theme.list.itemCheckColors.foregroundColor,
|
textColor: theme.list.itemCheckColors.foregroundColor,
|
||||||
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user