mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge commit '69c8e2634b'
This commit is contained in:
commit
4f513faa82
29 changed files with 348 additions and 153 deletions
|
|
@ -15813,6 +15813,11 @@ Error: %8$@";
|
|||
"Notification.CopyProtection.Disabled" = "%1$@ enabled sharing in this chat";
|
||||
"Notification.CopyProtection.DisabledYou" = "You enabled sharing in this chat";
|
||||
"Notification.CopyProtection.StillEnabled" = "Sharing in this chat is still disabled";
|
||||
"Notification.CopyProtection.Request" = "%@ would like to enable sharing in this chat";
|
||||
"Notification.CopyProtection.RequestYou" = "You suggested enabling sharing in this chat";
|
||||
"Notification.CopyProtection.RequestChat" = "%@ would like to enable sharing in this chat, which includes:";
|
||||
"Notification.CopyProtection.RequestChatYou" = "You suggested enabling sharing in this chat, which includes:";
|
||||
"Notification.CopyProtection.RequestChatInfo" = "forwarding messages\nsaving photos and videos\ncopying messages\ntaking screenshots";
|
||||
|
||||
"Gallery.CreateSticker" = "Create Sticker";
|
||||
|
||||
|
|
@ -15834,3 +15839,86 @@ Error: %8$@";
|
|||
"Channel.AdminLog.MessageRankNew" = "changed member tag:\n%1$@";
|
||||
|
||||
"TextFormat.Date" = "Date";
|
||||
|
||||
"Gift.Craft.UnavailableBlockchain.Title" = "Crafting Unavailable";
|
||||
"Gift.Craft.UnavailableBlockchain.Text" = "This gift can't be used as the first one in crafting because it has been recorded on the blockchain.";
|
||||
|
||||
"AuthConfirmation.Emoji.Title" = "Tap the emoji shown\non your other device";
|
||||
"AuthConfirmation.Emoji.Description" = "Telegram wants to make sure it's really you.";
|
||||
|
||||
"Chat.TagPlaceholder" = "Tag";
|
||||
|
||||
"Chat.EnableSharingOffer.Reject" = "Reject";
|
||||
"Chat.EnableSharingOffer.Accept" = "Accept";
|
||||
|
||||
"Chat.EnableSharingOffer.AcceptConfirmation.Title" = "Enable Sharing";
|
||||
"Chat.EnableSharingOffer.AcceptConfirmation.Text" = "Are you sure you want to enable sharing?";
|
||||
"Chat.EnableSharingOffer.AcceptConfirmation.Accept" = "Enable";
|
||||
|
||||
"Chat.EnableSharingOffer.RejectConfirmation.Title" = "Disable Sharing";
|
||||
"Chat.EnableSharingOffer.RejectConfirmation.Text" = "Are you sure you want to keep sharing disabled?";
|
||||
"Chat.EnableSharingOffer.RejectConfirmation.Reject" = "Keep Disabled";
|
||||
|
||||
"Chat.TagUpdated.Added" = "Tag added";
|
||||
"Chat.TagUpdated.Removed" = "Tag removed";
|
||||
|
||||
"Conversation.FormatDate.Title" = "Date";
|
||||
"Conversation.FormatDate.AddDate" = "Add Date";
|
||||
"Conversation.FormatDate.EditDate" = "Edit Date";
|
||||
"Conversation.FormatDate.RemoveDate" = "Remove Date";
|
||||
|
||||
"Conversation.CopyProtectionInfoPrivate" = "**%@** disabled copying and forwarding in this chat.";
|
||||
"Conversation.CopyProtectionInfoPrivateYou" = "You disabled copying and forwarding in this chat.";
|
||||
|
||||
"Chat.JoinedGroup.Text" = "You joined the group";
|
||||
"Chat.JoinedGroup.AddTag" = "Add Tag";
|
||||
|
||||
"GroupPermission.NoEditRank" = "no tag";
|
||||
|
||||
"Channel.BanUser.PermissionEditRank" = "Edit Member Tag";
|
||||
"Channel.BanUser.PermissionEditOwnRank" = "Edit Own Tags";
|
||||
|
||||
"Channel.Management.AddAdmin" = "Add Admin";
|
||||
"Channel.Management.SaveChanges" = "Save Changes";
|
||||
|
||||
"Preview.Gif.AddCaption" = "Add Caption";
|
||||
"Preview.Gif.Edit" = "Edit";
|
||||
|
||||
"GroupPermission.Member" = "Member";
|
||||
"GroupPermission.SaveChanges" = "Save Changes";
|
||||
"GroupPermission.AddException" = "Add Exception";
|
||||
|
||||
"GroupInfo.ActionSend" = "Send Message";
|
||||
"GroupInfo.ActionEditRank" = "Edit Member Tag";
|
||||
"GroupInfo.ActionRemove" = "Remove";
|
||||
|
||||
"PeerInfo.EnableSharing" = "Enable Sharing";
|
||||
"PeerInfo.DisableSharing" = "Disable Sharing";
|
||||
|
||||
"Chat.Context.Date.Copy" = "Copy Date";
|
||||
"Chat.Context.Date.AddToCalendar" = "Add to Calendar";
|
||||
"Chat.Context.Date.SetReminder" = "Set a Reminder";
|
||||
|
||||
"Conversation.DateCopied" = "Date copied to clipboard";
|
||||
"Conversation.DateReminderSet" = "You have set a reminder in [Saved Messages]().";
|
||||
|
||||
"RankInfo.Member.Title" = "Member Tag";
|
||||
"RankInfo.Member.Text" = "This gray tag %1$@ is **%2$@'s** member tag. **%3$@** is a member of **%4$@**.";
|
||||
"RankInfo.Admin.Title" = "Admin Tag";
|
||||
"RankInfo.Admin.Text" = "This green tag %1$@ is **%2$@'s** admin tag. **%3$@** is an administrator of **%4$@**.";
|
||||
"RankInfo.Owner.Title" = "Owner Tag";
|
||||
"RankInfo.Owner.Text" = "This purple tag %1$@ is **%2$@'s** owner tag. **%3$@** is the owner of **%4$@**.";
|
||||
"RankInfo.ChangeInfo" = "Only admins can assign member tags in this group.";
|
||||
"RankInfo.MemberTag" = "Admin Tag";
|
||||
"RankInfo.AdminTag" = "Admin Tag";
|
||||
"RankInfo.OwnerTag" = "Owner Tag";
|
||||
"RankInfo.SetMyTag" = "Set My Tag";
|
||||
|
||||
"EditRank.Title" = "Member Tag";
|
||||
"EditRank.Placeholder" = "Add Tag";
|
||||
"EditRank.Info" = "Add short tag next to %@'s name.";
|
||||
"EditRank.InfoYou" = "Share your role, title, or how you're known in this group. Your tag is visible to all members.";
|
||||
"EditRank.AddLater" = "Add Later";
|
||||
"EditRank.AddTag" = "Add Tag";
|
||||
"EditRank.EditTag" = "Edit Tag";
|
||||
"EditRank.RemoveTag" = "Remove Tag";
|
||||
|
|
|
|||
|
|
@ -5258,10 +5258,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
} else if let group = peer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) {
|
||||
canEditRank = true
|
||||
}
|
||||
//TODO:localize
|
||||
if canEditRank {
|
||||
let context = self.context
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: nil, text: "You joined the group", cancel: "Add Tag", destructive: false), elevatedLayout: true, action: { [weak self] action in
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: nil, text: self.presentationData.strings.Chat_JoinedGroup_Text, cancel: self.presentationData.strings.Chat_JoinedGroup_AddTag, destructive: false), elevatedLayout: true, action: { [weak self] action in
|
||||
if let self, case .undo = action {
|
||||
let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peerId, participantId: context.account.peerId, rank: nil, role: .member)
|
||||
self.push(tagController)
|
||||
|
|
|
|||
|
|
@ -1617,8 +1617,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
|||
}
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
var footerButtonTitle: String = "Save Changes"
|
||||
var footerButtonTitle: String = presentationData.strings.Channel_Management_SaveChanges
|
||||
var footerItem: ItemListControllerFooterItem?
|
||||
|
||||
var isCreator = false
|
||||
|
|
@ -1667,8 +1666,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
|||
})
|
||||
} else {
|
||||
title = presentationData.strings.Channel_Management_AddModerator
|
||||
footerButtonTitle = "Add Admin"
|
||||
//TODO:localize
|
||||
footerButtonTitle = presentationData.strings.Channel_Management_AddAdmin
|
||||
}
|
||||
} else {
|
||||
switch initialParticipant {
|
||||
|
|
|
|||
|
|
@ -637,7 +637,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
}
|
||||
let memberPresence = peersMap.1
|
||||
|
||||
var footerButtonTitle: String = "Save Changes"
|
||||
var footerButtonTitle: String = presentationData.strings.GroupPermission_SaveChanges
|
||||
|
||||
let rightButtonActionImpl = {
|
||||
let _ = (peerView.get()
|
||||
|
|
@ -819,16 +819,15 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
})
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let title: String
|
||||
if editMember {
|
||||
title = "Member"
|
||||
title = presentationData.strings.GroupPermission_Member
|
||||
} else {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo, _, _) = initialParticipant, banInfo != nil {
|
||||
title = presentationData.strings.GroupPermission_Title
|
||||
} else {
|
||||
title = presentationData.strings.GroupPermission_NewTitle
|
||||
footerButtonTitle = "Add Exception"
|
||||
footerButtonTitle = presentationData.strings.GroupPermission_AddException
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -535,11 +535,10 @@ func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatB
|
|||
} else if right.contains(.banSendInstantVideos) {
|
||||
return strings.Channel_BanUser_PermissionSendVideoMessage
|
||||
} else if right.contains(.banEditRank) {
|
||||
//TODO:localize
|
||||
if defaultPermissions {
|
||||
return "Edit Own Tags"
|
||||
return strings.Channel_BanUser_PermissionEditOwnRank
|
||||
} else {
|
||||
return "Edit Member Tag"
|
||||
return strings.Channel_BanUser_PermissionEditRank
|
||||
}
|
||||
} else {
|
||||
return ""
|
||||
|
|
@ -578,8 +577,7 @@ func compactStringForGroupPermission(strings: PresentationStrings, right: Telegr
|
|||
} else if right.contains(.banManageTopics) {
|
||||
return strings.GroupPermission_NoManageTopics
|
||||
} else if right.contains(.banEditRank) {
|
||||
//TODO:localize
|
||||
return "no tag"
|
||||
return strings.GroupPermission_NoEditRank
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -845,7 +845,7 @@ public enum PremiumPerk: CaseIterable {
|
|||
case .todo:
|
||||
return "Premium/Perk/Todo"
|
||||
case .copyProtection:
|
||||
return "Premium/Perk/CopyProtection"
|
||||
return "Premium/Perk/NoForward"
|
||||
case .businessLocation:
|
||||
return "Premium/BusinessPerk/Location"
|
||||
case .businessHours:
|
||||
|
|
|
|||
|
|
@ -864,6 +864,26 @@ public class PremiumLimitsListScreen: ViewController {
|
|||
)
|
||||
)
|
||||
|
||||
availableItems[.copyProtection] = DemoPagerComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: PremiumDemoScreen.Subject.copyProtection,
|
||||
component: AnyComponent(
|
||||
PageComponent(
|
||||
content: AnyComponent(PhoneDemoComponent(
|
||||
context: context,
|
||||
position: .top,
|
||||
model: .island,
|
||||
videoFile: videos["pm_noforwards"],
|
||||
decoration: .badgeStars
|
||||
)),
|
||||
title: strings.Premium_CopyProtection,
|
||||
text: strings.Premium_CopyProtectionInfo,
|
||||
textColor: textColor
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
availableItems[.business] = DemoPagerComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: PremiumDemoScreen.Subject.business,
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ final class TodoChecksView: UIView, PhoneDemoDecorationView {
|
|||
|
||||
private var displayLink: SharedDisplayLinkDriver.Link?
|
||||
|
||||
private var containerView = UIView()
|
||||
private var particleSet: ParticleSet?
|
||||
private var particleLayers: [CheckLayer] = []
|
||||
private var particleMap: [Int64: CheckLayer] = [:]
|
||||
|
|
@ -174,6 +175,10 @@ final class TodoChecksView: UIView, PhoneDemoDecorationView {
|
|||
self.displayLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] delta in
|
||||
self?.update(deltaTime: CGFloat(delta))
|
||||
})
|
||||
self.displayLink?.isPaused = true
|
||||
|
||||
self.containerView.frame = CGRect(origin: .zero, size: frame.size)
|
||||
self.addSubview(self.containerView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -218,7 +223,7 @@ final class TodoChecksView: UIView, PhoneDemoDecorationView {
|
|||
particleLayer.theme = CheckNodeTheme(backgroundColor: .white, strokeColor: .clear, borderColor: .white, overlayBorder: false, hasInset: false, hasShadow: false)
|
||||
particleLayer.bounds = CGRect(origin: CGPoint(), size: CGSize(width: 22.0, height: 22.0))
|
||||
self.particleLayers.append(particleLayer)
|
||||
self.layer.addSublayer(particleLayer)
|
||||
self.containerView.layer.addSublayer(particleLayer)
|
||||
}
|
||||
self.particleMap[particle.id] = particleLayer
|
||||
}
|
||||
|
|
@ -251,14 +256,16 @@ final class TodoChecksView: UIView, PhoneDemoDecorationView {
|
|||
|
||||
self.displayLink?.isPaused = !visible
|
||||
|
||||
// let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear)
|
||||
// transition.updateAlpha(layer: self.containerView.layer, alpha: visible ? 1.0 : 0.0, completion: { [weak self] finished in
|
||||
// if let strongSelf = self, finished && !visible && !strongSelf.visible {
|
||||
// for view in strongSelf.containerView.subviews {
|
||||
// view.removeFromSuperview()
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear)
|
||||
transition.updateAlpha(layer: self.containerView.layer, alpha: visible ? 1.0 : 0.0, completion: { [weak self] finished in
|
||||
if let strongSelf = self, finished && !visible && !strongSelf.visible {
|
||||
for particleLayer in strongSelf.particleLayers {
|
||||
particleLayer.removeFromSuperlayer()
|
||||
}
|
||||
strongSelf.particleLayers.removeAll()
|
||||
strongSelf.particleMap.removeAll()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func resetAnimation() {
|
||||
|
|
|
|||
|
|
@ -1716,7 +1716,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
priceString = formatTonAmountText(amount.amount.value, dateTimeFormat: dateTimeFormat) + " TON"
|
||||
}
|
||||
|
||||
let timeString = "[TODO]"
|
||||
let timeString = ""
|
||||
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[1] = boldAttributes
|
||||
|
|
@ -1755,7 +1755,6 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
attributedString = addAttributesToStringWithRanges(strings.Notification_GroupCreatorChangeApplied(authorName, targetName)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id), (1, groupCreatorChange.targetPeerId)]))
|
||||
}
|
||||
case let .copyProtectionToggle(previousValue, newValue):
|
||||
//TODO:localize
|
||||
if previousValue == newValue && newValue {
|
||||
attributedString = NSAttributedString(string: strings.Notification_CopyProtection_StillEnabled, font: titleFont, textColor: primaryTextColor)
|
||||
} else if message.author?.id == accountPeerId {
|
||||
|
|
@ -1775,12 +1774,11 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
}
|
||||
}
|
||||
case .copyProtectionRequest:
|
||||
//TODO:localize
|
||||
let peerName = message.peers[message.id.peerId].flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||
if message.author?.id == accountPeerId {
|
||||
attributedString = NSAttributedString(string: "You suggested enabling sharing in this chat", font: titleFont, textColor: primaryTextColor)
|
||||
attributedString = NSAttributedString(string: strings.Notification_CopyProtection_RequestYou, font: titleFont, textColor: primaryTextColor)
|
||||
} else {
|
||||
attributedString = NSAttributedString(string: "\(peerName) would like to enable sharing in this chat", font: titleFont, textColor: primaryTextColor)
|
||||
attributedString = NSAttributedString(string: strings.Notification_CopyProtection_Request(peerName).string, font: titleFont, textColor: primaryTextColor)
|
||||
}
|
||||
case .unknown:
|
||||
attributedString = nil
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ swift_library(
|
|||
"//submodules/PhoneNumberFormat",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/AccountUtils",
|
||||
"//submodules/ActivityIndicator",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/AccountPeerContextItem",
|
||||
],
|
||||
visibility = [
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import ContextUI
|
|||
import AccountUtils
|
||||
import GlassBackgroundComponent
|
||||
import AccountPeerContextItem
|
||||
import ActivityIndicator
|
||||
|
||||
private final class AuthConfirmationSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -65,6 +66,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
|
||||
var displayEmoji = false
|
||||
var matchCodes: [String]?
|
||||
var selectedMatchCode: String?
|
||||
|
||||
init(context: AccountContext, subject: MessageActionUrlAuthResult) {
|
||||
self.context = context
|
||||
|
|
@ -265,11 +267,13 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
state.displayPhoneNumberConfirmation(commit: { sharePhoneNumber in
|
||||
component.completion(accountContext, accountPeer, .accept(allowWriteAccess: allowWrite, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode))
|
||||
state.inProgress = true
|
||||
state.selectedMatchCode = matchCode
|
||||
state.updated()
|
||||
})
|
||||
} else {
|
||||
component.completion(accountContext, accountPeer, .accept(allowWriteAccess: allowWrite, sharePhoneNumber: false, matchCode: matchCode))
|
||||
state.inProgress = true
|
||||
state.selectedMatchCode = matchCode
|
||||
state.updated()
|
||||
}
|
||||
}
|
||||
|
|
@ -277,12 +281,11 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
var contentHeight: CGFloat = 32.0
|
||||
|
||||
if state.displayEmoji, let matchCodes = state.matchCodes {
|
||||
//TODO:localize
|
||||
contentHeight += 36.0
|
||||
|
||||
let emojiTitle = emojiTitle.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .markdown(text: "Tap the emoji shown\non your other device", attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.controlAccentColor), link: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })),
|
||||
text: .markdown(text: strings.AuthConfirmation_Emoji_Title, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.controlAccentColor), link: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 2,
|
||||
lineSpacing: 0.2
|
||||
|
|
@ -300,7 +303,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
|
||||
let emojiDescription = emojiDescription.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .markdown(text: "Telegram wants to make sure it's really you.", attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: theme.actionSheet.primaryTextColor), link: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })),
|
||||
text: .markdown(text: strings.AuthConfirmation_Emoji_Description, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: theme.actionSheet.primaryTextColor), link: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 3,
|
||||
lineSpacing: 0.2
|
||||
|
|
@ -322,18 +325,30 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
let totalWidth = CGFloat(matchCodes.count) * emojiSize.width + CGFloat(matchCodes.count - 1) * emojiSpacing
|
||||
var emojiOriginX = context.availableSize.width / 2.0 - totalWidth / 2.0
|
||||
for code in matchCodes {
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(id: "background", component: AnyComponent(
|
||||
FilledRoundedRectangleComponent(color: theme.list.itemBlocksBackgroundColor, cornerRadius: .minEdge, smoothCorners: false)
|
||||
))
|
||||
)
|
||||
if state.selectedMatchCode == code {
|
||||
items.append(
|
||||
AnyComponentWithIdentity(id: "progress", component: AnyComponent(
|
||||
ActivityIndicatorComponent(color: theme.list.itemAccentColor)
|
||||
))
|
||||
)
|
||||
}
|
||||
items.append(
|
||||
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
||||
Text(text: code, font: Font.regular(32.0), color: .black)
|
||||
))
|
||||
)
|
||||
|
||||
let emoji = emojis[code].update(
|
||||
component: AnyComponent(
|
||||
PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
ZStack([
|
||||
AnyComponentWithIdentity(id: "background", component: AnyComponent(
|
||||
FilledRoundedRectangleComponent(color: theme.list.itemBlocksBackgroundColor, cornerRadius: .minEdge, smoothCorners: false)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
||||
Text(text: code, font: Font.regular(32.0), color: .black)
|
||||
))
|
||||
])
|
||||
ZStack(items)
|
||||
),
|
||||
minSize: emojiSize,
|
||||
action: {
|
||||
|
|
@ -839,3 +854,68 @@ private final class AuthConfirmationReferenceContentSource: ContextReferenceCont
|
|||
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private final class ActivityIndicatorComponent: Component {
|
||||
let color: UIColor
|
||||
|
||||
init(
|
||||
color: UIColor
|
||||
) {
|
||||
self.color = color
|
||||
}
|
||||
|
||||
static func ==(lhs: ActivityIndicatorComponent, rhs: ActivityIndicatorComponent) -> Bool {
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let background = UIView()
|
||||
private let activityIndicator: ActivityIndicator
|
||||
|
||||
private var component: ActivityIndicatorComponent?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.activityIndicator = ActivityIndicator(type: .custom(.white, 64.0, 2.0, true))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
|
||||
self.addSubview(self.background)
|
||||
self.addSubview(self.activityIndicator.view)
|
||||
}
|
||||
|
||||
required public init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: ActivityIndicatorComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
|
||||
let size = CGSize(width: 64.0, height: 64.0)
|
||||
|
||||
self.background.backgroundColor = component.color.withMultipliedAlpha(0.1)
|
||||
self.background.layer.cornerRadius = 32.0
|
||||
self.background.clipsToBounds = true
|
||||
|
||||
if self.component == nil {
|
||||
self.activityIndicator.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
self.component = component
|
||||
|
||||
self.background.frame = CGRect(origin: .zero, size: size)
|
||||
self.activityIndicator.frame = CGRect(origin: .zero, size: size)
|
||||
self.activityIndicator.type = .custom(component.color, 64.0, 2.0, true)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View()
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,10 +263,9 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||
} else if let group = peer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) {
|
||||
canEditRank = true
|
||||
}
|
||||
//TODO:localize
|
||||
if canEditRank {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: "You joined the group", cancel: "Add Tag", destructive: false), elevatedLayout: true, action: { action in
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: presentationData.strings.Chat_JoinedGroup_Text, cancel: presentationData.strings.Chat_JoinedGroup_AddTag, destructive: false), elevatedLayout: true, action: { action in
|
||||
if case .undo = action {
|
||||
let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peer.id, participantId: context.account.peerId, rank: nil, role: .member)
|
||||
self.interfaceInteraction?.getNavigationController()?.pushViewController(tagController)
|
||||
|
|
|
|||
|
|
@ -2583,8 +2583,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
case let .member(rank):
|
||||
if let rank, !rank.isEmpty {
|
||||
if rank == "0️⃣" {
|
||||
//TODO:localize
|
||||
string = "Tag"
|
||||
string = item.presentationData.strings.Chat_TagPlaceholder
|
||||
defaultRankColor = defaultRankColor.withMultipliedAlpha(0.5)
|
||||
} else {
|
||||
string = rank.trimmingEmojis
|
||||
|
|
@ -2924,7 +2923,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
icon: .suggestedPostReject
|
||||
)
|
||||
]
|
||||
//TODO:localize
|
||||
let (minWidth, buttonsLayout) = actionButtonsLayout(
|
||||
item.context,
|
||||
item.presentationData.theme,
|
||||
|
|
@ -2934,8 +2932,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
ReplyMarkupMessageAttribute(
|
||||
rows: [
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: "Reject", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline), style: nil),
|
||||
ReplyMarkupButton(title: "Accept", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove), style: nil)
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_EnableSharingOffer_Reject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline), style: nil),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_EnableSharingOffer_Accept, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove), style: nil)
|
||||
])
|
||||
],
|
||||
flags: [],
|
||||
|
|
|
|||
|
|
@ -131,12 +131,12 @@ public class ChatMessageDisableCopyProtectionBubbleContentNode: ChatMessageBubbl
|
|||
|
||||
let peerName = item.message.peers[item.message.id.peerId].flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||
if incoming {
|
||||
text = "**\(peerName)** would like to enable sharing in this chat, which includes:"
|
||||
text = item.presentationData.strings.Notification_CopyProtection_RequestChat(peerName).string
|
||||
} else {
|
||||
text = "You suggested enabling sharing in this chat, which includes:"
|
||||
text = item.presentationData.strings.Notification_CopyProtection_RequestChatYou
|
||||
}
|
||||
|
||||
var infoText = "forwarding messages\nsaving photos and videos\ncopying messages\ntaking screenshots"
|
||||
var infoText = item.presentationData.strings.Notification_CopyProtection_RequestChatInfo
|
||||
infoText = " # \(infoText.replacingOccurrences(of: "\n", with: "\n # "))"
|
||||
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(
|
||||
|
|
|
|||
|
|
@ -122,39 +122,38 @@ private final class ChatRankInfoSheetContent: CombinedComponent {
|
|||
let textString: String
|
||||
let linkColor: UIColor
|
||||
var linkHasBackground = true
|
||||
let additionalTextString: String? = component.canChange ? nil : "Only admins can assign member tags in this group."
|
||||
var adminPreviewTag = "Admin Tag"
|
||||
let additionalTextString: String? = component.canChange ? nil : strings.RankInfo_ChangeInfo
|
||||
var adminPreviewTag = strings.RankInfo_AdminTag
|
||||
var adminPreviewRole: ChatRankInfoScreenRole = .admin
|
||||
|
||||
var canEdit = false
|
||||
let userName = context.component.userPeer.compactDisplayTitle
|
||||
let chatName = context.component.chatPeer.compactDisplayTitle
|
||||
|
||||
let canEdit = component.canChange
|
||||
switch context.component.role {
|
||||
case .creator:
|
||||
var rank = !context.component.rank.isEmpty ? context.component.rank : strings.Conversation_Owner
|
||||
rank = rank.replacingOccurrences(of: " ", with: "\u{00A0}")
|
||||
iconColor = UIColor(rgb: 0x956ac8)
|
||||
linkColor = iconColor
|
||||
titleString = "Owner Tag"
|
||||
textString = "This purple tag [\(rank)]() is **\(context.component.userPeer.compactDisplayTitle)'s** owner tag. **\(context.component.userPeer.compactDisplayTitle)** is the owner of **\(context.component.chatPeer.compactDisplayTitle)**."
|
||||
adminPreviewTag = "Owner Tag"
|
||||
titleString = strings.RankInfo_Owner_Title
|
||||
textString = strings.RankInfo_Owner_Text(" [\(rank)]() ", userName, userName, chatName).string
|
||||
adminPreviewTag = strings.RankInfo_OwnerTag
|
||||
adminPreviewRole = .creator
|
||||
case .admin:
|
||||
var rank = !context.component.rank.isEmpty ? context.component.rank : strings.Conversation_Admin
|
||||
rank = rank.replacingOccurrences(of: " ", with: "\u{00A0}")
|
||||
iconColor = UIColor(rgb: 0x49a355)
|
||||
linkColor = iconColor
|
||||
titleString = "Admin Tag"
|
||||
textString = "This green tag [\(rank)]() is **\(context.component.userPeer.compactDisplayTitle)'s** admin tag. **\(context.component.userPeer.compactDisplayTitle)** is an administrator of **\(context.component.chatPeer.compactDisplayTitle)**."
|
||||
titleString = strings.RankInfo_Admin_Title
|
||||
textString = strings.RankInfo_Admin_Text(" [\(rank)]() ", userName, userName, chatName).string
|
||||
case .member:
|
||||
let rank = context.component.rank.replacingOccurrences(of: " ", with: "\u{00A0}")
|
||||
iconColor = secondaryTextColor.withMultipliedAlpha(0.85)
|
||||
linkColor = secondaryTextColor
|
||||
linkHasBackground = false
|
||||
titleString = "Member Tag"
|
||||
textString = "This grey tag [\(rank)]() is **\(context.component.userPeer.compactDisplayTitle)'s** member tag. **\(context.component.userPeer.compactDisplayTitle)** is a member of **\(context.component.chatPeer.compactDisplayTitle)**."
|
||||
|
||||
if component.canChange {
|
||||
canEdit = true
|
||||
}
|
||||
titleString = strings.RankInfo_Member_Title
|
||||
textString = strings.RankInfo_Member_Text(" [\(rank)]() ", userName, userName, chatName).string
|
||||
}
|
||||
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||
|
|
@ -241,7 +240,7 @@ private final class ChatRankInfoSheetContent: CombinedComponent {
|
|||
text: "Reinhardt, we need to find you some new tunes, mkay?",
|
||||
entities: nil,
|
||||
media: [],
|
||||
rank: "Member Tag",
|
||||
rank: strings.RankInfo_MemberTag,
|
||||
rankRole: .member
|
||||
)
|
||||
|
||||
|
|
@ -379,7 +378,7 @@ private final class ChatRankInfoSheetContent: CombinedComponent {
|
|||
var buttonTitle: [AnyComponentWithIdentity<Empty>] = []
|
||||
if canEdit {
|
||||
buttonTitle.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(ButtonTextContentComponent(
|
||||
text: "Set My Tag",
|
||||
text: strings.RankInfo_SetMyTag,
|
||||
badge: 0,
|
||||
textColor: theme.list.itemCheckColors.foregroundColor,
|
||||
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
||||
|
|
|
|||
|
|
@ -2285,8 +2285,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||
}
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Add Caption", icon: { theme in
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Preview_Gif_AddCaption, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddCaption"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
|
@ -2295,7 +2294,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||
}
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit", icon: { theme in
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Preview_Gif_Edit, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Draw"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
let titleString: String
|
||||
switch component.subject {
|
||||
case .rank:
|
||||
titleString = "Member Tag"
|
||||
titleString = strings.EditRank_Title
|
||||
}
|
||||
|
||||
let title = title.update(
|
||||
|
|
@ -189,9 +189,9 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
switch component.subject {
|
||||
case let .rank(_, _, _, role):
|
||||
if peer.id == component.context.account.peerId {
|
||||
rankFooterString = "Share your role, title, or how you're known in this group. Your tag is visible to all members."
|
||||
rankFooterString = strings.EditRank_InfoYou
|
||||
} else {
|
||||
rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name."
|
||||
rankFooterString = strings.EditRank_Info(peer.compactDisplayTitle).string
|
||||
}
|
||||
switch role {
|
||||
case .creator:
|
||||
|
|
@ -239,7 +239,7 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
theme: theme,
|
||||
initialText: state.initialRank ?? "",
|
||||
resetText: nil,
|
||||
placeholder: "Add tag",
|
||||
placeholder: strings.EditRank_Placeholder,
|
||||
characterLimit: Int(rankMaxLength),
|
||||
autocapitalizationType: .sentences,
|
||||
autocorrectionType: .default,
|
||||
|
|
@ -291,13 +291,13 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
switch component.subject {
|
||||
case let .rank(_, _, initialRank, _):
|
||||
if (initialRank ?? "").isEmpty && (state.rank ?? "").isEmpty {
|
||||
buttonTitle = "Add Later"
|
||||
buttonTitle = strings.EditRank_AddLater
|
||||
} else if (initialRank ?? "").isEmpty && !(state.rank ?? "").isEmpty {
|
||||
buttonTitle = "Add Tag"
|
||||
buttonTitle = strings.EditRank_AddTag
|
||||
} else if !(initialRank ?? "").isEmpty && (state.rank ?? "").isEmpty {
|
||||
buttonTitle = "Remove Tag"
|
||||
buttonTitle = strings.EditRank_RemoveTag
|
||||
} else {
|
||||
buttonTitle = "Edit Tag"
|
||||
buttonTitle = strings.EditRank_EditTag
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -503,14 +503,13 @@ public class ChatParticipantRightsScreen: ViewControllerComponentContainer {
|
|||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
Queue.mainQueue().after(0.5) {
|
||||
//TODO:localize
|
||||
var title: String?
|
||||
var text: String
|
||||
if let rank {
|
||||
title = "Tag added"
|
||||
title = presentationData.strings.Chat_TagUpdated_Added
|
||||
text = rank
|
||||
} else {
|
||||
text = "Tag removed"
|
||||
text = presentationData.strings.Chat_TagUpdated_Removed
|
||||
}
|
||||
let toastController = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: title, text: text, cancel: nil, destructive: false), appearance: .init(isNarrow: true), action: { _ in return true})
|
||||
(navigationController.topViewController as? ViewController)?.present(toastController, in: .current)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
let mode: ChatScheduleTimeScreen.Mode
|
||||
let currentTime: Int32?
|
||||
let currentRepeatPeriod: Int32?
|
||||
let suggestedTime: Int32?
|
||||
let minimalTime: Int32?
|
||||
let externalState: ExternalState
|
||||
let dismiss: () -> Void
|
||||
|
|
@ -44,6 +45,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
mode: ChatScheduleTimeScreen.Mode,
|
||||
currentTime: Int32?,
|
||||
currentRepeatPeriod: Int32?,
|
||||
suggestedTime: Int32?,
|
||||
minimalTime: Int32?,
|
||||
externalState: ExternalState,
|
||||
dismiss: @escaping () -> Void
|
||||
|
|
@ -52,6 +54,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
self.mode = mode
|
||||
self.currentTime = currentTime
|
||||
self.currentRepeatPeriod = currentRepeatPeriod
|
||||
self.suggestedTime = suggestedTime
|
||||
self.minimalTime = minimalTime
|
||||
self.externalState = externalState
|
||||
self.dismiss = dismiss
|
||||
|
|
@ -157,8 +160,10 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
if case .format = component.mode {
|
||||
self.minDate = Date(timeIntervalSince1970: 0.0)
|
||||
self.maxDate = Date(timeIntervalSince1970: Double(Int32.max - 1))
|
||||
if let current = component.currentTime {
|
||||
self.date = Date(timeIntervalSince1970: Double(current))
|
||||
if let currentTime = component.currentTime {
|
||||
self.date = Date(timeIntervalSince1970: Double(currentTime))
|
||||
} else if let suggestedTime = component.suggestedTime {
|
||||
self.date = Date(timeIntervalSince1970: Double(suggestedTime))
|
||||
} else {
|
||||
self.date = Date()
|
||||
}
|
||||
|
|
@ -219,8 +224,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
case .reminders:
|
||||
title = strings.Conversation_SetReminder_Title
|
||||
case .format:
|
||||
//TODO:localize
|
||||
title = "Date"
|
||||
title = strings.Conversation_FormatDate_Title
|
||||
}
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
|
|
@ -481,8 +485,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
buttonTitle = strings.Conversation_SetReminder_RemindOn(self.dateFormatter.string(from: date), time).string
|
||||
}
|
||||
case .format:
|
||||
//TODO:localize
|
||||
buttonTitle = component.currentTime != nil ? "Edit Date" : "Add Date"
|
||||
buttonTitle = component.currentTime != nil ? strings.Conversation_FormatDate_EditDate : strings.Conversation_FormatDate_AddDate
|
||||
}
|
||||
|
||||
let buttonSideInset: CGFloat = 30.0
|
||||
|
|
@ -538,7 +541,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
pressedColor: environment.theme.list.itemDestructiveColor.withMultipliedAlpha(0.8),
|
||||
),
|
||||
content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(
|
||||
Text(text: "Remove Date", font: Font.semibold(17.0), color: environment.theme.list.itemDestructiveColor)
|
||||
Text(text: strings.Conversation_FormatDate_RemoveDate, font: Font.semibold(17.0), color: environment.theme.list.itemDestructiveColor)
|
||||
)),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
|
|
@ -754,6 +757,7 @@ private final class ChatScheduleTimeScreenComponent: Component {
|
|||
let mode: ChatScheduleTimeScreen.Mode
|
||||
let currentTime: Int32?
|
||||
let currentRepeatPeriod: Int32?
|
||||
let suggestedTime: Int32?
|
||||
let minimalTime: Int32?
|
||||
|
||||
init(
|
||||
|
|
@ -761,12 +765,14 @@ private final class ChatScheduleTimeScreenComponent: Component {
|
|||
mode: ChatScheduleTimeScreen.Mode,
|
||||
currentTime: Int32?,
|
||||
currentRepeatPeriod: Int32?,
|
||||
suggestedTime: Int32?,
|
||||
minimalTime: Int32?
|
||||
) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.currentTime = currentTime
|
||||
self.currentRepeatPeriod = currentRepeatPeriod
|
||||
self.suggestedTime = suggestedTime
|
||||
self.minimalTime = minimalTime
|
||||
}
|
||||
|
||||
|
|
@ -838,6 +844,7 @@ private final class ChatScheduleTimeScreenComponent: Component {
|
|||
mode: component.mode,
|
||||
currentTime: component.currentTime,
|
||||
currentRepeatPeriod: component.currentRepeatPeriod,
|
||||
suggestedTime: component.suggestedTime,
|
||||
minimalTime: component.minimalTime,
|
||||
externalState: self.contentExternalState,
|
||||
dismiss: { [weak self] in
|
||||
|
|
@ -920,8 +927,9 @@ public class ChatScheduleTimeScreen: ViewControllerComponentContainer {
|
|||
context: AccountContext,
|
||||
mode: Mode,
|
||||
currentTime: Int32?,
|
||||
currentRepeatPeriod: Int32?,
|
||||
minimalTime: Int32?,
|
||||
currentRepeatPeriod: Int32? = nil,
|
||||
suggestedTime: Int32? = nil,
|
||||
minimalTime: Int32? = nil,
|
||||
isDark: Bool,
|
||||
completion: @escaping (Result) -> Void
|
||||
) {
|
||||
|
|
@ -932,6 +940,7 @@ public class ChatScheduleTimeScreen: ViewControllerComponentContainer {
|
|||
mode: mode,
|
||||
currentTime: currentTime,
|
||||
currentRepeatPeriod: currentRepeatPeriod,
|
||||
suggestedTime: suggestedTime,
|
||||
minimalTime: minimalTime
|
||||
), navigationBarAppearance: .none, theme: isDark ? .dark : .default)
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ public func giftSaleAlertController(
|
|||
priceString = "**\(formatTonAmountText(resellAmount.amount.value, dateTimeFormat: presentationData.dateTimeFormat)) TON**"
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let text = strings.Gift_Sell_Confirm_Text(giftTitle, priceString).string
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "text",
|
||||
|
|
|
|||
|
|
@ -1103,17 +1103,40 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
controller.present(alertController, in: .window(.root))
|
||||
}
|
||||
|
||||
func presentGiftUnavailableForCraftingFirst(gift: StarGift.UniqueGift) {
|
||||
guard let controller = self.getController() as? GiftViewScreen else {
|
||||
return
|
||||
}
|
||||
let context = self.context
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let alertController = textAlertController(
|
||||
context: context,
|
||||
title: presentationData.strings.Gift_Craft_UnavailableBlockchain_Title,
|
||||
text: presentationData.strings.Gift_Craft_UnavailableBlockchain_Text,
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
||||
],
|
||||
actionLayout: .vertical
|
||||
)
|
||||
controller.present(alertController, in: .window(.root))
|
||||
}
|
||||
|
||||
func craftGift() {
|
||||
guard let arguments = self.subject.arguments, let controller = self.getController() as? GiftViewScreen, case let .unique(gift) = arguments.gift else {
|
||||
return
|
||||
}
|
||||
|
||||
controller.dismissAllTooltips()
|
||||
|
||||
guard gift.hostPeerId == nil else {
|
||||
self.presentActionLockedForHostedGift(gift: gift)
|
||||
return
|
||||
}
|
||||
|
||||
controller.dismissAllTooltips()
|
||||
|
||||
guard gift.giftAddress == nil else {
|
||||
self.presentGiftUnavailableForCraftingFirst(gift: gift)
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@ extension PeerInfoScreenNode {
|
|||
|
||||
let actions = availableActionsForMemberOfPeer(accountPeerId: self.context.account.peerId, peer: enclosingPeer, member: member)
|
||||
|
||||
//TODO:localize
|
||||
if member.id != self.context.account.peerId {
|
||||
items.append(.action(ContextMenuActionItem(text: "Send Message", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.GroupInfo_ActionSend, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self, let navigationController = self.controller?.navigationController as? NavigationController else {
|
||||
return
|
||||
|
|
@ -32,7 +31,7 @@ extension PeerInfoScreenNode {
|
|||
}
|
||||
|
||||
if actions.contains(.editRank) {
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit Member Tag", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tag"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.GroupInfo_ActionEditRank, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tag"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
|
|
@ -43,7 +42,7 @@ extension PeerInfoScreenNode {
|
|||
}
|
||||
|
||||
if actions.contains(.promote) && enclosingPeer is TelegramChannel {
|
||||
items.append(.action(ContextMenuActionItem(text: "Promote", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Promote"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.GroupInfo_ActionPromote, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Promote"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
|
|
@ -55,7 +54,7 @@ extension PeerInfoScreenNode {
|
|||
|
||||
if actions.contains(.restrict) {
|
||||
if enclosingPeer is TelegramChannel {
|
||||
items.append(.action(ContextMenuActionItem(text: "Restrict", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.GroupInfo_ActionRestrict, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
|
|
@ -65,7 +64,7 @@ extension PeerInfoScreenNode {
|
|||
})))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Remove", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.GroupInfo_ActionRemove, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -765,8 +765,7 @@ extension PeerInfoScreenNode {
|
|||
|
||||
if let user = data.peer as? TelegramUser, let cachedData = data.cachedData as? CachedUserData, user.botInfo == nil && !user.flags.contains(.isSupport) && user.id != strongSelf.context.account.peerId && strongSelf.peerId.namespace != Namespaces.Peer.SecretChat {
|
||||
let copyProtectionEnabled = cachedData.flags.contains(.myCopyProtectionEnabled) || cachedData.flags.contains(.copyProtectionEnabled)
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: !copyProtectionEnabled ? "Disable Sharing" : "Enable Sharing", icon: { theme in
|
||||
items.append(.action(ContextMenuActionItem(text: !copyProtectionEnabled ? strongSelf.presentationData.strings.PeerInfo_DisableSharing : strongSelf.presentationData.strings.PeerInfo_EnableSharing, icon: { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: !copyProtectionEnabled ? "Chat/Context Menu/ForwardDisable" : "Chat/Context Menu/ForwardEnable"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "disablesharing.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/disablesharing.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/disablesharing.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -3702,20 +3702,28 @@ extension ChatControllerImpl {
|
|||
})
|
||||
|
||||
var date: Int32?
|
||||
var suggestedDate: Int32?
|
||||
if let text {
|
||||
text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in
|
||||
if let dateAttribute = attributes[ChatTextInputAttributes.date] as? ChatTextInputTextDateAttribute {
|
||||
date = dateAttribute.date
|
||||
}
|
||||
}
|
||||
if let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType([.date]).rawValue) {
|
||||
detector.enumerateMatches(in: text.string, options: [], range: NSMakeRange(0, text.length), using: { result, _, _ in
|
||||
guard let result, result.resultType == .date, let date = result.date?.timeIntervalSince1970, date > 0.0 else {
|
||||
return
|
||||
}
|
||||
suggestedDate = Int32(date)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let controller = ChatScheduleTimeScreen(
|
||||
context: self.context,
|
||||
mode: .format,
|
||||
currentTime: date,
|
||||
currentRepeatPeriod: nil,
|
||||
minimalTime: nil,
|
||||
suggestedTime: suggestedDate,
|
||||
isDark: false,
|
||||
completion: { [weak self] result in
|
||||
guard let self, let inputMode = inputMode, let selectionRange = selectionRange else {
|
||||
|
|
|
|||
|
|
@ -45,10 +45,9 @@ extension ChatControllerImpl: EKEventEditViewDelegate {
|
|||
source = .extracted(ChatMessageLinkContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode))
|
||||
// }
|
||||
|
||||
//TODO:localize
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(
|
||||
.action(ContextMenuActionItem(text: "Copy Date", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Date_Copy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
guard let self else {
|
||||
|
|
@ -58,11 +57,11 @@ extension ChatControllerImpl: EKEventEditViewDelegate {
|
|||
let fullDate = stringForEntityFormattedDate(timestamp: date, format: .full(timeFormat: .short, dateFormat: .long, dayOfWeek: false), strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat)
|
||||
UIPasteboard.general.string = fullDate
|
||||
|
||||
self.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: "Date copied to clipboard."), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
self.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.Conversation_DateCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}))
|
||||
)
|
||||
items.append(
|
||||
.action(ContextMenuActionItem(text: "Add to Calendar", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Date_AddToCalendar, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
guard let self else {
|
||||
|
|
@ -85,7 +84,7 @@ extension ChatControllerImpl: EKEventEditViewDelegate {
|
|||
}))
|
||||
)
|
||||
items.append(
|
||||
.action(ContextMenuActionItem(text: "Set a Reminder", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Date_SetReminder, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
guard let self else {
|
||||
|
|
@ -109,7 +108,7 @@ extension ChatControllerImpl: EKEventEditViewDelegate {
|
|||
let forwardMessage: EnqueueMessage = .forward(source: message.id, threadId: nil, grouping: .auto, attributes: attributes, correlationId: nil)
|
||||
let _ = forwardMessage
|
||||
let _ = enqueueMessages(account: self.context.account, peerId: self.context.account.peerId, messages: [forwardMessage]).start()
|
||||
self.present(UndoOverlayController(presentationData: self.presentationData, content: .forward(savedMessages: true, text: "You have set a reminder in [Saved Messages]()."), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
self.present(UndoOverlayController(presentationData: self.presentationData, content: .forward(savedMessages: true, text: self.presentationData.strings.Conversation_DateReminderSet), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
)
|
||||
self.push(controller)
|
||||
|
|
|
|||
|
|
@ -89,14 +89,13 @@ extension ChatControllerImpl {
|
|||
}
|
||||
}
|
||||
if self.presentationInterfaceState.myCopyProtectionEnabled && !isAction && !isAd {
|
||||
tip = .messageCopyProtection(text: "You disabled copying and forwarding in this chat.")
|
||||
tip = .messageCopyProtection(text: self.presentationData.strings.Conversation_CopyProtectionInfoPrivateYou)
|
||||
} else if self.presentationInterfaceState.copyProtectionEnabled && !isAction && !isAd {
|
||||
if case .scheduledMessages = self.subject {
|
||||
} else {
|
||||
if let peer = self.presentationInterfaceState.renderedPeer?.peer {
|
||||
if peer is TelegramUser {
|
||||
//TODO:localize
|
||||
tip = .messageCopyProtection(text: "**\(EnginePeer(peer).compactDisplayTitle)** disabled copying and forwarding in this chat.")
|
||||
tip = .messageCopyProtection(text: self.presentationData.strings.Conversation_CopyProtectionInfoPrivate(EnginePeer(peer).compactDisplayTitle).string)
|
||||
} else {
|
||||
var isChannel = false
|
||||
if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
|
|
|
|||
|
|
@ -2551,12 +2551,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
return buffer.baseAddress!.assumingMemoryBound(to: UInt8.self).pointee
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
switch buttonType {
|
||||
case 0:
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: "Disable Sharing", text: "Are you sure you want to keep sharing disabled?", actions: [
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: strongSelf.presentationData.strings.Chat_EnableSharingOffer_RejectConfirmation_Title, text: strongSelf.presentationData.strings.Chat_EnableSharingOffer_RejectConfirmation_Text, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Yes", action: { [weak self] in
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Chat_EnableSharingOffer_RejectConfirmation_Reject, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -2564,9 +2563,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
case 1:
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: "Enable Sharing", text: "Are you sure you want to enable sharing?", actions: [
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: strongSelf.presentationData.strings.Chat_EnableSharingOffer_AcceptConfirmation_Title, text: strongSelf.presentationData.strings.Chat_EnableSharingOffer_AcceptConfirmation_Text, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Yes", action: { [weak self] in
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Chat_EnableSharingOffer_AcceptConfirmation_Accept, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ private let whitelistedHosts: Set<String> = Set([
|
|||
private let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType([.link]).rawValue)
|
||||
private let dataAndPhoneNumberDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType([.link, .phoneNumber]).rawValue)
|
||||
private let phoneNumberDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType([.phoneNumber]).rawValue)
|
||||
private let dataAndPhoneNumberAndDateDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType([.link, .phoneNumber, .date]).rawValue)
|
||||
|
||||
private let validHashtagSet: CharacterSet = {
|
||||
var set = CharacterSet.alphanumerics
|
||||
|
|
@ -102,9 +101,8 @@ public struct EnabledEntityTypes: OptionSet {
|
|||
public static let timecode = EnabledEntityTypes(rawValue: 1 << 5)
|
||||
public static let external = EnabledEntityTypes(rawValue: 1 << 6)
|
||||
public static let internalUrl = EnabledEntityTypes(rawValue: 1 << 7)
|
||||
public static let date = EnabledEntityTypes(rawValue: 1 << 8)
|
||||
|
||||
public static let all: EnabledEntityTypes = [.command, .mention, .hashtag, .allUrl, .phoneNumber, .date]
|
||||
public static let all: EnabledEntityTypes = [.command, .mention, .hashtag, .allUrl, .phoneNumber]
|
||||
}
|
||||
|
||||
private func commitEntity(_ utf16: String.UTF16View, _ type: CurrentEntityType, _ range: Range<String.UTF16View.Index>, _ enabledTypes: EnabledEntityTypes, _ entities: inout [MessageTextEntity], mediaDuration: Double? = nil) {
|
||||
|
|
@ -259,9 +257,7 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType
|
|||
let utf16 = text.utf16
|
||||
|
||||
var detector: NSDataDetector?
|
||||
if enabledTypes.contains(.phoneNumber) && enabledTypes.contains(.date) && (enabledTypes.contains(.allUrl) || enabledTypes.contains(.internalUrl)) {
|
||||
detector = dataAndPhoneNumberAndDateDetector
|
||||
} else if enabledTypes.contains(.phoneNumber) && (enabledTypes.contains(.allUrl) || enabledTypes.contains(.internalUrl)) {
|
||||
if enabledTypes.contains(.phoneNumber) && (enabledTypes.contains(.allUrl) || enabledTypes.contains(.internalUrl)) {
|
||||
detector = dataAndPhoneNumberDetector
|
||||
} else if enabledTypes.contains(.phoneNumber) {
|
||||
detector = phoneNumberDetector
|
||||
|
|
@ -274,7 +270,7 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType
|
|||
if let detector = detector {
|
||||
detector.enumerateMatches(in: text, options: [], range: NSMakeRange(0, utf16.count), using: { result, _, _ in
|
||||
if let result = result {
|
||||
if [NSTextCheckingResult.CheckingType.link, NSTextCheckingResult.CheckingType.phoneNumber, NSTextCheckingResult.CheckingType.date].contains(result.resultType) {
|
||||
if [NSTextCheckingResult.CheckingType.link, NSTextCheckingResult.CheckingType.phoneNumber].contains(result.resultType) {
|
||||
let lowerBound = utf16.index(utf16.startIndex, offsetBy: result.range.location).samePosition(in: text)
|
||||
let upperBound = utf16.index(utf16.startIndex, offsetBy: result.range.location + result.range.length).samePosition(in: text)
|
||||
if let lowerBound = lowerBound, let upperBound = upperBound {
|
||||
|
|
@ -300,35 +296,6 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType
|
|||
}
|
||||
|
||||
type = .Url
|
||||
} else if result.resultType == NSTextCheckingResult.CheckingType.date, let date = result.date?.timeIntervalSince1970, date > Date().timeIntervalSince1970 {
|
||||
#if DEBUG
|
||||
var dayOfWeek = false
|
||||
var format: MessageTextEntityType.DateTimeFormat?
|
||||
if text.contains("[rel]") {
|
||||
format = .relative
|
||||
}
|
||||
var timeFormat: MessageTextEntityType.DateTimeFormat.TimeFormat?
|
||||
if text.contains("[st]") {
|
||||
timeFormat = .short
|
||||
} else if text.contains("[lt]") {
|
||||
timeFormat = .long
|
||||
}
|
||||
var dateFormat: MessageTextEntityType.DateTimeFormat.DateFormat?
|
||||
if text.contains("[sd]") {
|
||||
dateFormat = .short
|
||||
} else if text.contains("[ld]") {
|
||||
dateFormat = .long
|
||||
}
|
||||
if text.contains("[d]") {
|
||||
dayOfWeek = true
|
||||
}
|
||||
if timeFormat != nil || dateFormat != nil {
|
||||
format = .full(timeFormat: timeFormat, dateFormat: dateFormat, dayOfWeek: dayOfWeek)
|
||||
}
|
||||
type = .FormattedDate(format: format, date: Int32(date))
|
||||
#else
|
||||
type = .FormattedDate(format: nil, date: Int32(date))
|
||||
#endif
|
||||
} else {
|
||||
type = .PhoneNumber
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue