Group boosts

This commit is contained in:
Ilya Laktyushin 2024-02-06 16:18:26 +04:00
parent b4b90b090c
commit b909d1bea0
16 changed files with 767 additions and 61 deletions

View File

@ -11092,3 +11092,8 @@ Sorry for the inconvenience.";
"Notification.Boost.SingleYou" = "You boosted the group";
"Notification.Boost.MultipleYou" = "You boosted the group %1$@";
"Contacts.SelectedContacts_1" = "%@ Selected";
"Contacts.SelectedContacts_any" = "%@ Selected";
"Emoji.GroupEmoji" = "GROUP EMOJI";

View File

@ -1067,7 +1067,7 @@ public final class Camera {
}
}
}
e
public final class CameraHolder {
public let camera: Camera
public let previewView: CameraPreviewView

View File

@ -118,6 +118,7 @@ public class ContactsController: ViewController {
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
private var authorizationDisposable: Disposable?
private var selectionDisposable: Disposable?
private var actionDisposable = MetaDisposable()
private let sortOrderPromise = Promise<ContactsSortOrder>()
private let isInVoiceOver = ValuePromise<Bool>(false)
@ -234,6 +235,7 @@ public class ContactsController: ViewController {
self.presentationDataDisposable?.dispose()
self.authorizationDisposable?.dispose()
self.actionDisposable.dispose()
self.selectionDisposable?.dispose()
}
private func updateThemeAndStrings() {
@ -343,8 +345,21 @@ public class ContactsController: ViewController {
self?.activateSearch()
}
self.contactsNode.contactListNode.openPeer = { peer, _ in
openPeer(peer, false)
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _ in
guard let self else {
return
}
if let _ = self.contactsNode.contactListNode.selectionState {
self.contactsNode.contactListNode.updateSelectionState({ current in
if let updatedState = current?.withToggledPeerId(peer.id), !updatedState.selectedPeerIndices.isEmpty {
return updatedState
} else {
return nil
}
})
} else {
openPeer(peer, false)
}
}
self.contactsNode.requestAddContact = { [weak self] phoneNumber in
@ -471,9 +486,46 @@ public class ContactsController: ViewController {
self?.presentSortMenu(sourceView: sourceNode.view, gesture: gesture)
}
let previousToolbarValue = Atomic<Toolbar?>(value: nil)
self.selectionDisposable = (self.contactsNode.contactListNode.selectionStateSignal
|> deliverOnMainQueue).start(next: { [weak self] state in
guard let self, let layout = self.validLayout else {
return
}
let toolbar: Toolbar?
if let state, state.selectedPeerIndices.count > 0 {
toolbar = Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: self.presentationData.strings.ContactList_DeleteConfirmation(Int32(state.selectedPeerIndices.count)), isEnabled: true, color: .custom(self.presentationData.theme.actionSheet.destructiveActionTextColor)))
} else {
toolbar = nil
}
let _ = self.contactsNode.updateNavigationBar(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
var transition: ContainedViewLayoutTransition = .immediate
let previousToolbar = previousToolbarValue.swap(toolbar)
if (previousToolbar == nil) != (toolbar == nil) {
transition = .animated(duration: 0.4, curve: .spring)
}
self.setToolbar(toolbar, transition: transition)
})
self.displayNodeDidLoad()
}
override public func toolbarActionSelected(action: ToolbarActionOption) {
guard case .middle = action, let selectionState = self.contactsNode.contactListNode.selectionState else {
return
}
var peerIds: [EnginePeer.Id] = []
for contactPeerId in selectionState.selectedPeerIndices.keys {
if case let .peer(peerId) = contactPeerId {
peerIds.append(peerId)
}
}
self.requestDeleteContacts(peerIds: peerIds)
}
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
@ -587,6 +639,9 @@ public class ContactsController: ViewController {
}
public func requestDeleteContacts(peerIds: [EnginePeer.Id]) {
guard !peerIds.isEmpty else {
return
}
let actionSheet = ActionSheetController(presentationData: self.presentationData)
var items: [ActionSheetItem] = []
@ -604,6 +659,10 @@ public class ContactsController: ViewController {
return
}
self.contactsNode.contactListNode.updateSelectionState { _ in
return nil
}
self.contactsNode.contactListNode.updatePendingRemovalPeerIds { state in
var state = state
for peerId in peerIds {

View File

@ -297,16 +297,31 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
return false
}
private func updateNavigationBar(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> (navigationHeight: CGFloat, storiesInset: CGFloat) {
func updateNavigationBar(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> (navigationHeight: CGFloat, storiesInset: CGFloat) {
let tabsNode: ASDisplayNode? = nil
let tabsNodeIsSearch = false
let primaryContent = ChatListHeaderComponent.Content(
title: self.presentationData.strings.Contacts_Title,
navigationBackTitle: nil,
titleComponent: nil,
chatListTitle: NetworkStatusTitle(text: self.presentationData.strings.Contacts_Title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: nil),
leftButton: AnyComponentWithIdentity(id: "sort", component: AnyComponent(NavigationButtonComponent(
let title: String
let leftButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
let rightButtons: [AnyComponentWithIdentity<NavigationButtonComponentEnvironment>]
if let selectionState = self.contactListNode.selectionState {
title = self.presentationData.strings.Contacts_SelectedContacts(Int32(selectionState.selectedPeerIndices.count))
leftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
content: .text(title: self.presentationData.strings.Common_Done, isBold: true),
pressed: { [weak self] sourceView in
guard let self else {
return
}
self.contactListNode.updateSelectionState { _ in
return nil
}
}
)))
rightButtons = []
} else {
title = self.presentationData.strings.Contacts_Title
leftButton = AnyComponentWithIdentity(id: "sort", component: AnyComponent(NavigationButtonComponent(
content: .text(title: self.presentationData.strings.Contacts_Sort, isBold: false),
pressed: { [weak self] sourceView in
guard let self else {
@ -315,8 +330,8 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self.controller?.presentSortMenu(sourceView: sourceView, gesture: nil)
}
))),
rightButtons: [AnyComponentWithIdentity(id: "add", component: AnyComponent(NavigationButtonComponent(
)))
rightButtons = [AnyComponentWithIdentity(id: "add", component: AnyComponent(NavigationButtonComponent(
content: .icon(imageName: "Chat List/AddIcon"),
pressed: { [weak self] _ in
guard let self else {
@ -324,7 +339,16 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
}
self.controller?.addPressed()
}
)))],
)))]
}
let primaryContent = ChatListHeaderComponent.Content(
title: self.presentationData.strings.Contacts_Title,
navigationBackTitle: nil,
titleComponent: nil,
chatListTitle: NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: nil),
leftButton: leftButton,
rightButtons: rightButtons,
backTitle: nil,
backPressed: nil
)

View File

@ -672,6 +672,7 @@ final class PenTool: DrawingElement {
return self.start.location.distance(to: self.end.location)
}
}
private func smoothPoints(_ input: SmootherInput) -> [Point] {
let segmentDistance: CGFloat = 6.0
let distance = input.distance

View File

@ -963,7 +963,7 @@ public final class ManagedAudioSession: NSObject {
if case let .record(_, video, _) = type, video, let input = AVAudioSession.sharedInstance().availableInputs?.first {
if let dataSources = input.dataSources {
for source in dataSources {
if source.dataSourceName.contains("Front") {
if source.dataSourceName.contains("Bottom") {
try? input.setPreferredDataSource(source)
break
}

View File

@ -129,7 +129,26 @@ func _internal_applyChannelBoost(account: Account, peerId: PeerId, slots: [Int32
|> mapToSignal { result -> Signal<MyBoostStatus?, NoError> in
if let result = result {
return account.postbox.transaction { transaction -> MyBoostStatus? in
return MyBoostStatus(apiMyBoostStatus: result, accountPeerId: account.peerId, transaction: transaction)
let myBoostStatus = MyBoostStatus(apiMyBoostStatus: result, accountPeerId: account.peerId, transaction: transaction)
var appliedBoosts: Int32 = 0
for boost in myBoostStatus.boosts {
if boost.peer?.id == peerId {
appliedBoosts += 1
}
}
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
var cachedData: CachedChannelData
if let current = current as? CachedChannelData {
cachedData = current
} else {
cachedData = CachedChannelData()
}
return cachedData.withUpdatedAppliedBoosts(appliedBoosts)
})
return myBoostStatus
}
} else {
return .single(nil)

View File

@ -533,7 +533,7 @@ public final class CachedChannelData: CachedPeerData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack)
}
public func withUpdateAppliedBoosts(_ appliedBoosts: Int32?) -> CachedChannelData {
public func withUpdatedAppliedBoosts(_ appliedBoosts: Int32?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, allowedReactions: self.allowedReactions, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: appliedBoosts, emojiPack: self.emojiPack)
}

View File

@ -675,7 +675,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
.withUpdatedViewForumAsMessages(.known(forumViewAsMessages))
.withUpdatedWallpaper(wallpaper)
.withUpdatedBoostsToUnrestrict(boostsUnrestrict)
.withUpdateAppliedBoosts(appliedBoosts)
.withUpdatedAppliedBoosts(appliedBoosts)
.withUpdatedEmojiPack(emojiPack)
})

View File

@ -278,7 +278,7 @@ public struct PresentationResourcesItemList {
public static func addBoostsIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListAddBoostsIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Premium/AddBoosts"), color: theme.list.itemAccentColor)
return generateTintedImage(image: UIImage(bundleImageName: "Premium/Gift"), color: theme.list.itemAccentColor)
})
}

View File

@ -1257,8 +1257,7 @@ public extension EmojiPagerContentComponent {
itemGroups[groupIndex].items.append(resultItem)
} else {
itemGroupIndexById[groupId] = itemGroups.count
//TODO:localize
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: peerSpecificPack.info.title, subtitle: nil, badge: "GROUP EMOJI", isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem]))
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: peerSpecificPack.info.title, subtitle: nil, badge: strings.Emoji_GroupEmoji, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem]))
}
}

