mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Various improvements
This commit is contained in:
parent
6f0e415c80
commit
624f2d8e84
BIN
Telegram/Telegram-iOS/Resources/Celebrate.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Celebrate.tgs
Normal file
Binary file not shown.
@ -10364,6 +10364,7 @@ Sorry for the inconvenience.";
|
||||
"Chat.Giveaway.Info.Won" = "You won a prize in this giveaway. %@";
|
||||
"Chat.Giveaway.Info.DidntWin" = "You didn't win a prize in this giveaway.";
|
||||
"Chat.Giveaway.Info.ViewPrize" = "View My Prize";
|
||||
"Chat.Giveaway.Info.AdditionalPrizes" = "**%1$@** also included **%2$@** in the prizes. Admins of the channel are responsible for delivering these prizes.";
|
||||
|
||||
"Chat.Giveaway.Info.FullDate" = "**%1$@** on **%2$@**";
|
||||
|
||||
@ -10390,6 +10391,7 @@ Sorry for the inconvenience.";
|
||||
"Chat.Giveaway.Message.CountriesLastDelimiter" = " and ";
|
||||
"Chat.Giveaway.Message.DateTitle" = "Winners Selection Date";
|
||||
"Chat.Giveaway.Message.LearnMore" = "LEARN MORE";
|
||||
"Chat.Giveaway.Message.With" = "with";
|
||||
|
||||
"GiftLink.Title" = "Gift Link";
|
||||
"GiftLink.UsedTitle" = "Used Gift Link";
|
||||
@ -10610,11 +10612,28 @@ Sorry for the inconvenience.";
|
||||
"Chat.RemoveWallpaper.Text" = "Are you sure you want to reset the wallpaper?";
|
||||
"Chat.RemoveWallpaper.Remove" = "Remove";
|
||||
|
||||
"Paint.CutOut" = "Cut Out";
|
||||
|
||||
"MediaEditor.Shortcut.Image" = "Image";
|
||||
"MediaEditor.Shortcut.Location" = "Location";
|
||||
"MediaEditor.Shortcut.Reaction" = "Reaction";
|
||||
"MediaEditor.Shortcut.Audio" = "Audio";
|
||||
|
||||
"BoostGift.WinnersTitle" = "WINNERS";
|
||||
"BoostGift.AdditionalPrizes" = "Additional Prizes";
|
||||
"BoostGift.AdditionalPrizesPlaceholder" = "Enter Your Prize";
|
||||
"BoostGift.AdditionalPrizesInfoOff" = "Turn this on if you want to give the winners your own prizes in addition to Premium subscriptions.";
|
||||
"BoostGift.AdditionalPrizesInfoOn" = "All prizes: **%1$@** %2$@ %3$@.";
|
||||
|
||||
"BoostGift.AdditionalPrizesInfoSubscriptions_1" = "%@ Telegram Premium subscription";
|
||||
"BoostGift.AdditionalPrizesInfoSubscriptions_any" = "%@ Telegram Premium subscriptions";
|
||||
|
||||
"BoostGift.AdditionalPrizesInfoWithSubscriptions_1" = "with %@ Telegram Premium subscription";
|
||||
"BoostGift.AdditionalPrizesInfoWithSubscriptions_any" = "with %@ Telegram Premium subscriptions";
|
||||
|
||||
"BoostGift.AdditionalPrizesInfoForMonths_1" = "for **%@** month";
|
||||
"BoostGift.AdditionalPrizesInfoForMonths_any" = "for **%@** months";
|
||||
|
||||
"BoostGift.Winners" = "Show Winners";
|
||||
"BoostGift.WinnersInfo" = "Choose whether to make the list of winners public when the giveaway ends.";
|
||||
|
||||
"Story.ViewList.TitleReactions" = "Reactions";
|
||||
|
@ -657,7 +657,7 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
try container.encode(countries, forKey: .countries)
|
||||
try container.encode(onlyNewSubscribers, forKey: .onlyNewSubscribers)
|
||||
try container.encode(showWinners, forKey: .showWinners)
|
||||
try container.encode(prizeDescription, forKey: .prizeDescription)
|
||||
try container.encodeIfPresent(prizeDescription, forKey: .prizeDescription)
|
||||
try container.encode(randomId, forKey: .randomId)
|
||||
try container.encode(untilDate, forKey: .untilDate)
|
||||
}
|
||||
|
@ -30,10 +30,11 @@ private final class CreateGiveawayControllerArguments {
|
||||
let openCountriesSelection: () -> Void
|
||||
let openPremiumIntro: () -> Void
|
||||
let scrollToDate: () -> Void
|
||||
let scrollToDescription: () -> Void
|
||||
let setItemIdWithRevealedOptions: (EnginePeer.Id?, EnginePeer.Id?) -> Void
|
||||
let removeChannel: (EnginePeer.Id) -> Void
|
||||
|
||||
init(context: AccountContext, updateState: @escaping ((CreateGiveawayControllerState) -> CreateGiveawayControllerState) -> Void, dismissInput: @escaping () -> Void, openPeersSelection: @escaping () -> Void, openChannelsSelection: @escaping () -> Void, openCountriesSelection: @escaping () -> Void, openPremiumIntro: @escaping () -> Void, scrollToDate: @escaping () -> Void, setItemIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removeChannel: @escaping (EnginePeer.Id) -> Void) {
|
||||
init(context: AccountContext, updateState: @escaping ((CreateGiveawayControllerState) -> CreateGiveawayControllerState) -> Void, dismissInput: @escaping () -> Void, openPeersSelection: @escaping () -> Void, openChannelsSelection: @escaping () -> Void, openCountriesSelection: @escaping () -> Void, openPremiumIntro: @escaping () -> Void, scrollToDate: @escaping () -> Void, scrollToDescription: @escaping () -> Void, setItemIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removeChannel: @escaping (EnginePeer.Id) -> Void) {
|
||||
self.context = context
|
||||
self.updateState = updateState
|
||||
self.dismissInput = dismissInput
|
||||
@ -42,6 +43,7 @@ private final class CreateGiveawayControllerArguments {
|
||||
self.openCountriesSelection = openCountriesSelection
|
||||
self.openPremiumIntro = openPremiumIntro
|
||||
self.scrollToDate = scrollToDate
|
||||
self.scrollToDescription = scrollToDescription
|
||||
self.setItemIdWithRevealedOptions = setItemIdWithRevealedOptions
|
||||
self.removeChannel = removeChannel
|
||||
}
|
||||
@ -60,6 +62,7 @@ private enum CreateGiveawaySection: Int32 {
|
||||
}
|
||||
|
||||
private enum CreateGiveawayEntryTag: ItemListItemTag {
|
||||
case description
|
||||
case date
|
||||
|
||||
func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
@ -94,12 +97,12 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
case usersNew(PresentationTheme, String, String, Bool)
|
||||
case usersInfo(PresentationTheme, String)
|
||||
|
||||
case winnersHeader(PresentationTheme, String)
|
||||
case winners(PresentationTheme, String, Bool)
|
||||
case winnersInfo(PresentationTheme, String)
|
||||
case durationHeader(PresentationTheme, String)
|
||||
case duration(Int32, PresentationTheme, Int32, String, String, String, String?, Bool)
|
||||
case durationInfo(PresentationTheme, String)
|
||||
|
||||
case prizeDescriptionHeader(PresentationTheme, String)
|
||||
case prizeDescription(PresentationTheme, String, String)
|
||||
case prizeDescription(PresentationTheme, String, Bool)
|
||||
case prizeDescriptionText(PresentationTheme, String, String, Int32)
|
||||
case prizeDescriptionInfo(PresentationTheme, String)
|
||||
|
||||
case timeHeader(PresentationTheme, String)
|
||||
@ -107,9 +110,8 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?, Int32?, Int32?, Bool, Bool)
|
||||
case timeInfo(PresentationTheme, String)
|
||||
|
||||
case durationHeader(PresentationTheme, String)
|
||||
case duration(Int32, PresentationTheme, Int32, String, String, String, String?, Bool)
|
||||
case durationInfo(PresentationTheme, String)
|
||||
case winners(PresentationTheme, String, Bool)
|
||||
case winnersInfo(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -123,77 +125,75 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
return CreateGiveawaySection.channels.rawValue
|
||||
case .usersHeader, .usersAll, .usersNew, .usersInfo:
|
||||
return CreateGiveawaySection.users.rawValue
|
||||
case .winnersHeader, .winners, .winnersInfo:
|
||||
return CreateGiveawaySection.winners.rawValue
|
||||
case .prizeDescriptionHeader, .prizeDescription, .prizeDescriptionInfo:
|
||||
case .durationHeader, .duration, .durationInfo:
|
||||
return CreateGiveawaySection.duration.rawValue
|
||||
case .prizeDescription, .prizeDescriptionText, .prizeDescriptionInfo:
|
||||
return CreateGiveawaySection.prizeDescription.rawValue
|
||||
case .timeHeader, .timeExpiryDate, .timeCustomPicker, .timeInfo:
|
||||
return CreateGiveawaySection.time.rawValue
|
||||
case .durationHeader, .duration, .durationInfo:
|
||||
return CreateGiveawaySection.duration.rawValue
|
||||
case .winners, .winnersInfo:
|
||||
return CreateGiveawaySection.winners.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .header:
|
||||
return -1
|
||||
case .createGiveaway:
|
||||
return 0
|
||||
case .awardUsers:
|
||||
return 1
|
||||
case .prepaidHeader:
|
||||
return 2
|
||||
case .prepaid:
|
||||
return 3
|
||||
case .subscriptionsHeader:
|
||||
return 4
|
||||
case .subscriptions:
|
||||
return 5
|
||||
case .subscriptionsInfo:
|
||||
return 6
|
||||
case .channelsHeader:
|
||||
return 7
|
||||
case let .channel(index, _, _, _, _):
|
||||
return 8 + index
|
||||
case .channelAdd:
|
||||
return 100
|
||||
case .channelsInfo:
|
||||
return 101
|
||||
case .usersHeader:
|
||||
return 102
|
||||
case .usersAll:
|
||||
return 103
|
||||
case .usersNew:
|
||||
return 104
|
||||
case .usersInfo:
|
||||
return 105
|
||||
case .winnersHeader:
|
||||
return 106
|
||||
case .winners:
|
||||
return 107
|
||||
case .winnersInfo:
|
||||
return 108
|
||||
case .prizeDescriptionHeader:
|
||||
return 109
|
||||
case .prizeDescription:
|
||||
return 110
|
||||
case .prizeDescriptionInfo:
|
||||
return 111
|
||||
case .timeHeader:
|
||||
return 112
|
||||
case .timeExpiryDate:
|
||||
return 113
|
||||
case .timeCustomPicker:
|
||||
return 114
|
||||
case .timeInfo:
|
||||
return 115
|
||||
case .durationHeader:
|
||||
return 116
|
||||
case let .duration(index, _, _, _, _, _, _, _):
|
||||
return 117 + index
|
||||
case .durationInfo:
|
||||
return 130
|
||||
case .header:
|
||||
return -1
|
||||
case .createGiveaway:
|
||||
return 0
|
||||
case .awardUsers:
|
||||
return 1
|
||||
case .prepaidHeader:
|
||||
return 2
|
||||
case .prepaid:
|
||||
return 3
|
||||
case .subscriptionsHeader:
|
||||
return 4
|
||||
case .subscriptions:
|
||||
return 5
|
||||
case .subscriptionsInfo:
|
||||
return 6
|
||||
case .channelsHeader:
|
||||
return 7
|
||||
case let .channel(index, _, _, _, _):
|
||||
return 8 + index
|
||||
case .channelAdd:
|
||||
return 100
|
||||
case .channelsInfo:
|
||||
return 101
|
||||
case .usersHeader:
|
||||
return 102
|
||||
case .usersAll:
|
||||
return 103
|
||||
case .usersNew:
|
||||
return 104
|
||||
case .usersInfo:
|
||||
return 105
|
||||
case .durationHeader:
|
||||
return 106
|
||||
case let .duration(index, _, _, _, _, _, _, _):
|
||||
return 107 + index
|
||||
case .durationInfo:
|
||||
return 200
|
||||
case .prizeDescription:
|
||||
return 201
|
||||
case .prizeDescriptionText:
|
||||
return 202
|
||||
case .prizeDescriptionInfo:
|
||||
return 203
|
||||
case .timeHeader:
|
||||
return 204
|
||||
case .timeExpiryDate:
|
||||
return 205
|
||||
case .timeCustomPicker:
|
||||
return 206
|
||||
case .timeInfo:
|
||||
return 207
|
||||
case .winners:
|
||||
return 208
|
||||
case .winnersInfo:
|
||||
return 209
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,32 +295,32 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .winnersHeader(lhsTheme, lhsText):
|
||||
if case let .winnersHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
case let .durationHeader(lhsTheme, lhsText):
|
||||
if case let .durationHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .winners(lhsTheme, lhsText, lhsValue):
|
||||
if case let .winners(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
case let .duration(lhsIndex, lhsTheme, lhsMonths, lhsTitle, lhsSubtitle, lhsLabel, lhsBadge, lhsIsSelected):
|
||||
if case let .duration(rhsIndex, rhsTheme, rhsMonths, rhsTitle, rhsSubtitle, rhsLabel, rhsBadge, rhsIsSelected) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsMonths == rhsMonths, lhsTitle == rhsTitle, lhsSubtitle == rhsSubtitle, lhsLabel == rhsLabel, lhsBadge == rhsBadge, lhsIsSelected == rhsIsSelected {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .winnersInfo(lhsTheme, lhsText):
|
||||
if case let .winnersInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
case let .durationInfo(lhsTheme, lhsText):
|
||||
if case let .durationInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .prizeDescriptionHeader(lhsTheme, lhsText):
|
||||
if case let .prizeDescriptionHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
case let .prizeDescription(lhsTheme, lhsText, lhsValue):
|
||||
if case let .prizeDescription(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .prizeDescription(lhsTheme, lhsPlaceholder, lhsValue):
|
||||
if case let .prizeDescription(rhsTheme, rhsPlaceholder, rhsValue) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsValue == rhsValue {
|
||||
case let .prizeDescriptionText(lhsTheme, lhsPlaceholder, lhsValue, lhsCount):
|
||||
if case let .prizeDescriptionText(rhsTheme, rhsPlaceholder, rhsValue, rhsCount) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsValue == rhsValue, lhsCount == rhsCount {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -355,20 +355,14 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .durationHeader(lhsTheme, lhsText):
|
||||
if case let .durationHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
case let .winners(lhsTheme, lhsText, lhsValue):
|
||||
if case let .winners(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .duration(lhsIndex, lhsTheme, lhsMonths, lhsTitle, lhsSubtitle, lhsLabel, lhsBadge, lhsIsSelected):
|
||||
if case let .duration(rhsIndex, rhsTheme, rhsMonths, rhsTitle, rhsSubtitle, rhsLabel, rhsBadge, rhsIsSelected) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsMonths == rhsMonths, lhsTitle == rhsTitle, lhsSubtitle == rhsSubtitle, lhsLabel == rhsLabel, lhsBadge == rhsBadge, lhsIsSelected == rhsIsSelected {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .durationInfo(lhsTheme, lhsText):
|
||||
if case let .durationInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
case let .winnersInfo(lhsTheme, lhsText):
|
||||
if case let .winnersInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -484,30 +478,44 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .usersInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .winnersHeader(_, text):
|
||||
case let .durationHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .winners(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
case let .duration(_, _, months, title, subtitle, label, badge, isSelected):
|
||||
return GiftOptionItem(presentationData: presentationData, context: arguments.context, title: title, subtitle: subtitle, subtitleFont: .small, label: .generic(label), badge: badge, isSelected: isSelected, sectionId: self.section, action: {
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.showWinners = value
|
||||
updatedState.selectedMonths = months
|
||||
return updatedState
|
||||
}
|
||||
})
|
||||
case let .winnersInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .prizeDescriptionHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .prizeDescription(_, placeholder, value):
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(), text: value, placeholder: placeholder, sectionId: self.section, textUpdated: { value in
|
||||
case let .durationInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in
|
||||
arguments.openPremiumIntro()
|
||||
})
|
||||
case let .prizeDescription(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.showPrizeDescription = value
|
||||
return updatedState
|
||||
}
|
||||
})
|
||||
case let .prizeDescriptionText(_, placeholder, value, count):
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: "\(count)"), text: value, placeholder: placeholder, returnKeyType: .done, spacing: 24.0, tag: CreateGiveawayEntryTag.description, sectionId: self.section, textUpdated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.prizeDescription = value
|
||||
return updatedState
|
||||
}
|
||||
}, action: {})
|
||||
}, updatedFocus: { focused in
|
||||
if focused {
|
||||
arguments.scrollToDescription()
|
||||
}
|
||||
}, action: {
|
||||
arguments.dismissInput()
|
||||
})
|
||||
case let .prizeDescriptionInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
|
||||
case let .timeHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .timeExpiryDate(theme, dateTimeFormat, value, active):
|
||||
@ -577,20 +585,16 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
}, tag: CreateGiveawayEntryTag.date)
|
||||
case let .timeInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .durationHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .duration(_, _, months, title, subtitle, label, badge, isSelected):
|
||||
return GiftOptionItem(presentationData: presentationData, context: arguments.context, title: title, subtitle: subtitle, subtitleFont: .small, label: .generic(label), badge: badge, isSelected: isSelected, sectionId: self.section, action: {
|
||||
case let .winners(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.selectedMonths = months
|
||||
updatedState.showWinners = value
|
||||
return updatedState
|
||||
}
|
||||
})
|
||||
case let .durationInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in
|
||||
arguments.openPremiumIntro()
|
||||
})
|
||||
case let .winnersInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -658,64 +662,7 @@ private func createGiveawayControllerEntries(
|
||||
entries.append(.prepaid(presentationData.theme, presentationData.strings.BoostGift_PrepaidGiveawayCount(prepaidGiveaway.quantity), presentationData.strings.BoostGift_PrepaidGiveawayMonths("\(prepaidGiveaway.months)").string, prepaidGiveaway))
|
||||
}
|
||||
|
||||
if case .giveaway = state.mode {
|
||||
if case .generic = subject {
|
||||
entries.append(.subscriptionsHeader(presentationData.theme, presentationData.strings.BoostGift_QuantityTitle.uppercased(), presentationData.strings.BoostGift_QuantityBoosts(state.subscriptions * 4)))
|
||||
entries.append(.subscriptions(presentationData.theme, state.subscriptions))
|
||||
entries.append(.subscriptionsInfo(presentationData.theme, presentationData.strings.BoostGift_QuantityInfo))
|
||||
}
|
||||
|
||||
entries.append(.channelsHeader(presentationData.theme, presentationData.strings.BoostGift_ChannelsTitle.uppercased()))
|
||||
var index: Int32 = 0
|
||||
let channels = [peerId] + state.channels
|
||||
for channelId in channels {
|
||||
if let channel = peers[channelId] {
|
||||
entries.append(.channel(index, presentationData.theme, channel, channel.id == peerId ? state.subscriptions * 4 : nil, false))
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
entries.append(.channelAdd(presentationData.theme, presentationData.strings.BoostGift_AddChannel))
|
||||
entries.append(.channelsInfo(presentationData.theme, presentationData.strings.BoostGift_ChannelsInfo))
|
||||
|
||||
entries.append(.usersHeader(presentationData.theme, presentationData.strings.BoostGift_UsersTitle.uppercased()))
|
||||
|
||||
let countriesText: String
|
||||
if state.countries.count > 2 {
|
||||
countriesText = presentationData.strings.BoostGift_FromCountries(Int32(state.countries.count))
|
||||
} else if !state.countries.isEmpty {
|
||||
if state.countries.count == 2 {
|
||||
let firstCountryCode = state.countries.first ?? ""
|
||||
let secondCountryCode = state.countries.last ?? ""
|
||||
let firstCountryName = locale.localizedString(forRegionCode: firstCountryCode) ?? firstCountryCode
|
||||
let secondCountryName = locale.localizedString(forRegionCode: secondCountryCode) ?? secondCountryCode
|
||||
countriesText = presentationData.strings.BoostGift_FromTwoCountries(firstCountryName, secondCountryName).string
|
||||
} else {
|
||||
let countryCode = state.countries.first ?? ""
|
||||
let countryName = locale.localizedString(forRegionCode: countryCode) ?? countryCode
|
||||
countriesText = presentationData.strings.BoostGift_FromOneCountry(countryName).string
|
||||
}
|
||||
} else {
|
||||
countriesText = presentationData.strings.BoostGift_FromAllCountries
|
||||
}
|
||||
|
||||
entries.append(.usersAll(presentationData.theme, presentationData.strings.BoostGift_AllSubscribers, countriesText, !state.onlyNewEligible))
|
||||
entries.append(.usersNew(presentationData.theme, presentationData.strings.BoostGift_OnlyNewSubscribers, countriesText, state.onlyNewEligible))
|
||||
entries.append(.usersInfo(presentationData.theme, presentationData.strings.BoostGift_LimitSubscribersInfo))
|
||||
|
||||
entries.append(.winnersHeader(presentationData.theme, presentationData.strings.BoostGift_WinnersTitle.uppercased()))
|
||||
entries.append(.winners(presentationData.theme, presentationData.strings.BoostGift_Winners, state.showWinners))
|
||||
entries.append(.winnersInfo(presentationData.theme, presentationData.strings.BoostGift_WinnersInfo))
|
||||
|
||||
entries.append(.prizeDescriptionHeader(presentationData.theme, "Additional Prizes".uppercased()))
|
||||
entries.append(.prizeDescription(presentationData.theme, "Prize Description (Optional)", state.prizeDescription))
|
||||
entries.append(.prizeDescriptionInfo(presentationData.theme, "Provide description of any additional prizes you plan to award to the winners, in addition to Telegram Premium."))
|
||||
|
||||
entries.append(.timeHeader(presentationData.theme, presentationData.strings.BoostGift_DateTitle.uppercased()))
|
||||
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time, minDate, maxDate, state.pickingExpiryDate, state.pickingExpiryTime))
|
||||
entries.append(.timeInfo(presentationData.theme, presentationData.strings.BoostGift_DateInfo(presentationData.strings.BoostGift_DateInfoSubscribers(Int32(state.subscriptions))).string))
|
||||
}
|
||||
|
||||
if case .generic = subject {
|
||||
let appendDurationEntries = {
|
||||
entries.append(.durationHeader(presentationData.theme, presentationData.strings.BoostGift_DurationTitle.uppercased()))
|
||||
|
||||
let recipientCount: Int
|
||||
@ -762,6 +709,82 @@ private func createGiveawayControllerEntries(
|
||||
entries.append(.durationInfo(presentationData.theme, presentationData.strings.BoostGift_PremiumInfo))
|
||||
}
|
||||
|
||||
switch state.mode {
|
||||
case .giveaway:
|
||||
if case .generic = subject {
|
||||
entries.append(.subscriptionsHeader(presentationData.theme, presentationData.strings.BoostGift_QuantityTitle.uppercased(), presentationData.strings.BoostGift_QuantityBoosts(state.subscriptions * 4)))
|
||||
entries.append(.subscriptions(presentationData.theme, state.subscriptions))
|
||||
entries.append(.subscriptionsInfo(presentationData.theme, presentationData.strings.BoostGift_QuantityInfo))
|
||||
}
|
||||
|
||||
entries.append(.channelsHeader(presentationData.theme, presentationData.strings.BoostGift_ChannelsTitle.uppercased()))
|
||||
var index: Int32 = 0
|
||||
let channels = [peerId] + state.channels
|
||||
for channelId in channels {
|
||||
if let channel = peers[channelId] {
|
||||
entries.append(.channel(index, presentationData.theme, channel, channel.id == peerId ? state.subscriptions * 4 : nil, false))
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
entries.append(.channelAdd(presentationData.theme, presentationData.strings.BoostGift_AddChannel))
|
||||
entries.append(.channelsInfo(presentationData.theme, presentationData.strings.BoostGift_ChannelsInfo))
|
||||
|
||||
entries.append(.usersHeader(presentationData.theme, presentationData.strings.BoostGift_UsersTitle.uppercased()))
|
||||
|
||||
let countriesText: String
|
||||
if state.countries.count > 2 {
|
||||
countriesText = presentationData.strings.BoostGift_FromCountries(Int32(state.countries.count))
|
||||
} else if !state.countries.isEmpty {
|
||||
if state.countries.count == 2 {
|
||||
let firstCountryCode = state.countries.first ?? ""
|
||||
let secondCountryCode = state.countries.last ?? ""
|
||||
let firstCountryName = locale.localizedString(forRegionCode: firstCountryCode) ?? firstCountryCode
|
||||
let secondCountryName = locale.localizedString(forRegionCode: secondCountryCode) ?? secondCountryCode
|
||||
countriesText = presentationData.strings.BoostGift_FromTwoCountries(firstCountryName, secondCountryName).string
|
||||
} else {
|
||||
let countryCode = state.countries.first ?? ""
|
||||
let countryName = locale.localizedString(forRegionCode: countryCode) ?? countryCode
|
||||
countriesText = presentationData.strings.BoostGift_FromOneCountry(countryName).string
|
||||
}
|
||||
} else {
|
||||
countriesText = presentationData.strings.BoostGift_FromAllCountries
|
||||
}
|
||||
|
||||
entries.append(.usersAll(presentationData.theme, presentationData.strings.BoostGift_AllSubscribers, countriesText, !state.onlyNewEligible))
|
||||
entries.append(.usersNew(presentationData.theme, presentationData.strings.BoostGift_OnlyNewSubscribers, countriesText, state.onlyNewEligible))
|
||||
entries.append(.usersInfo(presentationData.theme, presentationData.strings.BoostGift_LimitSubscribersInfo))
|
||||
|
||||
if case .generic = subject {
|
||||
appendDurationEntries()
|
||||
}
|
||||
|
||||
entries.append(.prizeDescription(presentationData.theme, presentationData.strings.BoostGift_AdditionalPrizes, state.showPrizeDescription))
|
||||
var prizeDescriptionInfoText = presentationData.strings.BoostGift_AdditionalPrizesInfoOff
|
||||
if state.showPrizeDescription {
|
||||
entries.append(.prizeDescriptionText(presentationData.theme, presentationData.strings.BoostGift_AdditionalPrizesPlaceholder, state.prizeDescription, state.subscriptions))
|
||||
|
||||
let monthsString = presentationData.strings.BoostGift_AdditionalPrizesInfoForMonths(state.selectedMonths ?? 3)
|
||||
if state.prizeDescription.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
let subscriptionsString = presentationData.strings.BoostGift_AdditionalPrizesInfoSubscriptions(state.subscriptions).replacingOccurrences(of: "\(state.subscriptions) ", with: "")
|
||||
prizeDescriptionInfoText = presentationData.strings.BoostGift_AdditionalPrizesInfoOn("\(state.subscriptions)", subscriptionsString, monthsString).string
|
||||
} else {
|
||||
let subscriptionsString = presentationData.strings.BoostGift_AdditionalPrizesInfoWithSubscriptions(state.subscriptions).replacingOccurrences(of: "\(state.subscriptions) ", with: "")
|
||||
let description = "\(state.prizeDescription) \(subscriptionsString)"
|
||||
prizeDescriptionInfoText = presentationData.strings.BoostGift_AdditionalPrizesInfoOn("\(state.subscriptions)", description, monthsString).string
|
||||
}
|
||||
}
|
||||
entries.append(.prizeDescriptionInfo(presentationData.theme, prizeDescriptionInfoText))
|
||||
|
||||
entries.append(.timeHeader(presentationData.theme, presentationData.strings.BoostGift_DateTitle.uppercased()))
|
||||
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time, minDate, maxDate, state.pickingExpiryDate, state.pickingExpiryTime))
|
||||
entries.append(.timeInfo(presentationData.theme, presentationData.strings.BoostGift_DateInfo(presentationData.strings.BoostGift_DateInfoSubscribers(Int32(state.subscriptions))).string))
|
||||
|
||||
entries.append(.winners(presentationData.theme, presentationData.strings.BoostGift_Winners, state.showWinners))
|
||||
entries.append(.winnersInfo(presentationData.theme, presentationData.strings.BoostGift_WinnersInfo))
|
||||
case .gift:
|
||||
appendDurationEntries()
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
@ -779,6 +802,7 @@ private struct CreateGiveawayControllerState: Equatable {
|
||||
var countries: [String] = []
|
||||
var onlyNewEligible: Bool = false
|
||||
var showWinners: Bool = false
|
||||
var showPrizeDescription: Bool = false
|
||||
var prizeDescription: String = ""
|
||||
var time: Int32
|
||||
var pickingExpiryTime = false
|
||||
@ -834,6 +858,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
var openPremiumIntroImpl: (() -> Void)?
|
||||
var presentControllerImpl: ((ViewController) -> Void)?
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var scrollToDescriptionImpl: (() -> Void)?
|
||||
var scrollToDateImpl: (() -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
var dismissInputImpl: (() -> Void)?
|
||||
@ -852,6 +877,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
openPremiumIntroImpl?()
|
||||
}, scrollToDate: {
|
||||
scrollToDateImpl?()
|
||||
}, scrollToDescription: {
|
||||
scrollToDescriptionImpl?()
|
||||
}, setItemIdWithRevealedOptions: { itemId, fromItemId in
|
||||
updateState { state in
|
||||
var updatedState = state
|
||||
@ -960,6 +987,9 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
if previousState.channels.count > state.channels.count {
|
||||
animateChanges = true
|
||||
}
|
||||
if previousState.showPrizeDescription != state.showPrizeDescription {
|
||||
animateChanges = true
|
||||
}
|
||||
}
|
||||
|
||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
@ -981,7 +1011,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
controller.beganInteractiveDragging = {
|
||||
dismissInputImpl?()
|
||||
// dismissInputImpl?()
|
||||
}
|
||||
presentControllerImpl = { [weak controller] c in
|
||||
controller?.present(c, in: .window(.root))
|
||||
@ -1260,6 +1290,28 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
pushControllerImpl?(controller)
|
||||
}
|
||||
|
||||
scrollToDescriptionImpl = { [weak controller] in
|
||||
controller?.afterLayout({
|
||||
guard let controller = controller else {
|
||||
return
|
||||
}
|
||||
|
||||
var resultItemNode: ListViewItemNode?
|
||||
let _ = controller.frameForItemNode({ listItemNode in
|
||||
if let itemNode = listItemNode as? ItemListItemNode {
|
||||
if let tag = itemNode.tag as? CreateGiveawayEntryTag, tag == .description {
|
||||
resultItemNode = listItemNode
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if let resultItemNode = resultItemNode {
|
||||
controller.ensureItemNodeVisible(resultItemNode)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
scrollToDateImpl = { [weak controller] in
|
||||
controller?.afterLayout({
|
||||
guard let controller = controller else {
|
||||
|
@ -55,6 +55,11 @@ public func presentGiveawayInfoController(
|
||||
dismissImpl?()
|
||||
})]
|
||||
|
||||
var additionalPrizes = ""
|
||||
if let prizeDescription = giveaway.prizeDescription, !prizeDescription.isEmpty {
|
||||
additionalPrizes = "\n\n" + presentationData.strings.Chat_Giveaway_Info_AdditionalPrizes(peerName, "\(giveaway.quantity) \(prizeDescription)").string
|
||||
}
|
||||
|
||||
switch giveawayInfo {
|
||||
case let .ongoing(start, status):
|
||||
let startDate = presentationData.strings.Chat_Giveaway_Info_FullDate(
|
||||
@ -124,7 +129,7 @@ public func presentGiveawayInfoController(
|
||||
participation = "\n\n\(participation)"
|
||||
}
|
||||
|
||||
text = "\(intro)\n\n\(ending)\(participation)"
|
||||
text = "\(intro)\(additionalPrizes)\n\n\(ending)\(participation)"
|
||||
case let .finished(status, start, finish, _, activatedCount):
|
||||
let startDate = presentationData.strings.Chat_Giveaway_Info_FullDate(
|
||||
stringForMessageTimestamp(timestamp: start, dateTimeFormat: presentationData.dateTimeFormat),
|
||||
@ -156,7 +161,7 @@ public func presentGiveawayInfoController(
|
||||
if activatedCount > 0 {
|
||||
ending += " " + presentationData.strings.Chat_Giveaway_Info_ActivatedLinks(activatedCount)
|
||||
}
|
||||
|
||||
|
||||
var result: String
|
||||
switch status {
|
||||
case .refunded:
|
||||
@ -166,9 +171,9 @@ public func presentGiveawayInfoController(
|
||||
dismissImpl?()
|
||||
})]
|
||||
case .notWon:
|
||||
result = "\n\n" + presentationData.strings.Chat_Giveaway_Info_DidntWin
|
||||
result = "**\(presentationData.strings.Chat_Giveaway_Info_DidntWin)**\n\n"
|
||||
case let .won(slug):
|
||||
result = "\n\n" + presentationData.strings.Chat_Giveaway_Info_Won("🏆").string
|
||||
result = "**\(presentationData.strings.Chat_Giveaway_Info_Won("").string)**\n\n"
|
||||
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, action: {
|
||||
dismissImpl?()
|
||||
openLink(slug)
|
||||
@ -177,7 +182,7 @@ public func presentGiveawayInfoController(
|
||||
})]
|
||||
}
|
||||
|
||||
text = "\(intro)\n\n\(ending)\(result)"
|
||||
text = "\(result)\(intro)\(additionalPrizes)\n\n\(ending)"
|
||||
}
|
||||
|
||||
let alertController = giveawayInfoAlertController(
|
||||
|
@ -464,7 +464,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
nil
|
||||
)
|
||||
|
||||
let hasMessages = stats.viewsPerPost.current > 0
|
||||
let hasMessages = stats.viewsPerPost.current > 0 || viewsPerPostDelta.hasValue
|
||||
let hasStories = stats.viewsPerStory.current > 0 || viewsPerStoryDelta.hasValue
|
||||
|
||||
var items: [Int: (String, String, (String, ValueItemNode.DeltaColor)?)] = [:]
|
||||
|
@ -543,6 +543,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
|
||||
dict[-1186937242] = { return Api.MessageMedia.parse_messageMediaGeoLive($0) }
|
||||
dict[-626162256] = { return Api.MessageMedia.parse_messageMediaGiveaway($0) }
|
||||
dict[-1323305567] = { return Api.MessageMedia.parse_messageMediaGiveawayResults($0) }
|
||||
dict[-156940077] = { return Api.MessageMedia.parse_messageMediaInvoice($0) }
|
||||
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
|
||||
dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) }
|
||||
@ -825,6 +826,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1352440415] = { return Api.StoryItem.parse_storyItem($0) }
|
||||
dict[1374088783] = { return Api.StoryItem.parse_storyItemDeleted($0) }
|
||||
dict[-5388013] = { return Api.StoryItem.parse_storyItemSkipped($0) }
|
||||
dict[-134495875] = { return Api.StoryPeerReaction.parse_storyPeerPublicRepost($0) }
|
||||
dict[2112668723] = { return Api.StoryPeerReaction.parse_storyPeerReaction($0) }
|
||||
dict[-1329730875] = { return Api.StoryView.parse_storyView($0) }
|
||||
dict[-1923523370] = { return Api.StoryViews.parse_storyViews($0) }
|
||||
dict[1964978502] = { return Api.TextWithEntities.parse_textWithEntities($0) }
|
||||
@ -1176,7 +1179,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1222446760] = { return Api.payments.CheckedGiftCode.parse_checkedGiftCode($0) }
|
||||
dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) }
|
||||
dict[1130879648] = { return Api.payments.GiveawayInfo.parse_giveawayInfo($0) }
|
||||
dict[-1966612121] = { return Api.payments.GiveawayInfo.parse_giveawayInfoResults($0) }
|
||||
dict[13456752] = { return Api.payments.GiveawayInfo.parse_giveawayInfoResults($0) }
|
||||
dict[-1610250415] = { return Api.payments.PaymentForm.parse_paymentForm($0) }
|
||||
dict[1891958275] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) }
|
||||
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
||||
@ -1216,6 +1219,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[291044926] = { return Api.stories.AllStories.parse_allStoriesNotModified($0) }
|
||||
dict[-890861720] = { return Api.stories.PeerStories.parse_peerStories($0) }
|
||||
dict[1574486984] = { return Api.stories.Stories.parse_stories($0) }
|
||||
dict[-664005078] = { return Api.stories.StoryReactionsList.parse_storyReactionsList($0) }
|
||||
dict[-560009955] = { return Api.stories.StoryViews.parse_storyViews($0) }
|
||||
dict[1189722604] = { return Api.stories.StoryViewsList.parse_storyViewsList($0) }
|
||||
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
|
||||
@ -1799,6 +1803,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StoryItem:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StoryPeerReaction:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StoryView:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StoryViews:
|
||||
@ -2133,6 +2139,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.stories.Stories:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.stories.StoryReactionsList:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.stories.StoryViews:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.stories.StoryViewsList:
|
||||
|
@ -698,6 +698,7 @@ public extension Api {
|
||||
case messageMediaGeo(geo: Api.GeoPoint)
|
||||
case messageMediaGeoLive(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32, proximityNotificationRadius: Int32?)
|
||||
case messageMediaGiveaway(flags: Int32, channels: [Int64], countriesIso2: [String]?, prizeDescription: String?, quantity: Int32, months: Int32, untilDate: Int32)
|
||||
case messageMediaGiveawayResults(flags: Int32, channelId: Int64, launchMsgId: Int32, winnersCount: Int32, unclaimedCount: Int32, winners: [Int64], months: Int32, prizeDescription: String?)
|
||||
case messageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, receiptMsgId: Int32?, currency: String, totalAmount: Int64, startParam: String, extendedMedia: Api.MessageExtendedMedia?)
|
||||
case messageMediaPhoto(flags: Int32, photo: Api.Photo?, ttlSeconds: Int32?)
|
||||
case messageMediaPoll(poll: Api.Poll, results: Api.PollResults)
|
||||
@ -782,6 +783,23 @@ public extension Api {
|
||||
serializeInt32(months, buffer: buffer, boxed: false)
|
||||
serializeInt32(untilDate, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageMediaGiveawayResults(let flags, let channelId, let launchMsgId, let winnersCount, let unclaimedCount, let winners, let months, let prizeDescription):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1323305567)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||
serializeInt32(launchMsgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(winnersCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(unclaimedCount, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(winners.count))
|
||||
for item in winners {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
serializeInt32(months, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(prizeDescription!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageMediaInvoice(let flags, let title, let description, let photo, let receiptMsgId, let currency, let totalAmount, let startParam, let extendedMedia):
|
||||
if boxed {
|
||||
buffer.appendInt32(-156940077)
|
||||
@ -865,6 +883,8 @@ public extension Api {
|
||||
return ("messageMediaGeoLive", [("flags", flags as Any), ("geo", geo as Any), ("heading", heading as Any), ("period", period as Any), ("proximityNotificationRadius", proximityNotificationRadius as Any)])
|
||||
case .messageMediaGiveaway(let flags, let channels, let countriesIso2, let prizeDescription, let quantity, let months, let untilDate):
|
||||
return ("messageMediaGiveaway", [("flags", flags as Any), ("channels", channels as Any), ("countriesIso2", countriesIso2 as Any), ("prizeDescription", prizeDescription as Any), ("quantity", quantity as Any), ("months", months as Any), ("untilDate", untilDate as Any)])
|
||||
case .messageMediaGiveawayResults(let flags, let channelId, let launchMsgId, let winnersCount, let unclaimedCount, let winners, let months, let prizeDescription):
|
||||
return ("messageMediaGiveawayResults", [("flags", flags as Any), ("channelId", channelId as Any), ("launchMsgId", launchMsgId as Any), ("winnersCount", winnersCount as Any), ("unclaimedCount", unclaimedCount as Any), ("winners", winners as Any), ("months", months as Any), ("prizeDescription", prizeDescription as Any)])
|
||||
case .messageMediaInvoice(let flags, let title, let description, let photo, let receiptMsgId, let currency, let totalAmount, let startParam, let extendedMedia):
|
||||
return ("messageMediaInvoice", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("receiptMsgId", receiptMsgId as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("startParam", startParam as Any), ("extendedMedia", extendedMedia as Any)])
|
||||
case .messageMediaPhoto(let flags, let photo, let ttlSeconds):
|
||||
@ -1030,6 +1050,40 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageMediaGiveawayResults(_ reader: BufferReader) -> MessageMedia? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
var _7: Int32?
|
||||
_7 = reader.readInt32()
|
||||
var _8: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_8 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||
return Api.MessageMedia.messageMediaGiveawayResults(flags: _1!, channelId: _2!, launchMsgId: _3!, winnersCount: _4!, unclaimedCount: _5!, winners: _6!, months: _7!, prizeDescription: _8)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageMediaInvoice(_ reader: BufferReader) -> MessageMedia? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
@ -1190,6 +1190,82 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum StoryPeerReaction: TypeConstructorDescription {
|
||||
case storyPeerPublicRepost(peerId: Api.Peer, story: Api.StoryItem)
|
||||
case storyPeerReaction(peerId: Api.Peer, date: Int32, reaction: Api.Reaction)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyPeerPublicRepost(let peerId, let story):
|
||||
if boxed {
|
||||
buffer.appendInt32(-134495875)
|
||||
}
|
||||
peerId.serialize(buffer, true)
|
||||
story.serialize(buffer, true)
|
||||
break
|
||||
case .storyPeerReaction(let peerId, let date, let reaction):
|
||||
if boxed {
|
||||
buffer.appendInt32(2112668723)
|
||||
}
|
||||
peerId.serialize(buffer, true)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
reaction.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyPeerPublicRepost(let peerId, let story):
|
||||
return ("storyPeerPublicRepost", [("peerId", peerId as Any), ("story", story as Any)])
|
||||
case .storyPeerReaction(let peerId, let date, let reaction):
|
||||
return ("storyPeerReaction", [("peerId", peerId as Any), ("date", date as Any), ("reaction", reaction as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyPeerPublicRepost(_ reader: BufferReader) -> StoryPeerReaction? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Api.StoryItem?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.StoryItem
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StoryPeerReaction.storyPeerPublicRepost(peerId: _1!, story: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyPeerReaction(_ reader: BufferReader) -> StoryPeerReaction? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Api.Reaction?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.StoryPeerReaction.storyPeerReaction(peerId: _1!, date: _2!, reaction: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StoryView: TypeConstructorDescription {
|
||||
case storyView(flags: Int32, userId: Int64, date: Int32, reaction: Api.Reaction?)
|
||||
@ -1430,111 +1506,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ThemeSettings: TypeConstructorDescription {
|
||||
case themeSettings(flags: Int32, baseTheme: Api.BaseTheme, accentColor: Int32, outboxAccentColor: Int32?, messageColors: [Int32]?, wallpaper: Api.WallPaper?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper):
|
||||
if boxed {
|
||||
buffer.appendInt32(-94849324)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
baseTheme.serialize(buffer, true)
|
||||
serializeInt32(accentColor, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(outboxAccentColor!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messageColors!.count))
|
||||
for item in messageColors! {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 1) != 0 {wallpaper!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper):
|
||||
return ("themeSettings", [("flags", flags as Any), ("baseTheme", baseTheme as Any), ("accentColor", accentColor as Any), ("outboxAccentColor", outboxAccentColor as Any), ("messageColors", messageColors as Any), ("wallpaper", wallpaper as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_themeSettings(_ reader: BufferReader) -> ThemeSettings? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.BaseTheme?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.BaseTheme
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_4 = reader.readInt32() }
|
||||
var _5: [Int32]?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
} }
|
||||
var _6: Api.WallPaper?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.WallPaper
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.ThemeSettings.themeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum TopPeer: TypeConstructorDescription {
|
||||
case topPeer(peer: Api.Peer, rating: Double)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .topPeer(let peer, let rating):
|
||||
if boxed {
|
||||
buffer.appendInt32(-305282981)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeDouble(rating, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .topPeer(let peer, let rating):
|
||||
return ("topPeer", [("peer", peer as Any), ("rating", rating as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_topPeer(_ reader: BufferReader) -> TopPeer? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Double?
|
||||
_2 = reader.readDouble()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.TopPeer.topPeer(peer: _1!, rating: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,111 @@
|
||||
public extension Api {
|
||||
enum ThemeSettings: TypeConstructorDescription {
|
||||
case themeSettings(flags: Int32, baseTheme: Api.BaseTheme, accentColor: Int32, outboxAccentColor: Int32?, messageColors: [Int32]?, wallpaper: Api.WallPaper?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper):
|
||||
if boxed {
|
||||
buffer.appendInt32(-94849324)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
baseTheme.serialize(buffer, true)
|
||||
serializeInt32(accentColor, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(outboxAccentColor!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messageColors!.count))
|
||||
for item in messageColors! {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 1) != 0 {wallpaper!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper):
|
||||
return ("themeSettings", [("flags", flags as Any), ("baseTheme", baseTheme as Any), ("accentColor", accentColor as Any), ("outboxAccentColor", outboxAccentColor as Any), ("messageColors", messageColors as Any), ("wallpaper", wallpaper as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_themeSettings(_ reader: BufferReader) -> ThemeSettings? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.BaseTheme?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.BaseTheme
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_4 = reader.readInt32() }
|
||||
var _5: [Int32]?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
} }
|
||||
var _6: Api.WallPaper?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.WallPaper
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.ThemeSettings.themeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum TopPeer: TypeConstructorDescription {
|
||||
case topPeer(peer: Api.Peer, rating: Double)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .topPeer(let peer, let rating):
|
||||
if boxed {
|
||||
buffer.appendInt32(-305282981)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeDouble(rating, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .topPeer(let peer, let rating):
|
||||
return ("topPeer", [("peer", peer as Any), ("rating", rating as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_topPeer(_ reader: BufferReader) -> TopPeer? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Double?
|
||||
_2 = reader.readDouble()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.TopPeer.topPeer(peer: _1!, rating: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum TopPeerCategory: TypeConstructorDescription {
|
||||
case topPeerCategoryBotsInline
|
||||
|
@ -911,7 +911,7 @@ public extension Api.payments {
|
||||
public extension Api.payments {
|
||||
enum GiveawayInfo: TypeConstructorDescription {
|
||||
case giveawayInfo(flags: Int32, startDate: Int32, joinedTooEarlyDate: Int32?, adminDisallowedChatId: Int64?, disallowedCountry: String?)
|
||||
case giveawayInfoResults(flags: Int32, startDate: Int32, giftCodeSlug: String?, finishDate: Int32, winnersCount: Int32, activatedCount: Int32, winners: [Api.User]?)
|
||||
case giveawayInfoResults(flags: Int32, startDate: Int32, giftCodeSlug: String?, finishDate: Int32, winnersCount: Int32, activatedCount: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -925,9 +925,9 @@ public extension Api.payments {
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt64(adminDisallowedChatId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 4) != 0 {serializeString(disallowedCountry!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount, let winners):
|
||||
case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1966612121)
|
||||
buffer.appendInt32(13456752)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(startDate, buffer: buffer, boxed: false)
|
||||
@ -935,11 +935,6 @@ public extension Api.payments {
|
||||
serializeInt32(finishDate, buffer: buffer, boxed: false)
|
||||
serializeInt32(winnersCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(activatedCount, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(winners!.count))
|
||||
for item in winners! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -948,8 +943,8 @@ public extension Api.payments {
|
||||
switch self {
|
||||
case .giveawayInfo(let flags, let startDate, let joinedTooEarlyDate, let adminDisallowedChatId, let disallowedCountry):
|
||||
return ("giveawayInfo", [("flags", flags as Any), ("startDate", startDate as Any), ("joinedTooEarlyDate", joinedTooEarlyDate as Any), ("adminDisallowedChatId", adminDisallowedChatId as Any), ("disallowedCountry", disallowedCountry as Any)])
|
||||
case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount, let winners):
|
||||
return ("giveawayInfoResults", [("flags", flags as Any), ("startDate", startDate as Any), ("giftCodeSlug", giftCodeSlug as Any), ("finishDate", finishDate as Any), ("winnersCount", winnersCount as Any), ("activatedCount", activatedCount as Any), ("winners", winners as Any)])
|
||||
case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount):
|
||||
return ("giveawayInfoResults", [("flags", flags as Any), ("startDate", startDate as Any), ("giftCodeSlug", giftCodeSlug as Any), ("finishDate", finishDate as Any), ("winnersCount", winnersCount as Any), ("activatedCount", activatedCount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -989,19 +984,14 @@ public extension Api.payments {
|
||||
_5 = reader.readInt32()
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: [Api.User]?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, finishDate: _4!, winnersCount: _5!, activatedCount: _6!, winners: _7)
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, finishDate: _4!, winnersCount: _5!, activatedCount: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -1420,6 +1420,80 @@ public extension Api.stories {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.stories {
|
||||
enum StoryReactionsList: TypeConstructorDescription {
|
||||
case storyReactionsList(flags: Int32, count: Int32, reactions: [Api.StoryPeerReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-664005078)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(reactions.count))
|
||||
for item in reactions {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset):
|
||||
return ("storyReactionsList", [("flags", flags as Any), ("count", count as Any), ("reactions", reactions as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyReactionsList(_ reader: BufferReader) -> StoryReactionsList? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.StoryPeerReaction]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryPeerReaction.self)
|
||||
}
|
||||
var _4: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _5: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.stories.StoryReactionsList.storyReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.stories {
|
||||
enum StoryViews: TypeConstructorDescription {
|
||||
case storyViews(views: [Api.StoryViews], users: [Api.User])
|
||||
@ -1540,175 +1614,3 @@ public extension Api.stories {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.updates {
|
||||
indirect enum ChannelDifference: TypeConstructorDescription {
|
||||
case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User])
|
||||
case channelDifferenceEmpty(flags: Int32, pts: Int32, timeout: Int32?)
|
||||
case channelDifferenceTooLong(flags: Int32, timeout: Int32?, dialog: Api.Dialog, messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(543450958)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(newMessages.count))
|
||||
for item in newMessages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(otherUpdates.count))
|
||||
for item in otherUpdates {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .channelDifferenceEmpty(let flags, let pts, let timeout):
|
||||
if boxed {
|
||||
buffer.appendInt32(1041346555)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1531132162)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
||||
dialog.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users):
|
||||
return ("channelDifference", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any), ("newMessages", newMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
case .channelDifferenceEmpty(let flags, let pts, let timeout):
|
||||
return ("channelDifferenceEmpty", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any)])
|
||||
case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users):
|
||||
return ("channelDifferenceTooLong", [("flags", flags as Any), ("timeout", timeout as Any), ("dialog", dialog as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_channelDifference(_ reader: BufferReader) -> ChannelDifference? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() }
|
||||
var _4: [Api.Message]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
|
||||
}
|
||||
var _5: [Api.Update]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self)
|
||||
}
|
||||
var _6: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _7: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.updates.ChannelDifference.channelDifference(flags: _1!, pts: _2!, timeout: _3, newMessages: _4!, otherUpdates: _5!, chats: _6!, users: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_channelDifferenceEmpty(_ reader: BufferReader) -> ChannelDifference? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.updates.ChannelDifference.channelDifferenceEmpty(flags: _1!, pts: _2!, timeout: _3)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_channelDifferenceTooLong(_ reader: BufferReader) -> ChannelDifference? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() }
|
||||
var _3: Api.Dialog?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Dialog
|
||||
}
|
||||
var _4: [Api.Message]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
|
||||
}
|
||||
var _5: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, timeout: _2, dialog: _3!, messages: _4!, chats: _5!, users: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,175 @@
|
||||
public extension Api.updates {
|
||||
indirect enum ChannelDifference: TypeConstructorDescription {
|
||||
case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User])
|
||||
case channelDifferenceEmpty(flags: Int32, pts: Int32, timeout: Int32?)
|
||||
case channelDifferenceTooLong(flags: Int32, timeout: Int32?, dialog: Api.Dialog, messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(543450958)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(newMessages.count))
|
||||
for item in newMessages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(otherUpdates.count))
|
||||
for item in otherUpdates {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .channelDifferenceEmpty(let flags, let pts, let timeout):
|
||||
if boxed {
|
||||
buffer.appendInt32(1041346555)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1531132162)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
||||
dialog.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users):
|
||||
return ("channelDifference", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any), ("newMessages", newMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
case .channelDifferenceEmpty(let flags, let pts, let timeout):
|
||||
return ("channelDifferenceEmpty", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any)])
|
||||
case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users):
|
||||
return ("channelDifferenceTooLong", [("flags", flags as Any), ("timeout", timeout as Any), ("dialog", dialog as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_channelDifference(_ reader: BufferReader) -> ChannelDifference? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() }
|
||||
var _4: [Api.Message]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
|
||||
}
|
||||
var _5: [Api.Update]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self)
|
||||
}
|
||||
var _6: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _7: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.updates.ChannelDifference.channelDifference(flags: _1!, pts: _2!, timeout: _3, newMessages: _4!, otherUpdates: _5!, chats: _6!, users: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_channelDifferenceEmpty(_ reader: BufferReader) -> ChannelDifference? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.updates.ChannelDifference.channelDifferenceEmpty(flags: _1!, pts: _2!, timeout: _3)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_channelDifferenceTooLong(_ reader: BufferReader) -> ChannelDifference? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() }
|
||||
var _3: Api.Dialog?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Dialog
|
||||
}
|
||||
var _4: [Api.Message]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
|
||||
}
|
||||
var _5: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, timeout: _2, dialog: _3!, messages: _4!, chats: _5!, users: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.updates {
|
||||
enum Difference: TypeConstructorDescription {
|
||||
case difference(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], state: Api.updates.State)
|
||||
|
@ -3795,21 +3795,6 @@ public extension Api.functions.help {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.help {
|
||||
static func getAppChangelog(prevAppVersion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1877938321)
|
||||
serializeString(prevAppVersion, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "help.getAppChangelog", parameters: [("prevAppVersion", String(describing: prevAppVersion))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.help {
|
||||
static func getAppConfig(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.help.AppConfig>) {
|
||||
let buffer = Buffer()
|
||||
@ -9084,6 +9069,26 @@ public extension Api.functions.stories {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.stories {
|
||||
static func getStoryReactionsList(flags: Int32, peer: Api.InputPeer, id: Int32, reaction: Api.Reaction?, offset: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.StoryReactionsList>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1179482081)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {reaction!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(offset!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "stories.getStoryReactionsList", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("reaction", String(describing: reaction)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.StoryReactionsList? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.stories.StoryReactionsList?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.stories.StoryReactionsList
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.stories {
|
||||
static func getStoryViewsList(flags: Int32, peer: Api.InputPeer, q: String?, id: Int32, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.StoryViewsList>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -202,6 +202,7 @@ private var declaredEncodables: Void = {
|
||||
declareEncodable(SynchronizePeerStoriesOperation.self, f: { SynchronizePeerStoriesOperation(decoder: $0) })
|
||||
declareEncodable(MapVenue.self, f: { MapVenue(decoder: $0) })
|
||||
declareEncodable(TelegramMediaGiveaway.self, f: { TelegramMediaGiveaway(decoder: $0) })
|
||||
declareEncodable(TelegramMediaGiveawayResults.self, f: { TelegramMediaGiveawayResults(decoder: $0) })
|
||||
declareEncodable(WebpagePreviewMessageAttribute.self, f: { WebpagePreviewMessageAttribute(decoder: $0) })
|
||||
declareEncodable(DerivedDataMessageAttribute.self, f: { DerivedDataMessageAttribute(decoder: $0) })
|
||||
return
|
||||
|
@ -427,10 +427,13 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
||||
if (apiFlags & (1 << 0)) != 0 {
|
||||
flags.insert(.onlyNewSubscribers)
|
||||
}
|
||||
if (apiFlags & (1 << 2)) != 0 {
|
||||
flags.insert(.showWinners)
|
||||
}
|
||||
return (TelegramMediaGiveaway(flags: flags, channelPeerIds: channels.map { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }, countries: countries ?? [], quantity: quantity, months: months, untilDate: untilDate, prizeDescription: prizeDescription), nil, nil, nil, nil)
|
||||
case let .messageMediaGiveawayResults(apiFlags, channelId, launchMsgId, winnersCount, unclaimedCount, winners, months, prizeDescription):
|
||||
var flags: TelegramMediaGiveawayResults.Flags = []
|
||||
if (apiFlags & (1 << 0)) != 0 {
|
||||
flags.insert(.refunded)
|
||||
}
|
||||
return (TelegramMediaGiveawayResults(flags: flags, launchMessageId: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), namespace: Namespaces.Message.Cloud, id: launchMsgId), winnersPeerIds: winners.map { PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) }, winnersCount: winnersCount, unclaimedCount: unclaimedCount, months: months, prizeDescription: prizeDescription), nil, nil, nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,6 @@ final class AccountTaskManager {
|
||||
tasks.add(managedAutodownloadSettingsUpdates(accountManager: self.accountManager, network: self.stateManager.network).start())
|
||||
tasks.add(managedTermsOfServiceUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager).start())
|
||||
tasks.add(managedAppUpdateInfo(network: self.stateManager.network, stateManager: self.stateManager).start())
|
||||
tasks.add(managedAppChangelog(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager, appVersion: self.networkArguments.appVersion).start())
|
||||
tasks.add(managedPromoInfoUpdates(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, viewTracker: self.viewTracker).start())
|
||||
tasks.add(managedLocalizationUpdatesOperations(accountManager: self.accountManager, postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
tasks.add(managedPendingPeerNotificationSettings(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
|
@ -5,39 +5,6 @@ import MtProtoKit
|
||||
import TelegramApi
|
||||
|
||||
func managedAppChangelog(postbox: Postbox, network: Network, stateManager: AccountStateManager, appVersion: String) -> Signal<Void, NoError> {
|
||||
return stateManager.isUpdating
|
||||
|> filter { !$0 }
|
||||
|> take(1)
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return postbox.transaction { transaction -> AppChangelogState in
|
||||
return transaction.getPreferencesEntry(key: PreferencesKeys.appChangelogState)?.get(AppChangelogState.self) ?? AppChangelogState.default
|
||||
}
|
||||
|> mapToSignal { appChangelogState -> Signal<Void, NoError> in
|
||||
let appChangelogState = appChangelogState
|
||||
if appChangelogState.checkedVersion == appVersion {
|
||||
return .complete()
|
||||
}
|
||||
let previousVersion = appChangelogState.previousVersion
|
||||
return network.request(Api.functions.help.getAppChangelog(prevAppVersion: previousVersion))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { updates -> Signal<Void, NoError> in
|
||||
if let updates = updates {
|
||||
stateManager.addUpdates(updates)
|
||||
}
|
||||
|
||||
return postbox.transaction { transaction in
|
||||
updateAppChangelogState(transaction: transaction, { state in
|
||||
var state = state
|
||||
state.checkedVersion = appVersion
|
||||
state.previousVersion = appVersion
|
||||
return state
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return .never()
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,103 @@
|
||||
import Postbox
|
||||
|
||||
public final class TelegramMediaGiveawayResults: Media, Equatable {
|
||||
public struct Flags: OptionSet {
|
||||
public var rawValue: Int32
|
||||
|
||||
public init(rawValue: Int32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
public static let refunded = Flags(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
public var id: MediaId? {
|
||||
return nil
|
||||
}
|
||||
public var peerIds: [PeerId] {
|
||||
return self.winnersPeerIds
|
||||
}
|
||||
|
||||
public let flags: Flags
|
||||
public let launchMessageId: MessageId
|
||||
public let winnersPeerIds: [PeerId]
|
||||
public let winnersCount: Int32
|
||||
public let unclaimedCount: Int32
|
||||
public let months: Int32
|
||||
public let prizeDescription: String?
|
||||
|
||||
public init(flags: Flags, launchMessageId: MessageId, winnersPeerIds: [PeerId], winnersCount: Int32, unclaimedCount: Int32, months: Int32, prizeDescription: String?) {
|
||||
self.flags = flags
|
||||
self.launchMessageId = launchMessageId
|
||||
self.winnersPeerIds = winnersPeerIds
|
||||
self.winnersCount = winnersCount
|
||||
self.unclaimedCount = unclaimedCount
|
||||
self.months = months
|
||||
self.prizeDescription = prizeDescription
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.flags = Flags(rawValue: decoder.decodeInt32ForKey("flg", orElse: 0))
|
||||
self.launchMessageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("msgp", orElse: 0)), namespace: Namespaces.Message.Cloud, id: decoder.decodeInt32ForKey("msgi", orElse: 0))
|
||||
self.winnersPeerIds = decoder.decodeInt64ArrayForKey("wnr").map { PeerId($0) }
|
||||
self.winnersCount = decoder.decodeInt32ForKey("wnc", orElse: 0)
|
||||
self.unclaimedCount = decoder.decodeInt32ForKey("unc", orElse: 0)
|
||||
self.months = decoder.decodeInt32ForKey("mts", orElse: 0)
|
||||
self.prizeDescription = decoder.decodeOptionalStringForKey("des")
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt32(self.flags.rawValue, forKey: "flg")
|
||||
encoder.encodeInt64(self.launchMessageId.peerId.toInt64(), forKey: "msgp")
|
||||
encoder.encodeInt32(self.launchMessageId.id, forKey: "msgi")
|
||||
encoder.encodeInt64Array(self.winnersPeerIds.map { $0.toInt64() }, forKey: "wnr")
|
||||
encoder.encodeInt32(self.winnersCount, forKey: "wnc")
|
||||
encoder.encodeInt32(self.unclaimedCount, forKey: "unc")
|
||||
encoder.encodeInt32(self.months, forKey: "mts")
|
||||
if let prizeDescription = self.prizeDescription {
|
||||
encoder.encodeString(prizeDescription, forKey: "des")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "des")
|
||||
}
|
||||
}
|
||||
|
||||
public func isLikelyToBeUpdated() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public func isEqual(to other: Media) -> Bool {
|
||||
guard let other = other as? TelegramMediaGiveawayResults else {
|
||||
return false
|
||||
}
|
||||
if self.flags != other.flags {
|
||||
return false
|
||||
}
|
||||
if self.launchMessageId != other.launchMessageId {
|
||||
return false
|
||||
}
|
||||
if self.winnersPeerIds != other.winnersPeerIds {
|
||||
return false
|
||||
}
|
||||
if self.winnersCount != other.winnersCount {
|
||||
return false
|
||||
}
|
||||
if self.unclaimedCount != other.unclaimedCount {
|
||||
return false
|
||||
}
|
||||
if self.months != other.months {
|
||||
return false
|
||||
}
|
||||
if self.prizeDescription != other.prizeDescription {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public func isSemanticallyEqual(to other: Media) -> Bool {
|
||||
return self.isEqual(to: other)
|
||||
}
|
||||
|
||||
public static func ==(lhs: TelegramMediaGiveawayResults, rhs: TelegramMediaGiveawayResults) -> Bool {
|
||||
return lhs.isEqual(to: rhs)
|
||||
}
|
||||
}
|
@ -19,47 +19,137 @@ public final class EngineStoryViewListContext {
|
||||
case recentFirst
|
||||
}
|
||||
|
||||
public final class Item: Equatable {
|
||||
public let peer: EnginePeer
|
||||
public let timestamp: Int32
|
||||
public let storyStats: PeerStoryStats?
|
||||
public let reaction: MessageReaction.Reaction?
|
||||
public let reactionFile: TelegramMediaFile?
|
||||
|
||||
public init(
|
||||
peer: EnginePeer,
|
||||
timestamp: Int32,
|
||||
storyStats: PeerStoryStats?,
|
||||
reaction: MessageReaction.Reaction?,
|
||||
reactionFile: TelegramMediaFile?
|
||||
) {
|
||||
self.peer = peer
|
||||
self.timestamp = timestamp
|
||||
self.storyStats = storyStats
|
||||
self.reaction = reaction
|
||||
self.reactionFile = reactionFile
|
||||
|
||||
public enum Item: Equatable {
|
||||
public final class View: Equatable {
|
||||
public let peer: EnginePeer
|
||||
public let timestamp: Int32
|
||||
public let storyStats: PeerStoryStats?
|
||||
public let reaction: MessageReaction.Reaction?
|
||||
public let reactionFile: TelegramMediaFile?
|
||||
|
||||
public init(
|
||||
peer: EnginePeer,
|
||||
timestamp: Int32,
|
||||
storyStats: PeerStoryStats?,
|
||||
reaction: MessageReaction.Reaction?,
|
||||
reactionFile: TelegramMediaFile?
|
||||
) {
|
||||
self.peer = peer
|
||||
self.timestamp = timestamp
|
||||
self.storyStats = storyStats
|
||||
self.reaction = reaction
|
||||
self.reactionFile = reactionFile
|
||||
}
|
||||
|
||||
public static func ==(lhs: View, rhs: View) -> Bool {
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.timestamp != rhs.timestamp {
|
||||
return false
|
||||
}
|
||||
if lhs.storyStats != rhs.storyStats {
|
||||
return false
|
||||
}
|
||||
if lhs.reaction != rhs.reaction {
|
||||
return false
|
||||
}
|
||||
if lhs.reactionFile?.fileId != rhs.reactionFile?.fileId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
public final class Repost: Equatable {
|
||||
public let peer: EnginePeer
|
||||
public let story: EngineStoryItem
|
||||
public let storyStats: PeerStoryStats?
|
||||
|
||||
init(peer: EnginePeer, story: EngineStoryItem, storyStats: PeerStoryStats?) {
|
||||
self.peer = peer
|
||||
self.story = story
|
||||
self.storyStats = storyStats
|
||||
}
|
||||
if lhs.timestamp != rhs.timestamp {
|
||||
return false
|
||||
|
||||
public static func ==(lhs: Repost, rhs: Repost) -> Bool {
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.story != rhs.story {
|
||||
return false
|
||||
}
|
||||
if lhs.storyStats != rhs.storyStats {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if lhs.storyStats != rhs.storyStats {
|
||||
return false
|
||||
}
|
||||
|
||||
case view(View)
|
||||
case repost(Repost)
|
||||
|
||||
public var peer: EnginePeer {
|
||||
switch self {
|
||||
case let .view(view):
|
||||
return view.peer
|
||||
case let .repost(repost):
|
||||
return repost.peer
|
||||
}
|
||||
if lhs.reaction != rhs.reaction {
|
||||
return false
|
||||
}
|
||||
|
||||
public var timestamp: Int32 {
|
||||
switch self {
|
||||
case let .view(view):
|
||||
return view.timestamp
|
||||
case let .repost(repost):
|
||||
return repost.story.timestamp
|
||||
}
|
||||
if lhs.reactionFile?.fileId != rhs.reactionFile?.fileId {
|
||||
return false
|
||||
}
|
||||
|
||||
public var reaction: MessageReaction.Reaction? {
|
||||
switch self {
|
||||
case let .view(view):
|
||||
return view.reaction
|
||||
case .repost:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var story: EngineStoryItem? {
|
||||
switch self {
|
||||
case .view:
|
||||
return nil
|
||||
case let .repost(repost):
|
||||
return repost.story
|
||||
}
|
||||
}
|
||||
|
||||
public var storyStats: PeerStoryStats? {
|
||||
switch self {
|
||||
case let .view(view):
|
||||
return view.storyStats
|
||||
case let .repost(repost):
|
||||
return repost.storyStats
|
||||
}
|
||||
}
|
||||
|
||||
public struct ItemHash: Hashable {
|
||||
var peerId: EnginePeer.Id
|
||||
var storyId: Int32?
|
||||
}
|
||||
|
||||
public var uniqueId: ItemHash {
|
||||
switch self {
|
||||
case let .view(view):
|
||||
return ItemHash(peerId: view.peer.id, storyId: nil)
|
||||
case let .repost(repost):
|
||||
return ItemHash(peerId: repost.peer.id, storyId: repost.story.id)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct State: Equatable {
|
||||
public var totalCount: Int
|
||||
public var totalReactedCount: Int
|
||||
@ -179,8 +269,8 @@ public final class EngineStoryViewListContext {
|
||||
switch sortMode {
|
||||
case .repostsFirst:
|
||||
items.sort(by: { lhs, rhs in
|
||||
if (lhs.reaction == nil) != (rhs.reaction == nil) {
|
||||
return lhs.reaction != nil
|
||||
if (lhs.story == nil) != (rhs.story == nil) {
|
||||
return lhs.story != nil
|
||||
}
|
||||
if lhs.timestamp != rhs.timestamp {
|
||||
return lhs.timestamp > rhs.timestamp
|
||||
@ -189,6 +279,9 @@ public final class EngineStoryViewListContext {
|
||||
})
|
||||
case .reactionsFirst:
|
||||
items.sort(by: { lhs, rhs in
|
||||
if (lhs.story == nil) != (rhs.story == nil) {
|
||||
return lhs.story == nil
|
||||
}
|
||||
if (lhs.reaction == nil) != (rhs.reaction == nil) {
|
||||
return lhs.reaction != nil
|
||||
}
|
||||
@ -280,166 +373,270 @@ public final class EngineStoryViewListContext {
|
||||
let searchQuery = self.searchQuery
|
||||
let currentOffset = state.nextOffset
|
||||
let limit = state.items.isEmpty ? 50 : 100
|
||||
let signal: Signal<InternalState, NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<InternalState, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .complete()
|
||||
|
||||
let signal: Signal<InternalState, NoError>
|
||||
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser {
|
||||
signal = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
switch listMode {
|
||||
case .everyone:
|
||||
break
|
||||
case .contacts:
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
switch sortMode {
|
||||
case .reactionsFirst:
|
||||
flags |= (1 << 2)
|
||||
case .recentFirst, .repostsFirst:
|
||||
break
|
||||
}
|
||||
if searchQuery != nil {
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.stories.getStoryViewsList(flags: flags, peer: inputPeer, q: searchQuery, id: storyId, offset: currentOffset?.value ?? "", limit: Int32(limit)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.stories.StoryViewsList?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<InternalState, NoError> in
|
||||
return account.postbox.transaction { transaction -> InternalState in
|
||||
switch result {
|
||||
case let .storyViewsList(_, count, reactionsCount, views, users, nextOffset):
|
||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users))
|
||||
|
||||
var items: [Item] = []
|
||||
for view in views {
|
||||
switch view {
|
||||
case let .storyView(flags, userId, date, reaction):
|
||||
let isBlocked = (flags & (1 << 0)) != 0
|
||||
let isBlockedFromStories = (flags & (1 << 1)) != 0
|
||||
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in
|
||||
let previousData: CachedUserData
|
||||
if let current = cachedData as? CachedUserData {
|
||||
previousData = current
|
||||
} else {
|
||||
previousData = CachedUserData()
|
||||
}
|
||||
var updatedFlags = previousData.flags
|
||||
if isBlockedFromStories {
|
||||
updatedFlags.insert(.isBlockedFromStories)
|
||||
} else {
|
||||
updatedFlags.remove(.isBlockedFromStories)
|
||||
}
|
||||
return previousData.withUpdatedIsBlocked(isBlocked).withUpdatedFlags(updatedFlags)
|
||||
})
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
let parsedReaction = reaction.flatMap(MessageReaction.Reaction.init(apiReaction:))
|
||||
items.append(Item(
|
||||
peer: EnginePeer(peer),
|
||||
timestamp: date,
|
||||
storyStats: transaction.getPeerStoryStats(peerId: peerId),
|
||||
reaction: parsedReaction,
|
||||
reactionFile: parsedReaction.flatMap { reaction -> TelegramMediaFile? in
|
||||
switch reaction {
|
||||
case .builtin:
|
||||
return nil
|
||||
case let .custom(fileId):
|
||||
return transaction.getMedia(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)) as? TelegramMediaFile
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<InternalState, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
switch listMode {
|
||||
case .everyone:
|
||||
break
|
||||
case .contacts:
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
switch sortMode {
|
||||
case .reactionsFirst:
|
||||
flags |= (1 << 2)
|
||||
case .recentFirst, .repostsFirst:
|
||||
break
|
||||
}
|
||||
if searchQuery != nil {
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.stories.getStoryViewsList(flags: flags, peer: inputPeer, q: searchQuery, id: storyId, offset: currentOffset?.value ?? "", limit: Int32(limit)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.stories.StoryViewsList?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<InternalState, NoError> in
|
||||
return account.postbox.transaction { transaction -> InternalState in
|
||||
switch result {
|
||||
case let .storyViewsList(_, count, reactionsCount, views, users, nextOffset):
|
||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users))
|
||||
|
||||
var items: [Item] = []
|
||||
for view in views {
|
||||
switch view {
|
||||
case let .storyView(flags, userId, date, reaction):
|
||||
let isBlocked = (flags & (1 << 0)) != 0
|
||||
let isBlockedFromStories = (flags & (1 << 1)) != 0
|
||||
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in
|
||||
let previousData: CachedUserData
|
||||
if let current = cachedData as? CachedUserData {
|
||||
previousData = current
|
||||
} else {
|
||||
previousData = CachedUserData()
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if listMode == .everyone, searchQuery == nil {
|
||||
if let storedItem = transaction.getStory(id: StoryId(peerId: account.peerId, id: storyId))?.get(Stories.StoredItem.self), case let .item(item) = storedItem, let currentViews = item.views {
|
||||
let updatedItem: Stories.StoredItem = .item(Stories.Item(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: item.media,
|
||||
mediaAreas: item.mediaAreas,
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: Stories.Item.Views(
|
||||
seenCount: Int(count),
|
||||
reactedCount: Int(reactionsCount),
|
||||
forwardCount: currentViews.forwardCount,
|
||||
seenPeerIds: currentViews.seenPeerIds,
|
||||
reactions: currentViews.reactions,
|
||||
hasList: currentViews.hasList
|
||||
),
|
||||
privacy: item.privacy,
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic,
|
||||
isCloseFriends: item.isCloseFriends,
|
||||
isContacts: item.isContacts,
|
||||
isSelectedContacts: item.isSelectedContacts,
|
||||
isForwardingDisabled: item.isForwardingDisabled,
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
transaction.setStory(id: StoryId(peerId: account.peerId, id: storyId), value: entry)
|
||||
var updatedFlags = previousData.flags
|
||||
if isBlockedFromStories {
|
||||
updatedFlags.insert(.isBlockedFromStories)
|
||||
} else {
|
||||
updatedFlags.remove(.isBlockedFromStories)
|
||||
}
|
||||
return previousData.withUpdatedIsBlocked(isBlocked).withUpdatedFlags(updatedFlags)
|
||||
})
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
let parsedReaction = reaction.flatMap(MessageReaction.Reaction.init(apiReaction:))
|
||||
items.append(.view(Item.View(
|
||||
peer: EnginePeer(peer),
|
||||
timestamp: date,
|
||||
storyStats: transaction.getPeerStoryStats(peerId: peerId),
|
||||
reaction: parsedReaction,
|
||||
reactionFile: parsedReaction.flatMap { reaction -> TelegramMediaFile? in
|
||||
switch reaction {
|
||||
case .builtin:
|
||||
return nil
|
||||
case let .custom(fileId):
|
||||
return transaction.getMedia(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)) as? TelegramMediaFile
|
||||
}
|
||||
}
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentItems = transaction.getStoryItems(peerId: account.peerId)
|
||||
for i in 0 ..< currentItems.count {
|
||||
if currentItems[i].id == storyId {
|
||||
if case let .item(item) = currentItems[i].value.get(Stories.StoredItem.self), let currentViews = item.views {
|
||||
let updatedItem: Stories.StoredItem = .item(Stories.Item(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: item.media,
|
||||
mediaAreas: item.mediaAreas,
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: Stories.Item.Views(
|
||||
seenCount: Int(count),
|
||||
reactedCount: Int(reactionsCount),
|
||||
forwardCount: currentViews.forwardCount,
|
||||
seenPeerIds: currentViews.seenPeerIds,
|
||||
reactions: currentViews.reactions,
|
||||
hasList: currentViews.hasList
|
||||
),
|
||||
privacy: item.privacy,
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic,
|
||||
isCloseFriends: item.isCloseFriends,
|
||||
isContacts: item.isContacts,
|
||||
isSelectedContacts: item.isSelectedContacts,
|
||||
isForwardingDisabled: item.isForwardingDisabled,
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
if listMode == .everyone, searchQuery == nil {
|
||||
if let storedItem = transaction.getStory(id: StoryId(peerId: account.peerId, id: storyId))?.get(Stories.StoredItem.self), case let .item(item) = storedItem, let currentViews = item.views {
|
||||
let updatedItem: Stories.StoredItem = .item(Stories.Item(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: item.media,
|
||||
mediaAreas: item.mediaAreas,
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: Stories.Item.Views(
|
||||
seenCount: Int(count),
|
||||
reactedCount: Int(reactionsCount),
|
||||
forwardCount: currentViews.forwardCount,
|
||||
seenPeerIds: currentViews.seenPeerIds,
|
||||
reactions: currentViews.reactions,
|
||||
hasList: currentViews.hasList
|
||||
),
|
||||
privacy: item.privacy,
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic,
|
||||
isCloseFriends: item.isCloseFriends,
|
||||
isContacts: item.isContacts,
|
||||
isSelectedContacts: item.isSelectedContacts,
|
||||
isForwardingDisabled: item.isForwardingDisabled,
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
transaction.setStory(id: StoryId(peerId: account.peerId, id: storyId), value: entry)
|
||||
}
|
||||
}
|
||||
|
||||
var currentItems = transaction.getStoryItems(peerId: account.peerId)
|
||||
for i in 0 ..< currentItems.count {
|
||||
if currentItems[i].id == storyId {
|
||||
if case let .item(item) = currentItems[i].value.get(Stories.StoredItem.self), let currentViews = item.views {
|
||||
let updatedItem: Stories.StoredItem = .item(Stories.Item(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: item.media,
|
||||
mediaAreas: item.mediaAreas,
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: Stories.Item.Views(
|
||||
seenCount: Int(count),
|
||||
reactedCount: Int(reactionsCount),
|
||||
forwardCount: currentViews.forwardCount,
|
||||
seenPeerIds: currentViews.seenPeerIds,
|
||||
reactions: currentViews.reactions,
|
||||
hasList: currentViews.hasList
|
||||
),
|
||||
privacy: item.privacy,
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic,
|
||||
isCloseFriends: item.isCloseFriends,
|
||||
isContacts: item.isContacts,
|
||||
isSelectedContacts: item.isSelectedContacts,
|
||||
isForwardingDisabled: item.isForwardingDisabled,
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
transaction.setStoryItems(peerId: account.peerId, items: currentItems)
|
||||
}
|
||||
|
||||
return InternalState(totalCount: Int(count), totalReactedCount: Int(reactionsCount), items: items, canLoadMore: nextOffset != nil, nextOffset: nextOffset.flatMap { NextOffset(value: $0) })
|
||||
case .none:
|
||||
return InternalState(totalCount: 0, totalReactedCount: 0, items: [], canLoadMore: false, nextOffset: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
signal = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<InternalState, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
if case .repostsFirst = sortMode {
|
||||
flags |= (1 << 2)
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.stories.getStoryReactionsList(flags: flags, peer: inputPeer, id: storyId, reaction: nil, offset: currentOffset?.value, limit: Int32(limit)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.stories.StoryReactionsList?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<InternalState, NoError> in
|
||||
return account.postbox.transaction { transaction -> InternalState in
|
||||
switch result {
|
||||
case let .storyReactionsList(_, count, reactions, chats, users, nextOffset):
|
||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(chats: chats, users: users))
|
||||
|
||||
var items: [Item] = []
|
||||
for reaction in reactions {
|
||||
switch reaction {
|
||||
case let .storyPeerReaction(peerId, date, reaction):
|
||||
if let peer = transaction.getPeer(peerId.peerId) {
|
||||
if let parsedReaction = MessageReaction.Reaction(apiReaction: reaction) {
|
||||
let reactionFile: TelegramMediaFile?
|
||||
switch parsedReaction {
|
||||
case .builtin:
|
||||
reactionFile = nil
|
||||
case let .custom(fileId):
|
||||
reactionFile = transaction.getMedia(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)) as? TelegramMediaFile
|
||||
}
|
||||
items.append(.view(Item.View(
|
||||
peer: EnginePeer(peer),
|
||||
timestamp: date,
|
||||
storyStats: transaction.getPeerStoryStats(peerId: peer.id),
|
||||
reaction: parsedReaction,
|
||||
reactionFile: reactionFile
|
||||
)))
|
||||
}
|
||||
}
|
||||
case let .storyPeerPublicRepost(peerId, story):
|
||||
if let peer = transaction.getPeer(peerId.peerId) {
|
||||
if let storedItem = Stories.StoredItem(apiStoryItem: story, peerId: peer.id, transaction: transaction), case let .item(item) = storedItem, let media = item.media {
|
||||
items.append(.repost(Item.Repost(
|
||||
peer: EnginePeer(peer),
|
||||
story: EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: EngineMedia(media),
|
||||
mediaAreas: item.mediaAreas,
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: item.views.flatMap { views in
|
||||
return EngineStoryItem.Views(
|
||||
seenCount: views.seenCount,
|
||||
reactedCount: views.reactedCount,
|
||||
forwardCount: views.forwardCount,
|
||||
seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
return transaction.getPeer(id).flatMap(EnginePeer.init)
|
||||
},
|
||||
reactions: views.reactions,
|
||||
hasList: views.hasList
|
||||
)
|
||||
},
|
||||
privacy: item.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic,
|
||||
isPending: false,
|
||||
isCloseFriends: item.isCloseFriends,
|
||||
isContacts: item.isContacts,
|
||||
isSelectedContacts: item.isSelectedContacts,
|
||||
isForwardingDisabled: item.isForwardingDisabled,
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
),
|
||||
storyStats: transaction.getPeerStoryStats(peerId: peer.id)
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
transaction.setStoryItems(peerId: account.peerId, items: currentItems)
|
||||
return InternalState(totalCount: Int(count), totalReactedCount: Int(count), items: items, canLoadMore: nextOffset != nil, nextOffset: nextOffset.flatMap { NextOffset(value: $0) })
|
||||
case .none:
|
||||
return InternalState(totalCount: 0, totalReactedCount: 0, items: [], canLoadMore: false, nextOffset: nil)
|
||||
}
|
||||
|
||||
return InternalState(totalCount: Int(count), totalReactedCount: Int(reactionsCount), items: items, canLoadMore: nextOffset != nil, nextOffset: nextOffset.flatMap { NextOffset(value: $0) })
|
||||
case .none:
|
||||
return InternalState(totalCount: 0, totalReactedCount: 0, items: [], canLoadMore: false, nextOffset: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -457,21 +654,17 @@ public final class EngineStoryViewListContext {
|
||||
var currentState = self.state ?? InternalState(
|
||||
totalCount: 0, totalReactedCount: 0, items: [], canLoadMore: false, nextOffset: nil)
|
||||
|
||||
struct ItemHash: Hashable {
|
||||
var peerId: EnginePeer.Id
|
||||
}
|
||||
|
||||
if self.parentSource != nil {
|
||||
currentState.items.removeAll()
|
||||
}
|
||||
|
||||
var existingItems = Set<ItemHash>()
|
||||
var existingItems = Set<Item.ItemHash>()
|
||||
for item in currentState.items {
|
||||
existingItems.insert(ItemHash(peerId: item.peer.id))
|
||||
existingItems.insert(item.uniqueId)
|
||||
}
|
||||
|
||||
for item in state.items {
|
||||
let itemHash = ItemHash(peerId: item.peer.id)
|
||||
let itemHash = item.uniqueId
|
||||
if existingItems.contains(itemHash) {
|
||||
continue
|
||||
}
|
||||
@ -481,7 +674,7 @@ public final class EngineStoryViewListContext {
|
||||
|
||||
var allReactedCount = 0
|
||||
for item in currentState.items {
|
||||
if item.reaction != nil {
|
||||
if case let .view(view) = item, view.reaction != nil {
|
||||
allReactedCount += 1
|
||||
} else {
|
||||
break
|
||||
@ -516,15 +709,15 @@ public final class EngineStoryViewListContext {
|
||||
for i in 0 ..< state.items.count {
|
||||
let item = items[i]
|
||||
let value = view.storyStats[item.peer.id]
|
||||
if item.storyStats != value {
|
||||
if case let .view(view) = item, view.storyStats != value {
|
||||
updated = true
|
||||
items[i] = Item(
|
||||
peer: item.peer,
|
||||
timestamp: item.timestamp,
|
||||
items[i] = .view(Item.View(
|
||||
peer: view.peer,
|
||||
timestamp: view.timestamp,
|
||||
storyStats: value,
|
||||
reaction: item.reaction,
|
||||
reactionFile: item.reactionFile
|
||||
)
|
||||
reaction: view.reaction,
|
||||
reactionFile: view.reactionFile
|
||||
))
|
||||
}
|
||||
}
|
||||
if updated {
|
||||
|
@ -18,6 +18,7 @@ public enum EngineMedia: Equatable {
|
||||
case webpage(TelegramMediaWebpage)
|
||||
case story(TelegramMediaStory)
|
||||
case giveaway(TelegramMediaGiveaway)
|
||||
case giveawayResults(TelegramMediaGiveawayResults)
|
||||
}
|
||||
|
||||
public extension EngineMedia {
|
||||
@ -53,6 +54,8 @@ public extension EngineMedia {
|
||||
return story.id
|
||||
case let .giveaway(giveaway):
|
||||
return giveaway.id
|
||||
case let .giveawayResults(giveawayResults):
|
||||
return giveawayResults.id
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,6 +93,8 @@ public extension EngineMedia {
|
||||
self = .story(story)
|
||||
case let giveaway as TelegramMediaGiveaway:
|
||||
self = .giveaway(giveaway)
|
||||
case let giveawayResults as TelegramMediaGiveawayResults:
|
||||
self = .giveawayResults(giveawayResults)
|
||||
default:
|
||||
preconditionFailure()
|
||||
}
|
||||
@ -127,6 +132,8 @@ public extension EngineMedia {
|
||||
return story
|
||||
case let .giveaway(giveaway):
|
||||
return giveaway
|
||||
case let .giveawayResults(giveawayResults):
|
||||
return giveawayResults
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ func _internal_getPremiumGiveawayInfo(account: Account, peerId: EnginePeer.Id, m
|
||||
} else {
|
||||
return .ongoing(startDate: startDate, status: .notQualified)
|
||||
}
|
||||
case let .giveawayInfoResults(flags, startDate, giftCodeSlug, finishDate, winnersCount, activatedCount, _):
|
||||
case let .giveawayInfoResults(flags, startDate, giftCodeSlug, finishDate, winnersCount, activatedCount):
|
||||
let status: PremiumGiveawayInfo.ResultStatus
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
status = .refunded
|
||||
|
@ -221,6 +221,9 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
} else if let _ = media as? TelegramMediaGiveaway {
|
||||
result.append((message, ChatMessageGiveawayBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
needReactions = false
|
||||
} else if let _ = media as? TelegramMediaGiveawayResults {
|
||||
result.append((message, ChatMessageGiveawayBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
needReactions = false
|
||||
} else if let _ = media as? TelegramMediaUnsupported {
|
||||
isUnsupportedMedia = true
|
||||
needReactions = false
|
||||
|
@ -33,9 +33,12 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
private let prizeTitleNode: TextNode
|
||||
private let prizeTextNode: TextNode
|
||||
|
||||
private let additionalPrizeTitleNode: TextNode
|
||||
private let additionalPrizeSeparatorNode: TextNode
|
||||
private let additionalPrizeTextNode: TextNode
|
||||
|
||||
private let additionalPrizeLeftLine: ASDisplayNode
|
||||
private let additionalPrizeRightLine: ASDisplayNode
|
||||
|
||||
private let participantsTitleNode: TextNode
|
||||
private let participantsTextNode: TextNode
|
||||
|
||||
@ -84,9 +87,12 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
self.prizeTitleNode = TextNode()
|
||||
self.prizeTextNode = TextNode()
|
||||
|
||||
self.additionalPrizeTitleNode = TextNode()
|
||||
self.additionalPrizeSeparatorNode = TextNode()
|
||||
self.additionalPrizeTextNode = TextNode()
|
||||
|
||||
self.additionalPrizeLeftLine = ASDisplayNode()
|
||||
self.additionalPrizeRightLine = ASDisplayNode()
|
||||
|
||||
self.participantsTitleNode = TextNode()
|
||||
self.participantsTextNode = TextNode()
|
||||
|
||||
@ -107,8 +113,10 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
self.addSubnode(self.prizeTitleNode)
|
||||
self.addSubnode(self.prizeTextNode)
|
||||
self.addSubnode(self.additionalPrizeTitleNode)
|
||||
self.addSubnode(self.additionalPrizeSeparatorNode)
|
||||
self.addSubnode(self.additionalPrizeTextNode)
|
||||
self.addSubnode(self.additionalPrizeLeftLine)
|
||||
self.addSubnode(self.additionalPrizeRightLine)
|
||||
self.addSubnode(self.participantsTitleNode)
|
||||
self.addSubnode(self.participantsTextNode)
|
||||
self.addSubnode(self.countriesTextNode)
|
||||
@ -189,7 +197,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
let makePrizeTitleLayout = TextNode.asyncLayout(self.prizeTitleNode)
|
||||
let makePrizeTextLayout = TextNode.asyncLayout(self.prizeTextNode)
|
||||
|
||||
let makeAdditionalPrizeTitleLayout = TextNode.asyncLayout(self.additionalPrizeTitleNode)
|
||||
let makeAdditionalPrizeSeparatorLayout = TextNode.asyncLayout(self.additionalPrizeSeparatorNode)
|
||||
let makeAdditionalPrizeTextLayout = TextNode.asyncLayout(self.additionalPrizeTextNode)
|
||||
|
||||
let makeParticipantsTitleLayout = TextNode.asyncLayout(self.participantsTitleNode)
|
||||
@ -210,9 +218,12 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
return { item, layoutConstants, _, _, constrainedSize, _ in
|
||||
var giveaway: TelegramMediaGiveaway?
|
||||
var giveawayResults: TelegramMediaGiveawayResults?
|
||||
for media in item.message.media {
|
||||
if let media = media as? TelegramMediaGiveaway {
|
||||
giveaway = media;
|
||||
} else if let media = media as? TelegramMediaGiveawayResults {
|
||||
giveawayResults = media
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,6 +239,8 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
let backgroundColor = incoming ? item.presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper.fill.first! : item.presentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper.fill.first!
|
||||
let textColor = incoming ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor
|
||||
let secondaryTextColor = incoming ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
|
||||
let lineColor = secondaryTextColor.withMultipliedAlpha(0.2)
|
||||
let accentColor = incoming ? item.presentationData.theme.theme.chat.message.incoming.accentTextColor : item.presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||
var badgeTextColor: UIColor = .white
|
||||
if badgeTextColor.distance(to: accentColor) < 1 {
|
||||
@ -239,15 +252,38 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
updatedBadgeImage = generateStretchableFilledCircleImage(diameter: 21.0, color: accentColor, strokeColor: backgroundColor, strokeWidth: 1.0 + UIScreenPixel, backgroundColor: nil)
|
||||
}
|
||||
|
||||
let badgeString = NSAttributedString(string: "X\(giveaway?.quantity ?? 1)", font: Font.with(size: 10.0, design: .round , weight: .bold, traits: .monospacedNumbers), textColor: badgeTextColor)
|
||||
|
||||
let prizeTitleString = NSAttributedString(string: item.presentationData.strings.Chat_Giveaway_Message_PrizeTitle, font: titleFont, textColor: textColor)
|
||||
let badgeString = NSAttributedString(string: "X\(giveaway?.quantity ?? 2)", font: Font.with(size: 10.0, design: .round , weight: .bold, traits: .monospacedNumbers), textColor: badgeTextColor)
|
||||
|
||||
let prizeTitleString = NSAttributedString(string: giveawayResults != nil ? "Winners Selected!" : item.presentationData.strings.Chat_Giveaway_Message_PrizeTitle, font: titleFont, textColor: textColor)
|
||||
var prizeTextString: NSAttributedString?
|
||||
var additionalPrizeTitleString: NSAttributedString?
|
||||
var additionalPrizeSeparatorString: NSAttributedString?
|
||||
var additionalPrizeTextString: NSAttributedString?
|
||||
if let giveaway {
|
||||
var prizeDescription: String?
|
||||
if let description = giveaway.prizeDescription {
|
||||
prizeDescription = description
|
||||
}
|
||||
var trimSubscriptionCount = false
|
||||
if let prizeDescription {
|
||||
additionalPrizeSeparatorString = NSAttributedString(string: item.presentationData.strings.Chat_Giveaway_Message_With, font: textFont, textColor: secondaryTextColor)
|
||||
additionalPrizeTextString = parseMarkdownIntoAttributedString("**\(giveaway.quantity)** \(prizeDescription)", attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
), textAlignment: .center)
|
||||
trimSubscriptionCount = true
|
||||
}
|
||||
|
||||
var subscriptionsString = item.presentationData.strings.Chat_Giveaway_Message_Subscriptions(giveaway.quantity)
|
||||
if trimSubscriptionCount {
|
||||
subscriptionsString = subscriptionsString.replacingOccurrences(of: "**\(giveaway.quantity)** ", with: "")
|
||||
}
|
||||
|
||||
prizeTextString = parseMarkdownIntoAttributedString(item.presentationData.strings.Chat_Giveaway_Message_PrizeText(
|
||||
item.presentationData.strings.Chat_Giveaway_Message_Subscriptions(giveaway.quantity),
|
||||
subscriptionsString,
|
||||
item.presentationData.strings.Chat_Giveaway_Message_Months(giveaway.months)
|
||||
).string, attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
@ -257,14 +293,18 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
return ("URL", url)
|
||||
}
|
||||
), textAlignment: .center)
|
||||
|
||||
if let prizeDescription = giveaway.prizeDescription {
|
||||
additionalPrizeTitleString = NSAttributedString(string: "Additional Prize", font: titleFont, textColor: textColor)
|
||||
additionalPrizeTextString = NSAttributedString(string: prizeDescription, font: textFont, textColor: textColor)
|
||||
}
|
||||
} else if let giveawayResults {
|
||||
prizeTextString = parseMarkdownIntoAttributedString("**\(giveawayResults.winnersCount)** winners of the [Giveaway]() were randomly selected by Telegram.", attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: textFont, textColor: accentColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
), textAlignment: .center)
|
||||
}
|
||||
|
||||
let participantsTitleString = NSAttributedString(string: item.presentationData.strings.Chat_Giveaway_Message_ParticipantsTitle, font: titleFont, textColor: textColor)
|
||||
let participantsTitleString = NSAttributedString(string: giveawayResults != nil ? "Winners" : item.presentationData.strings.Chat_Giveaway_Message_ParticipantsTitle, font: titleFont, textColor: textColor)
|
||||
let participantsText: String
|
||||
let countriesText: String
|
||||
|
||||
@ -314,15 +354,26 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
}
|
||||
|
||||
let participantsTextString = NSAttributedString(string: participantsText, font: textFont, textColor: textColor)
|
||||
|
||||
let countriesTextString = NSAttributedString(string: countriesText, font: textFont, textColor: textColor)
|
||||
|
||||
let dateTitleString = NSAttributedString(string: item.presentationData.strings.Chat_Giveaway_Message_DateTitle, font: titleFont, textColor: textColor)
|
||||
var dateTitleText = item.presentationData.strings.Chat_Giveaway_Message_DateTitle
|
||||
if let giveawayResults {
|
||||
if giveawayResults.winnersCount > giveawayResults.winnersPeerIds.count {
|
||||
let moreCount = giveawayResults.winnersCount - Int32(giveawayResults.winnersPeerIds.count)
|
||||
dateTitleText = "and \(moreCount) more!"
|
||||
} else {
|
||||
dateTitleText = ""
|
||||
}
|
||||
}
|
||||
|
||||
let dateTitleString = NSAttributedString(string: dateTitleText, font: titleFont, textColor: textColor)
|
||||
var dateTextString: NSAttributedString?
|
||||
if let giveaway {
|
||||
dateTextString = NSAttributedString(string: stringForFullDate(timestamp: giveaway.untilDate, strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat), font: textFont, textColor: textColor)
|
||||
} else if let _ = giveawayResults {
|
||||
dateTextString = NSAttributedString(string: "All winners received gift links in private messages.", font: textFont, textColor: textColor)
|
||||
}
|
||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none, hidesHeaders: true)
|
||||
|
||||
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
||||
let sideInsets = layoutConstants.text.bubbleInsets.right * 2.0
|
||||
@ -331,13 +382,13 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
let (badgeTextLayout, badgeTextApply) = makeBadgeTextLayout(TextNodeLayoutArguments(attributedString: badgeString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (prizeTitleLayout, prizeTitleApply) = makePrizeTitleLayout(TextNodeLayoutArguments(attributedString: prizeTitleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (prizeTextLayout, prizeTextApply) = makePrizeTextLayout(TextNodeLayoutArguments(attributedString: prizeTextString, backgroundColor: nil, maximumNumberOfLines: 5, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (additionalPrizeTitleLayout, additionalPrizeTitleApply) = makeAdditionalPrizeTitleLayout(TextNodeLayoutArguments(attributedString: additionalPrizeTitleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
|
||||
let (additionalPrizeTextLayout, additionalPrizeTextApply) = makeAdditionalPrizeTextLayout(TextNodeLayoutArguments(attributedString: additionalPrizeTextString, backgroundColor: nil, maximumNumberOfLines: 6, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (additionalPrizeSeparatorLayout, additionalPrizeSeparatorApply) = makeAdditionalPrizeSeparatorLayout(TextNodeLayoutArguments(attributedString: additionalPrizeSeparatorString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (prizeTextLayout, prizeTextApply) = makePrizeTextLayout(TextNodeLayoutArguments(attributedString: prizeTextString, backgroundColor: nil, maximumNumberOfLines: 5, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (participantsTitleLayout, participantsTitleApply) = makeParticipantsTitleLayout(TextNodeLayoutArguments(attributedString: participantsTitleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (participantsTextLayout, participantsTextApply) = makeParticipantsTextLayout(TextNodeLayoutArguments(attributedString: participantsTextString, backgroundColor: nil, maximumNumberOfLines: 5, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
@ -426,17 +477,22 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, nil, false, item.presentationData.strings.Chat_Giveaway_Message_LearnMore.uppercased(), titleColor, false, true)
|
||||
|
||||
let months = giveaway?.months ?? 0
|
||||
|
||||
let animationName: String
|
||||
switch months {
|
||||
case 12:
|
||||
animationName = "Gift12"
|
||||
case 6:
|
||||
animationName = "Gift6"
|
||||
case 3:
|
||||
animationName = "Gift3"
|
||||
default:
|
||||
animationName = "Gift3"
|
||||
let months = giveaway?.months ?? 0
|
||||
if let _ = giveaway {
|
||||
switch months {
|
||||
case 12:
|
||||
animationName = "Gift12"
|
||||
case 6:
|
||||
animationName = "Gift6"
|
||||
case 3:
|
||||
animationName = "Gift3"
|
||||
default:
|
||||
animationName = "Gift3"
|
||||
}
|
||||
} else {
|
||||
animationName = "Celebrate"
|
||||
}
|
||||
|
||||
var maxContentWidth: CGFloat = 0.0
|
||||
@ -445,7 +501,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
}
|
||||
maxContentWidth = max(maxContentWidth, prizeTitleLayout.size.width)
|
||||
maxContentWidth = max(maxContentWidth, prizeTextLayout.size.width)
|
||||
maxContentWidth = max(maxContentWidth, additionalPrizeTitleLayout.size.width)
|
||||
maxContentWidth = max(maxContentWidth, additionalPrizeSeparatorLayout.size.width)
|
||||
maxContentWidth = max(maxContentWidth, additionalPrizeTextLayout.size.width)
|
||||
maxContentWidth = max(maxContentWidth, participantsTitleLayout.size.width)
|
||||
maxContentWidth = max(maxContentWidth, participantsTextLayout.size.width)
|
||||
@ -460,6 +516,10 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
channelPeers.append(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let peer = item.message.peers[item.message.id.peerId] {
|
||||
channelPeers.append(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
let (channelsWidth, continueChannelLayout) = makeChannelsLayout(item.context, 220.0, channelPeers, accentColor, accentColor.withAlphaComponent(0.1), incoming, item.presentationData.theme.theme.overallDarkAppearance)
|
||||
maxContentWidth = max(maxContentWidth, channelsWidth)
|
||||
@ -475,14 +535,17 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
let statusSizeAndApply = statusSuggestedWidthAndContinue?.1(boundingWidth - sideInsets)
|
||||
|
||||
var layoutSize = CGSize(width: boundingWidth, height: 49.0 + prizeTitleLayout.size.height + prizeTextLayout.size.height + participantsTitleLayout.size.height + participantsTextLayout.size.height + dateTitleLayout.size.height + dateTextLayout.size.height + buttonSize.height + buttonSpacing + 120.0)
|
||||
var layoutSize = CGSize(width: boundingWidth, height: 49.0 + prizeTitleLayout.size.height + prizeTextLayout.size.height + participantsTitleLayout.size.height + participantsTextLayout.size.height + dateTextLayout.size.height + buttonSize.height + buttonSpacing + 120.0)
|
||||
|
||||
if additionalPrizeTextLayout.size.height > 0.0 {
|
||||
layoutSize.height += additionalPrizeTitleLayout.size.height + additionalPrizeTextLayout.size.height + 7.0
|
||||
layoutSize.height += additionalPrizeSeparatorLayout.size.height + additionalPrizeTextLayout.size.height + 7.0
|
||||
}
|
||||
if countriesTextLayout.size.height > 0.0 {
|
||||
layoutSize.height += countriesTextLayout.size.height + 7.0
|
||||
}
|
||||
if dateTitleLayout.size.height > 0.0 {
|
||||
layoutSize.height += dateTitleLayout.size.height
|
||||
}
|
||||
layoutSize.height += channelButtonsSize.height
|
||||
|
||||
if let statusSizeAndApply = statusSizeAndApply {
|
||||
@ -504,7 +567,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
let _ = badgeTextApply()
|
||||
let _ = prizeTitleApply()
|
||||
let _ = prizeTextApply()
|
||||
let _ = additionalPrizeTitleApply()
|
||||
let _ = additionalPrizeSeparatorApply()
|
||||
let _ = additionalPrizeTextApply()
|
||||
let _ = participantsTitleApply()
|
||||
let _ = participantsTextApply()
|
||||
@ -519,8 +582,9 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
var originY: CGFloat = 0.0
|
||||
|
||||
let iconSize = CGSize(width: 140.0, height: 140.0)
|
||||
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - iconSize.width) / 2.0), y: originY - 40.0), size: iconSize)
|
||||
let animationOffset: CGFloat = giveaway != nil ? -40.0 : 15.0
|
||||
let iconSize = giveaway != nil ? CGSize(width: 140.0, height: 140.0) : CGSize(width: 84.0, height: 84.0)
|
||||
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - iconSize.width) / 2.0), y: originY + animationOffset), size: iconSize)
|
||||
strongSelf.animationNode.updateLayout(size: iconSize)
|
||||
|
||||
let badgeTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - badgeTextLayout.size.width) / 2.0) + 1.0, y: originY + 88.0), size: badgeTextLayout.size)
|
||||
@ -534,16 +598,35 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
strongSelf.prizeTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - prizeTitleLayout.size.width) / 2.0), y: originY), size: prizeTitleLayout.size)
|
||||
originY += prizeTitleLayout.size.height + smallSpacing
|
||||
|
||||
if additionalPrizeTextLayout.size.height > 0.0 {
|
||||
strongSelf.additionalPrizeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - additionalPrizeTextLayout.size.width) / 2.0), y: originY), size: additionalPrizeTextLayout.size)
|
||||
originY += additionalPrizeTextLayout.size.height + smallSpacing
|
||||
|
||||
let separatorFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - additionalPrizeSeparatorLayout.size.width) / 2.0), y: originY), size: additionalPrizeSeparatorLayout.size)
|
||||
strongSelf.additionalPrizeSeparatorNode.frame = separatorFrame
|
||||
originY += additionalPrizeSeparatorLayout.size.height + smallSpacing
|
||||
|
||||
let lineSpacing: CGFloat = 7.0
|
||||
let lineWidth = (layoutSize.width - additionalPrizeSeparatorLayout.size.width - (27.0 + lineSpacing) * 2.0) / 2.0
|
||||
let lineHeight: CGFloat = 1.0 - UIScreenPixel
|
||||
let lineSize = CGSize(width: lineWidth, height: lineHeight)
|
||||
|
||||
strongSelf.additionalPrizeLeftLine.backgroundColor = lineColor
|
||||
strongSelf.additionalPrizeLeftLine.isHidden = false
|
||||
strongSelf.additionalPrizeLeftLine.frame = CGRect(origin: CGPoint(x: separatorFrame.minX - lineSize.width - lineSpacing, y: floorToScreenPixels(separatorFrame.midY - lineSize.height / 2.0)), size: lineSize)
|
||||
|
||||
strongSelf.additionalPrizeRightLine.backgroundColor = lineColor
|
||||
strongSelf.additionalPrizeRightLine.isHidden = false
|
||||
strongSelf.additionalPrizeRightLine.frame = CGRect(origin: CGPoint(x: separatorFrame.maxX + lineSpacing, y: floorToScreenPixels(separatorFrame.midY - lineSize.height / 2.0)), size: lineSize)
|
||||
} else {
|
||||
strongSelf.additionalPrizeLeftLine.isHidden = true
|
||||
strongSelf.additionalPrizeRightLine.isHidden = true
|
||||
}
|
||||
|
||||
strongSelf.prizeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - prizeTextLayout.size.width) / 2.0), y: originY), size: prizeTextLayout.size)
|
||||
originY += prizeTextLayout.size.height + largeSpacing
|
||||
|
||||
if additionalPrizeTextLayout.size.height > 0.0 {
|
||||
strongSelf.additionalPrizeTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - additionalPrizeTitleLayout.size.width) / 2.0), y: originY), size: additionalPrizeTitleLayout.size)
|
||||
originY += additionalPrizeTitleLayout.size.height + smallSpacing
|
||||
strongSelf.additionalPrizeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - additionalPrizeTextLayout.size.width) / 2.0), y: originY), size: additionalPrizeTextLayout.size)
|
||||
originY += additionalPrizeTextLayout.size.height + largeSpacing
|
||||
}
|
||||
|
||||
strongSelf.participantsTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - participantsTitleLayout.size.width) / 2.0), y: originY), size: participantsTitleLayout.size)
|
||||
originY += participantsTitleLayout.size.height + smallSpacing
|
||||
strongSelf.participantsTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - participantsTextLayout.size.width) / 2.0), y: originY), size: participantsTextLayout.size)
|
||||
|
@ -4034,13 +4034,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
let expiringStoryList = PeerExpiringStoryListContext(account: context.account, peerId: peerId)
|
||||
self.expiringStoryList = expiringStoryList
|
||||
self.storyUploadProgressDisposable = (
|
||||
combineLatest(context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> distinctUntilChanged,
|
||||
context.engine.messages.allStoriesUploadProgress()
|
||||
|> map { value -> Float? in
|
||||
return value[peerId]
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> distinctUntilChanged,
|
||||
context.engine.messages.allStoriesUploadProgress()
|
||||
|> map { value -> Float? in
|
||||
return value[peerId]
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
)).startStrict(next: { [weak self] peer, value in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -28,6 +28,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/PhotoResources",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -18,9 +18,11 @@ import EmojiStatusComponent
|
||||
import ContextUI
|
||||
import EmojiTextAttachmentView
|
||||
import TextFormat
|
||||
import PhotoResources
|
||||
|
||||
private let avatarFont = avatarPlaceholderFont(size: 15.0)
|
||||
private let readIconImage: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/MenuReadIcon"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
private let repostIconImage: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Stories/HeaderRepost"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
private let checkImage: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
private let disclosureImage: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
|
||||
@ -51,6 +53,7 @@ public final class PeerListItemComponent: Component {
|
||||
public enum SubtitleAccessory: Equatable {
|
||||
case none
|
||||
case checks
|
||||
case repost
|
||||
}
|
||||
|
||||
public enum RightAccessory: Equatable {
|
||||
@ -105,6 +108,7 @@ public final class PeerListItemComponent: Component {
|
||||
let presence: EnginePeer.Presence?
|
||||
let rightAccessory: RightAccessory
|
||||
let reaction: Reaction?
|
||||
let story: EngineStoryItem?
|
||||
let selectionState: SelectionState
|
||||
let selectionPosition: SelectionPosition
|
||||
let isEnabled: Bool
|
||||
@ -112,6 +116,7 @@ public final class PeerListItemComponent: Component {
|
||||
let action: (EnginePeer) -> Void
|
||||
let contextAction: ((EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void)?
|
||||
let openStories: ((EnginePeer, AvatarNode) -> Void)?
|
||||
let openStory: ((EnginePeer, Int32, UIView) -> Void)?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
@ -127,13 +132,15 @@ public final class PeerListItemComponent: Component {
|
||||
presence: EnginePeer.Presence?,
|
||||
rightAccessory: RightAccessory = .none,
|
||||
reaction: Reaction? = nil,
|
||||
story: EngineStoryItem? = nil,
|
||||
selectionState: SelectionState,
|
||||
selectionPosition: SelectionPosition = .left,
|
||||
isEnabled: Bool = true,
|
||||
hasNext: Bool,
|
||||
action: @escaping (EnginePeer) -> Void,
|
||||
contextAction: ((EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void)? = nil,
|
||||
openStories: ((EnginePeer, AvatarNode) -> Void)? = nil
|
||||
openStories: ((EnginePeer, AvatarNode) -> Void)? = nil,
|
||||
openStory: ((EnginePeer, Int32, UIView) -> Void)? = nil
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
@ -148,6 +155,7 @@ public final class PeerListItemComponent: Component {
|
||||
self.presence = presence
|
||||
self.rightAccessory = rightAccessory
|
||||
self.reaction = reaction
|
||||
self.story = story
|
||||
self.selectionState = selectionState
|
||||
self.selectionPosition = selectionPosition
|
||||
self.isEnabled = isEnabled
|
||||
@ -155,6 +163,7 @@ public final class PeerListItemComponent: Component {
|
||||
self.action = action
|
||||
self.contextAction = contextAction
|
||||
self.openStories = openStories
|
||||
self.openStory = openStory
|
||||
}
|
||||
|
||||
public static func ==(lhs: PeerListItemComponent, rhs: PeerListItemComponent) -> Bool {
|
||||
@ -197,6 +206,9 @@ public final class PeerListItemComponent: Component {
|
||||
if lhs.reaction != rhs.reaction {
|
||||
return false
|
||||
}
|
||||
if lhs.story != rhs.story {
|
||||
return false
|
||||
}
|
||||
if lhs.selectionState != rhs.selectionState {
|
||||
return false
|
||||
}
|
||||
@ -232,6 +244,9 @@ public final class PeerListItemComponent: Component {
|
||||
private var file: TelegramMediaFile?
|
||||
private var fileDisposable: Disposable?
|
||||
|
||||
private var imageButtonView: HighlightTrackingButton?
|
||||
private var imageNode: TransformImageNode?
|
||||
|
||||
private var component: PeerListItemComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
@ -340,6 +355,13 @@ public final class PeerListItemComponent: Component {
|
||||
component.openStories?(peer, self.avatarNode)
|
||||
}
|
||||
|
||||
@objc private func imageButtonPressed() {
|
||||
guard let component = self.component, let peer = component.peer, let story = component.story, let imageNode = self.imageNode else {
|
||||
return
|
||||
}
|
||||
component.openStory?(peer, story.id, imageNode.view)
|
||||
}
|
||||
|
||||
private func updateReactionLayer() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
@ -696,24 +718,34 @@ public final class PeerListItemComponent: Component {
|
||||
if let labelView = self.label.view {
|
||||
var iconLabelOffset: CGFloat = 0.0
|
||||
|
||||
if case .checks = component.subtitleAccessory {
|
||||
if case .none = component.subtitleAccessory {
|
||||
if let iconView = self.iconView {
|
||||
self.iconView = nil
|
||||
iconView.removeFromSuperview()
|
||||
}
|
||||
} else {
|
||||
let iconView: UIImageView
|
||||
if let current = self.iconView {
|
||||
iconView = current
|
||||
} else {
|
||||
iconView = UIImageView(image: readIconImage)
|
||||
iconView.tintColor = component.theme.list.itemSecondaryTextColor
|
||||
var image: UIImage?
|
||||
var color: UIColor = component.theme.list.itemSecondaryTextColor
|
||||
if case .checks = component.subtitleAccessory {
|
||||
image = readIconImage
|
||||
} else if case .repost = component.subtitleAccessory {
|
||||
image = repostIconImage
|
||||
color = UIColor(rgb: 0x34c759)
|
||||
}
|
||||
iconView = UIImageView(image: image)
|
||||
iconView.tintColor = color
|
||||
self.iconView = iconView
|
||||
self.containerButton.addSubview(iconView)
|
||||
}
|
||||
|
||||
if let image = iconView.image {
|
||||
iconLabelOffset = image.size.width + 4.0
|
||||
transition.setFrame(view: iconView, frame: CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleSpacing + 3.0 + floor((labelSize.height - image.size.height) * 0.5)), size: image.size))
|
||||
transition.setFrame(view: iconView, frame: CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleSpacing + 2.0 + floor((labelSize.height - image.size.height) * 0.5)), size: image.size))
|
||||
}
|
||||
} else if let iconView = self.iconView {
|
||||
self.iconView = nil
|
||||
iconView.removeFromSuperview()
|
||||
}
|
||||
|
||||
if labelView.superview == nil {
|
||||
@ -825,6 +857,72 @@ public final class PeerListItemComponent: Component {
|
||||
transition.setFrame(layer: reactionLayer, frame: adjustedIconFrame)
|
||||
}
|
||||
|
||||
var imageMedia: Media?
|
||||
if let story = component.story {
|
||||
if let image = story.media._asMedia() as? TelegramMediaImage {
|
||||
imageMedia = image
|
||||
} else if let file = story.media._asMedia() as? TelegramMediaFile {
|
||||
imageMedia = file
|
||||
}
|
||||
}
|
||||
|
||||
if let peer = component.peer, let story = component.story, let imageMedia {
|
||||
let contentImageSize = CGSize(width: 30.0, height: 42.0)
|
||||
var dimensions: CGSize?
|
||||
if let imageMedia = imageMedia as? TelegramMediaImage {
|
||||
dimensions = largestRepresentationForPhoto(imageMedia)?.dimensions.cgSize
|
||||
} else if let imageMedia = imageMedia as? TelegramMediaFile {
|
||||
dimensions = imageMedia.dimensions?.cgSize
|
||||
}
|
||||
|
||||
let imageButtonView: HighlightTrackingButton
|
||||
let imageNode: TransformImageNode
|
||||
if let current = self.imageNode, let currentButton = self.imageButtonView {
|
||||
imageNode = current
|
||||
imageButtonView = currentButton
|
||||
} else {
|
||||
imageNode = TransformImageNode()
|
||||
imageNode.displaysAsynchronously = false
|
||||
imageNode.isUserInteractionEnabled = false
|
||||
self.imageNode = imageNode
|
||||
|
||||
imageButtonView = HighlightTrackingButton()
|
||||
imageButtonView.addTarget(self, action: #selector(self.imageButtonPressed), for: .touchUpInside)
|
||||
self.imageButtonView = imageButtonView
|
||||
|
||||
self.containerButton.addSubview(imageNode.view)
|
||||
self.addSubview(imageButtonView)
|
||||
|
||||
var imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
||||
if let peerReference = PeerReference(peer._asPeer()) {
|
||||
if let image = imageMedia as? TelegramMediaImage {
|
||||
imageSignal = mediaGridMessagePhoto(account: component.context.account, userLocation: .peer(peer.id), photoReference: .story(peer: peerReference, id: story.id, media: image))
|
||||
} else if let file = imageMedia as? TelegramMediaFile {
|
||||
imageSignal = mediaGridMessageVideo(postbox: component.context.account.postbox, userLocation: .peer(peer.id), videoReference: .story(peer: peerReference, id: story.id, media: file), autoFetchFullSizeThumbnail: true)
|
||||
}
|
||||
}
|
||||
|
||||
if let imageSignal {
|
||||
imageNode.setSignal(imageSignal)
|
||||
}
|
||||
}
|
||||
|
||||
if let dimensions {
|
||||
let makeImageLayout = imageNode.asyncLayout()
|
||||
let applyImageLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: 5.0), imageSize: dimensions.aspectFilled(contentImageSize), boundingSize: contentImageSize, intrinsicInsets: UIEdgeInsets()))
|
||||
applyImageLayout()
|
||||
|
||||
let imageFrame = CGRect(origin: CGPoint(x: availableSize.width - contentImageSize.width - 10.0 - contextInset, y: floorToScreenPixels((height - contentImageSize.height) / 2.0)), size: contentImageSize)
|
||||
imageNode.frame = imageFrame
|
||||
transition.setFrame(view: imageButtonView, frame: imageFrame)
|
||||
}
|
||||
} else {
|
||||
self.imageNode?.removeFromSupernode()
|
||||
self.imageNode = nil
|
||||
self.imageButtonView?.removeFromSuperview()
|
||||
self.imageButtonView = nil
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
self.separatorLayer.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor
|
||||
}
|
||||
|
@ -2042,3 +2042,310 @@ private func getCachedStory(storyId: StoryId, transaction: Transaction) -> Engin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final class RepostStoriesContentContextImpl: StoryContentContext {
|
||||
private let context: AccountContext
|
||||
private let readGlobally: Bool
|
||||
|
||||
public private(set) var stateValue: StoryContentContextState?
|
||||
public var state: Signal<StoryContentContextState, NoError> {
|
||||
return self.statePromise.get()
|
||||
}
|
||||
private let statePromise = Promise<StoryContentContextState>()
|
||||
|
||||
private let updatedPromise = Promise<Void>()
|
||||
public var updated: Signal<Void, NoError> {
|
||||
return self.updatedPromise.get()
|
||||
}
|
||||
|
||||
private var storyDisposable: Disposable?
|
||||
|
||||
private var requestedStoryKeys = Set<StoryKey>()
|
||||
private var requestStoryDisposables = DisposableSet()
|
||||
|
||||
private var currentForwardInfoStories: [StoryId: Promise<EngineStoryItem?>] = [:]
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
storyId: StoryId,
|
||||
storyItems: [(EnginePeer, EngineStoryItem)],
|
||||
readGlobally: Bool
|
||||
) {
|
||||
self.context = context
|
||||
self.readGlobally = readGlobally
|
||||
|
||||
// let item: Signal<Stories.StoredItem?, NoError>
|
||||
// if let storyItem {
|
||||
// item = .single(.item(storyItem.asStoryItem()))
|
||||
// } else {
|
||||
// item = context.account.postbox.combinedView(keys: [PostboxViewKey.story(id: storyId)])
|
||||
// |> map { views -> Stories.StoredItem? in
|
||||
// return (views.views[PostboxViewKey.story(id: storyId)] as? StoryView)?.item?.get(Stories.StoredItem.self)
|
||||
// }
|
||||
// }
|
||||
|
||||
self.storyDisposable = (combineLatest(queue: .mainQueue(),
|
||||
context.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: storyId.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.Presence(id: storyId.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: storyId.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.CanViewStats(id: storyId.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: storyId.peerId),
|
||||
TelegramEngine.EngineData.Item.NotificationSettings.Global()
|
||||
),
|
||||
.single(0)
|
||||
// item |> mapToSignal { item -> Signal<(Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]), NoError> in
|
||||
// return context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]) in
|
||||
// guard let item else {
|
||||
// return (nil, [:], [:], [:])
|
||||
// }
|
||||
// var peers: [PeerId: Peer] = [:]
|
||||
// var stories: [StoryId: EngineStoryItem?] = [:]
|
||||
// var allEntityFiles: [MediaId: TelegramMediaFile] = [:]
|
||||
// if case let .item(item) = item {
|
||||
// if let views = item.views {
|
||||
// for id in views.seenPeerIds {
|
||||
// if let peer = transaction.getPeer(id) {
|
||||
// peers[peer.id] = peer
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if let forwardInfo = item.forwardInfo, case let .known(peerId, id, _) = forwardInfo {
|
||||
// if let peer = transaction.getPeer(peerId) {
|
||||
// peers[peer.id] = peer
|
||||
// }
|
||||
// let storyId = StoryId(peerId: peerId, id: id)
|
||||
// if let story = getCachedStory(storyId: storyId, transaction: transaction) {
|
||||
// stories[storyId] = story
|
||||
// } else {
|
||||
// stories.updateValue(nil, forKey: storyId)
|
||||
// }
|
||||
// }
|
||||
// for entity in item.entities {
|
||||
// if case let .CustomEmoji(_, fileId) = entity.type {
|
||||
// let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
||||
// if allEntityFiles[mediaId] == nil {
|
||||
// if let file = transaction.getMedia(mediaId) as? TelegramMediaFile {
|
||||
// allEntityFiles[file.fileId] = file
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for mediaArea in item.mediaAreas {
|
||||
// if case let .reaction(_, reaction, _) = mediaArea {
|
||||
// if case let .custom(fileId) = reaction {
|
||||
// let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
||||
// if allEntityFiles[mediaId] == nil {
|
||||
// if let file = transaction.getMedia(mediaId) as? TelegramMediaFile {
|
||||
// allEntityFiles[file.fileId] = file
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return (item, peers, allEntityFiles, stories)
|
||||
// }
|
||||
// }
|
||||
)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] data, skip in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = skip
|
||||
|
||||
let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings) = data
|
||||
|
||||
// let (item, peers, allEntityFiles, forwardInfoStories) = itemAndPeers
|
||||
guard let peer else {
|
||||
return
|
||||
}
|
||||
|
||||
let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: [])
|
||||
|
||||
let additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
||||
isMuted: isMuted,
|
||||
areVoiceMessagesAvailable: areVoiceMessagesAvailable,
|
||||
presence: presence,
|
||||
canViewStats: canViewStats
|
||||
)
|
||||
|
||||
if let first = storyItems.first {
|
||||
let (peer, storyItem) = first
|
||||
let (nextPeer, nextStoryItem) = storyItems[1]
|
||||
|
||||
let mainItem = StoryContentItem(
|
||||
position: 0,
|
||||
dayCounters: nil,
|
||||
peerId: peer.id,
|
||||
storyItem: storyItem,
|
||||
entityFiles: [:] //extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles)
|
||||
)
|
||||
|
||||
let nextItem = StoryContentItem(
|
||||
position: 0,
|
||||
dayCounters: nil,
|
||||
peerId: nextPeer.id,
|
||||
storyItem: nextStoryItem,
|
||||
entityFiles: [:] //extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles)
|
||||
)
|
||||
|
||||
let stateValue = StoryContentContextState(
|
||||
slice: StoryContentContextState.FocusedSlice(
|
||||
peer: peer,
|
||||
additionalPeerData: additionalPeerData,
|
||||
item: mainItem,
|
||||
totalCount: 1,
|
||||
previousItemId: nil,
|
||||
nextItemId: nil,
|
||||
allItems: [mainItem],
|
||||
forwardInfoStories: [:] //self.currentForwardInfoStories
|
||||
),
|
||||
previousSlice: nil,
|
||||
nextSlice: StoryContentContextState.FocusedSlice(
|
||||
peer: nextPeer,
|
||||
additionalPeerData: additionalPeerData,
|
||||
item: nextItem,
|
||||
totalCount: 1,
|
||||
previousItemId: nil,
|
||||
nextItemId: nil,
|
||||
allItems: [nextItem],
|
||||
forwardInfoStories: [:] //self.currentForwardInfoStories
|
||||
)
|
||||
)
|
||||
|
||||
if self.stateValue == nil || self.stateValue?.slice != stateValue.slice {
|
||||
self.stateValue = stateValue
|
||||
self.statePromise.set(.single(stateValue))
|
||||
self.updatedPromise.set(.single(Void()))
|
||||
}
|
||||
}
|
||||
|
||||
// for (storyId, story) in forwardInfoStories {
|
||||
// let promise: Promise<EngineStoryItem?>
|
||||
// var added = false
|
||||
// if let current = self.currentForwardInfoStories[storyId] {
|
||||
// promise = current
|
||||
// } else {
|
||||
// promise = Promise<EngineStoryItem?>()
|
||||
// self.currentForwardInfoStories[storyId] = promise
|
||||
// added = true
|
||||
// }
|
||||
// if let story {
|
||||
// promise.set(.single(story))
|
||||
// } else if added {
|
||||
// promise.set(self.context.engine.messages.getStory(peerId: storyId.peerId, id: storyId.id))
|
||||
// }
|
||||
// }
|
||||
|
||||
// if item == nil {
|
||||
// let storyKey = StoryKey(peerId: storyId.peerId, id: storyId.id)
|
||||
// if !self.requestedStoryKeys.contains(storyKey) {
|
||||
// self.requestedStoryKeys.insert(storyKey)
|
||||
//
|
||||
// self.requestStoryDisposables.add(self.context.engine.messages.refreshStories(peerId: storyId.peerId, ids: [storyId.id]).startStrict())
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// if let item, case let .item(itemValue) = item, let media = itemValue.media {
|
||||
// let mappedItem = EngineStoryItem(
|
||||
// id: itemValue.id,
|
||||
// timestamp: itemValue.timestamp,
|
||||
// expirationTimestamp: itemValue.expirationTimestamp,
|
||||
// media: EngineMedia(media),
|
||||
// mediaAreas: itemValue.mediaAreas,
|
||||
// text: itemValue.text,
|
||||
// entities: itemValue.entities,
|
||||
// views: itemValue.views.flatMap { views in
|
||||
// return EngineStoryItem.Views(
|
||||
// seenCount: views.seenCount,
|
||||
// reactedCount: views.reactedCount,
|
||||
// forwardCount: views.forwardCount,
|
||||
// seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
// return peers[id].flatMap(EnginePeer.init)
|
||||
// },
|
||||
// reactions: views.reactions,
|
||||
// hasList: views.hasList
|
||||
// )
|
||||
// },
|
||||
// privacy: itemValue.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
// isPinned: itemValue.isPinned,
|
||||
// isExpired: itemValue.isExpired,
|
||||
// isPublic: itemValue.isPublic,
|
||||
// isPending: false,
|
||||
// isCloseFriends: itemValue.isCloseFriends,
|
||||
// isContacts: itemValue.isContacts,
|
||||
// isSelectedContacts: itemValue.isSelectedContacts,
|
||||
// isForwardingDisabled: itemValue.isForwardingDisabled,
|
||||
// isEdited: itemValue.isEdited,
|
||||
// isMy: itemValue.isMy,
|
||||
// myReaction: itemValue.myReaction,
|
||||
// forwardInfo: itemValue.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
|
||||
// )
|
||||
//
|
||||
// let mainItem = StoryContentItem(
|
||||
// position: 0,
|
||||
// dayCounters: nil,
|
||||
// peerId: peer.id,
|
||||
// storyItem: mappedItem,
|
||||
// entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles)
|
||||
// )
|
||||
// let stateValue = StoryContentContextState(
|
||||
// slice: StoryContentContextState.FocusedSlice(
|
||||
// peer: peer,
|
||||
// additionalPeerData: additionalPeerData,
|
||||
// item: mainItem,
|
||||
// totalCount: 1,
|
||||
// previousItemId: nil,
|
||||
// nextItemId: nil,
|
||||
// allItems: [mainItem],
|
||||
// forwardInfoStories: self.currentForwardInfoStories
|
||||
// ),
|
||||
// previousSlice: nil,
|
||||
// nextSlice: nil
|
||||
// )
|
||||
//
|
||||
// if self.stateValue == nil || self.stateValue?.slice != stateValue.slice {
|
||||
// self.stateValue = stateValue
|
||||
// self.statePromise.set(.single(stateValue))
|
||||
// self.updatedPromise.set(.single(Void()))
|
||||
// }
|
||||
// } else {
|
||||
// let stateValue = StoryContentContextState(
|
||||
// slice: nil,
|
||||
// previousSlice: nil,
|
||||
// nextSlice: nil
|
||||
// )
|
||||
//
|
||||
// if self.stateValue == nil || self.stateValue?.slice != stateValue.slice {
|
||||
// self.stateValue = stateValue
|
||||
// self.statePromise.set(.single(stateValue))
|
||||
// self.updatedPromise.set(.single(Void()))
|
||||
// }
|
||||
// }
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.storyDisposable?.dispose()
|
||||
self.requestStoryDisposables.dispose()
|
||||
}
|
||||
|
||||
public func resetSideStates() {
|
||||
}
|
||||
|
||||
public func navigate(navigation: StoryContentContextNavigation) {
|
||||
}
|
||||
|
||||
public func markAsSeen(id: StoryId) {
|
||||
if self.readGlobally {
|
||||
if !self.context.sharedContext.immediateExperimentalUISettings.skipReadHistory {
|
||||
let _ = self.context.engine.messages.markStoryAsSeen(peerId: id.peerId, id: id.id, asPinned: false).startStandalone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -435,6 +435,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
var preparingToDisplayViewList: Bool = false
|
||||
|
||||
var viewListDisplayState: ViewListDisplayState = .hidden
|
||||
|
||||
private var targetViewListDisplayStateIsFull: Bool = false
|
||||
private var viewListMetrics: (minHeight: CGFloat, maxHeight: CGFloat, currentHeight: CGFloat)?
|
||||
|
||||
@ -1231,6 +1232,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
var displayViewLists = false
|
||||
if component.slice.peer.id == component.context.account.peerId {
|
||||
displayViewLists = true
|
||||
} else if case let .channel(channel) = component.slice.peer, channel.flags.contains(.isCreator) || channel.adminRights?.rights.contains(.canPostStories) == true {
|
||||
displayViewLists = true
|
||||
}
|
||||
|
||||
if displayViewLists {
|
||||
@ -1912,6 +1915,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
var displayViewLists = false
|
||||
if component.slice.peer.id == component.context.account.peerId {
|
||||
displayViewLists = true
|
||||
} else if case let .channel(channel) = component.slice.peer, channel.flags.contains(.isCreator) || channel.adminRights?.rights.contains(.canPostStories) == true {
|
||||
displayViewLists = true
|
||||
}
|
||||
|
||||
if displayViewLists {
|
||||
@ -3123,6 +3128,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
var displayViewLists = false
|
||||
if component.slice.peer.id == component.context.account.peerId {
|
||||
displayViewLists = true
|
||||
} else if case let .channel(channel) = component.slice.peer, channel.flags.contains(.isCreator) || channel.adminRights?.rights.contains(.canPostStories) == true {
|
||||
displayViewLists = true
|
||||
}
|
||||
|
||||
if displayViewLists, let currentIndex = component.slice.allItems.firstIndex(where: { $0.storyItem.id == component.slice.item.storyItem.id }) {
|
||||
@ -3497,6 +3504,12 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
self.openPeerStories(peer: peer, avatarNode: avatarNode)
|
||||
},
|
||||
openStory: { [weak self] peer, id, stories, sourceView in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openReposts(peer: peer, id: id, stories: stories, sourceView: sourceView)
|
||||
},
|
||||
openPremiumIntro: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
@ -5151,7 +5164,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
func openPeerStories(peer: EnginePeer, avatarNode: AvatarNode) {
|
||||
func openPeerStories(peer: EnginePeer, avatarNode: AvatarNode?) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
@ -5162,6 +5175,75 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
StoryContainerScreen.openPeerStories(context: component.context, peerId: peer.id, parentController: controller, avatarNode: avatarNode)
|
||||
}
|
||||
|
||||
func openReposts(peer: EnginePeer, id: Int32, stories: [(EnginePeer, EngineStoryItem)], sourceView: UIView) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
guard let controller = component.controller() else {
|
||||
return
|
||||
}
|
||||
|
||||
let context = component.context
|
||||
let storyContent = RepostStoriesContentContextImpl(context: context, storyId: StoryId(peerId: peer.id, id: id), storyItems: stories, readGlobally: false)
|
||||
let _ = (storyContent.state
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak controller, weak sourceView] _ in
|
||||
guard let controller, let sourceView else {
|
||||
return
|
||||
}
|
||||
let transitionIn = StoryContainerScreen.TransitionIn(
|
||||
sourceView: sourceView,
|
||||
sourceRect: sourceView.bounds,
|
||||
sourceCornerRadius: sourceView.bounds.width * 0.5,
|
||||
sourceIsAvatar: false
|
||||
)
|
||||
|
||||
let storyContainerScreen = StoryContainerScreen(
|
||||
context: context,
|
||||
content: storyContent,
|
||||
transitionIn: transitionIn,
|
||||
transitionOut: { [weak sourceView] peerId, storyIdValue in
|
||||
if let sourceView {
|
||||
let destinationView = sourceView
|
||||
return StoryContainerScreen.TransitionOut(
|
||||
destinationView: destinationView,
|
||||
transitionView: StoryContainerScreen.TransitionView(
|
||||
makeView: { [weak destinationView] in
|
||||
let parentView = UIView()
|
||||
if let copyView = destinationView?.snapshotContentTree(unhide: true) {
|
||||
parentView.addSubview(copyView)
|
||||
}
|
||||
return parentView
|
||||
},
|
||||
updateView: { copyView, state, transition in
|
||||
guard let view = copyView.subviews.first else {
|
||||
return
|
||||
}
|
||||
let size = state.sourceSize.interpolate(to: state.destinationSize, amount: state.progress)
|
||||
transition.setPosition(view: view, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5))
|
||||
transition.setScale(view: view, scale: size.width / state.destinationSize.width)
|
||||
},
|
||||
insertCloneTransitionView: nil
|
||||
),
|
||||
destinationRect: destinationView.bounds,
|
||||
destinationCornerRadius: destinationView.bounds.width * 0.5,
|
||||
destinationIsAvatar: false,
|
||||
completed: { [weak sourceView] in
|
||||
guard let sourceView else {
|
||||
return
|
||||
}
|
||||
sourceView.isHidden = false
|
||||
}
|
||||
)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
)
|
||||
controller.push(storyContainerScreen)
|
||||
})
|
||||
}
|
||||
|
||||
private let updateDisposable = MetaDisposable()
|
||||
func openStoryEditing(repost: Bool = false) {
|
||||
guard let component = self.component, let peerReference = PeerReference(component.slice.peer._asPeer()) else {
|
||||
|
@ -71,6 +71,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
let peerContextAction: (EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void
|
||||
let openPeerStories: (EnginePeer, AvatarNode) -> Void
|
||||
let openStory: (EnginePeer, Int32, [(EnginePeer, EngineStoryItem)], UIView) -> Void
|
||||
let openPremiumIntro: () -> Void
|
||||
let setIsSearchActive: (Bool) -> Void
|
||||
let controller: () -> ViewController?
|
||||
@ -95,6 +96,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
openPeer: @escaping (EnginePeer) -> Void,
|
||||
peerContextAction: @escaping (EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void,
|
||||
openPeerStories: @escaping (EnginePeer, AvatarNode) -> Void,
|
||||
openStory: @escaping (EnginePeer, Int32, [(EnginePeer, EngineStoryItem)], UIView) -> Void,
|
||||
openPremiumIntro: @escaping () -> Void,
|
||||
setIsSearchActive: @escaping (Bool) -> Void,
|
||||
controller: @escaping () -> ViewController?
|
||||
@ -118,6 +120,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.openPeer = openPeer
|
||||
self.peerContextAction = peerContextAction
|
||||
self.openPeerStories = openPeerStories
|
||||
self.openStory = openStory
|
||||
self.openPremiumIntro = openPremiumIntro
|
||||
self.setIsSearchActive = setIsSearchActive
|
||||
self.controller = controller
|
||||
@ -257,7 +260,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
let measureItem = ComponentView<Empty>()
|
||||
var placeholderImage: UIImage?
|
||||
|
||||
var visibleItems: [EnginePeer.Id: ComponentView<Empty>] = [:]
|
||||
var visibleItems: [EngineStoryViewListContext.Item.ItemHash: ComponentView<Empty>] = [:]
|
||||
var visiblePlaceholderViews: [Int: UIImageView] = [:]
|
||||
|
||||
var emptyIcon: ComponentView<Empty>?
|
||||
@ -388,7 +391,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
synchronousLoad = hint.synchronousLoad
|
||||
}
|
||||
|
||||
var validIds: [EnginePeer.Id] = []
|
||||
var validIds: [EngineStoryViewListContext.Item.ItemHash] = []
|
||||
var validPlaceholderIds: [Int] = []
|
||||
if let range = itemLayout.visibleItems(for: visibleBounds) {
|
||||
for index in range.lowerBound ..< range.upperBound {
|
||||
@ -432,21 +435,21 @@ final class StoryItemSetViewListComponent: Component {
|
||||
|
||||
var itemTransition = transition.withUserData(PeerListItemComponent.TransitionHint(synchronousLoad: true))
|
||||
let item = viewListState.items[index]
|
||||
validIds.append(item.peer.id)
|
||||
validIds.append(item.uniqueId)
|
||||
|
||||
let visibleItem: ComponentView<Empty>
|
||||
if let current = self.visibleItems[item.peer.id] {
|
||||
if let current = self.visibleItems[item.uniqueId] {
|
||||
visibleItem = current
|
||||
} else {
|
||||
if !transition.animation.isImmediate {
|
||||
itemTransition = .immediate
|
||||
}
|
||||
visibleItem = ComponentView()
|
||||
self.visibleItems[item.peer.id] = visibleItem
|
||||
self.visibleItems[item.uniqueId] = visibleItem
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let dateText = humanReadableStringForTimestamp(strings: component.strings, dateTimeFormat: presentationData.dateTimeFormat, timestamp: item.timestamp, alwaysShowTime: true, allowYesterday: true, format: HumanReadableStringFormat(
|
||||
var dateText = humanReadableStringForTimestamp(strings: component.strings, dateTimeFormat: presentationData.dateTimeFormat, timestamp: item.timestamp, alwaysShowTime: true, allowYesterday: true, format: HumanReadableStringFormat(
|
||||
dateFormatString: { value in
|
||||
return PresentationStrings.FormattedString(string: component.strings.Chat_MessageSeenTimestamp_Date(value).string, ranges: [])
|
||||
},
|
||||
@ -461,6 +464,10 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
)).string
|
||||
|
||||
if let story = item.story, !story.text.isEmpty {
|
||||
dateText += " • commented"
|
||||
}
|
||||
|
||||
let _ = visibleItem.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(PeerListItemComponent(
|
||||
@ -473,7 +480,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
peer: item.peer,
|
||||
storyStats: item.storyStats,
|
||||
subtitle: dateText,
|
||||
subtitleAccessory: .checks,
|
||||
subtitleAccessory: item.story != nil ? .repost : .checks,
|
||||
presence: nil,
|
||||
reaction: item.reaction.flatMap { reaction -> PeerListItemComponent.Reaction in
|
||||
var animationFileId: Int64?
|
||||
@ -490,7 +497,9 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
case let .custom(fileId):
|
||||
animationFileId = fileId
|
||||
animationFile = item.reactionFile
|
||||
if case let .view(view) = item {
|
||||
animationFile = view.reactionFile
|
||||
}
|
||||
}
|
||||
return PeerListItemComponent.Reaction(
|
||||
reaction: reaction,
|
||||
@ -498,6 +507,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
animationFileId: animationFileId
|
||||
)
|
||||
},
|
||||
story: item.story,
|
||||
selectionState: .none,
|
||||
hasNext: index != viewListState.totalCount - 1 || itemLayout.premiumFooterSize != nil,
|
||||
action: { [weak self] peer in
|
||||
@ -514,6 +524,19 @@ final class StoryItemSetViewListComponent: Component {
|
||||
return
|
||||
}
|
||||
component.openPeerStories(peer, avatarNode)
|
||||
},
|
||||
openStory: { [weak self] peer, id, sourceView in
|
||||
guard let self, let component = self.component, let state = self.viewListState else {
|
||||
return
|
||||
}
|
||||
|
||||
var stories: [(EnginePeer, EngineStoryItem)] = []
|
||||
for item in state.items {
|
||||
if let story = item.story {
|
||||
stories.append((item.peer, story))
|
||||
}
|
||||
}
|
||||
component.openStory(peer, id, stories, sourceView)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
@ -534,7 +557,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
var removeIds: [EnginePeer.Id] = []
|
||||
var removeIds: [EngineStoryViewListContext.Item.ItemHash] = []
|
||||
for (id, visibleItem) in self.visibleItems {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
@ -1292,25 +1315,26 @@ final class StoryItemSetViewListComponent: Component {
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
let sortMode = self.sortMode
|
||||
|
||||
// items.append(.action(ContextMenuActionItem(text: component.strings.Story_ViewList_ContextSortReposts, icon: { theme in
|
||||
// return generateTintedImage(image: UIImage(bundleImageName: "Stories/Context Menu/Repost"), color: theme.contextMenu.primaryColor)
|
||||
// }, additionalLeftIcon: { theme in
|
||||
// if sortMode != .repostsFirst {
|
||||
// return nil
|
||||
// }
|
||||
// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
|
||||
// }, action: { [weak self] _, a in
|
||||
// a(.default)
|
||||
//
|
||||
// guard let self else {
|
||||
// return
|
||||
// }
|
||||
// if self.sortMode != .repostsFirst {
|
||||
// self.sortMode = .repostsFirst
|
||||
// self.state?.updated(transition: .immediate)
|
||||
// }
|
||||
// })))
|
||||
if component.peerId.isGroupOrChannel {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_ViewList_ContextSortReposts, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Stories/Context Menu/Repost"), color: theme.contextMenu.primaryColor)
|
||||
}, additionalLeftIcon: { theme in
|
||||
if sortMode != .repostsFirst {
|
||||
return nil
|
||||
}
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.sortMode != .repostsFirst {
|
||||
self.sortMode = .repostsFirst
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
})))
|
||||
}
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_ViewList_ContextSortReactions, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: theme.contextMenu.primaryColor)
|
||||
}, additionalLeftIcon: { theme in
|
||||
@ -1531,7 +1555,9 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
|
||||
let titleText: String
|
||||
if let totalCount = currentTotalCount, let currentTotalReactionCount {
|
||||
if component.peerId.isGroupOrChannel {
|
||||
titleText = component.strings.Story_ViewList_TitleReactions
|
||||
} else if let totalCount = currentTotalCount, let currentTotalReactionCount {
|
||||
if totalCount > 0 && totalCount > currentTotalReactionCount {
|
||||
titleText = component.strings.Story_ViewList_ViewerCount(Int32(totalCount))
|
||||
} else {
|
||||
@ -1583,11 +1609,11 @@ final class StoryItemSetViewListComponent: Component {
|
||||
var displaySearchBar = false
|
||||
var displaySortSelector = false
|
||||
|
||||
if !component.hasPremium, component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) {
|
||||
if component.peerId == component.context.account.peerId && !component.hasPremium, component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) {
|
||||
} else {
|
||||
if let views = component.storyItem.views, views.hasList {
|
||||
if let views = component.storyItem.views, views.hasList || component.peerId.isGroupOrChannel {
|
||||
if let totalCount = currentTotalCount {
|
||||
if totalCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment {
|
||||
if !component.peerId.isGroupOrChannel, totalCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment {
|
||||
displayModeSelector = true
|
||||
displaySearchBar = true
|
||||
}
|
||||
|
@ -740,7 +740,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case .full:
|
||||
break
|
||||
}
|
||||
} else if let _ = media as? TelegramMediaGiveaway {
|
||||
} else if media is TelegramMediaGiveaway || media is TelegramMediaGiveawayResults {
|
||||
let progress = params.progress
|
||||
let presentationData = strongSelf.presentationData
|
||||
|
||||
|
@ -160,6 +160,9 @@ private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: EngineCo
|
||||
} else if let _ = media as? TelegramMediaGiveaway {
|
||||
hasUneditableAttributes = true
|
||||
break
|
||||
} else if let _ = media as? TelegramMediaGiveawayResults {
|
||||
hasUneditableAttributes = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user