View File

@ -571,7 +571,7 @@ public final class EntityKeyboardComponent: Component {
var topEmojiItems: [EntityKeyboardTopPanelComponent.Item] = []
for itemGroup in emojiContent.panelItemGroups {
if !itemGroup.items.isEmpty {
if let id = itemGroup.groupId.base as? String {
if let id = itemGroup.groupId.base as? String, id != "peerSpecific" {
if id == "recent" || id == "liked" {
let iconMapping: [String: EntityKeyboardIconTopPanelComponent.Icon] = [
"recent": .recent,

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "gift_30.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,443 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 6.250000 3.669434 cm
0.000000 0.000000 0.000000 scn
0.665000 11.330566 m
0.665000 11.697836 0.367269 11.995566 0.000000 11.995566 c
-0.367269 11.995566 -0.665000 11.697836 -0.665000 11.330566 c
0.665000 11.330566 l
h
18.165001 11.330566 m
18.165001 11.697836 17.867270 11.995566 17.500000 11.995566 c
17.132730 11.995566 16.834999 11.697836 16.834999 11.330566 c
18.165001 11.330566 l
h
1.365024 1.603050 m
1.666927 2.195569 l
1.365024 1.603050 l
h
0.272484 2.695590 m
0.865003 2.997494 l
0.272484 2.695590 l
h
16.134975 1.603050 m
16.436880 1.010530 l
16.134975 1.603050 l
h
17.227516 2.695590 m
17.820036 2.393686 l
17.227516 2.695590 l
h
13.500000 1.995566 m
4.000000 1.995566 l
4.000000 0.665566 l
13.500000 0.665566 l
13.500000 1.995566 l
h
0.665000 5.330566 m
0.665000 11.330566 l
-0.665000 11.330566 l
-0.665000 5.330566 l
0.665000 5.330566 l
h
16.834999 11.330566 m
16.834999 5.330566 l
18.165001 5.330566 l
18.165001 11.330566 l
16.834999 11.330566 l
h
4.000000 1.995566 m
3.288961 1.995566 2.795676 1.996083 2.412157 2.027418 c
2.036401 2.058119 1.824946 2.115055 1.666927 2.195569 c
1.063120 1.010530 l
1.439881 0.818562 1.845848 0.739256 2.303853 0.701836 c
2.754094 0.665050 3.310907 0.665566 4.000000 0.665566 c
4.000000 1.995566 l
h
-0.665000 5.330566 m
-0.665000 4.641474 -0.665517 4.084661 -0.628731 3.634419 c
-0.591311 3.176414 -0.512005 2.770447 -0.320036 2.393686 c
0.865003 2.997494 l
0.784489 3.155513 0.727552 3.366968 0.696852 3.742723 c
0.665517 4.126243 0.665000 4.619528 0.665000 5.330566 c
-0.665000 5.330566 l
h
1.666927 2.195569 m
1.321650 2.371497 1.040931 2.652216 0.865003 2.997494 c
-0.320036 2.393686 l
-0.016596 1.798154 0.467587 1.313970 1.063120 1.010530 c
1.666927 2.195569 l
h
13.500000 0.665566 m
14.189093 0.665566 14.745906 0.665050 15.196147 0.701836 c
15.654152 0.739256 16.060120 0.818562 16.436880 1.010530 c
15.833073 2.195569 l
15.675054 2.115055 15.463599 2.058119 15.087843 2.027418 c
14.704324 1.996083 14.211039 1.995566 13.500000 1.995566 c
13.500000 0.665566 l
h
16.834999 5.330566 m
16.834999 4.619527 16.834482 4.126243 16.803148 3.742723 c
16.772448 3.366968 16.715511 3.155513 16.634996 2.997494 c
17.820036 2.393686 l
18.012005 2.770447 18.091311 3.176414 18.128731 3.634419 c
18.165518 4.084660 18.165001 4.641474 18.165001 5.330566 c
16.834999 5.330566 l
h
16.436880 1.010530 m
17.032413 1.313970 17.516596 1.798154 17.820036 2.393686 c
16.634996 2.997494 l
16.459070 2.652216 16.178350 2.371497 15.833073 2.195569 c
16.436880 1.010530 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 5.000000 14.293945 cm
0.000000 0.000000 0.000000 scn
2.500000 0.666055 m
2.867269 0.666055 3.165000 0.963785 3.165000 1.331055 c
3.165000 1.698324 2.867269 1.996055 2.500000 1.996055 c
2.500000 0.666055 l
h
17.500000 1.996055 m
17.132730 1.996055 16.834999 1.698324 16.834999 1.331055 c
16.834999 0.963785 17.132730 0.666055 17.500000 0.666055 c
17.500000 1.996055 l
h
0.144016 2.189722 m
-0.457137 1.905398 l
0.144016 2.189722 l
h
0.858667 1.475071 m
1.142991 2.076224 l
0.858667 1.475071 l
h
19.855984 2.189722 m
20.457136 1.905398 l
19.855984 2.189722 l
h
19.141333 1.475071 m
18.857008 2.076224 l
19.141333 1.475071 l
h
18.568888 7.341028 m
18.284563 6.739875 l
18.568888 7.341028 l
h
19.759974 6.149942 m
19.158821 5.865618 l
19.759974 6.149942 l
h
3.906250 6.916055 m
16.093750 6.916055 l
16.093750 8.246055 l
3.906250 8.246055 l
3.906250 6.916055 l
h
2.343750 0.666055 m
2.500000 0.666055 l
2.500000 1.996055 l
2.343750 1.996055 l
2.343750 0.666055 l
h
17.656250 1.996055 m
17.500000 1.996055 l
17.500000 0.666055 l
17.656250 0.666055 l
17.656250 1.996055 l
h
-0.665000 3.674805 m
-0.665000 3.291543 -0.665455 2.966613 -0.645052 2.700472 c
-0.624109 2.427277 -0.578354 2.161689 -0.457137 1.905398 c
0.745169 2.474046 l
0.722370 2.522251 0.696117 2.605676 0.681057 2.802133 c
0.665455 3.005646 0.665000 3.271009 0.665000 3.674805 c
-0.665000 3.674805 l
h
2.343750 1.996055 m
1.939954 1.996055 1.674591 1.996510 1.471079 2.012111 c
1.274621 2.027172 1.191196 2.053424 1.142991 2.076224 c
0.574343 0.873918 l
0.830635 0.752701 1.096222 0.706945 1.369418 0.686002 c
1.635558 0.665600 1.960488 0.666055 2.343750 0.666055 c
2.343750 1.996055 l
h
-0.457137 1.905398 m
-0.242981 1.452604 0.121549 1.088073 0.574343 0.873918 c
1.142991 2.076224 l
0.968357 2.158820 0.827765 2.299412 0.745169 2.474046 c
-0.457137 1.905398 l
h
19.334999 3.674805 m
19.334999 3.271009 19.334545 3.005646 19.318943 2.802133 c
19.303883 2.605676 19.277630 2.522251 19.254831 2.474046 c
20.457136 1.905398 l
20.578354 2.161689 20.624109 2.427277 20.645052 2.700472 c
20.665455 2.966613 20.665001 3.291543 20.665001 3.674805 c
19.334999 3.674805 l
h
17.656250 0.666055 m
18.039513 0.666055 18.364443 0.665600 18.630583 0.686002 c
18.903778 0.706945 19.169365 0.752701 19.425657 0.873918 c
18.857008 2.076224 l
18.808804 2.053424 18.725378 2.027172 18.528921 2.012111 c
18.325409 1.996510 18.060045 1.996055 17.656250 1.996055 c
17.656250 0.666055 l
h
19.254831 2.474046 m
19.172235 2.299412 19.031643 2.158820 18.857008 2.076224 c
19.425657 0.873918 l
19.878450 1.088073 20.242981 1.452604 20.457136 1.905398 c
19.254831 2.474046 l
h
16.093750 6.916055 m
16.759899 6.916055 17.222025 6.915600 17.582088 6.887997 c
17.935097 6.860935 18.134859 6.810679 18.284563 6.739875 c
18.853212 7.942181 l
18.495422 8.111403 18.113497 8.181161 17.683750 8.214106 c
17.261059 8.246510 16.739365 8.246055 16.093750 8.246055 c
16.093750 6.916055 l
h
20.665001 3.674805 m
20.665001 4.320419 20.665455 4.842113 20.633051 5.264805 c
20.600107 5.694551 20.530348 6.076477 20.361126 6.434267 c
19.158821 5.865618 l
19.229626 5.715915 19.279881 5.516152 19.306942 5.163144 c
19.334545 4.803081 19.334999 4.340953 19.334999 3.674805 c
20.665001 3.674805 l
h
18.284563 6.739875 m
18.668341 6.558362 18.977308 6.249395 19.158821 5.865618 c
20.361126 6.434267 l
20.048054 7.096203 19.515148 7.629108 18.853212 7.942181 c
18.284563 6.739875 l
h
3.906250 8.246055 m
3.260636 8.246055 2.738941 8.246510 2.316250 8.214106 c
1.886503 8.181161 1.504578 8.111403 1.146788 7.942181 c
1.715436 6.739875 l
1.865140 6.810679 2.064903 6.860935 2.417911 6.887997 c
2.777974 6.915600 3.240102 6.916055 3.906250 6.916055 c
3.906250 8.246055 l
h
0.665000 3.674805 m
0.665000 4.340953 0.665455 4.803081 0.693058 5.163144 c
0.720120 5.516152 0.770375 5.715915 0.841180 5.865618 c
-0.361126 6.434267 l
-0.530348 6.076477 -0.600106 5.694551 -0.633051 5.264805 c
-0.665455 4.842113 -0.665000 4.320419 -0.665000 3.674805 c
0.665000 3.674805 l
h
1.146788 7.942181 m
0.484851 7.629108 -0.048053 7.096203 -0.361126 6.434267 c
0.841180 5.865618 l
1.022693 6.249395 1.331660 6.558362 1.715436 6.739875 c
1.146788 7.942181 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 10.000000 20.544922 cm
0.000000 0.000000 0.000000 scn
5.000000 1.330078 m
5.000000 0.665078 l
5.665000 0.665078 l
5.665000 1.330078 l
5.000000 1.330078 l
h
4.335000 3.830078 m
4.335000 1.330078 l
5.665000 1.330078 l
5.665000 3.830078 l
4.335000 3.830078 l
h
5.000000 1.995078 m
2.500000 1.995078 l
2.500000 0.665078 l
5.000000 0.665078 l
5.000000 1.995078 l
h
2.500000 1.995078 m
1.486557 1.995078 0.665000 2.816636 0.665000 3.830078 c
-0.665000 3.830078 l
-0.665000 2.082097 0.752019 0.665078 2.500000 0.665078 c
2.500000 1.995078 l
h
2.500000 5.665078 m
3.513443 5.665078 4.335000 4.843521 4.335000 3.830078 c
5.665000 3.830078 l
5.665000 5.578059 4.247981 6.995078 2.500000 6.995078 c
2.500000 5.665078 l
h
2.500000 6.995078 m
0.752019 6.995078 -0.665000 5.578059 -0.665000 3.830078 c
0.665000 3.830078 l
0.665000 4.843521 1.486557 5.665078 2.500000 5.665078 c
2.500000 6.995078 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 15.000000 20.544922 cm
0.000000 0.000000 0.000000 scn
0.000000 1.330078 m
-0.665000 1.330078 l
-0.665000 0.665078 l
0.000000 0.665078 l
0.000000 1.330078 l
h
2.500000 1.995078 m
0.000000 1.995078 l
0.000000 0.665078 l
2.500000 0.665078 l
2.500000 1.995078 l
h
0.665000 1.330078 m
0.665000 3.830078 l
-0.665000 3.830078 l
-0.665000 1.330078 l
0.665000 1.330078 l
h
4.335000 3.830078 m
4.335000 2.816636 3.513443 1.995078 2.500000 1.995078 c
2.500000 0.665078 l
4.247981 0.665078 5.665000 2.082097 5.665000 3.830078 c
4.335000 3.830078 l
h
2.500000 5.665078 m
3.513443 5.665078 4.335000 4.843521 4.335000 3.830078 c
5.665000 3.830078 l
5.665000 5.578059 4.247981 6.995078 2.500000 6.995078 c
2.500000 5.665078 l
h
2.500000 6.995078 m
0.752019 6.995078 -0.665000 5.578059 -0.665000 3.830078 c
0.665000 3.830078 l
0.665000 4.843521 1.486557 5.665078 2.500000 5.665078 c
2.500000 6.995078 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 15.000000 4.294922 cm
0.000000 0.000000 0.000000 scn
0.665000 3.830078 m
0.665000 4.197348 0.367269 4.495078 0.000000 4.495078 c
-0.367269 4.495078 -0.665000 4.197348 -0.665000 3.830078 c
0.665000 3.830078 l
h
-0.665000 1.330078 m
-0.665000 0.962809 -0.367269 0.665078 0.000000 0.665078 c
0.367269 0.665078 0.665000 0.962809 0.665000 1.330078 c
-0.665000 1.330078 l
h
-0.665000 3.830078 m
-0.665000 1.330078 l
0.665000 1.330078 l
0.665000 3.830078 l
-0.665000 3.830078 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 9.990234 9.851074 cm
0.000000 0.000000 0.000000 scn
4.751247 1.595008 m
2.492245 0.211133 l
2.257351 0.067236 1.950281 0.141003 1.806384 0.375896 c
1.736104 0.490619 1.715151 0.628868 1.748279 0.759263 c
2.097972 2.135665 l
2.224205 2.632523 2.564205 3.047855 3.026343 3.269736 c
5.490801 4.452966 l
5.605695 4.508129 5.654118 4.645987 5.598955 4.760881 c
5.554282 4.853927 5.453256 4.905994 5.351554 4.888387 c
2.608295 4.413459 l
2.050655 4.316917 1.478800 4.470905 1.045039 4.834409 c
0.178421 5.560659 l
-0.032710 5.737593 -0.060432 6.052180 0.116501 6.263311 c
0.202556 6.365997 0.326303 6.429747 0.459872 6.440200 c
3.107648 6.647414 l
3.294706 6.662054 3.457723 6.780433 3.529533 6.953778 c
4.550995 9.419525 l
4.656422 9.674018 4.948194 9.794860 5.202687 9.689434 c
5.324886 9.638812 5.421974 9.541724 5.472596 9.419525 c
6.494058 6.953778 l
6.565868 6.780433 6.728885 6.662054 6.915944 6.647414 c
9.578268 6.439061 l
9.852894 6.417569 10.058100 6.177518 10.036608 5.902892 c
10.026269 5.770781 9.963782 5.648214 9.862940 5.562243 c
7.832497 3.831244 l
7.689560 3.709387 7.627202 3.517563 7.671160 3.334950 c
8.295380 0.741805 l
8.359848 0.473989 8.195003 0.204619 7.927186 0.140151 c
7.798499 0.109174 7.662776 0.130618 7.549908 0.199762 c
5.272344 1.595008 l
5.112454 1.692957 4.911138 1.692957 4.751247 1.595008 c
h
f*
n
Q
endstream
endobj
3 0 obj
10525
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000010615 00000 n
0000010639 00000 n
0000010812 00000 n
0000010886 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
10945
%%EOF

View File

@ -164,38 +164,38 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
self.backdropNode.update(rect: rect, within: containerSize)
}
var isScheduledMessages = false
if case .scheduledMessages = interfaceState.subject {
isScheduledMessages = true
}
if let slowmodeState = interfaceState.slowmodeState, !isScheduledMessages && interfaceState.editMessageState == nil {
let sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode
if let current = self.sendButtonRadialStatusNode {
sendButtonRadialStatusNode = current
} else {
sendButtonRadialStatusNode = ChatSendButtonRadialStatusNode(color: interfaceState.theme.chat.inputPanel.panelControlAccentColor)
sendButtonRadialStatusNode.alpha = self.sendContainerNode.alpha
self.sendButtonRadialStatusNode = sendButtonRadialStatusNode
self.addSubnode(sendButtonRadialStatusNode)
}
transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 0.7575, y: 0.7575))
let defaultSendButtonSize: CGFloat = 25.0
let defaultOriginX = floorToScreenPixels((self.sendButton.bounds.width - defaultSendButtonSize) / 2.0)
let defaultOriginY = floorToScreenPixels((self.sendButton.bounds.height - defaultSendButtonSize) / 2.0)
let radialStatusFrame = CGRect(origin: CGPoint(x: defaultOriginX - 4.0, y: defaultOriginY - 4.0), size: CGSize(width: 33.0, height: 33.0))
sendButtonRadialStatusNode.frame = radialStatusFrame
sendButtonRadialStatusNode.slowmodeState = slowmodeState
} else {
if let sendButtonRadialStatusNode = self.sendButtonRadialStatusNode {
self.sendButtonRadialStatusNode = nil
sendButtonRadialStatusNode.removeFromSupernode()
}
transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 1.0, y: 1.0))
}
// var isScheduledMessages = false
// if case .scheduledMessages = interfaceState.subject {
// isScheduledMessages = true
// }
//
// if let slowmodeState = interfaceState.slowmodeState, !isScheduledMessages && interfaceState.editMessageState == nil {
// let sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode
// if let current = self.sendButtonRadialStatusNode {
// sendButtonRadialStatusNode = current
// } else {
// sendButtonRadialStatusNode = ChatSendButtonRadialStatusNode(color: interfaceState.theme.chat.inputPanel.panelControlAccentColor)
// sendButtonRadialStatusNode.alpha = self.sendContainerNode.alpha
// self.sendButtonRadialStatusNode = sendButtonRadialStatusNode
// self.addSubnode(sendButtonRadialStatusNode)
// }
//
// transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 0.7575, y: 0.7575))
//
// let defaultSendButtonSize: CGFloat = 25.0
// let defaultOriginX = floorToScreenPixels((self.sendButton.bounds.width - defaultSendButtonSize) / 2.0)
// let defaultOriginY = floorToScreenPixels((self.sendButton.bounds.height - defaultSendButtonSize) / 2.0)
//
// let radialStatusFrame = CGRect(origin: CGPoint(x: defaultOriginX - 4.0, y: defaultOriginY - 4.0), size: CGSize(width: 33.0, height: 33.0))
// sendButtonRadialStatusNode.frame = radialStatusFrame
// sendButtonRadialStatusNode.slowmodeState = slowmodeState
// } else {
// if let sendButtonRadialStatusNode = self.sendButtonRadialStatusNode {
// self.sendButtonRadialStatusNode = nil
// sendButtonRadialStatusNode.removeFromSupernode()
// }
// transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 1.0, y: 1.0))
// }
transition.updateFrame(node: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size))
let expanded = isMediaInputExpanded

View File

@ -41,6 +41,8 @@ import ChatContextQuery
import ChatInputTextNode
import ChatInputPanelNode
import TelegramNotices
import AnimatedCountLabelNode
import TelegramStringFormatting
private let accessoryButtonFont = Font.medium(14.0)
private let counterFont = Font.with(size: 14.0, design: .regular, traits: [.monospacedNumbers])
@ -535,6 +537,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
let textInputBackgroundNode: ASImageNode
private var transparentTextInputBackgroundImage: UIImage?
let actionButtons: ChatTextInputActionButtonsNode
private let slowModeButton: BoostSlowModeButton
var mediaRecordingAccessibilityArea: AccessibilityAreaNode?
private let counterTextNode: ImmediateTextNode
@ -570,6 +573,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, LayoutMetrics, Bool, Bool)?
private var leftMenuInset: CGFloat = 0.0
private var rightSlowModeInset: CGFloat = 0.0
var displayAttachmentMenu: () -> Void = { }
var sendMessage: () -> Void = { }
@ -855,10 +859,18 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.counterTextNode = ImmediateTextNode()
self.counterTextNode.textAlignment = .center
self.slowModeButton = BoostSlowModeButton()
self.slowModeButton.alpha = 0.0
self.viewOnceButton = ChatRecordingViewOnceButtonNode(icon: .viewOnce)
super.init()
self.slowModeButton.requestUpdate = { [weak self] in
self?.requestLayout(transition: .animated(duration: 0.2, curve: .easeInOut))
}
self.slowModeButton.addTarget(self, action: #selector(self.slowModeButtonPressed), forControlEvents: .touchUpInside)
self.viewForOverlayContent = ChatTextViewForOverlayContent(
ignoreHit: { [weak self] view, point in
guard let strongSelf = self else {
@ -1045,6 +1057,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.clippingNode.addSubnode(self.actionButtons)
self.clippingNode.addSubnode(self.counterTextNode)
self.clippingNode.addSubnode(self.slowModeButton)
self.clippingNode.view.addSubview(self.searchLayoutClearButton)
self.textInputBackgroundNode.clipsToBounds = true
@ -1416,11 +1430,11 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
}
}
func requestLayout() {
func requestLayout(transition: ContainedViewLayoutTransition = .immediate) {
guard let presentationInterfaceState = self.presentationInterfaceState, let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded) = self.validLayout else {
return
}
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics, isMediaInputExpanded: isMediaInputExpanded)
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: transition, interfaceState: presentationInterfaceState, metrics: metrics, isMediaInputExpanded: isMediaInputExpanded)
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
@ -1933,11 +1947,21 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
}
self.leftMenuInset = leftMenuInset
var rightSlowModeInset: CGFloat = 0.0
var slowModeButtonSize: CGSize = .zero
if let presentationInterfaceState = self.presentationInterfaceState {
slowModeButtonSize = self.slowModeButton.update(size: CGSize(width: width, height: 44.0), interfaceState: presentationInterfaceState)
if inputHasText {
rightSlowModeInset = slowModeButtonSize.width - 33.0
}
}
self.rightSlowModeInset = rightSlowModeInset
if buttonTitleUpdated && !transition.isAnimated {
transition = .animated(duration: 0.3, curve: .easeInOut)
}
let baseWidth = width - leftInset - leftMenuInset - rightInset
let baseWidth = width - leftInset - leftMenuInset - rightInset - rightSlowModeInset
let (accessoryButtonsWidth, textFieldHeight) = self.calculateTextFieldMetrics(width: baseWidth, maxHeight: maxHeight, metrics: metrics)
var panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
if displayBotStartButton {
@ -2332,6 +2356,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), isMediaInputExpanded: isMediaInputExpanded, transition: transition, interfaceState: presentationInterfaceState)
}
let slowModeButtonFrame = CGRect(origin: CGPoint(x: hideOffset.x + width - rightInset - 5.0 - slowModeButtonSize.width + composeButtonsOffset, y: hideOffset.y + panelHeight - minimalHeight + 6.0), size: slowModeButtonSize)
transition.updateFrame(node: self.slowModeButton, frame: slowModeButtonFrame)
if let _ = interfaceState.inputTextPanelState.mediaRecordingState {
let text: String = interfaceState.strings.VoiceOver_MessageContextSend
let mediaRecordingAccessibilityArea: AccessibilityAreaNode
@ -2463,7 +2490,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.slowmodePlaceholderNode?.isHidden = true
}
var nextButtonTopRight = CGPoint(x: hideOffset.x + width - rightInset - textFieldInsets.right - accessoryButtonInset, y: hideOffset.y + panelHeight - textFieldInsets.bottom - minimalInputHeight)
var nextButtonTopRight = CGPoint(x: hideOffset.x + width - rightInset - textFieldInsets.right - accessoryButtonInset - rightSlowModeInset, y: hideOffset.y + panelHeight - textFieldInsets.bottom - minimalInputHeight)
for (item, button) in self.accessoryItemButtons.reversed() {
let buttonSize = CGSize(width: button.buttonWidth, height: minimalInputHeight)
button.updateLayout(item: item, size: buttonSize)
@ -2736,6 +2763,10 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
return panelHeight
}
@objc private func slowModeButtonPressed() {
self.interfaceInteraction?.openBoostToUnrestrict()
}
@objc private func viewOncePressed() {
guard let context = self.context, let interfaceState = self.presentationInterfaceState else {
return
@ -3239,7 +3270,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
composeButtonsOffset = 44.0
}
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - self.leftMenuInset, maxHeight: maxHeight, metrics: metrics)
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - self.leftMenuInset - self.rightSlowModeInset, maxHeight: maxHeight, metrics: metrics)
let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
var textFieldMinHeight: CGFloat = 33.0
if let presentationInterfaceState = self.presentationInterfaceState {
@ -3544,7 +3575,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
}
}
self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: animated)
let _ = hideMicButton
// self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: animated)
self.updateTextHeight(animated: animated)
}
@ -3605,7 +3637,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
if (hasText || self.keepSendButtonEnabled && !mediaInputIsActive) {
hideMicButton = true
if self.actionButtons.sendContainerNode.alpha.isZero {
if self.actionButtons.sendContainerNode.alpha.isZero && self.rightSlowModeInset.isZero {
self.actionButtons.sendContainerNode.alpha = 1.0
self.actionButtons.sendButtonRadialStatusNode?.alpha = 1.0
self.actionButtons.updateAccessibility()
@ -3621,6 +3654,17 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
}
}
}
if self.slowModeButton.alpha.isZero && self.rightSlowModeInset > 0.0 {
self.slowModeButton.alpha = 1.0
if animated {
self.slowModeButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
if animateWithBounce {
self.slowModeButton.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6)
} else {
self.slowModeButton.layer.animateScale(from: 0.2, to: 1.0, duration: 0.25)
}
}
}
} else {
if !self.actionButtons.sendContainerNode.alpha.isZero {
self.actionButtons.sendContainerNode.alpha = 0.0
@ -3637,6 +3681,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.actionButtons.sendButtonRadialStatusNode?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
}
}
if !self.slowModeButton.alpha.isZero {
self.slowModeButton.alpha = 0.0
if animated {
self.slowModeButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
}
}
}
}
@ -3694,7 +3744,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
private func updateTextHeight(animated: Bool) {
if let (width, leftInset, rightInset, _, additionalSideInsets, maxHeight, metrics, _, _) = self.validLayout {
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - additionalSideInsets.right - self.leftMenuInset, maxHeight: maxHeight, metrics: metrics)
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - additionalSideInsets.right - self.leftMenuInset - self.rightSlowModeInset, maxHeight: maxHeight, metrics: metrics)
let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
if !self.bounds.size.height.isEqual(to: panelHeight) {
self.updateHeight(animated)
@ -4620,3 +4670,97 @@ private final class MenuIconNode: ManagedAnimationNode {
}
}
}
private func generateClearImage(color: UIColor) -> UIImage? {
return generateImage(CGSize(width: 17.0, height: 17.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(color.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.copy)
context.setStrokeColor(UIColor.clear.cgColor)
context.setLineCap(.round)
context.setLineWidth(1.66)
context.move(to: CGPoint(x: 6.0, y: 6.0))
context.addLine(to: CGPoint(x: 11.0, y: 11.0))
context.strokePath()
context.move(to: CGPoint(x: size.width - 6.0, y: 6.0))
context.addLine(to: CGPoint(x: size.width - 11.0, y: 11.0))
context.strokePath()
})
}
private final class BoostSlowModeButton: HighlightTrackingButtonNode {
let backgroundNode: ASImageNode
let textNode: ImmediateAnimatedCountLabelNode
let iconNode: ASImageNode
private var updateTimer: SwiftSignalKit.Timer?
var requestUpdate: () -> Void = {}
override init(pointerStyle: PointerStyle? = nil) {
self.backgroundNode = ASImageNode()
self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.clipsToBounds = true
self.backgroundNode.image = generateGradientImage(size: CGSize(width: 100.0, height: 2.0), scale: 1.0, colors: [UIColor(rgb: 0x9076ff), UIColor(rgb: 0xbc6de8)], locations: [0.0, 1.0], direction: .horizontal)
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
self.iconNode.image = generateClearImage(color: .white)
self.textNode = ImmediateAnimatedCountLabelNode()
self.textNode.isUserInteractionEnabled = false
super.init(pointerStyle: pointerStyle)
self.addSubnode(self.backgroundNode)
self.addSubnode(self.iconNode)
self.addSubnode(self.textNode)
}
func update(size: CGSize, interfaceState: ChatPresentationInterfaceState) -> CGSize {
var text = ""
if let slowmodeState = interfaceState.slowmodeState, case let .timestamp(validUntilTimestamp) = slowmodeState.variant {
let timestamp = CGFloat(Date().timeIntervalSince1970)
let relativeTimestamp = CGFloat(validUntilTimestamp) - timestamp
text = stringForDuration(Int32(relativeTimestamp))
self.updateTimer?.invalidate()
self.updateTimer = SwiftSignalKit.Timer(timeout: 1.0 / 60.0, repeat: false, completion: { [weak self] in
self?.requestUpdate()
}, queue: .mainQueue())
self.updateTimer?.start()
} else {
self.updateTimer?.invalidate()
self.updateTimer = nil
}
let font = Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers])
let textColor = UIColor.white
var segments: [AnimatedCountLabelNode.Segment] = []
var textCount = 0
for char in text {
if let intValue = Int(String(char)) {
segments.append(.number(intValue, NSAttributedString(string: String(char), font: font, textColor: textColor)))
} else {
segments.append(.text(textCount, NSAttributedString(string: String(char), font: font, textColor: textColor)))
textCount += 1
}
}
self.textNode.segments = segments
let textSize = self.textNode.updateLayout(size: CGSize(width: 200.0, height: 100.0), animated: true)
let totalSize = CGSize(width: textSize.width > 0.0 ? textSize.width + 38.0 : 33.0, height: 33.0)
self.backgroundNode.frame = CGRect(origin: .zero, size: totalSize)
self.backgroundNode.cornerRadius = totalSize.height / 2.0
self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: floorToScreenPixels((totalSize.height - textSize.height) / 2.0)), size: textSize)
if let icon = self.iconNode.image {
self.iconNode.frame = CGRect(origin: CGPoint(x: totalSize.width - icon.size.width - 7.0, y: floorToScreenPixels((totalSize.height - icon.size.height) / 2.0)), size: icon.size)
}
return totalSize
}
}