Merge branch 'master' into experimental-2

This commit is contained in:
Ali 2021-09-03 00:59:06 +04:00
commit 8a6f5dd66e
175 changed files with 5413 additions and 1009 deletions

View File

@ -8,7 +8,7 @@ on:
jobs:
build:
runs-on: macos-10.15
runs-on: macos-11
steps:
- uses: actions/checkout@v2
@ -17,7 +17,7 @@ jobs:
fetch-depth: '0'
- name: Set active Xcode path
run: sudo xcode-select -s /Applications/Xcode_12.4.app/Contents/Developer
run: sudo xcode-select -s /Applications/Xcode_12.5.1.app/Contents/Developer
- name: Create canonical source directory
run: |

View File

@ -104,6 +104,7 @@
"PUSH_ALBUM" = "%1$@|sent you an album";
"PUSH_MESSAGE_FILES_TEXT_1" = "sent you a file";
"PUSH_MESSAGE_FILES_TEXT_any" = "sent you %d files";
"PUSH_MESSAGE_THEME" = "%1$@|changed theme to %2$@";
"PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@|%2$@";
"PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@|posted a message";
@ -176,6 +177,7 @@
"PUSH_CHAT_ALBUM" = "%2$@|%1$@ sent an album";
"PUSH_CHAT_MESSAGE_DOCS_TEXT_1" = "{author} sent a file";
"PUSH_CHAT_MESSAGE_DOCS_TEXT_any" = "{author} sent %d files";
"PUSH_CHAT_MESSAGE_THEME" = "%1$@|set theme to %3$@ in the group %2$@";
"PUSH_PINNED_TEXT" = "%1$@|pinned \"%2$@\" ";
"PUSH_PINNED_NOTEXT" = "%1$@|pinned a message";
@ -2478,11 +2480,15 @@ Unused sets are archived when you add more.";
"Call.CallInProgressTitle" = "Call in Progress";
"Call.CallInProgressMessage" = "Finish call with %1$@ and start a new one with %2$@?";
"Call.CallInProgressVoiceChatMessage" = "Finish call with %1$@ and start a voice chat with %2$@?";
"Call.CallInProgressLiveStreamMessage" = "Finish call with %1$@ and start a live stream with %2$@?";
"Call.ExternalCallInProgressMessage" = "Please finish the current call first.";
"Call.VoiceChatInProgressTitle" = "Voice Chat in Progress";
"Call.LiveStreamInProgressTitle" = "Live Stream in Progress";
"Call.VoiceChatInProgressMessage" = "Leave voice chat in %1$@ and start a new one with %2$@?";
"Call.LiveStreamInProgressMessage" = "Leave live stream in %1$@ and start a new one with %2$@?";
"Call.VoiceChatInProgressCallMessage" = "Leave voice chat in %1$@ and start a call with %2$@?";
"Call.LiveStreamInProgressCallMessage" = "Leave live stream in %1$@ and start a call with %2$@?";
"Call.Message" = "Message";
@ -2616,6 +2622,7 @@ Unused sets are archived when you add more.";
"Channel.AdminLog.CanBeAnonymous" = "Remain Anonymous";
"Channel.AdminLog.CanEditMessages" = "Edit Messages";
"Channel.AdminLog.CanManageCalls" = "Manage Voice Chats";
"Channel.AdminLog.CanManageLiveStreams" = "Manage Live Streams";
"Channel.AdminLog.MessageToggleInvitesOn" = "%@ enabled group invites";
"Channel.AdminLog.MessageToggleInvitesOff" = "%@ disabled group invites";
@ -2674,6 +2681,7 @@ Unused sets are archived when you add more.";
"Channel.AdminLogFilter.EventsPinned" = "Pinned Messages";
"Channel.AdminLogFilter.EventsLeaving" = "Members Removed";
"Channel.AdminLogFilter.EventsCalls" = "Voice Chats";
"Channel.AdminLogFilter.EventsLiveStreams" = "Live Streams";
"Channel.AdminLogFilter.EventsInviteLinks" = "Invite Links";
"Channel.AdminLogFilter.AdminsTitle" = "ADMINS";
"Channel.AdminLogFilter.AdminsAll" = "All Admins";
@ -2694,6 +2702,7 @@ Unused sets are archived when you add more.";
"Conversation.ViewChannel" = "VIEW CHANNEL";
"Conversation.ViewGroup" = "VIEW GROUP";
"Conversation.ViewBot" = "VIEW BOT";
"GroupInfo.ActionPromote" = "Promote";
"GroupInfo.ActionRestrict" = "Restrict";
@ -5705,6 +5714,7 @@ Sorry for the inconvenience.";
"VoiceChat.SpeakPermissionAdmin" = "New paricipants are muted";
"VoiceChat.Share" = "Share Invite Link";
"VoiceChat.EndVoiceChat" = "End Voice Chat";
"VoiceChat.EndLiveStream" = "End Live Stream";
"VoiceChat.CopyInviteLink" = "Copy Invite Link";
"VoiceChat.InviteLinkCopiedText" = "Invite link copied to clipboard";
@ -5717,18 +5727,20 @@ Sorry for the inconvenience.";
"VoiceChat.PanelJoin" = "Join";
"VoiceChat.Title" = "Voice Chat";
"VoiceChatChannel.Title" = "Live Stream";
"VoiceChat.InviteMember" = "Invite Member";
"VoiceChat.UserInvited" = "You invited **%@** to the voice chat";
"Notification.VoiceChatInvitation" = "%1$@ invited %2$@ to the voice chat";
"Notification.VoiceChatInvitationForYou" = "%1$@ invited you to the voice chat";
"VoiceChat.InvitedPeerText" = "You invited %@ to the voice chat";
"LiveStream.InvitedPeerText" = "You invited %@ to the live stream";
"VoiceChat.RemovedPeerText" = "You removed %@ from this group";
"Notification.VoiceChatStarted" = "%1$@ started a voice chat";
"Notification.VoiceChatEnded" = "Voice chat ended (%@)";
"Notification.LiveStreamEnded" = "Live stream ended (%@)";
"Notification.VoiceChatEndedGroup" = "%1$@ ended the voice chat (%2$@)";
"VoiceChat.Panel.TapToJoin" = "Tap to join";
@ -5748,12 +5760,17 @@ Sorry for the inconvenience.";
"VoiceChat.Status.MembersFormat" = "%1$@ %2$@";
"ChannelInfo.CreateVoiceChat" = "Start Voice Chat";
"ChannelInfo.CreateLiveStream" = "Start Live Stream";
"VoiceChat.AnonymousDisabledAlertText" = "Sorry, you can't join voice chat as an anonymous admin.";
"LiveStream.AnonymousDisabledAlertText" = "Sorry, you can't join live stream as an anonymous admin.";
"VoiceChat.ChatFullAlertText" = "Sorry, this voice chat has too many participants at the moment.";
"LiveStream.ChatFullAlertText" = "Sorry, this live stream has too many participants at the moment.";
"VoiceChat.EndConfirmationTitle" = "End voice chat";
"LiveStream.EndConfirmationTitle" = "End live stream";
"VoiceChat.EndConfirmationText" = "Are you sure you want to end this voice chat?";
"LiveStream.EndConfirmationText" = "Are you sure you want to end this live stream?";
"VoiceChat.EndConfirmationEnd" = "End";
"VoiceChat.InviteMemberToGroupFirstText" = "%1$@ isn't a member of \"%2$@\" yet. Add them to the group?";
@ -5761,6 +5778,7 @@ Sorry for the inconvenience.";
"VoiceChat.InviteMemberToGroupFirstAdd" = "Add";
"VoiceChat.CreateNewVoiceChatText" = "Voice chat ended. Start a new one?";
"LiveStream.CreateNewVoiceChatText" = "Live stream ended. Start a new one?";
"VoiceChat.CreateNewVoiceChatStart" = "Start";
"VoiceChat.CreateNewVoiceChatStartNow" = "Start Now";
"VoiceChat.CreateNewVoiceChatSchedule" = "Schedule";
@ -5770,8 +5788,10 @@ Sorry for the inconvenience.";
"PUSH_CHAT_VOICECHAT_INVITE_YOU" = "%2$@|%1$@ invited you to the voice chat";
"PUSH_CHAT_VOICECHAT_END" = "%2$@|%1$@ has ended the voice chat";
"Call.VoiceChatInProgressTitle" = "Voice Chat in Progress";
"Call.VoiceChatInProgressMessageCall" = "Leave voice chat in %1$@ and start a call with %2$@?";
"PUSH_CHAT_LIVESTREAM_START" = "%2$@|%1$@ started a live stream";
"PUSH_CHAT_LIVESTREAM_INVITE" = "%2$@|%1$@ invited %3$@ to the live stream";
"PUSH_CHAT_LIVESTREAM_INVITE_YOU" = "%2$@|%1$@ invited you to the live stream";
"PUSH_CHAT_LIVESTREAM_END" = "%2$@|%1$@ has ended the live stream";
"Conversation.Dice.u1F3B3" = "Send a bowling emoji to try your luck.";
@ -5787,7 +5807,9 @@ Sorry for the inconvenience.";
"VoiceOver.Chat.MessagesSelected_any" = "%@ messages selected";
"Channel.AdminLog.StartedVoiceChat" = "%1$@ started voice chat";
"Channel.AdminLog.StartedLiveStream" = "%1$@ started live stream";
"Channel.AdminLog.EndedVoiceChat" = "%1$@ ended voice chat";
"Channel.AdminLog.EndedLiveStream" = "%1$@ ended live stream";
"Channel.AdminLog.MutedParticipant" = "%1$@ muted %2$@";
"Channel.AdminLog.UnmutedMutedParticipant" = "%1$@ unmuted %2$@";
"Channel.AdminLog.AllowedNewMembersToSpeak" = "%1$@ allowed new members to speak";
@ -5796,22 +5818,29 @@ Sorry for the inconvenience.";
"Group.GroupMembersHeader" = "GROUP MEMBERS";
"Conversation.VoiceChatMediaRecordingRestricted" = "You can't record voice and video messages during a voice chat.";
"Conversation.LiveStreamMediaRecordingRestricted" = "You can't record voice and video messages during a live stream.";
"CallList.ActiveVoiceChatsHeader" = "ACTIVE VOICE CHATS";
"CallList.RecentCallsHeader" = "RECENT CALLS";
"VoiceChat.PeerJoinedText" = "%@ joined the voice chat";
"LiveStream.PeerJoinedText" = "%@ joined the live stream";
"VoiceChat.StartRecording" = "Start Recording";
"LiveStream.StartRecording" = "Start Recording";
"VoiceChat.StopRecording" = "Stop Recording";
"VoiceChat.StartRecordingTitle" = "Start Recording";
"LiveStream.StartRecordingTitle" = "Start Recording";
"VoiceChat.StartRecordingText" = "Do you want to start recording this chat and save the result into an audio file?\n\nOther members will see that the chat is being recorded.";
"LiveStream.StartRecordingText" = "Do you want to start recording this live stream and save the result into an audio file?\n\nOther members will see that the chat is being recorded.";
"VoiceChat.StartRecordingStart" = "Start";
"VoiceChat.RecordingTitlePlaceholder" = "Audio Title (Optional)";
"VoiceChat.RecordingStarted" = "Voice chat recording started";
"LiveStream.RecordingStarted" = "Live stream recording started";
"VoiceChat.RecordingInProgress" = "Voice chat is being recorded";
"LiveStream.RecordingInProgress" = "Live stream is being recorded";
"VoiceChat.StopRecordingTitle" = "Stop Recording?";
"VoiceChat.StopRecordingStop" = "Stop";
@ -5836,6 +5865,7 @@ Sorry for the inconvenience.";
"VoiceChat.MuteForMe" = "Mute for Me";
"PeerInfo.ButtonVoiceChat" = "Voice Chat";
"PeerInfo.ButtonLiveStream" = "Live Stream";
"VoiceChat.OpenChat" = "Open Chat";
"GroupInfo.InviteLinks" = "Invite Links";
@ -6183,16 +6213,20 @@ Sorry for the inconvenience.";
"VoiceChat.DisplayAsInfo" = "Choose whether you want to be displayed as your personal account or as your channel.";
"VoiceChat.DisplayAsInfoGroup" = "Choose whether you want to be displayed as your personal account, this group, or one of your channels.";
"VoiceChat.DisplayAsSuccess" = "Members of this voice chat will now see your as **%@**.";
"LiveStream.DisplayAsSuccess" = "Members of this live stream will now see your as **%@**.";
"VoiceChat.PersonalAccount" = "personal account";
"VoiceChat.EditTitle" = "Edit Voice Chat Title";
"LiveStream.EditTitle" = "Edit Live Stream Title";
"VoiceChat.EditPermissions" = "Edit Permissions";
"VoiceChat.OpenChannel" = "Open Channel";
"VoiceChat.EditTitleTitle" = "Voice Chat Title";
"VoiceChat.EditTitleText" = "Edit a title of this voice chat.";
"LiveStream.EditTitleText" = "Edit a title of this live stream.";
"VoiceChat.EditTitleSuccess" = "Voice chat title changed to **%@**.";
"LiveStream.EditTitleSuccess" = "Live stream title changed to **%@**.";
"VoiceChat.EditTitleRemoveSuccess" = "Voice chat title removed.";
"LiveStream.EditTitleRemoveSuccess" = "Live Stream title removed.";
"VoiceChat.InviteLink.Speaker" = "Speaker";
"VoiceChat.InviteLink.Listener" = "Listener";
@ -6213,8 +6247,6 @@ Sorry for the inconvenience.";
"VoiceChat.InviteLink.InviteListeners_many" = "[%@] Invite Listeners";
"VoiceChat.InviteLink.InviteListeners_any" = "[%@] Invite Listeners";
"Conversation.JoinVoiceChat" = "JOIN VOICE CHAT";
"Conversation.CancelForwardTitle" = "Cancel Forwarding";
"Conversation.CancelForwardText" = "Do you want to cancel forwarding or send messages to a different chat?";
"Conversation.CancelForwardCancelForward" = "Cancel Forwarding";
@ -6256,8 +6288,10 @@ Sorry for the inconvenience.";
"VoiceChat.RemovePeerConfirmationChannel" = "Are you sure you want to remove %@ from the channel?";
"VoiceChat.RemoveAndBanPeerConfirmation" = "Do you want to remove %1$@ from the voice chat and ban them in %2$@?";
"LiveStream.RemoveAndBanPeerConfirmation" = "Do you want to remove %1$@ from the live stream and ban them in %2$@?";
"Notification.VoiceChatStartedChannel" = "Voice chat started";
"Notification.LiveStreamStarted" = "Live stream started";
"Conversation.MessageCopied" = "Message copied to clipboard";
"Conversation.LinkCopied" = "Link copied to clipboard";
@ -6276,14 +6310,19 @@ Sorry for the inconvenience.";
"Conversation.DeletedFromContacts" = "**%@** deleted from your contacts";
"Conversation.VoiceChat" = "Voice Chat";
"Conversation.LiveStream" = "Live Stream";
"Conversation.JoinVoiceChatAsSpeaker" = "JOIN AS SPEAKER";
"Conversation.JoinVoiceChatAsListener" = "JOIN AS LISTENER";
"VoiceChat.LeaveConfirmation" = "Are you sure you want to leave this voice chat?";
"LiveStream.LeaveConfirmation" = "Are you sure you want to leave this live stream?";
"VoiceChat.LeaveVoiceChat" = "Leave Voice Chat";
"LiveStream.LeaveVoiceChat" = "Leave Live Stream";
"VoiceChat.LeaveAndEndVoiceChat" = "End Voice Chat";
"LiveStream.LeaveAndEndVoiceChat" = "End Live Stream";
"VoiceChat.LeaveAndCancelVoiceChat" = "Abort Voice Chat";
"LiveStream.LeaveAndCancelVoiceChat" = "Abort Live Stream";
"VoiceChat.ForwardTooltip.Chat" = "Invite link forwarded to **%@**";
"VoiceChat.ForwardTooltip.TwoChats" = "Invite link forwarded to **%@** and **%@**";
@ -6337,12 +6376,15 @@ Sorry for the inconvenience.";
"VoiceChat.UnpinVideo" = "Unpin Video";
"Notification.VoiceChatScheduledChannel" = "Voice chat scheduled for %@";
"Notification.LiveStreamScheduled" = "Live stream scheduled for %@";
"Notification.VoiceChatScheduled" = "%1$@ scheduled a voice chat for %2$@";
"Notification.VoiceChatScheduledTodayChannel" = "Voice chat scheduled for today at %@";
"Notification.LiveStreamScheduledToday" = "Live stream scheduled for today at %@";
"Notification.VoiceChatScheduledToday" = "%1$@ scheduled a voice chat for today at %2$@";
"Notification.VoiceChatScheduledTomorrowChannel" = "Voice chat scheduled for tomorrow at %@";
"Notification.LiveStreamScheduledTomorrow" = "Live stream scheduled for tomorrow at %@";
"Notification.VoiceChatScheduledTomorrow" = "%1$@ scheduled a voice chat for tomorrow at %2$@";
"VoiceChat.StartsIn" = "Starts in";
@ -6361,27 +6403,36 @@ Sorry for the inconvenience.";
"VoiceChat.TapToEditTitle" = "Tap to edit title";
"ChannelInfo.ScheduleVoiceChat" = "Schedule Voice Chat";
"ChannelInfo.ScheduleLiveStream" = "Schedule Live Stream";
"ScheduleVoiceChat.Title" = "Schedule Voice Chat";
"ScheduleLiveStream.Title" = "Schedule Live Stream";
"ScheduleVoiceChat.GroupText" = "The members of the group will be notified that the voice chat will start in %@.";
"ScheduleVoiceChat.ChannelText" = "The members of the channel will be notified that the voice chat will start in %@.";
"ScheduleLiveStream.ChannelText" = "The members of the channel will be notified that the live stream will start in %@.";
"ScheduleVoiceChat.ScheduleToday" = "Start today at %@";
"ScheduleVoiceChat.ScheduleTomorrow" = "Start tomorrow at %@";
"ScheduleVoiceChat.ScheduleOn" = "Start on %@ at %@";
"Conversation.ScheduledVoiceChat" = "Scheduled Voice Chat";
"Conversation.ScheduledLiveStream" = "Scheduled Live Stream";
"Conversation.ScheduledVoiceChatStartsOn" = "Voice chat starts on %@";
"Conversation.ScheduledLiveStreamStartsOn" = "Live stream starts on %@";
"Conversation.ScheduledVoiceChatStartsOnShort" = "Starts on %@";
"Conversation.ScheduledVoiceChatStartsToday" = "Voice chat starts today at %@";
"Conversation.ScheduledLiveStreamStartsToday" = "Live stream starts today at %@";
"Conversation.ScheduledVoiceChatStartsTodayShort" = "Starts today at %@";
"Conversation.ScheduledVoiceChatStartsTomorrow" = "Voice chat starts tomorrow at %@";
"Conversation.ScheduledLiveStreamStartsTomorrow" = "Live stream starts tomorrow at %@";
"Conversation.ScheduledVoiceChatStartsTomorrowShort" = "Starts tomorrow at %@";
"VoiceChat.CancelVoiceChat" = "Abort Voice Chat";
"VoiceChat.CancelLiveStream" = "Abort Live Stream";
"VoiceChat.CancelConfirmationTitle" = "Abort Voice Chat";
"LiveStream.CancelConfirmationTitle" = "Abort Live Stream";
"VoiceChat.CancelConfirmationText" = "Do you want to abort the scheduled voice chat?";
"LiveStream.CancelConfirmationText" = "Do you want to abort the scheduled live stream?";
"VoiceChat.CancelConfirmationEnd" = "Abort";
"ScheduledIn.Seconds_1" = "%@ second";
@ -6588,11 +6639,48 @@ Sorry for the inconvenience.";
"Conversation.ForwardOptions.Text" = "What whould you like to do with %1$@ from %2$@?";
"Conversation.ForwardOptions.TextPersonal" = "What whould you like to do with %1$@ from your chat with %2$@?";
"Conversation.ForwardOptions.ForwardToAnotherChat" = "Forward to Another Chat";
"Conversation.ForwardOptions.ShowOptions" = "Show Forwarding Options";
"Conversation.ForwardOptions.CancelForwarding" = "Cancel Forwarding";
"Conversation.ForwardOptions.HideSendersName" = "Hide Sender's Name";
"Conversation.ForwardOptions.ShowSendersName" = "Show Sender's Name";
"Conversation.ForwardOptions.HideSendersNames" = "Hide Senders' Names";
"Conversation.ForwardOptions.ShowSendersNames" = "Show Senders' Names";
"Conversation.ForwardOptions.ShowCaption" = "Show Captions";
"Conversation.ForwardOptions.HideCaption" = "Hide Captions";
"Conversation.ForwardOptions.ChangeRecipient" = "Change Recipient";
"Conversation.ForwardOptions.SendMessage" = "Send Message";
"Conversation.ForwardOptions.SendMessages" = "Send Messages";
"Conversation.ForwardOptions.TapForOptions" = "Tap here for forwarding options";
"Conversation.ForwardOptions.TapForOptionsShort" = "Tap here for options";
"Conversation.ForwardOptions.UserMessageForwardVisible" = "%@ will see that it was forwarded";
"Conversation.ForwardOptions.UserMessageForwardHidden" = "%@ won't see that it was forwarded";
"Conversation.ForwardOptions.UserMessagesForwardVisible" = "%@ will see they were forwarded";
"Conversation.ForwardOptions.UserMessagesForwardHidden" = "%@ won't see they were forwarded";
"Conversation.ForwardOptions.GroupMessageForwardVisible" = "Members will see that it was forwarded";
"Conversation.ForwardOptions.GroupMessageForwardHidden" = "Members won't see that it was forwarded";
"Conversation.ForwardOptions.GroupMessagesForwardVisible" = "Members will see they were forwarded";
"Conversation.ForwardOptions.GroupMessagesForwardHidden" = "Members won't see they were forwarded";
"Conversation.ForwardOptions.ChannelMessageForwardVisible" = "Subscribers will see that it was forwarded";
"Conversation.ForwardOptions.ChannelMessageForwardHidden" = "Subscribers won't see that it was forwarded";
"Conversation.ForwardOptions.ChannelMessagesForwardVisible" = "Subscribers will see they were forwarded";
"Conversation.ForwardOptions.ChannelMessagesForwardHidden" = "Subscribers won't see they were forwarded";
"Conversation.ForwardOptions.ForwardTitleSingle" = "Forward Message";
"Conversation.ForwardOptions.ForwardTitle_1" = "Forward %@ Message";
"Conversation.ForwardOptions.ForwardTitle_2" = "Forward %@ Messages";
"Conversation.ForwardOptions.ForwardTitle_3_10" = "Forward %@ Messages";
"Conversation.ForwardOptions.ForwardTitle_any" = "Forward %@ Messages";
"Conversation.ForwardOptions.ForwardTitle_many" = "Forward %@ Messages";
"Conversation.ForwardOptions.ForwardTitle_0" = "Forward %@ Messages";
"Conversation.ForwardOptions.Title_1" = "%@ Message";
"Conversation.ForwardOptions.Title_2" = "%@ Messages";
"Conversation.ForwardOptions.Title_3_10" = "%@ Messages";
@ -6607,9 +6695,9 @@ Sorry for the inconvenience.";
"Conversation.ForwardOptions.Messages_many" = "%@ messages";
"Conversation.ForwardOptions.Messages_0" = "%@ messages";
"Conversation.ForwardOptions.You" = "You (senders' names hidden)";
"Activity.ChoosingSticker" = "choosing sticker";
"DialogList.SingleChoosingStickerSuffix" = "%@ is choosing sticker";
"WallpaperPreview.Animate" = "Animate";
"WallpaperPreview.AnimateDescription" = "Colors will move when you send messages";
@ -6637,6 +6725,9 @@ Sorry for the inconvenience.";
"Notification.YouChangedTheme" = "You changed chat theme to %@";
"Notification.YouDisabledTheme" = "You disabled chat theme";
"Notification.ChannelChangedTheme" = "Channel theme changed to %1$@";
"Notification.ChannelDisabledTheme" = "Channel theme disabled";
"Appstore.Cloud" = "**Cloud-based**\nUnlimited storage for chats,\nmedia and documents.";
"Appstore.Cloud.Profile" = "**Jennifer**\n23 y.o. designer from San Francisco.";
"Appstore.Creative" = "**Creative**\nColor themes, stickers, GIFs,\nvideo messages and more.";
@ -6668,3 +6759,37 @@ Sorry for the inconvenience.";
"Appstore.Secure" = "**Secure**\nAll chats are protected\nwith strong encryption.";
"Appstore.Secure.Chat" = "**Little Sister**\nAny gift ideas for mom?\n**You**A dog!\n**You**I'm serious. Let's get her a puppy. \n**You**\nI saw this!\n**Little Sister**\nI needed proof this was your idea!";
"Appstore.Secure.Chat.Name" = "**Little Sister**";
"Conversation.ReplyMessagePanelTitle" = "Reply to %@";
"Channel.AdminLog.MessageChangedThemeSet" = "%1$@ changed chat theme to %2$@";
"Channel.AdminLog.MessageChangedThemeRemove" = "%1$@ disabled chat theme";
"SponsoredMessageMenu.Info" = "What are sponsored\nmessages?";
"SponsoredMessageInfo.Text" = "See https://telegram.org";
"SponsoredMessageInfo.Action" = "Learn More";
"SponsoredMessageInfo.ActionUrl" = "https://telegram.org";
"Chat.NavigationNoChannels" = "You have no unread channels";
"Message.SponsoredLabel" = "sponsored";
"Stickers.Favorites" = "Favorites";
"Stickers.Recent" = "Recent";
"Stickers.Stickers" = "Stickers";
"Stickers.Gifs" = "GIFs";
"Stickers.Trending" = "Trending";
"Stickers.Settings" = "Settings";
"Gif.Emotion.Angry" = "Angry";
"Gif.Emotion.Surprised" = "Surprised";
"Gif.Emotion.Joy" = "Joy";
"Gif.Emotion.Kiss" = "Kiss";
"Gif.Emotion.Hearts" = "Hearts";
"Gif.Emotion.ThumbsUp" = "Thumbs Up";
"Gif.Emotion.ThumbsDown" = "Thumbs Down";
"Gif.Emotion.RollEyes" = "Roll-Eyes";
"Gif.Emotion.Cool" = "Cool";
"Gif.Emotion.Party" = "Party";
"Conversation.ForwardFrom" = "From: %@";

View File

@ -130,6 +130,37 @@ public enum ChatControllerInteractionNavigateToPeer {
case withBotStartPayload(ChatControllerInitialBotStart)
}
public struct ChatInterfaceForwardOptionsState: Codable, Equatable {
public var hideNames: Bool
public var hideCaptions: Bool
public var unhideNamesOnCaptionChange: Bool
public static func ==(lhs: ChatInterfaceForwardOptionsState, rhs: ChatInterfaceForwardOptionsState) -> Bool {
return lhs.hideNames == rhs.hideNames && lhs.hideCaptions == rhs.hideCaptions && lhs.unhideNamesOnCaptionChange == rhs.unhideNamesOnCaptionChange
}
public init(hideNames: Bool, hideCaptions: Bool, unhideNamesOnCaptionChange: Bool) {
self.hideNames = hideNames
self.hideCaptions = hideCaptions
self.unhideNamesOnCaptionChange = unhideNamesOnCaptionChange
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.hideNames = (try? container.decodeIfPresent(Bool.self, forKey: "hn")) ?? false
self.hideCaptions = (try? container.decodeIfPresent(Bool.self, forKey: "hc")) ?? false
self.unhideNamesOnCaptionChange = false
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.hideNames, forKey: "hn")
try container.encode(self.hideCaptions, forKey: "hc")
}
}
public struct ChatTextInputState: Codable, Equatable {
public let inputText: NSAttributedString
public let selectionRange: Range<Int>
@ -328,9 +359,49 @@ public struct ChatTextInputStateText: Codable, Equatable {
}
public enum ChatControllerSubject: Equatable {
public struct ForwardOptions: Equatable {
public let hideNames: Bool
public let hideCaptions: Bool
public init(hideNames: Bool, hideCaptions: Bool) {
self.hideNames = hideNames
self.hideCaptions = hideCaptions
}
}
case message(id: EngineMessage.Id, highlight: Bool, timecode: Double?)
case scheduledMessages
case pinnedMessages(id: EngineMessage.Id?)
case forwardedMessages(ids: [EngineMessage.Id], options: Signal<ForwardOptions, NoError>)
public static func ==(lhs: ChatControllerSubject, rhs: ChatControllerSubject) -> Bool {
switch lhs {
case let .message(lhsId, lhsHighlight, lhsTimecode):
if case let .message(rhsId, rhsHighlight, rhsTimecode) = rhs, lhsId == rhsId && lhsHighlight == rhsHighlight && lhsTimecode == rhsTimecode {
return true
} else {
return false
}
case .scheduledMessages:
if case .scheduledMessages = rhs {
return true
} else {
return false
}
case let .pinnedMessages(id):
if case .pinnedMessages(id) = rhs {
return true
} else {
return false
}
case let .forwardedMessages(lhsIds, _):
if case let .forwardedMessages(rhsIds, _) = rhs, lhsIds == rhsIds {
return true
} else {
return false
}
}
}
}
public enum ChatControllerPresentationMode: Equatable {

View File

@ -6,6 +6,7 @@ import SwiftSignalKit
import Display
import AsyncDisplayKit
import UniversalMediaPlayer
import TelegramPresentationData
public enum ChatControllerInteractionOpenMessageMode {
case `default`
@ -18,6 +19,7 @@ public enum ChatControllerInteractionOpenMessageMode {
public final class OpenChatMessageParams {
public let context: AccountContext
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
public let chatLocation: ChatLocation?
public let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>?
public let message: Message
@ -44,6 +46,7 @@ public final class OpenChatMessageParams {
public init(
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
chatLocation: ChatLocation?,
chatLocationContextHolder: Atomic<ChatLocationContextHolder?>?,
message: Message,
@ -69,6 +72,7 @@ public final class OpenChatMessageParams {
centralItemUpdated: ((MessageId) -> Void)? = nil
) {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.chatLocation = chatLocation
self.chatLocationContextHolder = chatLocationContextHolder
self.message = message

View File

@ -66,7 +66,7 @@ public enum PeerSelectionControllerSendMode {
public protocol PeerSelectionController: ViewController {
var peerSelected: ((Peer) -> Void)? { get set }
var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode, Bool) -> Void)? { get set }
var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode, ChatInterfaceForwardOptionsState?) -> Void)? { get set }
var inProgress: Bool { get set }
var customDismiss: (() -> Void)? { get set }
}

View File

@ -44,8 +44,6 @@ public final class AnimationNode : ASDisplayNode {
}
}
view.logHierarchyKeypaths()
return view
} else {
return LOTAnimationView()

View File

@ -264,7 +264,7 @@ public final class ChatInterfaceState: Codable, Equatable {
public let composeDisableUrlPreview: String?
public let replyMessageId: EngineMessage.Id?
public let forwardMessageIds: [EngineMessage.Id]?
public let forwardMessageHideSendersNames: Bool
public let forwardOptionsState: ChatInterfaceForwardOptionsState?
public let editMessage: ChatEditMessageState?
public let selectionState: ChatInterfaceSelectionState?
public let messageActionsState: ChatInterfaceMessageActionsState
@ -307,7 +307,7 @@ public final class ChatInterfaceState: Codable, Equatable {
self.composeDisableUrlPreview = nil
self.replyMessageId = nil
self.forwardMessageIds = nil
self.forwardMessageHideSendersNames = false
self.forwardOptionsState = nil
self.editMessage = nil
self.selectionState = nil
self.messageActionsState = ChatInterfaceMessageActionsState()
@ -317,13 +317,13 @@ public final class ChatInterfaceState: Codable, Equatable {
self.inputLanguage = nil
}
public init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreview: String?, replyMessageId: EngineMessage.Id?, forwardMessageIds: [EngineMessage.Id]?, forwardMessageHideSendersNames: Bool, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState, historyScrollState: ChatInterfaceHistoryScrollState?, mediaRecordingMode: ChatTextInputMediaRecordingButtonMode, silentPosting: Bool, inputLanguage: String?) {
public init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreview: String?, replyMessageId: EngineMessage.Id?, forwardMessageIds: [EngineMessage.Id]?, forwardOptionsState: ChatInterfaceForwardOptionsState?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState, historyScrollState: ChatInterfaceHistoryScrollState?, mediaRecordingMode: ChatTextInputMediaRecordingButtonMode, silentPosting: Bool, inputLanguage: String?) {
self.timestamp = timestamp
self.composeInputState = composeInputState
self.composeDisableUrlPreview = composeDisableUrlPreview
self.replyMessageId = replyMessageId
self.forwardMessageIds = forwardMessageIds
self.forwardMessageHideSendersNames = forwardMessageHideSendersNames
self.forwardOptionsState = forwardOptionsState
self.editMessage = editMessage
self.selectionState = selectionState
self.messageActionsState = messageActionsState
@ -360,7 +360,11 @@ public final class ChatInterfaceState: Codable, Equatable {
} else {
self.forwardMessageIds = nil
}
self.forwardMessageHideSendersNames = ((try? container.decodeIfPresent(Int32.self, forKey: "fhn")) ?? 0) != 0
if let forwardOptionsState = try? container.decodeIfPresent(ChatInterfaceForwardOptionsState.self, forKey: "fo") {
self.forwardOptionsState = forwardOptionsState
} else {
self.forwardOptionsState = nil
}
if let editMessage = try? container.decodeIfPresent(ChatEditMessageState.self, forKey: "em") {
self.editMessage = editMessage
} else {
@ -410,7 +414,11 @@ public final class ChatInterfaceState: Codable, Equatable {
} else {
try container.encodeNil(forKey: "fm")
}
try container.encode((self.forwardMessageHideSendersNames ? 1 : 0) as Int32, forKey: "fhn")
if let forwardOptionsState = self.forwardOptionsState {
try container.encode(forwardOptionsState, forKey: "fo")
} else {
try container.encodeNil(forKey: "fo")
}
if let editMessage = self.editMessage {
try container.encode(editMessage, forKey: "em")
} else {
@ -451,7 +459,7 @@ public final class ChatInterfaceState: Codable, Equatable {
} else if (lhs.forwardMessageIds != nil) != (rhs.forwardMessageIds != nil) {
return false
}
if lhs.forwardMessageHideSendersNames != rhs.forwardMessageHideSendersNames {
if lhs.forwardOptionsState != rhs.forwardOptionsState {
return false
}
if lhs.messageActionsState != rhs.messageActionsState {
@ -475,11 +483,11 @@ public final class ChatInterfaceState: Codable, Equatable {
public func withUpdatedComposeInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
let updatedComposeInputState = inputState
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedComposeDisableUrlPreview(_ disableUrlPreview: String?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: disableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: disableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedEffectiveInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
@ -491,19 +499,19 @@ public final class ChatInterfaceState: Codable, Equatable {
updatedComposeInputState = inputState
}
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: updatedEditMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: updatedEditMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedReplyMessageId(_ replyMessageId: EngineMessage.Id?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedForwardMessageIds(_ forwardMessageIds: [EngineMessage.Id]?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedForwardMessageHideSendersNames(_ forwardMessageHideSendersNames: Bool) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
public func withUpdatedForwardOptionsState(_ forwardOptionsState: ChatInterfaceForwardOptionsState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedSelectedMessages(_ messageIds: [EngineMessage.Id]) -> ChatInterfaceState {
@ -514,7 +522,7 @@ public final class ChatInterfaceState: Codable, Equatable {
for messageId in messageIds {
selectedIds.insert(messageId)
}
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withToggledSelectedMessages(_ messageIds: [EngineMessage.Id], value: Bool) -> ChatInterfaceState {
@ -529,39 +537,39 @@ public final class ChatInterfaceState: Codable, Equatable {
selectedIds.remove(messageId)
}
}
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withoutSelectionState() -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: nil, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: nil, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedTimestamp(_ timestamp: Int32) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedEditMessage(_ editMessage: ChatEditMessageState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedMessageActionsState(_ f: (ChatInterfaceMessageActionsState) -> ChatInterfaceMessageActionsState) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: f(self.messageActionsState), historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: f(self.messageActionsState), historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedHistoryScrollState(_ historyScrollState: ChatInterfaceHistoryScrollState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedMediaRecordingMode(_ mediaRecordingMode: ChatTextInputMediaRecordingButtonMode) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedSilentPosting(_ silentPosting: Bool) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: silentPosting, inputLanguage: self.inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: silentPosting, inputLanguage: self.inputLanguage)
}
public func withUpdatedInputLanguage(_ inputLanguage: String?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardMessageHideSendersNames: self.forwardMessageHideSendersNames, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: inputLanguage)
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: inputLanguage)
}
public static func parse(_ state: OpaqueChatInterfaceState) -> ChatInterfaceState {

View File

@ -891,7 +891,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}).start()
let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled], multipleSelection: true))
peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode, hideSendersNames in
peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode, forwardOptions in
guard let strongSelf = self, let strongController = peerSelectionController else {
return
}
@ -913,9 +913,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
var attributes: [MessageAttribute] = []
if hideSendersNames {
attributes.append(ForwardHideSendersNamesMessageAttribute())
}
attributes.append(ForwardOptionsMessageAttribute(hideNames: forwardOptions?.hideNames == true, hideCaptions: forwardOptions?.hideCaptions == true))
result.append(contentsOf: messageIds.map { messageId -> EnqueueMessage in
return .forward(source: messageId, grouping: .auto, attributes: attributes, correlationId: nil)

View File

@ -1725,7 +1725,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
})
}
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true)
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, presentationData: self.presentationData, displayBackground: true)
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
mediaAccessoryPanel.getController = { [weak self] in
return self?.navigationController?.topViewController as? ViewController

View File

@ -104,7 +104,7 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
case .typingText:
text = strings.DialogList_SingleTypingSuffix(peerTitle).string
case .choosingSticker:
text = ""
text = strings.DialogList_SingleChoosingStickerSuffix(peerTitle).string
case .speakingInGroupCall:
text = ""
}

View File

@ -90,10 +90,6 @@ private class ChatChoosingStickerActivityIndicatorNode: ChatTitleActivityIndicat
context.strokeEllipse(in: CGRect(x: rightCenter.x - eyeWidth / 2.0, y: rightCenter.y - eyeHeight / 2.0, width: eyeWidth, height: eyeHeight))
context.fillEllipse(in: CGRect(x: rightCenter.x - pupilSize / 2.0 + pupilCenter.x * eyeWidth / 4.0, y: rightCenter.y - pupilSize / 2.0, width: pupilSize, height: pupilSize))
// context.strokeEllipse(in: CGRect(x: 0.0, y: 0.0, width: 10.0, height: 20.0))
// context.fillEllipse(in: CGRect(x: , y: , width: 4.0, height: 4.0))
}
}
@ -105,7 +101,7 @@ class ChatChoosingStickerActivityContentNode: ChatTitleActivityContentNode {
self.indicatorNode = ChatChoosingStickerActivityIndicatorNode(color: color)
var text = text
self.advanced = text.string == "choosing sticker"
self.advanced = text.string == "choosing a sticker"
if self.advanced {
let mutable = text.mutableCopy() as? NSMutableAttributedString
mutable?.replaceCharacters(in: NSMakeRange(2, 2), with: " ")
@ -121,6 +117,7 @@ class ChatChoosingStickerActivityContentNode: ChatTitleActivityContentNode {
override func updateLayout(_ constrainedSize: CGSize, offset: CGFloat, alignment: NSTextAlignment) -> CGSize {
let size = self.textNode.updateLayout(constrainedSize)
let scale = size.height / 15.0
let indicatorSize = CGSize(width: 24.0, height: 16.0)
let originX: CGFloat
let indicatorOriginX: CGFloat
@ -131,15 +128,30 @@ class ChatChoosingStickerActivityContentNode: ChatTitleActivityContentNode {
originX = floorToScreenPixels((indicatorSize.width - size.width) / 2.0)
}
} else {
originX = indicatorSize.width
if self.advanced {
originX = 4.0
} else {
originX = indicatorSize.width * scale - 1.0
}
}
self.textNode.frame = CGRect(origin: CGPoint(x: originX, y: 0.0), size: size)
if self.advanced {
indicatorOriginX = self.textNode.frame.minX + 14.0 + UIScreenPixel
if case .center = alignment {
indicatorOriginX = self.textNode.frame.minX + 26.0 + UIScreenPixel
} else {
var scale = scale
if scale > 1.25 {
scale *= 0.95
}
indicatorOriginX = self.textNode.frame.minX + floorToScreenPixels(26.0 * scale) + UIScreenPixel
}
} else {
indicatorOriginX = self.textNode.frame.minX - indicatorSize.width
indicatorOriginX = self.textNode.frame.minX - (indicatorSize.width * scale) / 2.0 + 3.0
}
self.indicatorNode.frame = CGRect(origin: CGPoint(x: indicatorOriginX, y: 0.0), size: indicatorSize)
self.indicatorNode.bounds = CGRect(origin: CGPoint(), size: indicatorSize)
self.indicatorNode.position = CGPoint(x: indicatorOriginX, y: size.height / 2.0)
self.indicatorNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
return CGSize(width: size.width + indicatorSize.width, height: size.height)
}
}

View File

@ -12,6 +12,7 @@ private let animationDurationFactor: Double = 1.0
public protocol ContextControllerProtocol {
var useComplexItemsTransitionAnimation: Bool { get set }
var immediateItemsTransitionAnimation: Bool { get set }
func setItems(_ items: Signal<[ContextMenuItem], NoError>)
func dismiss(completion: (() -> Void)?)
@ -119,6 +120,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private let reactionSelected: (ReactionContextItem.Reaction) -> Void
private let beganAnimatingOut: () -> Void
private let attemptTransitionControllerIntoNavigation: () -> Void
fileprivate var dismissedForCancel: (() -> Void)?
private let getController: () -> ContextControllerProtocol?
private weak var gesture: ContextGesture?
private var displayTextSelectionTip: Bool
@ -157,6 +159,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private let hapticFeedback = HapticFeedback()
private var animatedIn = false
private var isAnimatingOut = false
private let itemsDisposable = MetaDisposable()
@ -477,6 +480,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
@objc private func dimNodeTapped() {
guard self.animatedIn else {
return
}
self.dismissedForCancel?()
self.beginDismiss(.default)
}
@ -649,7 +656,9 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
let contentContainerOffset = CGPoint(x: localContentSourceFrame.center.x - self.contentContainerNode.frame.center.x, y: localContentSourceFrame.center.y - self.contentContainerNode.frame.center.y)
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true, completion: { [weak self] _ in
self?.animatedIn = true
})
}
case let .extracted(extracted, keepInPlace):
let springDuration: Double = 0.42 * animationDurationFactor
@ -689,6 +698,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
let contentContainerOffset = CGPoint(x: localContentSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localContentSourceFrame.center.y - self.contentContainerNode.frame.center.y - contentParentNode.contentRect.minY)
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: contentDuration, initialVelocity: 0.0, damping: springDamping, additive: true, completion: { [weak self] _ in
self?.clippingNode.view.mask = nil
self?.animatedIn = true
})
contentParentNode.applyAbsoluteOffsetSpring?(-contentContainerOffset.y, springDuration, springDamping)
}
@ -732,7 +742,9 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
}
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true, completion: { [weak self] _ in
self?.animatedIn = true
})
}
}
}
@ -1164,6 +1176,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
private func setItems(items: [ContextMenuItem]) {
if let _ = self.currentItems, !self.didCompleteAnimationIn && self.getController()?.immediateItemsTransitionAnimation == true {
return
}
self.currentItems = items
let previousActionsContainerNode = self.actionsContainerNode
@ -1600,7 +1616,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
if let previousActionsContainerNode = previousActionsContainerNode {
if transition.isAnimated {
if transition.isAnimated && self.getController()?.immediateItemsTransitionAnimation == false {
if previousActionsContainerNode.hasAdditionalActions && !self.actionsContainerNode.hasAdditionalActions && self.getController()?.useComplexItemsTransitionAnimation == true {
var initialFrame = self.actionsContainerNode.frame
let delta = (previousActionsContainerNode.frame.height - self.actionsContainerNode.frame.height)
@ -1715,9 +1731,17 @@ public final class ContextControllerReferenceViewInfo {
}
public protocol ContextReferenceContentSource: AnyObject {
var shouldBeDismissed: Signal<Bool, NoError> { get }
func transitionInfo() -> ContextControllerReferenceViewInfo?
}
public extension ContextReferenceContentSource {
var shouldBeDismissed: Signal<Bool, NoError> {
return .single(false)
}
}
public final class ContextControllerTakeViewInfo {
public let contentContainingNode: ContextExtractedContentContainingNode
public let contentAreaInScreenSpace: CGRect
@ -1812,8 +1836,14 @@ public final class ContextController: ViewController, StandalonePresentableContr
public var reactionSelected: ((ReactionContextItem.Reaction) -> Void)?
public var dismissed: (() -> Void)?
public var dismissedForCancel: (() -> Void)? {
didSet {
self.controllerNode.dismissedForCancel = self.dismissedForCancel
}
}
public var useComplexItemsTransitionAnimation = false
public var immediateItemsTransitionAnimation = false
private var shouldBeDismissedDisposable: Disposable?
@ -1830,8 +1860,18 @@ public final class ContextController: ViewController, StandalonePresentableContr
super.init(navigationBarPresentationData: nil)
switch source {
case .reference:
case let .reference(referenceSource):
self.statusBar.statusBarStyle = .Ignore
self.shouldBeDismissedDisposable = (referenceSource.shouldBeDismissed
|> filter { $0 }
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.dismiss(result: .default, completion: {})
})
case let .extracted(extractedSource):
if extractedSource.blurBackground {
self.statusBar.statusBarStyle = .Hide
@ -1866,7 +1906,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
override public func loadDisplayNode() {
self.displayNode = ContextControllerNode(account: self.account, controller: self, presentationData: self.presentationData, source: self.source, items: self.items, reactionItems: self.reactionItems, beginDismiss: { [weak self] result in
self?.dismiss(result: result, completion: nil)
}, recognizer: self.recognizer, gesture: self.gesture, reactionSelected: { [weak self] value in
}, recognizer: self.recognizer, gesture: self.gesture, reactionSelected: { [weak self] value in
guard let strongSelf = self else {
return
}
@ -1887,7 +1927,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
break
}
}, displayTextSelectionTip: self.displayTextSelectionTip)
self.controllerNode.dismissedForCancel = self.dismissedForCancel
self.displayNodeDidLoad()
self._ready.set(combineLatest(queue: .mainQueue(), self.controllerNode.itemsReady.get(), self.controllerNode.contentReady.get())

View File

@ -32,6 +32,7 @@ extension PeekControllerTheme {
public final class PeekController: ViewController, ContextControllerProtocol {
public var useComplexItemsTransitionAnimation: Bool = false
public var immediateItemsTransitionAnimation = false
public func setItems(_ items: Signal<[ContextMenuItem], NoError>) {

View File

@ -142,7 +142,11 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode {
}
}
case .removeFromGroup:
text = strings.VoiceChat_RemoveAndBanPeerConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder), chatPeer.displayTitle(strings: strings, displayOrder: nameOrder))
if case let .channel(channel) = chatPeer, case .broadcast = channel.info {
text = strings.LiveStream_RemoveAndBanPeerConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder), chatPeer.displayTitle(strings: strings, displayOrder: nameOrder))
} else {
text = strings.VoiceChat_RemoveAndBanPeerConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder), chatPeer.displayTitle(strings: strings, displayOrder: nameOrder))
}
case .removeFromChannel:
text = strings.VoiceChat_RemovePeerConfirmationChannel(peer.displayTitle(strings: strings, displayOrder: nameOrder))
default:

View File

@ -328,6 +328,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
private var reorderInProgress: Bool = false
private var reorderingItemsCompleted: (() -> Void)?
private var reorderScrollStartTimestamp: Double?
private var reorderScrollUpdateTimestamp: Double?
private var reorderLastTimestamp: Double?
public var reorderedItemHasShadow = true
private let waitingForNodesDisposable = MetaDisposable()
@ -562,14 +564,22 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
return
}
let timestamp = CACurrentMediaTime()
if let reorderItemNode = reorderNode.itemNode, let reorderItemIndex = reorderItemNode.index, reorderItemNode.supernode == self {
let verticalOffset = verticalTopOffset
var closestIndex: (Int, CGFloat)?
for i in 0 ..< self.itemNodes.count {
if let itemNodeIndex = self.itemNodes[i].index, itemNodeIndex != reorderItemIndex {
let itemFrame = self.itemNodes[i].apparentContentFrame
let itemOffset = itemFrame.midY
let deltaOffset = itemOffset - verticalOffset
let offsetToMin = itemFrame.minY - verticalOffset
let offsetToMax = itemFrame.maxY - verticalOffset
let deltaOffset: CGFloat
if abs(offsetToMin) > abs(offsetToMax) {
deltaOffset = offsetToMax
} else {
deltaOffset = offsetToMin
}
if let (_, closestOffset) = closestIndex {
if abs(deltaOffset) < abs(closestOffset) {
closestIndex = (itemNodeIndex, deltaOffset)
@ -580,7 +590,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
}
if let (closestIndexValue, offset) = closestIndex {
//print("closest \(closestIndexValue) offset \(offset)")
// print("closest \(closestIndexValue) offset \(offset)")
var toIndex: Int
if offset > 0 {
toIndex = closestIndexValue
@ -594,7 +604,12 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
}
if toIndex != reorderItemNode.index {
if let reorderLastTimestamp = self.reorderLastTimestamp, timestamp < reorderLastTimestamp + 0.2 {
return
}
if reorderNode.currentState?.0 != reorderItemIndex || reorderNode.currentState?.1 != toIndex {
self.reorderLastTimestamp = timestamp
reorderNode.currentState = (reorderItemIndex, toIndex)
//print("reorder \(reorderItemIndex) to \(toIndex) offset \(offset)")
if self.reorderFeedbackDisposable == nil {
@ -2126,7 +2141,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
if let accessoryItemNode = node.accessoryItemNode {
node.layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
apply().1(ListViewItemApply(isOnScreen: visibleBounds.intersects(nodeFrame)))
apply().1(ListViewItemApply(isOnScreen: visibleBounds.intersects(nodeFrame), timestamp: timestamp))
self.itemNodes.insert(node, at: nodeIndex)
var offsetHeight = node.apparentHeight
@ -2548,7 +2563,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
var apparentFrame = node.apparentFrame
apparentFrame.size.height = updatedApparentHeight
apply().1(ListViewItemApply(isOnScreen: visibleBounds.intersects(apparentFrame)))
apply().1(ListViewItemApply(isOnScreen: visibleBounds.intersects(apparentFrame), timestamp: timestamp))
var offsetRanges = OffsetRanges()
@ -3981,7 +3996,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
var offset: CGFloat = 6.0
if let reorderScrollStartTimestamp = self.reorderScrollStartTimestamp, reorderScrollStartTimestamp + 2.0 < timestamp {
offset *= 2.0
offset *= 1.5
}
if reorderOffset < effectiveInsets.top + 10.0 {
if self.itemNodes[0].apparentFrame.minY < effectiveInsets.top {
@ -4086,7 +4101,13 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
self.enqueueUpdateVisibleItems(synchronous: false)
}
self.checkItemReordering()
if scrollingForReorder {
if let reorderScrollUpdateTimestamp = self.reorderScrollUpdateTimestamp, timestamp < reorderScrollUpdateTimestamp + 0.05 {
return
}
self.reorderScrollUpdateTimestamp = timestamp
self.checkItemReordering(force: true)
}
}
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

View File

@ -32,9 +32,11 @@ public struct ListViewItemConfigureNodeFlags: OptionSet {
public struct ListViewItemApply {
public let isOnScreen: Bool
public let timestamp: Double?
public init(isOnScreen: Bool) {
public init(isOnScreen: Bool, timestamp: Double? = nil) {
self.isOnScreen = isOnScreen
self.timestamp = timestamp
}
}

View File

@ -47,7 +47,7 @@ public final class ListViewReorderingGestureRecognizer: UIGestureRecognizer {
private func startLongPressTimer() {
self.longPressTimer?.invalidate()
let longPressTimer = SwiftSignalKit.Timer(timeout: 0.8, repeat: false, completion: { [weak self] in
let longPressTimer = SwiftSignalKit.Timer(timeout: 0.6, repeat: false, completion: { [weak self] in
self?.longPressTimerFired()
}, queue: Queue.mainQueue())
self.longPressTimer = longPressTimer

View File

@ -136,7 +136,7 @@ public extension String {
continue
}
string.unicodeScalars.append(scalar)
if scalar.value == 0x2764, self.unicodeScalars.count > 1, self.emojis.count == 1 {
if scalar.value == 0x2764 && self.unicodeScalars.count < 3 {
break
}
}

View File

@ -30,7 +30,7 @@ objc_library(
]),
copts = [
"-I{}/PublicHeaders/LegacyComponents".format(package_name()),
"-Werror",
#"-Werror",
],
includes = [
"PublicHeaders",

View File

@ -322,7 +322,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
#pragma mark - Properties
- (bool)isTracking
- (BOOL)isTracking
{
return _knobView.highlighted;
}

View File

@ -6,7 +6,7 @@
@implementation UICollectionView (TGTransitioning)
- (BOOL)isTransitionInProgress
- (bool)isTransitionInProgress
{
return ([self tg_transitionData] != nil);
}

View File

@ -70,11 +70,11 @@ public final class LocationPickerController: ViewController {
private var interaction: LocationPickerInteraction?
public init(context: AccountContext, mode: LocationPickerMode, completion: @escaping (TelegramMediaMap, String?) -> Void) {
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: LocationPickerMode, completion: @escaping (TelegramMediaMap, String?) -> Void) {
self.context = context
self.mode = mode
self.completion = completion
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme).withUpdatedSeparatorColor(.clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
@ -85,7 +85,7 @@ public final class LocationPickerController: ViewController {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.searchPressed))
self.navigationItem.rightBarButtonItem?.accessibilityLabel = self.presentationData.strings.Common_Search
self.presentationDataDisposable = (context.sharedContext.presentationData
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
guard let strongSelf = self, strongSelf.presentationData.theme !== presentationData.theme else {
return

View File

@ -85,12 +85,12 @@ public final class LocationViewController: ViewController {
private var rightBarButtonAction: LocationViewRightBarButton = .none
public init(context: AccountContext, subject: Message, params: LocationViewParams) {
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, subject: Message, params: LocationViewParams) {
self.context = context
self.subject = subject
self.showAll = params.showAll
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme).withUpdatedSeparatorColor(.clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
@ -99,7 +99,7 @@ public final class LocationViewController: ViewController {
self.title = self.presentationData.strings.Map_LocationTitle
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Close, style: .plain, target: self, action: #selector(self.cancelPressed))
self.presentationDataDisposable = (context.sharedContext.presentationData
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
guard let strongSelf = self, strongSelf.presentationData.theme !== presentationData.theme else {
return

View File

@ -293,3 +293,22 @@ public final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepre
}
}
}
public final class CachedPreparedPatternWallpaperRepresentation: CachedMediaResourceRepresentation {
public let keepDuration: CachedMediaRepresentationKeepDuration = .general
public var uniqueId: String {
return "prepared-pattern-wallpaper"
}
public init() {
}
public func isEqual(to: CachedMediaResourceRepresentation) -> Bool {
if to is CachedPreparedPatternWallpaperRepresentation {
return true
} else {
return false
}
}
}

View File

@ -421,11 +421,11 @@ static int32_t fixedTimeDifferenceValue = 0;
_cleanupSessionIdsByAuthKeyId = [[NSMutableDictionary alloc] initWithDictionary:cleanupSessionIdsByAuthKeyId];
if (MTLogEnabled()) {
MTLog(@"[MTContext#%llx: received keychain globalTimeDifference:%f datacenterAuthInfoById:%@]", (intptr_t)self, _globalTimeDifference, _datacenterAuthInfoById);
MTLog(@"[MTContext#%" PRIxPTR ": received keychain globalTimeDifference:%f datacenterAuthInfoById:%@]", (intptr_t)self, _globalTimeDifference, _datacenterAuthInfoById);
}
} else {
if (MTLogEnabled()) {
MTLog(@"[MTContext#%llx: received keychain nil]", (intptr_t)self);
MTLog(@"[MTContext#%" PRIxPTR ": received keychain nil]", (intptr_t)self);
}
}
}];
@ -484,7 +484,7 @@ static int32_t fixedTimeDifferenceValue = 0;
_globalTimeDifference = globalTimeDifference;
if (MTLogEnabled()) {
MTLog(@"[MTContext#%llx: global time difference changed: %.1fs]", (intptr_t)self, globalTimeDifference);
MTLog(@"[MTContext#%" PRIxPTR ": global time difference changed: %.1fs]", (intptr_t)self, globalTimeDifference);
}
[_keychain setObject:@(_globalTimeDifference) forKey:@"globalTimeDifference" group:@"temp"];
@ -506,7 +506,7 @@ static int32_t fixedTimeDifferenceValue = 0;
if (addressSet != nil && datacenterId != 0)
{
if (MTLogEnabled()) {
MTLog(@"[MTContext#%llx: address set updated for %d]", (intptr_t)self, datacenterId);
MTLog(@"[MTContext#%" PRIxPTR ": address set updated for %d]", (intptr_t)self, datacenterId);
}
bool updateSchemes = forceUpdateSchemes;
@ -599,7 +599,7 @@ static int32_t fixedTimeDifferenceValue = 0;
if (updated)
{
if (MTLogEnabled()) {
MTLog(@"[MTContext#%llx: added address %@ for datacenter %d]", (intptr_t)self, address, datacenterId);
MTLog(@"[MTContext#%" PRIxPTR ": added address %@ for datacenter %d]", (intptr_t)self, address, datacenterId);
}
_datacenterAddressSetById[@(datacenterId)] = addressSet;
@ -638,7 +638,7 @@ static int32_t fixedTimeDifferenceValue = 0;
if (MTLogEnabled()) {
MTDatacenterAuthInfo *persistentInfo = _datacenterAuthInfoById[authInfoMapIntegerKey((int32_t)datacenterId, MTDatacenterAuthInfoSelectorPersistent)];
MTLog(@"[MTContext#%llx: auth info updated for %d selector %d to %@ (persistent key id is %llu)]", (intptr_t)self, datacenterId, selector, authInfo, persistentInfo.authKeyId);
MTLog(@"[MTContext#%" PRIxPTR ": auth info updated for %d selector %d to %@ (persistent key id is %llu)]", (intptr_t)self, datacenterId, selector, authInfo, persistentInfo.authKeyId);
}
[_keychain setObject:_datacenterAuthInfoById forKey:@"datacenterAuthInfoById" group:@"persistent"];
@ -713,7 +713,7 @@ static int32_t fixedTimeDifferenceValue = 0;
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
if (MTLogEnabled()) {
MTLog(@"[MTContext#%llx: %@ transport scheme updated for %d: %@]", (intptr_t)self, media ? @"media" : @"generic", datacenterId, transportScheme);
MTLog(@"[MTContext#%" PRIxPTR ": %@ transport scheme updated for %d: %@]", (intptr_t)self, media ? @"media" : @"generic", datacenterId, transportScheme);
}
for (id<MTContextChangeListener> listener in currentListeners) {

View File

@ -117,7 +117,7 @@
if (request.requestContext.messageId != 0) {
if (MTLogEnabled()) {
MTLog(@"[MTRequestMessageService#%llx drop %" PRId64 "]", (intptr_t)self, request.requestContext.messageId);
MTLog(@"[MTRequestMessageService#%" PRIxPTR " drop %" PRId64 "]", (intptr_t)self, request.requestContext.messageId);
}
}
@ -933,8 +933,8 @@
request.requestContext.responseMessageId = responseMessageId;
return true;
} else {
MTLog(@"[MTRequestMessageService#%llx will not request message %" PRId64 " (transaction was not completed)]", (intptr_t)self, messageId);
MTLog(@"[MTRequestMessageService#%llx but today it will]", (intptr_t)self);
MTLog(@"[MTRequestMessageService#%" PRIxPTR " will not request message %" PRId64 " (transaction was not completed)]", (intptr_t)self, messageId);
MTLog(@"[MTRequestMessageService#%" PRIxPTR " but today it will]", (intptr_t)self);
return true;
}
}

View File

@ -861,14 +861,14 @@ struct ctr_state {
if (MTLogEnabled()) {
if (strongSelf->_socksIp != nil) {
if (strongSelf->_socksUsername.length == 0) {
MTLog(@"[MTTcpConnection#%llx connecting to %@:%d via %@:%d]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_socksIp, (int)strongSelf->_socksPort);
MTLog(@"[MTTcpConnection#%" PRIxPTR " connecting to %@:%d via %@:%d]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_socksIp, (int)strongSelf->_socksPort);
} else {
MTLog(@"[MTTcpConnection#%llx connecting to %@:%d via %@:%d using %@:%@]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_socksIp, (int)strongSelf->_socksPort, strongSelf->_socksUsername, strongSelf->_socksPassword);
MTLog(@"[MTTcpConnection#%" PRIxPTR " connecting to %@:%d via %@:%d using %@:%@]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_socksIp, (int)strongSelf->_socksPort, strongSelf->_socksUsername, strongSelf->_socksPassword);
}
} else if (strongSelf->_mtpIp != nil) {
MTLog(@"[MTTcpConnection#%llx connecting to %@:%d via mtp://%@:%d:%@]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_mtpIp, (int)strongSelf->_mtpPort, strongSelf->_mtpSecret);
MTLog(@"[MTTcpConnection#%" PRIxPTR " connecting to %@:%d via mtp://%@:%d:%@]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_mtpIp, (int)strongSelf->_mtpPort, strongSelf->_mtpSecret);
} else {
MTLog(@"[MTTcpConnection#%llx connecting to %@:%d]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port);
MTLog(@"[MTTcpConnection#%" PRIxPTR " connecting to %@:%d]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port);
}
}
@ -1280,7 +1280,7 @@ struct ctr_state {
_responseTimeoutTimer = nil;
if (MTLogEnabled()) {
MTLog(@"[MTTcpConnection#%llx response timeout]", (intptr_t)self);
MTLog(@"[MTTcpConnection#%" PRIxPTR " response timeout]", (intptr_t)self);
}
[self closeAndNotifyWithError:true];
}
@ -1421,7 +1421,7 @@ struct ctr_state {
if (resp.Reply != 0x00) {
if (MTLogEnabled()) {
MTLog(@"***** %llx %s: socks5 connect failed, error 0x%02x", (intptr_t)self, __PRETTY_FUNCTION__, resp.Reply);
MTLog(@"***** " PRIxPTR " %s: socks5 connect failed, error 0x%02x", (intptr_t)self, __PRETTY_FUNCTION__, resp.Reply);
}
[self closeAndNotifyWithError:true];
return;
@ -1772,7 +1772,7 @@ struct ctr_state {
} else {
if (length > 16 * 1024 * 1024) {
if (MTLogEnabled()) {
MTLog(@"[MTTcpConnection#%llx received invalid length %d]", (intptr_t)self, length);
MTLog(@"[MTTcpConnection#%" PRIxPTR " received invalid length %d]", (intptr_t)self, length);
}
[self closeAndNotifyWithError:true];
} else {
@ -1848,7 +1848,7 @@ struct ctr_state {
}
} else if (header == 0 && packetData.length < 16) {
if (MTLogEnabled()) {
MTLog(@"[MTTcpConnection#%llx received nop packet]", (intptr_t)self);
MTLog(@"[MTTcpConnection#%" PRIxPTR " received nop packet]", (intptr_t)self);
}
ignorePacket = true;
}
@ -1907,12 +1907,12 @@ struct ctr_state {
{
if (error != nil) {
if (MTLogEnabled()) {
MTLog(@"[MTTcpConnection#%llx disconnected from %@ (%@)]", (intptr_t)self, _scheme.address.ip, error);
MTLog(@"[MTTcpConnection#%" PRIxPTR " disconnected from %@ (%@)]", (intptr_t)self, _scheme.address.ip, error);
}
}
else {
if (MTLogEnabled()) {
MTLog(@"[MTTcpConnection#%llx disconnected from %@]", (intptr_t)self, _scheme.address.ip);
MTLog(@"[MTTcpConnection#%" PRIxPTR " disconnected from %@]", (intptr_t)self, _scheme.address.ip);
}
}

View File

@ -573,7 +573,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
if (!transportContext.didSendActualizationPingAfterConnection)
{
if (MTLogEnabled()) {
MTLog(@"[MTTcpTransport#%llx unlocking transaction processing due to connection context update task]", (intptr_t)self);
MTLog(@"[MTTcpTransport#%" PRIxPTR " unlocking transaction processing due to connection context update task]", (intptr_t)self);
}
transportContext.isWaitingForTransactionToBecomeReady = false;
transportContext.transactionLockTime = 0.0;
@ -581,7 +581,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
else if (CFAbsoluteTimeGetCurrent() > transportContext.transactionLockTime + 1.0)
{
if (MTLogEnabled()) {
MTLog(@"[MTTcpTransport#%llx unlocking transaction processing due to timeout]", (intptr_t)self);
MTLog(@"[MTTcpTransport#%" PRIxPTR " unlocking transaction processing due to timeout]", (intptr_t)self);
}
transportContext.isWaitingForTransactionToBecomeReady = false;
transportContext.transactionLockTime = 0.0;
@ -589,7 +589,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
else
{
if (MTLogEnabled()) {
MTLog(@"[MTTcpTransport#%llx skipping transaction request]", (intptr_t)self);
MTLog(@"[MTTcpTransport#%" PRIxPTR " skipping transaction request]", (intptr_t)self);
}
transportContext.requestAnotherTransactionWhenReady = true;

View File

@ -379,7 +379,7 @@ private struct ChannelAdminControllerState: Equatable {
}
}
private func stringForRight(strings: PresentationStrings, right: TelegramChatAdminRightsFlags, isGroup: Bool, defaultBannedRights: TelegramChatBannedRights?) -> String {
private func stringForRight(strings: PresentationStrings, right: TelegramChatAdminRightsFlags, isGroup: Bool, isChannel: Bool, defaultBannedRights: TelegramChatBannedRights?) -> String {
if right.contains(.canChangeInfo) {
return isGroup ? strings.Group_EditAdmin_PermissionChangeInfo : strings.Channel_EditAdmin_PermissionChangeInfo
} else if right.contains(.canPostMessages) {
@ -407,7 +407,11 @@ private func stringForRight(strings: PresentationStrings, right: TelegramChatAdm
} else if right.contains(.canBeAnonymous) {
return strings.Channel_AdminLog_CanBeAnonymous
} else if right.contains(.canManageCalls) {
return strings.Channel_AdminLog_CanManageCalls
if isChannel {
return strings.Channel_AdminLog_CanManageLiveStreams
} else {
return strings.Channel_AdminLog_CanManageCalls
}
} else {
return ""
}
@ -498,6 +502,11 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
if let channel = channelView.peers[channelView.peerId] as? TelegramChannel, let admin = adminView.peers[adminView.peerId] {
entries.append(.info(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, admin, adminView.peerPresences[admin.id] as? TelegramUserPresence))
var isChannel = false
if case .broadcast = channel.info {
isChannel = true
}
var isCreator = false
if let initialParticipant = initialParticipant, case .creator = initialParticipant {
@ -566,7 +575,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
var index = 0
for right in rightsOrder {
if accountUserRightsFlags.contains(right) {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), right == .canBeAnonymous))
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), right == .canBeAnonymous))
index += 1
}
}
@ -596,7 +605,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
var index = 0
for right in rightsOrder {
if accountUserRightsFlags.contains(right) {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating && admin.id != accountPeerId && !rightEnabledByDefault(channelPeer: channel, right: right)))
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating && admin.id != accountPeerId && !rightEnabledByDefault(channelPeer: channel, right: right)))
index += 1
}
}
@ -628,7 +637,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminInfo, _, _) = initialParticipant, let adminInfo = maybeAdminInfo {
var index = 0
for right in rightsOrder {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, defaultBannedRights: channel.defaultBannedRights), right, adminInfo.rights.rights, adminInfo.rights.rights.contains(right), false))
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, defaultBannedRights: channel.defaultBannedRights), right, adminInfo.rights.rights, adminInfo.rights.rights.contains(right), false))
index += 1
}
}
@ -683,6 +692,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
entries.append(.rightsTitle(presentationData.theme, presentationData.strings.Channel_EditAdmin_PermissionsHeader))
let isGroup = true
let isChannel = false
let maskRightsFlags: TelegramChatAdminRightsFlags = .groupSpecific
let rightsOrder: [TelegramChatAdminRightsFlags] = [
.canChangeInfo,
@ -714,7 +724,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
var index = 0
for right in rightsOrder {
if accountUserRightsFlags.contains(right) {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, defaultBannedRights: group.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating && accountIsCreator))
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, defaultBannedRights: group.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating && accountIsCreator))
index += 1
}
}

View File

@ -642,6 +642,14 @@ public final class Message {
self.associatedMessageIds = associatedMessageIds
}
public func withUpdatedText(_ text: String) -> Message {
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
}
public func withUpdatedTimestamp(_ timestamp: Int32) -> Message {
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
}
public func withUpdatedMedia(_ media: [Media]) -> Message {
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
}
@ -665,6 +673,14 @@ public final class Message {
func withUpdatedAssociatedMessages(_ associatedMessages: SimpleDictionary<MessageId, Message>) -> Message {
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: associatedMessages, associatedMessageIds: self.associatedMessageIds)
}
public func withUpdatedForwardInfo(_ forwardInfo: MessageForwardInfo?) -> Message {
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
}
public func withUpdatedAuthor(_ author: Peer?) -> Message {
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
}
}
public struct StoreMessageFlags: OptionSet {

View File

@ -329,11 +329,11 @@ public final class ShareController: ViewController {
public var debugAction: (() -> Void)?
public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil) {
self.init(sharedContext: context.sharedContext, currentContext: context, subject: subject, presetText: presetText, preferredAction: preferredAction, showInChat: showInChat, fromForeignApp: fromForeignApp, segmentedValues: segmentedValues, externalShare: externalShare, immediateExternalShare: immediateExternalShare, switchableAccounts: switchableAccounts, immediatePeerId: immediatePeerId, forceTheme: forceTheme, forcedActionTitle: forcedActionTitle)
public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil) {
self.init(sharedContext: context.sharedContext, currentContext: context, subject: subject, presetText: presetText, preferredAction: preferredAction, showInChat: showInChat, fromForeignApp: fromForeignApp, segmentedValues: segmentedValues, externalShare: externalShare, immediateExternalShare: immediateExternalShare, switchableAccounts: switchableAccounts, immediatePeerId: immediatePeerId, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, forcedActionTitle: forcedActionTitle)
}
public init(sharedContext: SharedAccountContext, currentContext: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil) {
public init(sharedContext: SharedAccountContext, currentContext: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil) {
self.sharedContext = sharedContext
self.currentContext = currentContext
self.currentAccount = currentContext.account
@ -347,7 +347,7 @@ public final class ShareController: ViewController {
self.segmentedValues = segmentedValues
self.forceTheme = forceTheme
self.presentationData = self.sharedContext.currentPresentationData.with { $0 }
self.presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 }
if let forceTheme = self.forceTheme {
self.presentationData = self.presentationData.withUpdated(theme: forceTheme)
}
@ -463,7 +463,7 @@ public final class ShareController: ViewController {
})
}
self.presentationDataDisposable = (self.sharedContext.presentationData
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? self.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self, strongSelf.isNodeLoaded {
strongSelf.controllerNode.updatePresentationData(presentationData)
@ -484,7 +484,7 @@ public final class ShareController: ViewController {
}
override public func loadDisplayNode() {
self.displayNode = ShareControllerNode(sharedContext: self.sharedContext, presetText: self.presetText, defaultAction: self.defaultAction, requestLayout: { [weak self] transition in
self.displayNode = ShareControllerNode(sharedContext: self.sharedContext, presentationData: self.presentationData, presetText: self.presetText, defaultAction: self.defaultAction, requestLayout: { [weak self] transition in
self?.requestLayout(transition: transition)
}, presentError: { [weak self] title, text in
guard let strongSelf = self else {

View File

@ -80,9 +80,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
private let presetText: String?
init(sharedContext: SharedAccountContext, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, fromForeignApp: Bool, forceTheme: PresentationTheme?, segmentedValues: [ShareControllerSegmentedValue]?) {
init(sharedContext: SharedAccountContext, presentationData: PresentationData, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, fromForeignApp: Bool, forceTheme: PresentationTheme?, segmentedValues: [ShareControllerSegmentedValue]?) {
self.sharedContext = sharedContext
self.presentationData = sharedContext.currentPresentationData.with { $0 }
self.presentationData = presentationData
self.forceTheme = forceTheme
self.externalShare = externalShare
self.immediateExternalShare = immediateExternalShare

View File

@ -74,6 +74,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
self.titleNode = ImmediateTextNode()
self.titleNode.isUserInteractionEnabled = false
self.titleNode.displaysAsynchronously = false
self.subtitleNode = ImmediateTextNode()
self.subtitleNode.isUserInteractionEnabled = false
@ -129,6 +130,10 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
self.buttonGlossNode.color = theme.foregroundColor
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(17.0) : Font.regular(17.0), textColor: theme.foregroundColor)
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle ?? "", font: Font.regular(14.0), textColor: theme.foregroundColor)
if let width = self.validLayout {
_ = self.updateLayout(width: width, transition: .immediate)
}
}
public func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {

View File

@ -4,6 +4,9 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size);
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor);
#endif /* Lottie_h */

View File

@ -229,3 +229,431 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b
return resultImage;
}
@interface CGContextCoder : NSObject {
NSMutableData *_data;
}
@property (nonatomic, readonly) NSData *data;
@end
@implementation CGContextCoder
- (instancetype)initWithSize:(CGSize)size {
self = [super init];
if (self != nil) {
_data = [[NSMutableData alloc] init];
int32_t intWidth = size.width;
int32_t intHeight = size.height;
[_data appendBytes:&intWidth length:sizeof(intWidth)];
[_data appendBytes:&intHeight length:sizeof(intHeight)];
}
return self;
}
- (void)setFillColorWithOpacity:(CGFloat)opacity {
uint8_t command = 1;
[_data appendBytes:&command length:sizeof(command)];
uint8_t intOpacity = opacity * 255.0;
[_data appendBytes:&intOpacity length:sizeof(intOpacity)];
}
- (void)setupStrokeOpacity:(CGFloat)opacity mitterLimit:(CGFloat)mitterLimit lineWidth:(CGFloat)lineWidth lineCap:(CGLineCap)lineCap lineJoin:(CGLineJoin)lineJoin {
uint8_t command = 2;
[_data appendBytes:&command length:sizeof(command)];
uint8_t intOpacity = opacity * 255.0;
[_data appendBytes:&intOpacity length:sizeof(intOpacity)];
float floatMitterLimit = mitterLimit;
[_data appendBytes:&floatMitterLimit length:sizeof(floatMitterLimit)];
float floatLineWidth = lineWidth;
[_data appendBytes:&floatLineWidth length:sizeof(floatLineWidth)];
uint8_t intLineCap = lineCap;
[_data appendBytes:&intLineCap length:sizeof(intLineCap)];
uint8_t intLineJoin = lineJoin;
[_data appendBytes:&intLineJoin length:sizeof(intLineJoin)];
}
- (void)beginPath {
uint8_t command = 3;
[_data appendBytes:&command length:sizeof(command)];
}
- (void)moveToPoint:(CGPoint)point {
uint8_t command = 4;
[_data appendBytes:&command length:sizeof(command)];
float floatX = point.x;
[_data appendBytes:&floatX length:sizeof(floatX)];
float floatY = point.y;
[_data appendBytes:&floatY length:sizeof(floatY)];
}
- (void)addLineToPoint:(CGPoint)point {
uint8_t command = 5;
[_data appendBytes:&command length:sizeof(command)];
float floatX = point.x;
[_data appendBytes:&floatX length:sizeof(floatX)];
float floatY = point.y;
[_data appendBytes:&floatY length:sizeof(floatY)];
}
- (void)addCurveToPoint:(CGPoint)p1 p2:(CGPoint)p2 p3:(CGPoint)p3 {
uint8_t command = 6;
[_data appendBytes:&command length:sizeof(command)];
float floatX1 = p1.x;
[_data appendBytes:&floatX1 length:sizeof(floatX1)];
float floatY1 = p1.y;
[_data appendBytes:&floatY1 length:sizeof(floatY1)];
float floatX2 = p2.x;
[_data appendBytes:&floatX2 length:sizeof(floatX2)];
float floatY2 = p2.y;
[_data appendBytes:&floatY2 length:sizeof(floatY2)];
float floatX3 = p3.x;
[_data appendBytes:&floatX3 length:sizeof(floatX3)];
float floatY3 = p3.y;
[_data appendBytes:&floatY3 length:sizeof(floatY3)];
}
- (void)closePath {
uint8_t command = 7;
[_data appendBytes:&command length:sizeof(command)];
}
- (void)eoFillPath {
uint8_t command = 8;
[_data appendBytes:&command length:sizeof(command)];
}
- (void)fillPath {
uint8_t command = 9;
[_data appendBytes:&command length:sizeof(command)];
}
- (void)strokePath {
uint8_t command = 10;
[_data appendBytes:&command length:sizeof(command)];
}
@end
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size) {
NSDate *startTime = [NSDate date];
UIColor *foregroundColor = [UIColor whiteColor];
UIColor *backgroundColor = [UIColor blackColor];
int32_t ptr = 0;
int32_t width;
int32_t height;
[data getBytes:&width range:NSMakeRange(ptr, sizeof(width))];
ptr += sizeof(width);
[data getBytes:&height range:NSMakeRange(ptr, sizeof(height))];
ptr += sizeof(height);
UIGraphicsBeginImageContextWithOptions(size, true, 1.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
CGSize svgSize = CGSizeMake(width, height);
CGSize drawingSize = aspectFillSize(svgSize, size);
CGFloat scale = MAX(size.width / MAX(1.0, svgSize.width), size.height / MAX(1.0, svgSize.height));
CGContextScaleCTM(context, scale, scale);
CGContextTranslateCTM(context, (size.width - drawingSize.width) / 2.0, (size.height - drawingSize.height) / 2.0);
while (ptr < data.length) {
uint8_t cmd;
[data getBytes:&cmd range:NSMakeRange(ptr, sizeof(cmd))];
ptr += sizeof(cmd);
switch (cmd) {
case 1:
{
uint8_t opacity;
[data getBytes:&opacity range:NSMakeRange(ptr, sizeof(opacity))];
ptr += sizeof(opacity);
CGContextSetFillColorWithColor(context, [foregroundColor colorWithAlphaComponent:opacity / 255.0].CGColor);
}
break;
case 2:
{
uint8_t opacity;
[data getBytes:&opacity range:NSMakeRange(ptr, sizeof(opacity))];
ptr += sizeof(opacity);
CGContextSetStrokeColorWithColor(context, [foregroundColor colorWithAlphaComponent:opacity / 255.0].CGColor);
float mitterLimit;
[data getBytes:&mitterLimit range:NSMakeRange(ptr, sizeof(mitterLimit))];
ptr += sizeof(mitterLimit);
CGContextSetMiterLimit(context, mitterLimit);
float lineWidth;
[data getBytes:&lineWidth range:NSMakeRange(ptr, sizeof(lineWidth))];
ptr += sizeof(lineWidth);
CGContextSetLineWidth(context, lineWidth);
uint8_t lineCap;
[data getBytes:&lineCap range:NSMakeRange(ptr, sizeof(lineCap))];
ptr += sizeof(lineCap);
CGContextSetLineCap(context, lineCap);
uint8_t lineJoin;
[data getBytes:&lineJoin range:NSMakeRange(ptr, sizeof(lineJoin))];
ptr += sizeof(lineJoin);
CGContextSetLineCap(context, lineJoin);
}
break;
case 3:
{
CGContextBeginPath(context);
}
break;
case 4:
{
float x;
[data getBytes:&x range:NSMakeRange(ptr, sizeof(x))];
ptr += sizeof(x);
float y;
[data getBytes:&y range:NSMakeRange(ptr, sizeof(y))];
ptr += sizeof(y);
CGContextMoveToPoint(context, x, y);
}
break;
case 5:
{
float x;
[data getBytes:&x range:NSMakeRange(ptr, sizeof(x))];
ptr += sizeof(x);
float y;
[data getBytes:&y range:NSMakeRange(ptr, sizeof(y))];
ptr += sizeof(y);
CGContextAddLineToPoint(context, x, y);
}
break;
case 6:
{
float x1;
[data getBytes:&x1 range:NSMakeRange(ptr, sizeof(x1))];
ptr += sizeof(x1);
float y1;
[data getBytes:&y1 range:NSMakeRange(ptr, sizeof(y1))];
ptr += sizeof(y1);
float x2;
[data getBytes:&x2 range:NSMakeRange(ptr, sizeof(x2))];
ptr += sizeof(x2);
float y2;
[data getBytes:&y2 range:NSMakeRange(ptr, sizeof(y2))];
ptr += sizeof(y2);
float x3;
[data getBytes:&x3 range:NSMakeRange(ptr, sizeof(x3))];
ptr += sizeof(x3);
float y3;
[data getBytes:&y3 range:NSMakeRange(ptr, sizeof(y3))];
ptr += sizeof(y3);
CGContextAddCurveToPoint(context, x1, y1, x2, y2, x3, y3);
}
break;
case 7:
{
CGContextClosePath(context);
}
break;
case 8:
{
CGContextEOFillPath(context);
}
break;
case 9:
{
CGContextFillPath(context);
}
break;
case 10:
{
CGContextStrokePath(context);
}
break;
default:
break;
}
}
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
double deltaTime = -1.0f * [startTime timeIntervalSinceNow];
printf("drawingTime %fx%f = %f\n", size.width, size.height, deltaTime);
return resultImage;
}
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data) {
NSDate *startTime = [NSDate date];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
if (parser == nil) {
return nil;
}
SvgXMLParsingDelegate *delegate = [[SvgXMLParsingDelegate alloc] init];
parser.delegate = delegate;
[parser parse];
NSMutableString *xmlString = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (xmlString == nil) {
return nil;
}
for (NSString *styleName in delegate.styles) {
NSString *styleValue = delegate.styles[styleName];
[xmlString replaceOccurrencesOfString:[NSString stringWithFormat:@"class=\"%@\"", styleName] withString:[NSString stringWithFormat:@"style=\"%@\"", styleValue] options:0 range:NSMakeRange(0, xmlString.length)];
}
const char *zeroTerminatedData = xmlString.UTF8String;
NSVGimage *image = nsvgParse((char *)zeroTerminatedData, "px", 96);
if (image == nil || image->width < 1.0f || image->height < 1.0f) {
return nil;
}
double deltaTime = -1.0f * [startTime timeIntervalSinceNow];
printf("parseTime = %f\n", deltaTime);
startTime = [NSDate date];
CGContextCoder *context = [[CGContextCoder alloc] initWithSize:CGSizeMake(image->width, image->height)];
for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
if (!(shape->flags & NSVG_FLAGS_VISIBLE)) {
continue;
}
if (shape->fill.type != NSVG_PAINT_NONE) {
[context setFillColorWithOpacity:shape->opacity];
bool isFirst = true;
bool hasStartPoint = false;
CGPoint startPoint;
for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
if (isFirst) {
[context beginPath];
isFirst = false;
hasStartPoint = true;
startPoint.x = path->pts[0];
startPoint.y = path->pts[1];
}
[context moveToPoint:CGPointMake(path->pts[0], path->pts[1])];
for (int i = 0; i < path->npts - 1; i += 3) {
float *p = &path->pts[i * 2];
[context addCurveToPoint:CGPointMake(p[2], p[3]) p2:CGPointMake(p[4], p[5]) p3:CGPointMake(p[6], p[7])];
}
if (path->closed) {
if (hasStartPoint) {
hasStartPoint = false;
[context addLineToPoint:startPoint];
}
}
}
switch (shape->fillRule) {
case NSVG_FILLRULE_EVENODD:
[context eoFillPath];
break;
default:
[context fillPath];
break;
}
}
if (shape->stroke.type != NSVG_PAINT_NONE) {
CGLineCap lineCap = kCGLineCapButt;
CGLineJoin lineJoin = kCGLineJoinMiter;
switch (shape->strokeLineCap) {
case NSVG_CAP_BUTT:
lineCap = kCGLineCapButt;
break;
case NSVG_CAP_ROUND:
lineCap = kCGLineCapRound;
break;
case NSVG_CAP_SQUARE:
lineCap = kCGLineCapSquare;
break;
default:
break;
}
switch (shape->strokeLineJoin) {
case NSVG_JOIN_BEVEL:
lineJoin = kCGLineJoinBevel;
break;
case NSVG_JOIN_MITER:
lineJoin = kCGLineJoinMiter;
break;
case NSVG_JOIN_ROUND:
lineJoin = kCGLineJoinRound;
break;
default:
break;
}
[context setupStrokeOpacity:shape->opacity mitterLimit:shape->miterLimit lineWidth:shape->strokeWidth lineCap:lineCap lineJoin:lineJoin];
for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
[context beginPath];
[context moveToPoint:CGPointMake(path->pts[0], path->pts[1])];
for (int i = 0; i < path->npts - 1; i += 3) {
float *p = &path->pts[i * 2];
[context addCurveToPoint:CGPointMake(p[2], p[3]) p2:CGPointMake(p[4], p[5]) p3:CGPointMake(p[6], p[7])];
}
if (path->closed) {
[context closePath];
}
[context strokePath];
}
}
}
nsvgDelete(image);
return context.data;
}

View File

@ -465,6 +465,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-384910503] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteEdit($0) }
dict[1048537159] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantVolume($0) }
dict[1855199800] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeHistoryTTL($0) }
dict[-26672755] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeTheme($0) }
dict[-1271602504] = { return Api.auth.ExportedAuthorization.parse_exportedAuthorization($0) }
dict[2103482845] = { return Api.SecurePlainData.parse_securePlainPhone($0) }
dict[569137759] = { return Api.SecurePlainData.parse_securePlainEmail($0) }
@ -737,7 +738,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1625153079] = { return Api.InputWebFileLocation.parse_inputWebFileGeoPointLocation($0) }
dict[-1275374751] = { return Api.EmojiLanguage.parse_emojiLanguage($0) }
dict[1601666510] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
dict[-160304943] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[708589599] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[-1012849566] = { return Api.BaseTheme.parse_baseThemeClassic($0) }
dict[-69724536] = { return Api.BaseTheme.parse_baseThemeDay($0) }
dict[-1212997976] = { return Api.BaseTheme.parse_baseThemeNight($0) }

View File

@ -11316,6 +11316,7 @@ public extension Api {
case channelAdminLogEventActionExportedInviteEdit(prevInvite: Api.ExportedChatInvite, newInvite: Api.ExportedChatInvite)
case channelAdminLogEventActionParticipantVolume(participant: Api.GroupCallParticipant)
case channelAdminLogEventActionChangeHistoryTTL(prevValue: Int32, newValue: Int32)
case channelAdminLogEventActionChangeTheme(prevValue: String, newValue: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -11525,6 +11526,13 @@ public extension Api {
serializeInt32(prevValue, buffer: buffer, boxed: false)
serializeInt32(newValue, buffer: buffer, boxed: false)
break
case .channelAdminLogEventActionChangeTheme(let prevValue, let newValue):
if boxed {
buffer.appendInt32(-26672755)
}
serializeString(prevValue, buffer: buffer, boxed: false)
serializeString(newValue, buffer: buffer, boxed: false)
break
}
}
@ -11594,6 +11602,8 @@ public extension Api {
return ("channelAdminLogEventActionParticipantVolume", [("participant", participant)])
case .channelAdminLogEventActionChangeHistoryTTL(let prevValue, let newValue):
return ("channelAdminLogEventActionChangeHistoryTTL", [("prevValue", prevValue), ("newValue", newValue)])
case .channelAdminLogEventActionChangeTheme(let prevValue, let newValue):
return ("channelAdminLogEventActionChangeTheme", [("prevValue", prevValue), ("newValue", newValue)])
}
}
@ -12039,6 +12049,20 @@ public extension Api {
return nil
}
}
public static func parse_channelAdminLogEventActionChangeTheme(_ reader: BufferReader) -> ChannelAdminLogEventAction? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeTheme(prevValue: _1!, newValue: _2!)
}
else {
return nil
}
}
}
public enum SecurePlainData: TypeConstructorDescription {
@ -19214,20 +19238,19 @@ public extension Api {
}
public enum SponsoredMessage: TypeConstructorDescription {
case sponsoredMessage(flags: Int32, randomId: Buffer, peerId: Api.Peer, fromId: Api.Peer, message: String, media: Api.MessageMedia?, entities: [Api.MessageEntity]?)
case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer, startParam: String?, message: String, entities: [Api.MessageEntity]?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .sponsoredMessage(let flags, let randomId, let peerId, let fromId, let message, let media, let entities):
case .sponsoredMessage(let flags, let randomId, let fromId, let startParam, let message, let entities):
if boxed {
buffer.appendInt32(-160304943)
buffer.appendInt32(708589599)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeBytes(randomId, buffer: buffer, boxed: false)
peerId.serialize(buffer, true)
fromId.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
serializeString(message, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {media!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(entities!.count))
for item in entities! {
@ -19239,8 +19262,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .sponsoredMessage(let flags, let randomId, let peerId, let fromId, let message, let media, let entities):
return ("sponsoredMessage", [("flags", flags), ("randomId", randomId), ("peerId", peerId), ("fromId", fromId), ("message", message), ("media", media), ("entities", entities)])
case .sponsoredMessage(let flags, let randomId, let fromId, let startParam, let message, let entities):
return ("sponsoredMessage", [("flags", flags), ("randomId", randomId), ("fromId", fromId), ("startParam", startParam), ("message", message), ("entities", entities)])
}
}
@ -19253,29 +19276,22 @@ public extension Api {
if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _4: Api.Peer?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _4: String?
if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
var _5: String?
_5 = parseString(reader)
var _6: Api.MessageMedia?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.MessageMedia
} }
var _7: [Api.MessageEntity]?
var _6: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, peerId: _3!, fromId: _4!, message: _5!, media: _6, entities: _7)
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, fromId: _3!, startParam: _4, message: _5!, entities: _6)
}
else {
return nil

View File

@ -4447,6 +4447,21 @@ public extension Api {
return result
})
}
public static func getMessageReadParticipants(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int64]>) {
let buffer = Buffer()
buffer.appendInt32(745510839)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getMessageReadParticipants", parameters: [("peer", peer), ("msgId", msgId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int64]? in
let reader = BufferReader(buffer)
var result: [Int64]?
if let _ = reader.readInt32() {
result = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
return result
})
}
}
public struct channels {
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {

View File

@ -17,14 +17,14 @@ public final class MediaNavigationAccessoryContainerNode: ASDisplayNode, UIGestu
private var presentationData: PresentationData
init(context: AccountContext, displayBackground: Bool) {
init(context: AccountContext, presentationData: PresentationData, displayBackground: Bool) {
self.displayBackground = displayBackground
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.presentationData = presentationData
self.backgroundNode = ASDisplayNode()
self.separatorNode = ASDisplayNode()
self.headerNode = MediaNavigationAccessoryHeaderNode(context: context)
self.headerNode = MediaNavigationAccessoryHeaderNode(context: context, presentationData: presentationData)
super.init()

View File

@ -217,9 +217,11 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
}
}
public init(context: AccountContext) {
private let dismissedPromise = ValuePromise<Bool>(false)
public init(context: AccountContext, presentationData: PresentationData) {
self.context = context
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.theme = presentationData.theme
self.strings = presentationData.strings
self.dateTimeFormat = presentationData.dateTimeFormat
@ -437,6 +439,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
guard let (size, _, _) = self.validLayout else {
return
}
self.dismissedPromise.set(true)
transition.updatePosition(node: self.separatorNode, position: self.separatorNode.position.offsetBy(dx: 0.0, dy: size.height))
}
@ -551,7 +555,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
return
}
let items: Signal<[ContextMenuItem], NoError> = self.contextMenuSpeedItems()
let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode)), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, reactionItems: [], gesture: gesture)
self.presentInGlobalOverlay?(contextController)
}
@ -766,11 +771,14 @@ private final class HeaderContextReferenceContentSource: ContextReferenceContent
private let controller: ViewController
private let sourceNode: ContextReferenceContentNode
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
var shouldBeDismissed: Signal<Bool, NoError>
init(controller: ViewController, sourceNode: ContextReferenceContentNode, shouldBeDismissed: Signal<Bool, NoError>) {
self.controller = controller
self.sourceNode = sourceNode
self.shouldBeDismissed = shouldBeDismissed
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
}

View File

@ -5,6 +5,7 @@ import AsyncDisplayKit
import TelegramCore
import AccountContext
import TelegramUIPreferences
import TelegramPresentationData
public final class MediaNavigationAccessoryPanel: ASDisplayNode {
public let containerNode: MediaNavigationAccessoryContainerNode
@ -19,8 +20,8 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode {
public var getController: (() -> ViewController?)?
public var presentInGlobalOverlay: ((ViewController) -> Void)?
public init(context: AccountContext, displayBackground: Bool = false) {
self.containerNode = MediaNavigationAccessoryContainerNode(context: context, displayBackground: displayBackground)
public init(context: AccountContext, presentationData: PresentationData, displayBackground: Bool = false) {
self.containerNode = MediaNavigationAccessoryContainerNode(context: context, presentationData: presentationData, displayBackground: displayBackground)
super.init()

View File

@ -265,20 +265,31 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
let availableGroupCall: Signal<GroupCallPanelData?, NoError>
if case let .peer(peerId) = groupCallPanelSource {
availableGroupCall = context.account.viewTracker.peerView(peerId)
|> map { peerView -> CachedChannelData.ActiveCall? in
|> map { peerView -> (CachedChannelData.ActiveCall?, EnginePeer?) in
let peer = peerView.peers[peerId].flatMap(EnginePeer.init)
if let cachedData = peerView.cachedData as? CachedChannelData {
return cachedData.activeCall
return (cachedData.activeCall, peer)
} else if let cachedData = peerView.cachedData as? CachedGroupData {
return cachedData.activeCall
return (cachedData.activeCall, peer)
} else {
return nil
return (nil, peer)
}
}
|> distinctUntilChanged
|> mapToSignal { activeCall -> Signal<GroupCallPanelData?, NoError> in
|> distinctUntilChanged(isEqual: { lhs, rhs in
if lhs.0 != rhs.0 {
return false
}
return true
})
|> mapToSignal { activeCall, peer -> Signal<GroupCallPanelData?, NoError> in
guard let activeCall = activeCall else {
return .single(nil)
}
var isChannel = false
if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info {
isChannel = true
}
return Signal { [weak context] subscriber in
guard let context = context, let callContextCache = context.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl else {
@ -288,7 +299,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
let disposable = MetaDisposable()
callContextCache.impl.syncWith { impl in
let callContext = impl.get(account: context.account, engine: context.engine, peerId: peerId, call: EngineGroupCallDescription(activeCall))
let callContext = impl.get(account: context.account, engine: context.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(activeCall))
disposable.set((callContext.context.panelData
|> deliverOnMainQueue).start(next: { panelData in
callContext.keep()
@ -333,7 +344,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
}
}
self.presentationDataDisposable = (context.sharedContext.presentationData
self.presentationDataDisposable = (self.updatedPresentationData.1
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme
@ -350,6 +361,10 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
})
}
open var updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) {
return (self.presentationData, self.context.sharedContext.presentationData)
}
deinit {
self.mediaStatusDisposable?.dispose()
self.locationBroadcastDisposable?.dispose()
@ -643,7 +658,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
})
}
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context)
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, presentationData: self.updatedPresentationData.0)
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
mediaAccessoryPanel.getController = { [weak self] in
return self

View File

@ -40,6 +40,7 @@ public enum GroupCallPanelSource {
public final class GroupCallPanelData {
public let peerId: PeerId
public let isChannel: Bool
public let info: GroupCallInfo
public let topParticipants: [GroupCallParticipantsContext.Participant]
public let participantCount: Int
@ -48,6 +49,7 @@ public final class GroupCallPanelData {
public init(
peerId: PeerId,
isChannel: Bool,
info: GroupCallInfo,
topParticipants: [GroupCallParticipantsContext.Participant],
participantCount: Int,
@ -55,6 +57,7 @@ public final class GroupCallPanelData {
groupCall: PresentationGroupCall?
) {
self.peerId = peerId
self.isChannel = isChannel
self.info = info
self.topParticipants = topParticipants
self.participantCount = participantCount
@ -523,6 +526,11 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
var joinText = self.strings.VoiceChat_PanelJoin
var title = self.strings.VoiceChat_Title
var isChannel = false
if let currentData = self.currentData, currentData.isChannel {
title = self.strings.VoiceChatChannel_Title
isChannel = true
}
var text = self.currentText
var isScheduled = false
var isLate = false
@ -530,9 +538,9 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
isScheduled = true
if let voiceChatTitle = self.currentData?.info.title {
title = voiceChatTitle
text = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: scheduleTime, alwaysShowTime: true, format: HumanReadableStringFormat(dateFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsOn($0) }, tomorrowFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsTomorrow($0) }, todayFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsToday($0) })).string
text = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: scheduleTime, alwaysShowTime: true, format: HumanReadableStringFormat(dateFormatString: { isChannel ? self.strings.Conversation_ScheduledLiveStreamStartsOn($0) : self.strings.Conversation_ScheduledVoiceChatStartsOn($0) }, tomorrowFormatString: { isChannel ? self.strings.Conversation_ScheduledLiveStreamStartsTomorrow($0) : self.strings.Conversation_ScheduledVoiceChatStartsTomorrow($0) }, todayFormatString: { isChannel ? self.strings.Conversation_ScheduledLiveStreamStartsToday($0) : self.strings.Conversation_ScheduledVoiceChatStartsToday($0) })).string
} else {
title = self.strings.Conversation_ScheduledVoiceChat
title = isChannel ? self.strings.Conversation_ScheduledLiveStream : self.strings.Conversation_ScheduledVoiceChat
text = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: scheduleTime, alwaysShowTime: true, format: HumanReadableStringFormat(dateFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsOnShort($0) }, tomorrowFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsTomorrowShort($0) }, todayFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsTodayShort($0) })).string
}

View File

@ -650,9 +650,12 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
}
|> runOn(Queue.mainQueue())
return accessEnabledSignal
return combineLatest(queue: .mainQueue(),
accessEnabledSignal,
accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
)
|> deliverOnMainQueue
|> mapToSignal { [weak self] accessEnabled -> Signal<Bool, NoError> in
|> mapToSignal { [weak self] accessEnabled, peer -> Signal<Bool, NoError> in
guard let strongSelf = self else {
return .single(false)
}
@ -660,6 +663,11 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
if !accessEnabled {
return .single(false)
}
var isChannel = false
if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info {
isChannel = true
}
let call = PresentationGroupCallImpl(
accountContext: accountContext,
@ -669,6 +677,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
initialCall: nil,
internalId: internalId,
peerId: peerId,
isChannel: isChannel,
invite: nil,
joinAsPeerId: nil
)
@ -803,9 +812,12 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
}
|> runOn(Queue.mainQueue())
return accessEnabledSignal
return combineLatest(queue: .mainQueue(),
accessEnabledSignal,
accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
)
|> deliverOnMainQueue
|> mapToSignal { [weak self] accessEnabled -> Signal<Bool, NoError> in
|> mapToSignal { [weak self] accessEnabled, peer -> Signal<Bool, NoError> in
guard let strongSelf = self else {
return .single(false)
}
@ -813,6 +825,11 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
if !accessEnabled {
return .single(false)
}
var isChannel = false
if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info {
isChannel = true
}
let call = PresentationGroupCallImpl(
accountContext: accountContext,
@ -822,6 +839,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
initialCall: initialCall,
internalId: internalId,
peerId: peerId,
isChannel: isChannel,
invite: invite,
joinAsPeerId: joinAsPeerId
)

View File

@ -91,9 +91,10 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
return self.panelDataPromise.get()
}
public init(account: Account, engine: TelegramEngine, peerId: PeerId, call: EngineGroupCallDescription) {
public init(account: Account, engine: TelegramEngine, peerId: PeerId, isChannel: Bool, call: EngineGroupCallDescription) {
self.panelDataPromise.set(.single(GroupCallPanelData(
peerId: peerId,
isChannel: isChannel,
info: GroupCallInfo(
id: call.id,
accessHash: call.accessHash,
@ -113,13 +114,18 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
activeSpeakers: Set(),
groupCall: nil
)))
let state = engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil)
|> map(Optional.init)
|> `catch` { _ -> Signal<GroupCallParticipantsContext.State?, NoError> in
return .single(nil)
}
self.disposable = (engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil)
|> map(Optional.init)
|> `catch` { _ -> Signal<GroupCallParticipantsContext.State?, NoError> in
return .single(nil)
}
|> deliverOnMainQueue).start(next: { [weak self] state in
self.disposable = (combineLatest(queue: .mainQueue(),
state,
engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
)
|> deliverOnMainQueue).start(next: { [weak self] state, peer in
guard let strongSelf = self, let state = state else {
return
}
@ -145,8 +151,15 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
}
topParticipants.append(participant)
}
var isChannel = false
if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info {
isChannel = true
}
return GroupCallPanelData(
peerId: peerId,
isChannel: isChannel,
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit),
topParticipants: topParticipants,
participantCount: state.totalCount,
@ -183,12 +196,12 @@ public final class AccountGroupCallContextCacheImpl: AccountGroupCallContextCach
self.queue = queue
}
public func get(account: Account, engine: TelegramEngine, peerId: PeerId, call: EngineGroupCallDescription) -> AccountGroupCallContextImpl.Proxy {
public func get(account: Account, engine: TelegramEngine, peerId: PeerId, isChannel: Bool, call: EngineGroupCallDescription) -> AccountGroupCallContextImpl.Proxy {
let result: Record
if let current = self.contexts[call.id] {
result = current
} else {
let context = AccountGroupCallContextImpl(account: account, engine: engine, peerId: peerId, call: call)
let context = AccountGroupCallContextImpl(account: account, engine: engine, peerId: peerId, isChannel: isChannel, call: call)
result = Record(context: context)
self.contexts[call.id] = result
}
@ -385,6 +398,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var initialCall: EngineGroupCallDescription?
public let internalId: CallSessionInternalId
public let peerId: PeerId
private let isChannel: Bool
private var invite: String?
private var joinAsPeerId: PeerId
private var ignorePreviousJoinAsPeerId: (PeerId, UInt32)?
@ -624,6 +638,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
initialCall: EngineGroupCallDescription?,
internalId: CallSessionInternalId,
peerId: PeerId,
isChannel: Bool,
invite: String?,
joinAsPeerId: PeerId?
) {
@ -636,6 +651,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.initialCall = initialCall
self.internalId = internalId
self.peerId = peerId
self.isChannel = isChannel
self.invite = invite
self.joinAsPeerId = joinAsPeerId ?? accountContext.account.peerId
self.schedulePending = initialCall == nil
@ -822,7 +838,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
})
if let initialCall = initialCall, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in
impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, call: EngineGroupCallDescription(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled))
impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled))
}) {
self.switchToTemporaryParticipantsContext(sourceContext: temporaryParticipantsContext.context.participantsContext, oldMyPeerId: self.joinAsPeerId)
} else {
@ -1510,12 +1526,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
if case .anonymousNotAllowed = error {
let presentationData = strongSelf.accountContext.sharedContext.currentPresentationData.with { $0 }
strongSelf.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.VoiceChat_AnonymousDisabledAlertText, actions: [
strongSelf.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: strongSelf.isChannel ? presentationData.strings.LiveStream_AnonymousDisabledAlertText : presentationData.strings.VoiceChat_AnonymousDisabledAlertText, actions: [
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})
]), on: .root, blockInteraction: false, completion: {})
} else if case .tooManyParticipants = error {
let presentationData = strongSelf.accountContext.sharedContext.currentPresentationData.with { $0 }
strongSelf.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.VoiceChat_ChatFullAlertText, actions: [
strongSelf.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: strongSelf.isChannel ? presentationData.strings.LiveStream_ChatFullAlertText : presentationData.strings.VoiceChat_ChatFullAlertText, actions: [
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})
]), on: .root, blockInteraction: false, completion: {})
} else if case .invalidJoinAsPeer = error {

View File

@ -858,6 +858,8 @@ public final class VoiceChatController: ViewController {
private var currentLoadToken: String?
private var scrollAtTop = true
private var effectiveMuteState: GroupCallParticipantsContext.Participant.MuteState? {
if self.pushingToTalk {
return nil
@ -1236,7 +1238,13 @@ public final class VoiceChatController: ViewController {
dismissController?()
if strongSelf.call.invitePeer(participant.peer.id) {
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: participant.peer, text: strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string), action: { _ in return false })
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
} else {
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: participant.peer, text: text), action: { _ in return false })
}
} else {
if let groupPeer = groupPeer as? TelegramChannel, let listenerLink = inviteLinks?.listenerLink, !groupPeer.hasPermission(.inviteMembers) {
@ -1336,7 +1344,13 @@ public final class VoiceChatController: ViewController {
dismissController?()
if strongSelf.call.invitePeer(peer.id) {
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string), action: { _ in return false })
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
} else {
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text), action: { _ in return false })
}
}))
} else if let groupPeer = groupPeer as? TelegramGroup {
@ -1398,7 +1412,13 @@ public final class VoiceChatController: ViewController {
dismissController?()
if strongSelf.call.invitePeer(peer.id) {
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string), action: { _ in return false })
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
} else {
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text), action: { _ in return false })
}
}))
}
@ -2157,6 +2177,22 @@ public final class VoiceChatController: ViewController {
}
}
self.listNode.visibleContentOffsetChanged = { [weak self] offset in
guard let strongSelf = self else {
return
}
var scrollAtTop = false
if case let .known(value) = offset, value < 180.0 {
scrollAtTop = true
} else {
scrollAtTop = false
}
if scrollAtTop != strongSelf.scrollAtTop {
strongSelf.scrollAtTop = scrollAtTop
strongSelf.updateTitle(transition: .immediate)
}
}
self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in
guard let strongSelf = self else {
return
@ -2175,7 +2211,13 @@ public final class VoiceChatController: ViewController {
return
}
if event.joined {
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: event.peer, text: strongSelf.presentationData.strings.VoiceChat_PeerJoinedText(event.peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string), action: { _ in return false })
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_PeerJoinedText(event.peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
} else {
text = strongSelf.presentationData.strings.VoiceChat_PeerJoinedText(event.peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: event.peer, text: text), action: { _ in return false })
}
}))
@ -2184,7 +2226,13 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else {
return
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.VoiceChat_DisplayAsSuccess(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string), action: { _ in return false })
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_DisplayAsSuccess(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
} else {
text = strongSelf.presentationData.strings.VoiceChat_DisplayAsSuccess(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text), action: { _ in return false })
}))
self.stateVersionDisposable.set((self.call.stateVersion
@ -2210,7 +2258,13 @@ public final class VoiceChatController: ViewController {
}
if !hasTooltipAlready {
let location = strongSelf.titleNode.recordingIconNode.convert(strongSelf.titleNode.recordingIconNode.bounds, to: nil)
strongSelf.controller?.present(TooltipScreen(text: presentationData.strings.VoiceChat_RecordingInProgress, icon: nil, location: .point(location.offsetBy(dx: 1.0, dy: 0.0), .top), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = presentationData.strings.LiveStream_RecordingInProgress
} else {
text = presentationData.strings.VoiceChat_RecordingInProgress
}
strongSelf.controller?.present(TooltipScreen(text: text, icon: nil, location: .point(location.offsetBy(dx: 1.0, dy: 0.0), .top), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: true)
}), in: .window(.root))
}
@ -2457,7 +2511,13 @@ public final class VoiceChatController: ViewController {
}
if canManageCall {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EditTitle, icon: { theme -> UIImage? in
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_EditTitle
} else {
text = strongSelf.presentationData.strings.VoiceChat_EditTitle
}
items.append(.action(ContextMenuActionItem(text: text, icon: { theme -> UIImage? in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pencil"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
f(.default)
@ -2571,8 +2631,14 @@ public final class VoiceChatController: ViewController {
self?.controller?.present(alertController, in: .window(.root))
}), false))
} else {
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_StartRecording
} else {
text = strongSelf.presentationData.strings.VoiceChat_StartRecording
}
if strongSelf.callState?.scheduleTimestamp == nil {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_StartRecording, icon: { theme -> UIImage? in
items.append(.action(ContextMenuActionItem(text: text, icon: { theme -> UIImage? in
return generateStartRecordingIcon(color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
f(.dismissWithoutContent)
@ -2581,22 +2647,40 @@ public final class VoiceChatController: ViewController {
return
}
let controller = VoiceChatRecordingSetupController(context: strongSelf.context, completion: { [weak self] videoOrientation in
if let strongSelf = self {
strongSelf.call.setShouldBeRecording(true, title: "", videoOrientation: videoOrientation)
strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: strongSelf.presentationData.strings.VoiceChat_RecordingStarted), action: { _ in return false })
strongSelf.call.playTone(.recordingStarted)
}
})
// let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_StartRecordingTitle, text: presentationData.strings.VoiceChat_StartRecordingText, placeholder: presentationData.strings.VoiceChat_RecordingTitlePlaceholder, value: nil, maxLength: 40, apply: { title in
// if let strongSelf = self, let title = title {
// strongSelf.call.setShouldBeRecording(true, title: title)
// let controller = VoiceChatRecordingSetupController(context: strongSelf.context, completion: { [weak self] videoOrientation in
// if let strongSelf = self {
// strongSelf.call.setShouldBeRecording(true, title: "", videoOrientation: videoOrientation)
//
// strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: strongSelf.presentationData.strings.VoiceChat_RecordingStarted), action: { _ in return false })
// strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: text), action: { _ in return false })
// strongSelf.call.playTone(.recordingStarted)
// }
// })
let title: String
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
title = strongSelf.presentationData.strings.LiveStream_StartRecordingTitle
text = strongSelf.presentationData.strings.LiveStream_StartRecordingText
} else {
title = strongSelf.presentationData.strings.VoiceChat_StartRecordingTitle
text = strongSelf.presentationData.strings.VoiceChat_StartRecordingText
}
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: title, text: text, placeholder: strongSelf.presentationData.strings.VoiceChat_RecordingTitlePlaceholder, value: nil, maxLength: 40, apply: { title in
if let strongSelf = self, let title = title {
strongSelf.call.setShouldBeRecording(true, title: title, videoOrientation: nil)
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_RecordingStarted
} else {
text = strongSelf.presentationData.strings.VoiceChat_RecordingStarted
}
strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: text), action: { _ in return false })
strongSelf.call.playTone(.recordingStarted)
}
})
self?.controller?.present(controller, in: .window(.root))
})))
}
@ -2604,7 +2688,13 @@ public final class VoiceChatController: ViewController {
}
if canManageCall {
items.append(.action(ContextMenuActionItem(text: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelVoiceChat : strongSelf.presentationData.strings.VoiceChat_EndVoiceChat, textColor: .destructive, icon: { theme in
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelLiveStream : strongSelf.presentationData.strings.VoiceChat_EndLiveStream
} else {
text = isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelVoiceChat : strongSelf.presentationData.strings.VoiceChat_EndVoiceChat
}
items.append(.action(ContextMenuActionItem(text: text, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor)
}, action: { _, f in
f(.dismissWithoutContent)
@ -2626,13 +2716,29 @@ public final class VoiceChatController: ViewController {
})
}
let alertController = textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationTitle : strongSelf.presentationData.strings.VoiceChat_EndConfirmationTitle, text: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationText : strongSelf.presentationData.strings.VoiceChat_EndConfirmationText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationEnd : strongSelf.presentationData.strings.VoiceChat_EndConfirmationEnd, action: {
let title: String
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
title = isScheduled ? strongSelf.presentationData.strings.LiveStream_CancelConfirmationTitle : strongSelf.presentationData.strings.LiveStream_EndConfirmationTitle
text = isScheduled ? strongSelf.presentationData.strings.LiveStream_CancelConfirmationText : strongSelf.presentationData.strings.LiveStream_EndConfirmationText
} else {
title = isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationTitle : strongSelf.presentationData.strings.VoiceChat_EndConfirmationTitle
text = isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationText : strongSelf.presentationData.strings.VoiceChat_EndConfirmationText
}
let alertController = textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationEnd : strongSelf.presentationData.strings.VoiceChat_EndConfirmationEnd, action: {
action()
})])
strongSelf.controller?.present(alertController, in: .window(.root))
})))
} else {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_LeaveVoiceChat, textColor: .destructive, icon: { theme in
let leaveText: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
leaveText = strongSelf.presentationData.strings.LiveStream_LeaveVoiceChat
} else {
leaveText = strongSelf.presentationData.strings.VoiceChat_LeaveVoiceChat
}
items.append(.action(ContextMenuActionItem(text: leaveText, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor)
}, action: { _, f in
f(.dismissWithoutContent)
@ -2949,7 +3055,7 @@ public final class VoiceChatController: ViewController {
isGroup = false
}
let intervalString = scheduledTimeIntervalString(strings: self.presentationData.strings, value: max(60, delta))
self.scheduleTextNode.attributedText = NSAttributedString(string: isGroup ? self.presentationData.strings.ScheduleVoiceChat_GroupText(intervalString).string : self.presentationData.strings.ScheduleVoiceChat_ChannelText(intervalString).string, font: Font.regular(14.0), textColor: UIColor(rgb: 0x8e8e93))
self.scheduleTextNode.attributedText = NSAttributedString(string: isGroup ? self.presentationData.strings.ScheduleVoiceChat_GroupText(intervalString).string : self.presentationData.strings.ScheduleLiveStream_ChannelText(intervalString).string, font: Font.regular(14.0), textColor: UIColor(rgb: 0x8e8e93))
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
@ -3090,14 +3196,35 @@ public final class VoiceChatController: ViewController {
let actionSheet = ActionSheetController(presentationData: self.presentationData.withUpdated(theme: self.darkTheme))
var items: [ActionSheetItem] = []
let leaveTitle: String
let leaveAndCancelTitle: String
if let channel = self.peer as? TelegramChannel, case .broadcast = channel.info {
leaveTitle = self.presentationData.strings.LiveStream_LeaveConfirmation
leaveAndCancelTitle = self.isScheduled ? self.presentationData.strings.LiveStream_LeaveAndCancelVoiceChat : self.presentationData.strings.LiveStream_LeaveAndEndVoiceChat
} else {
leaveTitle = self.presentationData.strings.VoiceChat_LeaveConfirmation
leaveAndCancelTitle = self.isScheduled ? self.presentationData.strings.VoiceChat_LeaveAndCancelVoiceChat : self.presentationData.strings.VoiceChat_LeaveAndEndVoiceChat
}
items.append(ActionSheetTextItem(title: self.presentationData.strings.VoiceChat_LeaveConfirmation))
items.append(ActionSheetButtonItem(title: self.isScheduled ? self.presentationData.strings.VoiceChat_LeaveAndCancelVoiceChat : self.presentationData.strings.VoiceChat_LeaveAndEndVoiceChat, color: .destructive, action: { [weak self, weak actionSheet] in
items.append(ActionSheetTextItem(title: leaveTitle))
items.append(ActionSheetButtonItem(title: leaveAndCancelTitle, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
if let strongSelf = self {
let title: String
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
title = strongSelf.isScheduled ? strongSelf.presentationData.strings.LiveStream_CancelConfirmationTitle : strongSelf.presentationData.strings.LiveStream_EndConfirmationTitle
text = strongSelf.isScheduled ? strongSelf.presentationData.strings.LiveStream_CancelConfirmationText : strongSelf.presentationData.strings.LiveStream_EndConfirmationText
} else {
title = strongSelf.isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationTitle : strongSelf.presentationData.strings.VoiceChat_EndConfirmationTitle
text = strongSelf.isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationText : strongSelf.presentationData.strings.VoiceChat_EndConfirmationText
}
if let (members, _) = strongSelf.currentCallMembers, members.count >= 10 || true {
let alertController = textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: strongSelf.isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationTitle : strongSelf.presentationData.strings.VoiceChat_EndConfirmationTitle, text: strongSelf.isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationText : strongSelf.presentationData.strings.VoiceChat_EndConfirmationText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationEnd : strongSelf.presentationData.strings.VoiceChat_EndConfirmationEnd, action: {
let alertController = textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationEnd : strongSelf.presentationData.strings.VoiceChat_EndConfirmationEnd, action: {
action()
})])
strongSelf.controller?.present(alertController, in: .window(.root))
@ -3106,7 +3233,15 @@ public final class VoiceChatController: ViewController {
}
}
}))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.VoiceChat_LeaveVoiceChat, color: .accent, action: { [weak self, weak actionSheet] in
let leaveText: String
if let channel = self.peer as? TelegramChannel, case .broadcast = channel.info {
leaveText = self.presentationData.strings.LiveStream_LeaveVoiceChat
} else {
leaveText = self.presentationData.strings.VoiceChat_LeaveVoiceChat
}
items.append(ActionSheetButtonItem(title: leaveText, color: .accent, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
@ -3920,19 +4055,34 @@ public final class VoiceChatController: ViewController {
var title = self.currentTitle
if self.isScheduling {
title = self.presentationData.strings.ScheduleVoiceChat_Title
if let peer = self.peer as? TelegramChannel, case .broadcast = peer.info {
title = self.presentationData.strings.ScheduleLiveStream_Title
} else {
title = self.presentationData.strings.ScheduleVoiceChat_Title
}
} else if case .modal(_, false) = self.displayMode, !self.currentTitleIsCustom {
if let navigationController = self.controller?.navigationController as? NavigationController {
for controller in navigationController.viewControllers.reversed() {
if let controller = controller as? ChatController, case let .peer(peerId) = controller.chatLocation, peerId == self.call.peerId {
title = self.presentationData.strings.VoiceChat_Title
if let peer = self.peer as? TelegramChannel, case .broadcast = peer.info {
title = self.presentationData.strings.VoiceChatChannel_Title
} else {
title = self.presentationData.strings.VoiceChat_Title
}
}
}
}
}
var subtitle = self.currentSpeakingSubtitle ?? self.currentSubtitle
var speaking = self.currentSpeakingSubtitle != nil
var subtitle = ""
var speaking = false
if self.scrollAtTop {
subtitle = self.currentSubtitle
speaking = false
} else {
subtitle = self.currentSpeakingSubtitle ?? self.currentSubtitle
speaking = self.currentSpeakingSubtitle != nil
}
if self.isScheduling {
subtitle = ""
speaking = false
@ -4258,7 +4408,7 @@ public final class VoiceChatController: ViewController {
childrenSafeInsets.right = childrenSafeInsets.left + (size.width - contentLeftInset)
}
} else if !self.isLandscape, case .fullscreen = effectiveDisplayMode {
childrenInsets.bottom += self.effectiveBottomAreaHeight + fullscreenListHeight + 30.0
childrenInsets.bottom += self.effectiveBottomAreaHeight + fullscreenListHeight + 36.0
}
childrenLayout.safeInsets = childrenSafeInsets
childrenLayout.intrinsicInsets = childrenInsets
@ -5849,11 +5999,29 @@ public final class VoiceChatController: ViewController {
}
let initialTitle = strongSelf.callState?.title ?? ""
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: strongSelf.presentationData.strings.VoiceChat_EditTitleTitle, text: strongSelf.presentationData.strings.VoiceChat_EditTitleText, placeholder: chatPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), value: initialTitle, maxLength: 40, apply: { title in
let title: String
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
title = strongSelf.presentationData.strings.LiveStream_EditTitle
text = strongSelf.presentationData.strings.LiveStream_EditTitleText
} else {
title = strongSelf.presentationData.strings.VoiceChat_EditTitle
text = strongSelf.presentationData.strings.VoiceChat_EditTitleText
}
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: title, text: text, placeholder: chatPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), value: initialTitle, maxLength: 40, apply: { title in
if let strongSelf = self, let title = title, title != initialTitle {
strongSelf.call.updateTitle(title)
strongSelf.presentUndoOverlay(content: .voiceChatFlag(text: title.isEmpty ? strongSelf.presentationData.strings.VoiceChat_EditTitleRemoveSuccess : strongSelf.presentationData.strings.VoiceChat_EditTitleSuccess(title).string), action: { _ in return false })
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = title.isEmpty ? strongSelf.presentationData.strings.LiveStream_EditTitleRemoveSuccess : strongSelf.presentationData.strings.LiveStream_EditTitleSuccess(title).string
} else {
text = title.isEmpty ? strongSelf.presentationData.strings.VoiceChat_EditTitleRemoveSuccess : strongSelf.presentationData.strings.VoiceChat_EditTitleSuccess(title).string
}
strongSelf.presentUndoOverlay(content: .voiceChatFlag(text: text), action: { _ in return false })
}
})
strongSelf.controller?.present(controller, in: .window(.root))

View File

@ -389,7 +389,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
profileNode.frame = CGRect(origin: CGPoint(), size: extractedRect.size)
self.profileNode = profileNode
self.contextSourceNode.contentNode.addSubnode(profileNode)
profileNode.animateIn(from: self, targetRect: extractedRect, transition: transition)
var appearenceTransition = transition
if transition.isAnimated {
@ -706,20 +706,28 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
audioLevelView.layer.mask = playbackMaskLayer
audioLevelView.setColor(wavesColor)
audioLevelView.alpha = strongSelf.isExtracted ? 0.0 : 1.0
strongSelf.audioLevelView = audioLevelView
strongSelf.offsetContainerNode.view.insertSubview(audioLevelView, at: 0)
if let _ = strongSelf.item, strongSelf.videoNode != nil && !active {
audioLevelView.alpha = 0.0
}
}
let level = min(1.0, max(0.0, CGFloat(value)))
if let audioLevelView = strongSelf.audioLevelView {
audioLevelView.updateLevel(CGFloat(value))
var hasVideo = false
if let videoNode = strongSelf.videoNode, videoNode.supernode == strongSelf.videoContainerNode, !videoNode.alpha.isZero {
hasVideo = true
}
var audioLevelAlpha: CGFloat = 1.0
if strongSelf.isExtracted {
audioLevelAlpha = 0.0
} else {
audioLevelAlpha = hasVideo ? 0.0 : 1.0
}
audioLevelView.alpha = audioLevelAlpha
let avatarScale: CGFloat
if value > 0.02 {
audioLevelView.startAnimating()

View File

@ -565,23 +565,35 @@ private class PreviewIconNode: ASDisplayNode {
override init() {
self.avatar1Node = ASImageNode()
self.avatar1Node.cornerRadius = 4.0
self.avatar1Node.clipsToBounds = true
self.avatar1Node.displaysAsynchronously = false
self.avatar1Node.backgroundColor = UIColor(rgb: 0x834fff)
self.avatar1Node.image = UIImage(bundleImageName: "Call/Avatar1")
self.avatar1Node.contentMode = .bottom
self.avatar2Node = ASImageNode()
self.avatar2Node.cornerRadius = 4.0
self.avatar2Node.clipsToBounds = true
self.avatar2Node.displaysAsynchronously = false
self.avatar2Node.backgroundColor = UIColor(rgb: 0x63d5c9)
self.avatar2Node.image = UIImage(bundleImageName: "Call/Avatar2")
self.avatar2Node.contentMode = .scaleAspectFit
self.avatar3Node = ASImageNode()
self.avatar3Node.cornerRadius = 4.0
self.avatar3Node.clipsToBounds = true
self.avatar3Node.displaysAsynchronously = false
self.avatar3Node.backgroundColor = UIColor(rgb: 0xccff60)
self.avatar3Node.image = UIImage(bundleImageName: "Call/Avatar3")
self.avatar3Node.contentMode = .scaleAspectFit
self.avatar4Node = ASImageNode()
self.avatar4Node.cornerRadius = 4.0
self.avatar4Node.clipsToBounds = true
self.avatar4Node.displaysAsynchronously = false
self.avatar4Node.backgroundColor = UIColor(rgb: 0xf5512a)
self.avatar4Node.image = UIImage(bundleImageName: "Call/Avatar4")
self.avatar4Node.contentMode = .scaleAspectFit
super.init()

View File

@ -234,7 +234,7 @@ private var declaredEncodables: Void = {
declareEncodable(CachedDisplayAsPeers.self, f: { CachedDisplayAsPeers(decoder: $0) })
declareEncodable(WallpapersState.self, f: { WallpapersState(decoder: $0) })
declareEncodable(WallpaperDataResource.self, f: { WallpaperDataResource(decoder: $0) })
declareEncodable(ForwardHideSendersNamesMessageAttribute.self, f: { ForwardHideSendersNamesMessageAttribute(decoder: $0) })
declareEncodable(ForwardOptionsMessageAttribute.self, f: { ForwardOptionsMessageAttribute(decoder: $0) })
declareEncodable(ChatTheme.self, f: { ChatTheme(decoder: $0) })
declareEncodable(ChatThemes.self, f: { ChatThemes(decoder: $0) })

View File

@ -3,9 +3,11 @@ import Postbox
public final class AdMessageAttribute: MessageAttribute {
public let opaqueId: Data
public let startParam: String?
public init(opaqueId: Data) {
public init(opaqueId: Data, startParam: String?) {
self.opaqueId = opaqueId
self.startParam = startParam
}
public init(decoder: PostboxDecoder) {

View File

@ -109,18 +109,46 @@ private func roundUp(_ value: Int, to multiple: Int) -> Int {
private let dataHashLength: Int32 = 128 * 1024
private final class MultipartCdnHashSource {
private final class ClusterContext {
final class Subscriber {
let completion: ([Int32: Data]) -> Void
let error: (MultipartFetchDownloadError) -> Void
init(completion: @escaping ([Int32: Data]) -> Void, error: @escaping (MultipartFetchDownloadError) -> Void) {
self.completion = completion
self.error = error
}
}
let disposable: Disposable
let subscribers = Bag<Subscriber>()
var result: [Int32: Data]?
var error: MultipartFetchDownloadError?
init(disposable: Disposable) {
self.disposable = disposable
}
deinit {
self.disposable.dispose()
}
}
private let queue: Queue
private let fileToken: Data
private let masterDownload: DownloadWrapper
private let continueInBackground: Bool
private var clusterContexts: [Int32: ClusterContext] = [:]
private var knownUpperBound: Int32
/*private var knownUpperBound: Int32
private var hashes: [Int32: Data] = [:]
private var requestOffsetAndDisposable: (Int32, Disposable)?
private var requestedUpperBound: Int32?
private var subscribers = Bag<(Int32, Int32, ([Int32: Data]) -> Void)>()
private var subscribers = Bag<(Int32, Int32, ([Int32: Data]) -> Void)>()*/
init(queue: Queue, fileToken: Data, hashes: [Int32: Data], masterDownload: DownloadWrapper, continueInBackground: Bool) {
assert(queue.isCurrent())
@ -130,22 +158,17 @@ private final class MultipartCdnHashSource {
self.masterDownload = masterDownload
self.continueInBackground = continueInBackground
let knownUpperBound: Int32 = 0
/*self.hashes = hashes
for (offset, _) in hashes {
assert(offset % dataHashLength == 0)
knownUpperBound = max(knownUpperBound, offset + dataHashLength)
}*/
self.knownUpperBound = knownUpperBound
/*let knownUpperBound: Int32 = 0
self.knownUpperBound = knownUpperBound*/
}
deinit {
assert(self.queue.isCurrent())
self.requestOffsetAndDisposable?.1.dispose()
//self.requestOffsetAndDisposable?.1.dispose()
}
private func take(offset: Int32, limit: Int32) -> [Int32: Data]? {
/*private func take(offset: Int32, limit: Int32) -> [Int32: Data]? {
assert(offset % dataHashLength == 0)
assert(limit % dataHashLength == 0)
@ -162,9 +185,9 @@ private final class MultipartCdnHashSource {
}
return result
}
}*/
func get(offset: Int32, limit: Int32) -> Signal<[Int32: Data], MultipartFetchDownloadError> {
/*func get(offset: Int32, limit: Int32) -> Signal<[Int32: Data], MultipartFetchDownloadError> {
assert(self.queue.isCurrent())
let queue = self.queue
@ -262,6 +285,130 @@ private final class MultipartCdnHashSource {
}
}
}))
}*/
func getCluster(offset: Int32, completion: @escaping ([Int32: Data]) -> Void, error: @escaping (MultipartFetchDownloadError) -> Void) -> Disposable {
precondition(offset % (1 * 1024 * 1024) == 0)
let clusterContext: ClusterContext
if let current = self.clusterContexts[offset] {
clusterContext = current
} else {
let disposable = MetaDisposable()
clusterContext = ClusterContext(disposable: disposable)
self.clusterContexts[offset] = clusterContext
disposable.set((self.masterDownload.request(Api.functions.upload.getCdnFileHashes(fileToken: Buffer(data: self.fileToken), offset: offset), tag: nil, continueInBackground: self.continueInBackground)
|> map { partHashes -> [Int32: Data] in
var parsedPartHashes: [Int32: Data] = [:]
for part in partHashes {
switch part {
case let .fileHash(offset, limit, bytes):
assert(limit == 128 * 1024)
parsedPartHashes[offset] = bytes.makeData()
}
}
return parsedPartHashes
}
|> deliverOn(self.queue)).start(next: { [weak self, weak clusterContext] result in
guard let _ = self, let clusterContext = clusterContext else {
return
}
clusterContext.result = result
for subscriber in clusterContext.subscribers.copyItems() {
subscriber.completion(result)
}
}, error: { [weak self, weak clusterContext] _ in
guard let _ = self, let clusterContext = clusterContext else {
return
}
clusterContext.error = .generic
for subscriber in clusterContext.subscribers.copyItems() {
subscriber.error(.generic)
}
}))
}
if let result = clusterContext.result {
completion(result)
return EmptyDisposable
} else if let errorValue = clusterContext.error {
error(errorValue)
return EmptyDisposable
} else {
let index = clusterContext.subscribers.add(ClusterContext.Subscriber(completion: completion, error: error))
let queue = self.queue
return ActionDisposable { [weak self, weak clusterContext] in
queue.async {
guard let strongSelf = self, let clusterContext = clusterContext else {
return
}
clusterContext.subscribers.remove(index)
if clusterContext.subscribers.isEmpty {
if strongSelf.clusterContexts[offset] === clusterContext {
strongSelf.clusterContexts.removeValue(forKey: offset)
}
}
}
}
}
}
private func cluster(offset: Int32) -> Signal<[Int32: Data], MultipartFetchDownloadError> {
let queue = self.queue
return Signal { [weak self] subscriber in
let disposable = MetaDisposable()
queue.async {
guard let strongSelf = self else {
subscriber.putError(.generic)
return
}
disposable.set(strongSelf.getCluster(offset: offset, completion: { result in
subscriber.putNext(result)
subscriber.putCompletion()
}, error: { error in
subscriber.putError(error)
}))
}
return disposable
}
}
func get(offset: Int32, limit: Int32) -> Signal<[Int32: Data], MultipartFetchDownloadError> {
precondition(offset % dataHashLength == 0)
precondition((offset + limit) % dataHashLength == 0)
var clusterOffsets = Set<Int32>()
for partOffset in stride(from: offset, to: offset + limit, by: Int(dataHashLength)) {
clusterOffsets.insert(partOffset - (partOffset % (1 * 1024 * 1024)))
}
return combineLatest(clusterOffsets.map { clusterOffset in
return self.cluster(offset: clusterOffset)
})
|> mapToSignal { clusterResults -> Signal<[Int32: Data], MultipartFetchDownloadError> in
var result: [Int32: Data] = [:]
for partOffset in stride(from: offset, to: offset + limit, by: Int(dataHashLength)) {
var found = false
for cluster in clusterResults {
if let data = cluster[partOffset] {
result[partOffset] = data
found = true
}
}
if !found {
return .fail(.generic)
}
}
return .single(result)
}
}
}

View File

@ -113,7 +113,7 @@ private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAtt
return true
case _ as EmojiSearchQueryMessageAttribute:
return true
case _ as ForwardHideSendersNamesMessageAttribute:
case _ as ForwardOptionsMessageAttribute:
return true
default:
return false
@ -132,7 +132,7 @@ private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAt
return true
case _ as OutgoingScheduleInfoMessageAttribute:
return true
case _ as ForwardHideSendersNamesMessageAttribute:
case _ as ForwardOptionsMessageAttribute:
return true
default:
return false
@ -520,6 +520,8 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
case let .forward(source, grouping, requestedAttributes, correlationId):
let sourceMessage = transaction.getMessage(source)
if let sourceMessage = sourceMessage, let author = sourceMessage.author ?? sourceMessage.peers[sourceMessage.id.peerId] {
var messageText = sourceMessage.text
if let peer = peer as? TelegramSecretChat {
var isAction = false
for media in sourceMessage.media {
@ -564,13 +566,24 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
var forwardInfo: StoreMessageForwardInfo?
var hideSendersNames = false
var hideCaptions = false
for attribute in requestedAttributes {
if let _ = attribute as? ForwardHideSendersNamesMessageAttribute {
hideSendersNames = true
if let attribute = attribute as? ForwardOptionsMessageAttribute {
hideSendersNames = attribute.hideNames
hideCaptions = attribute.hideCaptions
break
}
}
if hideCaptions {
for media in sourceMessage.media {
if media is TelegramMediaImage || media is TelegramMediaFile {
messageText = ""
break
}
}
}
if sourceMessage.id.namespace == Namespaces.Message.Cloud && peerId.namespace != Namespaces.Peer.SecretChat {
attributes.append(ForwardSourceInfoAttribute(messageId: sourceMessage.id))
@ -725,7 +738,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
augmentedMediaList = augmentedMediaList.map(convertForwardedMediaForSecretChat)
}
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: sourceMessage.text, attributes: attributes, media: augmentedMediaList))
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: augmentedMediaList))
}
}
}

View File

@ -2447,7 +2447,7 @@ func replayFinalState(accountManager: AccountManager<TelegramAccountManagerTypes
})
}
switch action.action {
case let .setChatTheme(emoji):
case let .setChatTheme(emoticon):
transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in
var current = current
if current == nil {
@ -2460,11 +2460,11 @@ func replayFinalState(accountManager: AccountManager<TelegramAccountManagerTypes
}
}
if let cachedData = current as? CachedUserData {
return cachedData.withUpdatedThemeEmoticon(!emoji.isEmpty ? emoji : nil)
return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil)
} else if let cachedData = current as? CachedGroupData {
return cachedData.withUpdatedThemeEmoticon(!emoji.isEmpty ? emoji : nil)
return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil)
} else if let cachedData = current as? CachedChannelData {
return cachedData.withUpdatedThemeEmoticon(!emoji.isEmpty ? emoji : nil)
return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil)
} else {
return current
}

View File

@ -698,6 +698,7 @@ public final class PendingMessageManager {
} else if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
var isForward = false
var hideSendersNames = false
var hideCaptions = false
var replyMessageId: Int32?
var scheduleTime: Int32?
@ -715,8 +716,9 @@ public final class PendingMessageManager {
} else if let attribute = attribute as? OutgoingScheduleInfoMessageAttribute {
flags |= Int32(1 << 10)
scheduleTime = attribute.scheduleTime
} else if let _ = attribute as? ForwardHideSendersNamesMessageAttribute {
hideSendersNames = true
} else if let attribute = attribute as? ForwardOptionsMessageAttribute {
hideSendersNames = attribute.hideNames
hideCaptions = attribute.hideCaptions
}
}
@ -728,6 +730,9 @@ public final class PendingMessageManager {
if hideSendersNames {
flags |= (1 << 11)
}
if hideCaptions {
flags |= (1 << 12)
}
var forwardIds: [(MessageId, Int64)] = []
for (message, content) in messages {

View File

@ -309,6 +309,6 @@ public final class CachedGroupData: CachedPeerData {
}
public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: callJoinPeerId, themeEmoticon: themeEmoticon)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: themeEmoticon)
}
}

View File

@ -1,13 +1,22 @@
import Foundation
import Postbox
public class ForwardHideSendersNamesMessageAttribute: MessageAttribute {
public init() {
public class ForwardOptionsMessageAttribute: MessageAttribute {
public let hideNames: Bool
public let hideCaptions: Bool
public init(hideNames: Bool, hideCaptions: Bool) {
self.hideNames = hideNames
self.hideCaptions = hideCaptions
}
required public init(decoder: PostboxDecoder) {
self.hideNames = decoder.decodeBoolForKey("hideNames", orElse: false)
self.hideCaptions = decoder.decodeBoolForKey("hideCaptions", orElse: false)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeBool(self.hideNames, forKey: "hideNames")
encoder.encodeBool(self.hideCaptions, forKey: "hideCaptions")
}
}

View File

@ -111,5 +111,13 @@ public extension TelegramEngine {
public func groupCall(peerId: PeerId, myPeerId: PeerId, id: Int64, accessHash: Int64, state: GroupCallParticipantsContext.State, previousServiceState: GroupCallParticipantsContext.ServiceState?) -> GroupCallParticipantsContext {
return GroupCallParticipantsContext(account: self.account, peerId: peerId, myPeerId: myPeerId, id: id, accessHash: accessHash, state: state, previousServiceState: previousServiceState)
}
public func serverTime() -> Signal<Int64, NoError> {
return self.account.network.currentGlobalTime
|> map { value -> Int64 in
return Int64(value * 1000.0)
}
|> take(1)
}
}
}

View File

@ -11,6 +11,7 @@ private class AdMessagesHistoryContextImpl {
case textEntities
case media
case authorId
case startParam
}
public let opaqueId: Data
@ -18,19 +19,22 @@ private class AdMessagesHistoryContextImpl {
public let textEntities: [MessageTextEntity]
public let media: [Media]
public let authorId: PeerId
public let startParam: String?
public init(
opaqueId: Data,
text: String,
textEntities: [MessageTextEntity],
media: [Media],
authorId: PeerId
authorId: PeerId,
startParam: String?
) {
self.opaqueId = opaqueId
self.text = text
self.textEntities = textEntities
self.media = media
self.authorId = authorId
self.startParam = startParam
}
public init(from decoder: Decoder) throws {
@ -47,6 +51,8 @@ private class AdMessagesHistoryContextImpl {
}
self.authorId = try container.decode(PeerId.self, forKey: .authorId)
self.startParam = try container.decodeIfPresent(String.self, forKey: .startParam)
}
public func encode(to encoder: Encoder) throws {
@ -64,6 +70,7 @@ private class AdMessagesHistoryContextImpl {
try container.encode(mediaData, forKey: .media)
try container.encode(self.authorId, forKey: .authorId)
try container.encodeIfPresent(self.startParam, forKey: .startParam)
}
public static func ==(lhs: CachedMessage, rhs: CachedMessage) -> Bool {
@ -87,13 +94,16 @@ private class AdMessagesHistoryContextImpl {
if lhs.authorId != rhs.authorId {
return false
}
if lhs.startParam != rhs.startParam {
return false
}
return true
}
func toMessage(peerId: PeerId, transaction: Transaction) -> Message {
var attributes: [MessageAttribute] = []
attributes.append(AdMessageAttribute(opaqueId: self.opaqueId))
attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, startParam: self.startParam))
if !self.textEntities.isEmpty {
let attribute = TextEntitiesMessageAttribute(entities: self.textEntities)
attributes.append(attribute)
@ -307,25 +317,27 @@ private class AdMessagesHistoryContextImpl {
for message in messages {
switch message {
case let .sponsoredMessage(_, randomId, _, fromId, message, media, entities):
case let .sponsoredMessage(_, randomId, fromId, startParam, message, entities):
var parsedEntities: [MessageTextEntity] = []
if let entities = entities {
parsedEntities = messageTextEntitiesFromApiEntities(entities)
}
var parsedMedia: [Media] = []
if let media = media {
let parsedMedia: [Media] = []
/*if let media = media {
let (mediaValue, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let mediaValue = mediaValue {
parsedMedia.append(mediaValue)
}
}
}*/
parsedMessages.append(CachedMessage(
opaqueId: randomId.makeData(),
text: message,
textEntities: parsedEntities,
media: parsedMedia, authorId: fromId.peerId
media: parsedMedia,
authorId: fromId.peerId,
startParam: startParam
))
}
}

View File

@ -63,6 +63,7 @@ public enum AdminLogEventAction {
case editExportedInvitation(previous: ExportedInvitation, updated: ExportedInvitation)
case participantJoinedViaInvite(ExportedInvitation)
case changeHistoryTTL(previousValue: Int32?, updatedValue: Int32?)
case changeTheme(previous: String?, updated: String?)
}
public enum ChannelAdminLogEventError {
@ -249,6 +250,8 @@ func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: PeerId, m
action = .groupCallUpdateParticipantVolume(peerId: parsedParticipant.peerId, volume: parsedParticipant.volume ?? 10000)
case let .channelAdminLogEventActionChangeHistoryTTL(prevValue, newValue):
action = .changeHistoryTTL(previousValue: prevValue, updatedValue: newValue)
case let .channelAdminLogEventActionChangeTheme(prevValue, newValue):
action = .changeTheme(previous: prevValue, updated: newValue)
}
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
if let action = action {

View File

@ -549,8 +549,11 @@ public extension TelegramEngine {
}
}
guard let peerGroupId = transaction.getPeerChatListIndex(peerId)?.0 else {
return nil
let peerGroupId: PeerGroupId
if let peerGroupIdValue = transaction.getPeerChatListIndex(peerId)?.0 {
peerGroupId = peerGroupIdValue
} else {
peerGroupId = .root
}
if let filterId = chatListFilterId {

View File

@ -111,7 +111,7 @@ func _internal_getChatThemes(accountManager: AccountManager<TelegramAccountManag
}
}
func _internal_setChatTheme(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, emoji: String?) -> Signal<Void, NoError> {
func _internal_setChatTheme(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, emoticon: String?) -> Signal<Void, NoError> {
return postbox.loadedPeerWithId(peerId)
|> mapToSignal { peer in
guard let inputPeer = apiInputPeer(peer) else {
@ -121,17 +121,17 @@ func _internal_setChatTheme(postbox: Postbox, network: Network, stateManager: Ac
return postbox.transaction { transaction -> Signal<Void, NoError> in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedUserData {
return current.withUpdatedThemeEmoticon(emoji)
return current.withUpdatedThemeEmoticon(emoticon)
} else if let current = current as? CachedGroupData {
return current.withUpdatedThemeEmoticon(emoji)
return current.withUpdatedThemeEmoticon(emoticon)
} else if let current = current as? CachedChannelData {
return current.withUpdatedThemeEmoticon(emoji)
return current.withUpdatedThemeEmoticon(emoticon)
} else {
return current
}
})
return network.request(Api.functions.messages.setChatTheme(peer: inputPeer, emoticon: emoji ?? ""))
return network.request(Api.functions.messages.setChatTheme(peer: inputPeer, emoticon: emoticon ?? ""))
|> `catch` { error in
return .complete()
}

View File

@ -13,8 +13,8 @@ public extension TelegramEngine {
return _internal_getChatThemes(accountManager: accountManager, network: self.account.network, forceUpdate: forceUpdate, onlyCached: onlyCached)
}
public func setChatTheme(peerId: PeerId, emoji: String?) -> Signal<Void, NoError> {
return _internal_setChatTheme(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, emoji: emoji)
public func setChatTheme(peerId: PeerId, emoticon: String?) -> Signal<Void, NoError> {
return _internal_setChatTheme(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, emoticon: emoticon)
}
}
}

View File

@ -167,6 +167,8 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case locationProximityAlertTip = 20
case nextChatSuggestionTip = 21
case dismissedTrendingStickerPacks = 22
case chatSpecificThemesDarkPreviewTip = 23
case chatForwardOptionsTip = 24
var key: ValueBoxKey {
let v = ValueBoxKey(length: 4)
@ -306,6 +308,14 @@ private struct ApplicationSpecificNoticeKeys {
static func dismissedTrendingStickerPacks() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedTrendingStickerPacks.key)
}
static func chatSpecificThemesDarkPreviewTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatSpecificThemesDarkPreviewTip.key)
}
static func chatForwardOptionsTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatForwardOptionsTip.key)
}
}
public struct ApplicationSpecificNotice {
@ -813,6 +823,56 @@ public struct ApplicationSpecificNotice {
}
}
public static func getChatSpecificThemesDarkPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatSpecificThemesDarkPreviewTip()) as? ApplicationSpecificCounterNotice {
return value.value
} else {
return 0
}
}
}
public static func incrementChatSpecificThemesDarkPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
return accountManager.transaction { transaction -> Int in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatSpecificThemesDarkPreviewTip()) as? ApplicationSpecificCounterNotice {
currentValue = value.value
}
let previousValue = currentValue
currentValue += Int32(count)
transaction.setNotice(ApplicationSpecificNoticeKeys.chatSpecificThemesDarkPreviewTip(), ApplicationSpecificCounterNotice(value: currentValue))
return Int(previousValue)
}
}
public static func getChatForwardOptionsTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatForwardOptionsTip()) as? ApplicationSpecificCounterNotice {
return value.value
} else {
return 0
}
}
}
public static func incrementChatForwardOptionsTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
return accountManager.transaction { transaction -> Int in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatForwardOptionsTip()) as? ApplicationSpecificCounterNotice {
currentValue = value.value
}
let previousValue = currentValue
currentValue += Int32(count)
transaction.setNotice(ApplicationSpecificNoticeKeys.chatForwardOptionsTip(), ApplicationSpecificCounterNotice(value: currentValue))
return Int(previousValue)
}
}
public static func reset(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
}

View File

@ -696,8 +696,10 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres
let buttonStrokeColor = accentColor.withMultiplied(hue: 1.014, saturation: 0.56, brightness: 0.64).withAlphaComponent(0.15)
let incomingFillColor = mainBackgroundColor.withMultipliedAlpha(0.9)
let incomingBubbleAlpha: CGFloat = 0.9
let message = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: [incomingFillColor], highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor, shadow: nil), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: [incomingFillColor], highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor, shadow: nil)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: UIColor(rgb: 0xff6767), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor, barIconForeground: .white, barPositive: UIColor(rgb: 0x00A700), barNegative: UIColor(rgb: 0xFE3824)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: [incomingFillColor.withAlphaComponent(incomingBubbleAlpha)], highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor, shadow: nil), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: [incomingFillColor.withAlphaComponent(incomingBubbleAlpha)], highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor, shadow: nil)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: UIColor(rgb: 0xff6767), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor, barIconForeground: .white, barPositive: UIColor(rgb: 0x00A700), barNegative: UIColor(rgb: 0xFE3824)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColors, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColors[0], shadow: nil), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColors, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColors[0], shadow: nil)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColors[0], pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: outgoingPrimaryTextColor, barIconForeground: .clear, barPositive: outgoingPrimaryTextColor, barNegative: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: [mainBackgroundColor], highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor, shadow: nil), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: [mainBackgroundColor], highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor, shadow: nil)),
infoPrimaryTextColor: UIColor(rgb: 0xffffff),

View File

@ -35,7 +35,7 @@ public let defaultServiceBackgroundColor = UIColor(rgb: 0x000000, alpha: 0.2)
public let defaultPresentationTheme = makeDefaultDayPresentationTheme(serviceBackgroundColor: defaultServiceBackgroundColor, day: false, preview: false)
public let defaultDayAccentColor = UIColor(rgb: 0x007ee5)
public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper forcedWallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor?) -> PresentationTheme {
public func customizeDefaultDayTheme(theme: PresentationTheme, specialMode: Bool = false, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper forcedWallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor?) -> PresentationTheme {
if (theme.referenceTheme != .day && theme.referenceTheme != .dayClassic) {
return theme
}
@ -52,24 +52,74 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti
var suggestedWallpaper: TelegramWallpaper?
var bubbleColors = bubbleColors
if bubbleColors.isEmpty, editing {
if day {
let accentColor = accentColor ?? defaultDayAccentColor
bubbleColors = [accentColor.withMultiplied(hue: 0.966, saturation: 0.61, brightness: 0.98).rgb, accentColor.rgb]
} else {
if let accentColor = accentColor, !accentColor.alpha.isZero {
let hsb = accentColor.hsb
bubbleColors = [UIColor(hue: hsb.0, saturation: (hsb.1 > 0.0 && hsb.2 > 0.0) ? 0.14 : 0.0, brightness: 0.79 + hsb.2 * 0.21, alpha: 1.0).rgb]
if accentColor.lightness > 0.705 {
outgoingAccent = UIColor(hue: hsb.0, saturation: min(1.0, hsb.1 * 1.1), brightness: min(hsb.2, 0.6), alpha: 1.0)
} else {
outgoingAccent = accentColor
if specialMode, bubbleColors.count < 3, let color = bubbleColors.first.flatMap({ UIColor(rgb: $0) }) {
let colorHSB = color.hsb
if colorHSB.b > 0.9 {
let bubbleColor = color.withMultiplied(hue: 0.9, saturation: 1.3, brightness: 1.0)
bubbleColors = [bubbleColor.rgb]
let colorPairs: [(UInt32, UInt32)] = [
(0xe5f9d7, 0x6cd516),
(0xe7f5ff, 0x43b6f9),
(0xe3f7f5, 0x4ccbb8),
(0xfff6cf, 0xe8b816),
(0xfffac9, 0xe2c714),
(0xc5a61e, 0xd6b534)
]
func generateAccentColor(color: UIColor) -> UIColor {
var nearest: (color: (UInt32, UInt32), distance: Int32)?
for (sample, accentSample) in colorPairs {
let distance = color.distance(to: UIColor(rgb: sample))
if let currentNearest = nearest {
if distance < currentNearest.distance {
nearest = ((sample, accentSample), distance)
}
} else {
nearest = ((sample, accentSample), distance)
}
}
suggestedWallpaper = .gradient(TelegramWallpaper.Gradient(id: nil, colors: defaultBuiltinWallpaperGradientColors.map(\.rgb), settings: WallpaperSettings()))
if let colors = nearest?.color {
let colorHsb = color.hsb
let similarColorHsb = UIColor(rgb: colors.0).hsb
let accentColorHsb = UIColor(rgb: colors.1).hsb
let correction = (similarColorHsb.0 > 0.0 ? colorHsb.0 / similarColorHsb.0 : 1.0, similarColorHsb.1 > 0.0 ? colorHsb.1 / similarColorHsb.1 : 1.0, similarColorHsb.2 > 0.0 ? colorHsb.2 / similarColorHsb.2 : 1.0)
let correctedComplementingColor = UIColor(hue: min(1.0, accentColorHsb.0 * correction.0), saturation: min(1.0, accentColorHsb.1 * correction.1), brightness: min(1.0, accentColorHsb.2 * correction.2), alpha: 1.0)
return correctedComplementingColor
} else {
return color
}
}
outgoingAccent = generateAccentColor(color: color)
} else {
let bubbleColor = color.withMultiplied(hue: 1.014, saturation: 0.12, brightness: 1.29)
bubbleColors = [bubbleColor.rgb]
outgoingAccent = color
}
} else {
if bubbleColors.isEmpty, editing {
if day {
let accentColor = accentColor ?? defaultDayAccentColor
bubbleColors = [accentColor.withMultiplied(hue: 0.966, saturation: 0.61, brightness: 0.98).rgb, accentColor.rgb]
} else {
bubbleColors = [UIColor(rgb: 0xe1ffc7).rgb]
suggestedWallpaper = .gradient(TelegramWallpaper.Gradient(id: nil, colors: defaultBuiltinWallpaperGradientColors.map(\.rgb), settings: WallpaperSettings()))
if let accentColor = accentColor, !accentColor.alpha.isZero {
let hsb = accentColor.hsb
bubbleColors = [UIColor(hue: hsb.0, saturation: (hsb.1 > 0.0 && hsb.2 > 0.0) ? 0.14 : 0.0, brightness: 0.79 + hsb.2 * 0.21, alpha: 1.0).rgb]
if accentColor.lightness > 0.705 {
outgoingAccent = UIColor(hue: hsb.0, saturation: min(1.0, hsb.1 * 1.1), brightness: min(hsb.2, 0.6), alpha: 1.0)
} else {
outgoingAccent = accentColor
}
suggestedWallpaper = .gradient(TelegramWallpaper.Gradient(id: nil, colors: defaultBuiltinWallpaperGradientColors.map(\.rgb), settings: WallpaperSettings()))
} else {
bubbleColors = [UIColor(rgb: 0xe1ffc7).rgb]
suggestedWallpaper = .gradient(TelegramWallpaper.Gradient(id: nil, colors: defaultBuiltinWallpaperGradientColors.map(\.rgb), settings: WallpaperSettings()))
}
}
}
}
@ -168,7 +218,7 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti
outgoingLinkTextColor = outgoingAccent
outgoingScamColor = UIColor(rgb: 0xff3b30)
outgoingControlColor = outgoingAccent
outgoingInactiveControlColor = outgoingAccent //1111
outgoingInactiveControlColor = outgoingAccent
outgoingFileTitleColor = outgoingAccent
outgoingPollsProgressColor = accentColor
outgoingSelectionColor = outgoingAccent.withMultiplied(hue: 1.0, saturation: 1.292, brightness: 0.871)

View File

@ -19,13 +19,13 @@ public func makeDefaultPresentationTheme(reference: PresentationBuiltinThemeRefe
return theme
}
public func customizePresentationTheme(_ theme: PresentationTheme, editing: Bool, title: String? = nil, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil) -> PresentationTheme {
public func customizePresentationTheme(_ theme: PresentationTheme, specialMode: Bool = false, editing: Bool, title: String? = nil, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil) -> PresentationTheme {
if accentColor == nil && bubbleColors.isEmpty && backgroundColors.isEmpty && wallpaper == nil {
return theme
}
switch theme.referenceTheme {
case .day, .dayClassic:
return customizeDefaultDayTheme(theme: theme, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors ?? false, wallpaper: wallpaper, serviceBackgroundColor: nil)
return customizeDefaultDayTheme(theme: theme, specialMode: specialMode, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors ?? false, wallpaper: wallpaper, serviceBackgroundColor: nil)
case .night:
return customizeDefaultDarkPresentationTheme(theme: theme, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors ?? false, wallpaper: wallpaper, baseColor: baseColor)
case .nightAccent:
@ -33,17 +33,21 @@ public func customizePresentationTheme(_ theme: PresentationTheme, editing: Bool
}
}
public func makePresentationTheme(settings: TelegramThemeSettings, title: String? = nil, serviceBackgroundColor: UIColor? = nil) -> PresentationTheme? {
let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), extendingThemeReference: nil, serviceBackgroundColor: serviceBackgroundColor, preview: false)
return customizePresentationTheme(defaultTheme, editing: true, title: title, accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper)
public func makePresentationTheme(settings: TelegramThemeSettings, specialMode: Bool = false, title: String? = nil, serviceBackgroundColor: UIColor? = nil) -> PresentationTheme? {
var baseTheme: TelegramBaseTheme = settings.baseTheme
if specialMode && baseTheme == .night {
baseTheme = .tinted
}
let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: baseTheme), extendingThemeReference: nil, serviceBackgroundColor: serviceBackgroundColor, preview: false)
return customizePresentationTheme(defaultTheme, specialMode: specialMode, editing: true, title: title, accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper)
}
public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, extendingThemeReference: PresentationThemeReference? = nil, accentColor: UIColor? = nil, backgroundColors: [UInt32] = [], bubbleColors: [UInt32] = [], animateBubbleColors: Bool? = nil, wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil, serviceBackgroundColor: UIColor? = nil, preview: Bool = false) -> PresentationTheme? {
public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, extendingThemeReference: PresentationThemeReference? = nil, accentColor: UIColor? = nil, backgroundColors: [UInt32] = [], bubbleColors: [UInt32] = [], animateBubbleColors: Bool? = nil, wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil, serviceBackgroundColor: UIColor? = nil, specialMode: Bool = false, preview: Bool = false) -> PresentationTheme? {
let theme: PresentationTheme
switch themeReference {
case let .builtin(reference):
let defaultTheme = makeDefaultPresentationTheme(reference: reference, extendingThemeReference: extendingThemeReference, serviceBackgroundColor: serviceBackgroundColor, preview: preview)
theme = customizePresentationTheme(defaultTheme, editing: true, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors, wallpaper: wallpaper, baseColor: baseColor)
theme = customizePresentationTheme(defaultTheme, specialMode: specialMode, editing: true, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors, wallpaper: wallpaper, baseColor: baseColor)
case let .local(info):
if let path = mediaBox.completedResourcePath(info.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, themeReference: themeReference, resolvedWallpaper: info.resolvedWallpaper) {
theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors, wallpaper: wallpaper)

View File

@ -136,6 +136,11 @@ public enum PresentationResourceKey: Int32 {
case chatInputPanelEncircledCloseIconImage
case chatInputPanelVerticalSeparatorLineImage
case chatInputPanelForwardIconImage
case chatInputPanelReplyIconImage
case chatInputPanelEditIconImage
case chatInputPanelWebpageIconImage
case chatMediaInputPanelHighlightedIconImage
case chatInputMediaPanelSavedStickersIconImage
case chatInputMediaPanelRecentStickersIconImage

View File

@ -195,7 +195,7 @@ public struct PresentationResourcesChat {
return generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.copy)
context.setStrokeColor(theme.chat.inputPanel.panelControlColor.cgColor)
context.setStrokeColor(theme.chat.inputPanel.panelControlAccentColor.cgColor)
context.setLineWidth(2.0)
context.setLineCap(.round)
context.move(to: CGPoint(x: 1.0, y: 1.0))
@ -226,6 +226,30 @@ public struct PresentationResourcesChat {
})
}
public static func chatInputPanelForwardIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelForwardIconImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/ForwardIcon"), color: theme.chat.inputPanel.panelControlAccentColor)
})
}
public static func chatInputPanelReplyIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelReplyIconImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/ReplyIcon"), color: theme.chat.inputPanel.panelControlAccentColor)
})
}
public static func chatInputPanelEditIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelEditIconImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/EditIcon"), color: theme.chat.inputPanel.panelControlAccentColor)
})
}
public static func chatInputPanelWebpageIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelWebpageIconImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/WebpageIcon"), color: theme.chat.inputPanel.panelControlAccentColor)
})
}
public static func chatMediaInputPanelHighlightedIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatMediaInputPanelHighlightedIconImage.rawValue, { theme in
return generateStretchableFilledCircleImage(radius: 9.0, color: theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor)
@ -234,7 +258,7 @@ public struct PresentationResourcesChat {
public static func chatInputMediaPanelSavedStickersIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputMediaPanelSavedStickersIconImage.rawValue, { theme in
return generateImage(CGSize(width: 26.0, height: 26.0), contextGenerator: { size, context in
return generateImage(CGSize(width: 42.0, height: 42.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/SavedStickersTabIcon"), color: theme.chat.inputMediaPanel.panelIconColor) {
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size))
@ -245,7 +269,7 @@ public struct PresentationResourcesChat {
public static func chatInputMediaPanelStickersModeIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputMediaPanelStickersModeIcon.rawValue, { theme in
return generateImage(CGSize(width: 26.0, height: 26.0), contextGenerator: { size, context in
return generateImage(CGSize(width: 42.0, height: 42.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/StickersMode"), color: theme.chat.inputMediaPanel.panelIconColor) {
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size))
@ -256,7 +280,7 @@ public struct PresentationResourcesChat {
public static func chatInputMediaPanelTrendingGifsIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputMediaPanelTrendingGifsIcon.rawValue, { theme in
return generateImage(CGSize(width: 26.0, height: 26.0), contextGenerator: { size, context in
return generateImage(CGSize(width: 42.0, height: 42.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/TrendingGifs"), color: theme.chat.inputMediaPanel.panelIconColor) {
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size))
@ -267,7 +291,7 @@ public struct PresentationResourcesChat {
public static func chatInputMediaPanelRecentStickersIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputMediaPanelRecentStickersIconImage.rawValue, { theme in
return generateImage(CGSize(width: 26.0, height: 26.0), contextGenerator: { size, context in
return generateImage(CGSize(width: 42.0, height: 42.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/RecentTabIcon"), color: theme.chat.inputMediaPanel.panelIconColor) {
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size))
@ -278,7 +302,7 @@ public struct PresentationResourcesChat {
public static func chatInputMediaPanelRecentGifsIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputMediaPanelRecentGifsIconImage.rawValue, { theme in
return generateImage(CGSize(width: 26.0, height: 26.0), contextGenerator: { size, context in
return generateImage(CGSize(width: 42.0, height: 42.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/GifsTabIcon"), color: theme.chat.inputMediaPanel.panelIconColor) {
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size))

View File

@ -41,6 +41,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
let bodyAttributes = MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [:])
let boldAttributes = MarkdownAttributeSet(font: titleBoldFont, textColor: primaryTextColor, additionalAttributes: [:])
for media in message.media {
if let action = media as? TelegramMediaAction {
@ -439,7 +440,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
case let .groupPhoneCall(_, _, scheduleDate, duration):
if let scheduleDate = scheduleDate {
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
let titleString = humanReadableStringForTimestamp(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: scheduleDate, alwaysShowTime: true, allowYesterday: false, format: HumanReadableStringFormat(dateFormatString: { strings.Notification_VoiceChatScheduledChannel($0) }, tomorrowFormatString: { strings.Notification_VoiceChatScheduledTomorrowChannel($0) }, todayFormatString: { strings.Notification_VoiceChatScheduledTodayChannel($0) }))
let titleString: PresentationStrings.FormattedString
if let channel = message.author as? TelegramChannel, case .broadcast = channel.info {
titleString = humanReadableStringForTimestamp(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: scheduleDate, alwaysShowTime: true, allowYesterday: false, format: HumanReadableStringFormat(dateFormatString: { strings.Notification_LiveStreamScheduled($0) }, tomorrowFormatString: { strings.Notification_LiveStreamScheduledTomorrow($0) }, todayFormatString: { strings.Notification_LiveStreamScheduledToday($0) }))
} else {
titleString = humanReadableStringForTimestamp(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: scheduleDate, alwaysShowTime: true, allowYesterday: false, format: HumanReadableStringFormat(dateFormatString: { strings.Notification_VoiceChatScheduledChannel($0) }, tomorrowFormatString: { strings.Notification_VoiceChatScheduledTomorrowChannel($0) }, todayFormatString: { strings.Notification_VoiceChatScheduledTodayChannel($0) }))
}
attributedString = NSAttributedString(string: titleString.string, font: titleFont, textColor: primaryTextColor)
} else {
let titleString = humanReadableStringForTimestamp(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: scheduleDate, alwaysShowTime: true, allowYesterday: false, format: HumanReadableStringFormat(dateFormatString: { strings.Notification_VoiceChatScheduled(authorName, $0) }, tomorrowFormatString: { strings.Notification_VoiceChatScheduledTomorrow(authorName, $0) }, todayFormatString: { strings.Notification_VoiceChatScheduledToday(authorName, $0) }))
@ -448,7 +454,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
} else if let duration = duration {
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
let titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).string
let titleString: String
if let channel = message.author as? TelegramChannel, case .broadcast = channel.info {
titleString = strings.Notification_LiveStreamEnded(callDurationString(strings: strings, value: duration)).string
} else {
titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).string
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
} else {
let attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
@ -457,8 +468,13 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
} else {
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
let titleString = strings.Notification_VoiceChatStartedChannel
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
let titleString: String
if let channel = message.author as? TelegramChannel, case .broadcast = channel.info {
titleString = strings.Notification_LiveStreamStarted
} else {
titleString = strings.Notification_VoiceChatStartedChannel
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
} else {
let attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
let titleString = strings.Notification_VoiceChatStarted(authorName)
@ -530,7 +546,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
case let .setChatTheme(emoji):
if emoji.isEmpty {
if message.author?.id == accountPeerId {
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
attributedString = NSAttributedString(string: strings.Notification_ChannelDisabledTheme, font: titleFont, textColor: primaryTextColor)
} else if message.author?.id == accountPeerId {
attributedString = NSAttributedString(string: strings.Notification_YouDisabledTheme, font: titleFont, textColor: primaryTextColor)
} else {
let attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
@ -538,12 +556,13 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
}
} else {
if message.author?.id == accountPeerId {
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
attributedString = NSAttributedString(string: strings.Notification_ChannelChangedTheme(emoji).string, font: titleFont, textColor: primaryTextColor)
} else if message.author?.id == accountPeerId {
attributedString = NSAttributedString(string: strings.Notification_YouChangedTheme(emoji).string, font: titleFont, textColor: primaryTextColor)
} else {
let attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
let resultTitleString = strings.Notification_ChangedTheme(authorName, emoji)
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
}
}
case .unknown:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,105 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 5.557617 5.409590 cm
0.000000 0.000000 0.000000 scn
17.940434 18.091030 m
17.391258 18.640203 16.500872 18.640203 15.951696 18.091030 c
15.220630 17.359962 l
15.166833 17.292160 l
15.092115 17.171638 15.092115 17.017958 15.166833 16.897436 c
15.220630 16.829634 l
16.682323 15.373500 l
16.750134 15.319892 l
16.894726 15.230575 17.086790 15.248649 17.212147 15.374005 c
17.940434 16.102291 l
18.049709 16.224945 l
18.486807 16.776924 18.450382 17.581081 17.940434 18.091030 c
h
14.264824 15.991543 m
14.120199 16.081203 13.927823 16.063271 13.802298 15.937746 c
1.567463 3.702911 l
1.505585 3.635180 l
1.420033 3.534018 1.322288 3.389440 1.212350 3.201446 c
1.038298 2.886894 l
0.845957 2.507218 l
0.812376 2.438513 0.778033 2.367094 0.742928 2.292961 c
0.523152 1.815601 l
0.285087 1.273121 l
0.028709 0.665525 l
0.004916 0.582302 l
-0.022651 0.414373 0.067465 0.243452 0.231453 0.175440 c
0.323435 0.137291 0.426812 0.137291 0.518794 0.175440 c
1.126400 0.431795 l
1.668884 0.669861 l
1.753871 0.708015 1.836145 0.745407 1.915705 0.782036 c
2.360504 0.992668 l
2.740180 1.185009 l
3.054732 1.359062 l
3.289725 1.496485 3.456881 1.614855 3.556201 1.714174 c
15.791036 13.949008 l
15.844832 14.016811 l
15.934494 14.161437 15.916561 14.353812 15.791036 14.479338 c
14.332628 15.937746 l
14.264824 15.991543 l
h
f*
n
Q
endstream
endobj
3 0 obj
1385
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001475 00000 n
0000001498 00000 n
0000001671 00000 n
0000001745 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1804
%%EOF

View File

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

View File

@ -0,0 +1,100 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 4.000732 4.530336 cm
0.000000 0.000000 0.000000 scn
12.230824 17.257004 m
12.217503 17.237732 12.204754 17.209534 12.204754 17.161430 c
12.204754 13.844664 l
12.204754 13.386267 11.833150 13.014664 11.374754 13.014664 c
7.468079 13.014664 5.174883 11.337335 3.779674 9.261284 c
2.536480 7.411429 1.984342 5.205478 1.745940 3.479214 c
3.423516 5.195779 6.440499 7.174664 11.374754 7.174664 c
11.594884 7.174664 11.805998 7.087217 11.961653 6.931562 c
12.117308 6.775908 12.204754 6.564794 12.204754 6.344664 c
12.204754 3.027895 l
12.204754 2.979794 12.217503 2.951595 12.230824 2.932323 c
12.246586 2.909521 12.272385 2.886978 12.307174 2.871906 c
12.341963 2.856834 12.376053 2.853432 12.403469 2.857529 c
12.426641 2.860991 12.455934 2.870977 12.491026 2.903875 c
20.028908 9.970642 l
20.100548 10.037804 20.100548 10.151523 20.028908 10.218686 c
12.491024 17.285452 l
12.455932 17.318352 12.426641 17.328337 12.403469 17.331800 c
12.376053 17.335897 12.341963 17.332493 12.307174 17.317421 c
12.272386 17.302349 12.246587 17.279808 12.230824 17.257004 c
h
10.544754 17.161430 m
10.544754 18.763487 12.457608 19.592196 13.626367 18.496485 c
21.164249 11.429716 l
21.935432 10.706734 21.935434 9.482596 21.164251 8.759610 c
13.626369 1.692844 l
12.457612 0.597136 10.544754 1.425837 10.544754 3.027895 c
10.544754 5.493531 l
6.222875 5.270346 3.785475 3.326345 2.558439 1.912291 c
2.109840 1.395319 1.440466 1.356445 0.981544 1.502619 c
0.518860 1.649990 -0.070318 2.115324 0.006868 2.903498 c
0.193950 4.813872 0.749841 7.728971 2.401903 10.187214 c
3.980999 12.536886 6.535074 14.422771 10.544754 14.651388 c
10.544754 17.161430 l
h
f*
n
Q
endstream
endobj
3 0 obj
1710
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001800 00000 n
0000001823 00000 n
0000001996 00000 n
0000002070 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2129
%%EOF

View File

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

View File

@ -0,0 +1,100 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
-1.000000 -0.000000 -0.000000 1.000000 25.743408 4.905336 cm
0.000000 0.000000 0.000000 scn
12.230824 17.257004 m
12.217503 17.237732 12.204754 17.209534 12.204754 17.161430 c
12.204754 13.844664 l
12.204754 13.386267 11.833150 13.014664 11.374754 13.014664 c
7.468079 13.014664 5.174883 11.337335 3.779674 9.261284 c
2.536480 7.411429 1.984342 5.205478 1.745940 3.479214 c
3.423516 5.195779 6.440499 7.174664 11.374754 7.174664 c
11.594884 7.174664 11.805998 7.087217 11.961653 6.931562 c
12.117308 6.775908 12.204754 6.564794 12.204754 6.344664 c
12.204754 3.027895 l
12.204754 2.979794 12.217503 2.951595 12.230824 2.932323 c
12.246586 2.909521 12.272385 2.886978 12.307174 2.871906 c
12.341963 2.856834 12.376053 2.853432 12.403469 2.857529 c
12.426641 2.860991 12.455934 2.870977 12.491026 2.903875 c
20.028908 9.970642 l
20.100548 10.037804 20.100548 10.151523 20.028908 10.218686 c
12.491024 17.285452 l
12.455932 17.318352 12.426641 17.328337 12.403469 17.331800 c
12.376053 17.335897 12.341963 17.332493 12.307174 17.317421 c
12.272386 17.302349 12.246587 17.279808 12.230824 17.257004 c
h
10.544754 17.161430 m
10.544754 18.763487 12.457608 19.592196 13.626367 18.496485 c
21.164249 11.429716 l
21.935432 10.706734 21.935434 9.482596 21.164251 8.759610 c
13.626369 1.692844 l
12.457612 0.597136 10.544754 1.425837 10.544754 3.027895 c
10.544754 5.493531 l
6.222875 5.270346 3.785475 3.326345 2.558439 1.912291 c
2.109840 1.395319 1.440466 1.356445 0.981544 1.502619 c
0.518860 1.649990 -0.070318 2.115324 0.006868 2.903498 c
0.193950 4.813872 0.749841 7.728971 2.401903 10.187214 c
3.980999 12.536886 6.535074 14.422771 10.544754 14.651388 c
10.544754 17.161430 l
h
f*
n
Q
endstream
endobj
3 0 obj
1713
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001803 00000 n
0000001826 00000 n
0000001999 00000 n
0000002073 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2132
%%EOF

View File

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

View File

@ -0,0 +1,107 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 4.120117 9.915810 cm
0.000000 0.000000 0.000000 scn
12.468664 12.994003 m
14.215596 14.740936 17.047935 14.740936 18.794868 12.994003 c
20.541801 11.247070 20.541801 8.414734 18.794868 6.667801 c
16.294868 4.167801 l
14.547935 2.420868 11.715596 2.420868 9.968664 4.167801 c
9.736349 4.400116 9.535478 4.650879 9.365597 4.915045 c
9.117653 5.300597 8.604102 5.412152 8.218549 5.164208 c
7.832996 4.916265 7.721442 4.402714 7.969386 4.017161 c
8.202941 3.653981 8.478252 3.310617 8.794867 2.994003 c
11.190070 0.598801 15.073462 0.598801 17.468666 2.994003 c
19.968666 5.494003 l
22.363869 7.889207 22.363869 11.772597 19.968666 14.167801 c
17.573463 16.563004 13.690070 16.563004 11.294867 14.167801 c
8.794867 11.667801 l
8.470732 11.343666 8.470732 10.818138 8.794867 10.494003 c
9.119002 10.169868 9.644528 10.169868 9.968664 10.494003 c
12.468664 12.994003 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 4.120117 9.915810 cm
0.000000 0.000000 0.000000 scn
9.296402 -2.832201 m
7.549469 -4.579134 4.717132 -4.579134 2.970200 -2.832201 c
1.223267 -1.085268 1.223267 1.747069 2.970200 3.494002 c
5.470199 5.994002 l
7.217132 7.740935 10.049470 7.740935 11.796402 5.994002 c
12.028717 5.761687 12.229589 5.510924 12.399469 5.246758 c
12.647413 4.861206 13.160964 4.749651 13.546517 4.997595 c
13.932070 5.245539 14.043624 5.759089 13.795681 6.144642 c
13.562125 6.507822 13.286814 6.851186 12.970200 7.167800 c
10.574996 9.563003 6.691605 9.563003 4.296402 7.167800 c
1.796402 4.667800 l
-0.598801 2.272596 -0.598801 -1.610796 1.796402 -4.005999 c
4.191605 -6.401201 8.074997 -6.401201 10.470200 -4.005999 c
12.970200 -1.505999 l
13.294334 -1.181864 13.294334 -0.656336 12.970200 -0.332201 c
12.646064 -0.008066 12.120538 -0.008066 11.796402 -0.332201 c
9.296402 -2.832201 l
h
f
n
Q
endstream
endobj
3 0 obj
1852
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001942 00000 n
0000001965 00000 n
0000002138 00000 n
0000002212 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2271
%%EOF

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "ic_input_gifs.pdf",
"filename" : "gif_48.pdf",
"idiom" : "universal"
}
],

View File

@ -0,0 +1,249 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 30.400024 17.342773 cm
0.000000 0.000000 0.000000 scn
-1.328000 2.657227 m
-1.328000 1.923792 -0.733434 1.329226 0.000000 1.329226 c
0.733434 1.329226 1.328000 1.923792 1.328000 2.657227 c
-1.328000 2.657227 l
h
4.000000 9.329226 m
4.733434 9.329226 5.328000 9.923793 5.328000 10.657227 c
5.328000 11.390660 4.733434 11.985227 4.000000 11.985227 c
4.000000 9.329226 l
h
1.328000 2.657227 m
1.328000 9.377226 l
-1.328000 9.377226 l
-1.328000 2.657227 l
1.328000 2.657227 l
h
1.280000 9.329226 m
4.000000 9.329226 l
4.000000 11.985227 l
1.280000 11.985227 l
1.280000 9.329226 l
h
1.328000 9.377226 m
1.328000 9.492270 1.328034 9.578946 1.329198 9.653058 c
1.330364 9.727207 1.332460 9.769691 1.334489 9.794523 c
1.338678 9.845800 1.338141 9.750368 1.270451 9.617519 c
-1.096062 10.823318 l
-1.250946 10.519341 -1.295081 10.226333 -1.312690 10.010806 c
-1.329033 9.810783 -1.328000 9.579334 -1.328000 9.377226 c
1.328000 9.377226 l
h
1.280000 11.985227 m
1.077892 11.985227 0.846443 11.986259 0.646420 11.969916 c
0.430894 11.952308 0.137886 11.908173 -0.166092 11.753288 c
1.039707 9.386775 l
0.906858 9.319085 0.811426 9.318548 0.862703 9.322738 c
0.887536 9.324766 0.930019 9.326862 1.004169 9.328028 c
1.078280 9.329193 1.164957 9.329226 1.280000 9.329226 c
1.280000 11.985227 l
h
1.270451 9.617519 m
1.219830 9.518170 1.139057 9.437396 1.039707 9.386775 c
-0.166092 11.753288 l
-0.566501 11.549270 -0.892043 11.223727 -1.096062 10.823318 c
1.270451 9.617519 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 30.400024 22.144531 cm
0.000000 0.000000 0.000000 scn
0.000000 3.183762 m
-0.733434 3.183762 -1.328000 2.589196 -1.328000 1.855762 c
-1.328000 1.122328 -0.733434 0.527762 0.000000 0.527762 c
0.000000 3.183762 l
h
4.000000 0.527762 m
4.733434 0.527762 5.328000 1.122328 5.328000 1.855762 c
5.328000 2.589196 4.733434 3.183762 4.000000 3.183762 c
4.000000 0.527762 l
h
0.000000 0.527762 m
4.000000 0.527762 l
4.000000 3.183762 l
0.000000 3.183762 l
0.000000 0.527762 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 24.799927 17.343750 cm
0.000000 0.000000 0.000000 scn
2.128000 10.656250 m
2.128000 11.389684 1.533434 11.984250 0.800000 11.984250 c
0.066566 11.984250 -0.528000 11.389684 -0.528000 10.656250 c
2.128000 10.656250 l
h
-0.528000 2.656250 m
-0.528000 1.922815 0.066566 1.328250 0.800000 1.328250 c
1.533434 1.328250 2.128000 1.922815 2.128000 2.656250 c
-0.528000 2.656250 l
h
-0.528000 10.656250 m
-0.528000 2.656250 l
2.128000 2.656250 l
2.128000 10.656250 l
-0.528000 10.656250 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 13.599976 17.343750 cm
0.000000 0.000000 0.000000 scn
5.231635 9.028395 m
5.882138 8.689614 6.684110 8.942316 7.022890 9.592818 c
7.361670 10.243321 7.108968 11.045293 6.458466 11.384073 c
5.231635 9.028395 l
h
7.584592 5.588284 m
8.889243 5.340351 l
8.901488 5.404787 l
8.907325 5.470116 l
7.584592 5.588284 l
h
5.600000 7.984250 m
4.866566 7.984250 4.272000 7.389684 4.272000 6.656250 c
4.272000 5.922816 4.866566 5.328250 5.600000 5.328250 c
5.600000 7.984250 l
h
6.458466 11.384073 m
5.721497 11.767884 4.884068 11.984250 4.000000 11.984250 c
4.000000 9.328250 l
4.446894 9.328250 4.864512 9.219591 5.231635 9.028395 c
6.458466 11.384073 l
h
4.000000 11.984250 m
1.057427 11.984250 -1.328000 9.598824 -1.328000 6.656250 c
1.328000 6.656250 l
1.328000 8.131955 2.524295 9.328250 4.000000 9.328250 c
4.000000 11.984250 l
h
-1.328000 6.656250 m
-1.328000 3.713677 1.057427 1.328250 4.000000 1.328250 c
4.000000 3.984250 l
2.524295 3.984250 1.328000 5.180545 1.328000 6.656250 c
-1.328000 6.656250 l
h
4.000000 1.328250 m
6.144260 1.328250 8.394547 2.737200 8.889243 5.340351 c
6.279942 5.836216 l
6.056704 4.661514 5.055740 3.984250 4.000000 3.984250 c
4.000000 1.328250 l
h
8.907325 5.470116 m
8.956036 6.015371 l
6.310571 6.251707 l
6.261860 5.706451 l
8.907325 5.470116 l
h
7.155208 7.984250 m
5.600000 7.984250 l
5.600000 5.328250 l
7.155208 5.328250 l
7.155208 7.984250 l
h
8.956036 6.015371 m
9.050537 7.073197 8.217244 7.984250 7.155208 7.984250 c
7.155208 5.328250 l
6.657084 5.328250 6.266247 5.755558 6.310571 6.251707 c
8.956036 6.015371 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 5.599976 2.943848 cm
0.000000 0.000000 0.000000 scn
35.472000 21.056055 m
35.472000 11.627451 27.828604 3.984055 18.400000 3.984055 c
18.400000 1.328056 l
29.295473 1.328056 38.127998 10.160582 38.127998 21.056055 c
35.472000 21.056055 l
h
18.400000 3.984055 m
8.971395 3.984055 1.328000 11.627451 1.328000 21.056055 c
-1.328000 21.056055 l
-1.328000 10.160582 7.504526 1.328056 18.400000 1.328056 c
18.400000 3.984055 l
h
1.328000 21.056055 m
1.328000 30.484661 8.971395 38.128056 18.400000 38.128056 c
18.400000 40.784054 l
7.504526 40.784054 -1.328000 31.951529 -1.328000 21.056055 c
1.328000 21.056055 l
h
18.400000 38.128056 m
27.828604 38.128056 35.472000 30.484661 35.472000 21.056055 c
38.127998 21.056055 l
38.127998 31.951529 29.295473 40.784054 18.400000 40.784054 c
18.400000 38.128056 l
h
f
n
Q
endstream
endobj
3 0 obj
5025
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 48.000000 48.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000005115 00000 n
0000005138 00000 n
0000005311 00000 n
0000005385 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
5444
%%EOF

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "ic_input_recent.pdf",
"filename" : "recent_48.pdf",
"idiom" : "universal"
}
],

View File

@ -0,0 +1,89 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 4.271973 4.271973 cm
0.000000 0.000000 0.000000 scn
2.656000 19.728054 m
2.656000 29.156660 10.299395 36.800056 19.728001 36.800056 c
29.156605 36.800056 36.799999 29.156660 36.799999 19.728054 c
36.799999 10.299450 29.156605 2.656055 19.728001 2.656055 c
10.299395 2.656055 2.656000 10.299450 2.656000 19.728054 c
h
19.728001 39.456055 m
8.832526 39.456055 0.000000 30.623528 0.000000 19.728054 c
0.000000 8.832581 8.832526 0.000057 19.728001 0.000057 c
30.623474 0.000057 39.455997 8.832581 39.455997 19.728054 c
39.455997 30.623528 30.623474 39.456055 19.728001 39.456055 c
h
21.056000 30.928055 m
21.056000 31.661489 20.461435 32.256054 19.728001 32.256054 c
18.994566 32.256054 18.400002 31.661489 18.400002 30.928055 c
18.400002 20.440632 l
18.400002 19.610584 18.752302 18.819542 19.369272 18.264267 c
26.839615 11.540958 l
27.384773 11.050316 28.224453 11.094513 28.715096 11.639668 c
29.205738 12.184826 29.161543 13.024506 28.616386 13.515148 c
21.146042 20.238457 l
21.088728 20.290041 21.056000 20.363525 21.056000 20.440632 c
21.056000 30.928055 l
h
f*
n
Q
endstream
endobj
3 0 obj
1137
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 48.000000 48.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001227 00000 n
0000001250 00000 n
0000001423 00000 n
0000001497 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1556
%%EOF

View File

@ -1,22 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "StickerKeyboardFavoriteTab@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "StickerKeyboardFavoriteTab@3x.png",
"scale" : "3x"
"filename" : "fave_48.pdf",
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View File

@ -0,0 +1,247 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 5.000000 3.946289 cm
0.000000 0.000000 0.000000 scn
8.635828 2.820679 m
9.321689 1.681164 l
8.635828 2.820679 l
h
9.731453 16.567810 m
10.600920 17.574253 l
9.731453 16.567810 l
h
1.384685 25.091970 m
1.496809 23.766705 l
1.384685 25.091970 l
h
1.051567 24.066368 m
1.921034 25.072811 l
1.051567 24.066368 l
h
20.038393 37.786930 m
18.814356 37.266705 l
20.038393 37.786930 l
h
18.961609 37.786930 m
17.737572 38.307156 l
18.961609 37.786930 l
h
26.180216 26.059437 m
26.292339 27.384703 l
26.180216 26.059437 l
h
24.508167 27.270023 m
23.284132 26.749798 l
24.508167 27.270023 l
h
37.948433 24.066368 m
38.817898 23.059925 l
37.948433 24.066368 l
h
37.615314 25.091970 m
37.503193 23.766705 l
37.615314 25.091970 l
h
28.628592 14.598795 m
27.333555 14.295841 l
28.628592 14.598795 l
h
29.268547 16.567810 m
28.399080 17.574253 l
29.268547 16.567810 l
h
30.364174 2.820679 m
31.050034 3.960194 l
31.050034 3.960194 l
30.364174 2.820679 l
h
31.235470 3.455151 m
32.530506 3.758102 l
31.235470 3.455151 l
h
19.801676 9.178118 m
19.115814 8.038603 l
19.115814 8.038603 l
19.801676 9.178118 l
h
19.115814 8.038603 m
29.678312 1.681164 l
31.050034 3.960194 l
20.487535 10.317635 l
19.115814 8.038603 l
h
32.530506 3.758102 m
29.923628 14.901747 l
27.333555 14.295841 l
29.940432 3.152195 l
32.530506 3.758102 l
h
30.138014 15.561367 m
38.817898 23.059925 l
37.078964 25.072811 l
28.399080 17.574253 l
30.138014 15.561367 l
h
37.727440 26.417236 m
26.292339 27.384703 l
26.068092 24.734173 l
37.503193 23.766705 l
37.727440 26.417236 l
h
25.732204 27.790249 m
21.262428 38.307156 l
18.814356 37.266705 l
23.284132 26.749798 l
25.732204 27.790249 l
h
17.737572 38.307156 m
13.267798 27.790249 l
15.715871 26.749798 l
20.185644 37.266705 l
17.737572 38.307156 l
h
12.707662 27.384703 m
1.272561 26.417236 l
1.496809 23.766705 l
12.931910 24.734173 l
12.707662 27.384703 l
h
0.182100 23.059925 m
8.861986 15.561367 l
10.600920 17.574253 l
1.921034 25.072811 l
0.182100 23.059925 l
h
9.076371 14.901747 m
6.469495 3.758102 l
9.059568 3.152195 l
11.666444 14.295841 l
9.076371 14.901747 l
h
9.321689 1.681164 m
19.884186 8.038603 l
18.512465 10.317635 l
7.949968 3.960194 l
9.321689 1.681164 l
h
6.469495 3.758102 m
6.083770 2.109238 7.870839 0.807915 9.321689 1.681164 c
7.949968 3.960194 l
8.514397 4.299919 9.209628 3.793659 9.059568 3.152195 c
6.469495 3.758102 l
h
8.861986 15.561367 m
9.050550 15.398468 9.133131 15.144379 9.076371 14.901747 c
11.666444 14.295841 l
11.948550 15.501760 11.538106 16.764618 10.600920 17.574253 c
8.861986 15.561367 l
h
1.272561 26.417236 m
-0.416872 26.274302 -1.100905 24.168316 0.182100 23.059925 c
1.921034 25.072811 l
2.420168 24.641609 2.154054 23.822311 1.496809 23.766705 c
1.272561 26.417236 l
h
13.267798 27.790249 m
13.170667 27.561710 12.955100 27.405638 12.707662 27.384703 c
12.931910 24.734173 l
14.161719 24.838221 15.233116 25.613928 15.715871 26.749798 c
13.267798 27.790249 l
h
21.262428 38.307156 m
20.601709 39.861759 18.398293 39.861763 17.737572 38.307156 c
20.185644 37.266705 l
19.928600 36.661911 19.071398 36.661915 18.814356 37.266705 c
21.262428 38.307156 l
h
26.292339 27.384703 m
26.044901 27.405638 25.829334 27.561710 25.732204 27.790249 c
23.284132 26.749798 l
23.766886 25.613926 24.838282 24.838221 26.068092 24.734173 c
26.292339 27.384703 l
h
38.817898 23.059925 m
40.100903 24.168312 39.416878 26.274300 37.727440 26.417236 c
37.503193 23.766705 l
36.845943 23.822311 36.579834 24.641613 37.078964 25.072811 c
38.817898 23.059925 l
h
29.923628 14.901747 m
29.866869 15.144379 29.949450 15.398466 30.138014 15.561367 c
28.399080 17.574253 l
27.461895 16.764618 27.051451 15.501762 27.333555 14.295841 c
29.923628 14.901747 l
h
29.678312 1.681164 m
31.129168 0.807911 32.916229 2.109245 32.530506 3.758102 c
29.940432 3.152195 l
29.790373 3.793655 30.485600 4.299923 31.050034 3.960194 c
29.678312 1.681164 l
h
20.487535 10.317635 m
19.879963 10.683325 19.120041 10.683329 18.512465 10.317635 c
19.884186 8.038603 l
19.647816 7.896336 19.352180 7.896338 19.115814 8.038603 c
20.487535 10.317635 l
h
f
n
Q
endstream
endobj
3 0 obj
4135
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 48.000000 48.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000004225 00000 n
0000004248 00000 n
0000004421 00000 n
0000004495 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
4554
%%EOF

View File

@ -1,22 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "StickerKeyboardSettingsIcon@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "StickerKeyboardSettingsIcon@3x.png",
"scale" : "3x"
"filename" : "settings_48.pdf",
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,198 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 3.500000 3.500000 cm
0.000000 0.000000 0.000000 scn
22.000000 39.500000 m
22.000000 40.328426 21.328428 41.000000 20.500000 41.000000 c
19.671574 41.000000 19.000000 40.328426 19.000000 39.500000 c
19.000000 38.338337 l
19.000000 37.690460 18.579983 37.117363 17.962181 36.922268 c
17.088881 36.646984 l
16.504770 36.524204 15.932035 36.370529 15.372461 36.187752 c
14.486683 35.991013 l
13.854101 35.851067 13.203807 36.137375 12.879869 36.698452 c
12.299038 37.704483 l
11.884825 38.421921 10.967439 38.667736 10.250000 38.253521 c
9.532561 37.839306 9.286749 36.921921 9.700962 36.204483 c
10.281794 35.198452 l
10.605731 34.637375 10.528535 33.931049 10.091049 33.453190 c
9.444299 32.748505 l
9.026765 32.371395 8.628606 31.973236 8.251495 31.555702 c
7.546809 30.908951 l
7.068950 30.471466 6.362623 30.394268 5.801547 30.718206 c
4.795517 31.299038 l
4.078078 31.713251 3.160693 31.467438 2.746479 30.750000 c
2.332266 30.032562 2.578078 29.115175 3.295517 28.700962 c
4.301547 28.120131 l
4.862623 27.796192 5.148933 27.145899 5.008989 26.513317 c
4.812248 25.627539 l
4.629469 25.067965 4.475796 24.495230 4.353016 23.911119 c
4.077733 23.037819 l
3.882637 22.420017 3.309538 22.000000 2.661662 22.000000 c
1.500000 22.000000 l
0.671573 22.000000 0.000000 21.328426 0.000000 20.500000 c
0.000000 19.671572 0.671573 19.000000 1.500000 19.000000 c
2.661662 19.000000 l
3.309538 19.000000 3.882637 18.579983 4.077733 17.962181 c
4.353333 17.087372 l
4.475950 16.504307 4.629352 15.932577 4.811759 15.373960 c
5.008989 14.486683 l
5.148933 13.854101 4.862623 13.203808 4.301547 12.879869 c
3.295517 12.299038 l
2.578078 11.884825 2.332266 10.967440 2.746479 10.250000 c
3.160693 9.532560 4.078078 9.286747 4.795517 9.700962 c
5.801547 10.281792 l
6.362623 10.605730 7.068950 10.528536 7.546809 10.091049 c
8.250140 9.445797 l
8.627652 9.027718 9.026265 8.629059 9.444299 8.251495 c
10.091049 7.546806 l
10.528535 7.068951 10.605731 6.362625 10.281794 5.801548 c
9.700962 4.795517 l
9.286749 4.078079 9.532561 3.160690 10.250000 2.746479 c
10.967439 2.332264 11.884825 2.578079 12.299038 3.295517 c
12.879869 4.301548 l
13.203807 4.862625 13.854101 5.148933 14.486683 5.008987 c
15.384456 4.808334 l
15.943589 4.626175 16.515841 4.473072 17.099430 4.350803 c
17.962181 4.077732 l
18.125601 4.015381 l
18.652878 3.777821 19.000000 3.250641 19.000000 2.661663 c
19.000000 1.500000 l
19.010092 1.325066 l
19.096739 0.579060 19.730747 0.000000 20.500000 0.000000 c
21.328428 0.000000 22.000000 0.671574 22.000000 1.500000 c
22.000000 2.661663 l
22.010246 2.836273 l
22.078001 3.410614 22.476181 3.900372 23.037819 4.077732 c
23.905090 4.351749 l
24.486071 4.473644 25.055811 4.626099 25.612547 4.807358 c
26.513317 5.008987 l
27.145899 5.148933 27.796192 4.862625 28.120131 4.301548 c
28.700962 3.295517 l
29.115175 2.578079 30.032560 2.332264 30.750000 2.746479 c
31.467440 3.160690 31.713253 4.078079 31.299038 4.795517 c
30.718208 5.801548 l
30.394270 6.362625 30.471464 7.068951 30.908951 7.546806 c
31.555702 8.251495 l
31.973736 8.629059 32.372349 9.027718 32.749863 9.445797 c
33.453194 10.091049 l
33.931049 10.528536 34.637375 10.605730 35.198452 10.281792 c
36.204483 9.700962 l
36.921921 9.286747 37.839310 9.532560 38.253521 10.250000 c
38.667736 10.967440 38.421921 11.884825 37.704483 12.299038 c
36.698452 12.879869 l
36.137375 13.203808 35.851067 13.854101 35.991013 14.486683 c
36.192642 15.387453 l
36.373901 15.944189 36.526356 16.513929 36.648251 17.094910 c
36.922268 17.962181 l
37.117363 18.579983 37.690460 19.000000 38.338337 19.000000 c
39.500000 19.000000 l
40.328426 19.000000 41.000000 19.671572 41.000000 20.500000 c
41.000000 21.328426 40.328426 22.000000 39.500000 22.000000 c
38.338337 22.000000 l
37.690460 22.000000 37.117363 22.420017 36.922268 23.037819 c
36.648884 23.902077 l
36.526775 24.484623 36.373940 25.055870 36.192154 25.614044 c
35.991013 26.513317 l
35.851067 27.145899 36.137375 27.796192 36.698452 28.120131 c
37.704483 28.700962 l
38.421921 29.115175 38.667736 30.032562 38.253521 30.750000 c
37.839310 31.467438 36.921921 31.713251 36.204483 31.299038 c
35.198452 30.718206 l
34.637375 30.394268 33.931049 30.471466 33.453194 30.908951 c
32.748505 31.555702 l
32.370941 31.973736 31.972282 32.372349 31.554203 32.749859 c
30.908951 33.453190 l
30.471464 33.931049 30.394270 34.637375 30.718208 35.198452 c
31.299038 36.204483 l
31.713253 36.921921 31.467440 37.839306 30.750000 38.253521 c
30.032560 38.667736 29.115175 38.421921 28.700962 37.704483 c
28.120131 36.698452 l
27.796192 36.137375 27.145899 35.851067 26.513317 35.991013 c
25.626040 36.188240 l
25.067423 36.370647 24.495693 36.524048 23.912628 36.646667 c
23.037819 36.922268 l
22.420017 37.117363 22.000000 37.690460 22.000000 38.338337 c
22.000000 39.500000 l
h
6.999939 20.500244 m
6.999939 27.956089 13.044094 34.000244 20.499939 34.000244 c
27.955784 34.000244 33.999939 27.956089 33.999939 20.500244 c
33.999939 13.044399 27.955784 7.000244 20.499939 7.000244 c
13.044094 7.000244 6.999939 13.044399 6.999939 20.500244 c
h
14.233011 30.086567 m
14.833106 30.510162 15.662971 30.367085 16.086567 29.766989 c
21.689148 21.830000 l
30.500000 21.830000 l
31.234539 21.830000 31.830000 21.234539 31.830000 20.500000 c
31.830000 19.765461 31.234539 19.170000 30.500000 19.170000 c
21.689148 19.170000 l
16.086567 11.233011 l
15.662971 10.632917 14.833106 10.489836 14.233011 10.913433 c
13.632916 11.337029 13.489837 12.166895 13.913433 12.766989 c
19.372028 20.500000 l
13.913433 28.233011 l
13.489837 28.833107 13.632916 29.662971 14.233011 30.086567 c
h
f*
n
Q
endstream
endobj
3 0 obj
5648
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 48.000000 48.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000005738 00000 n
0000005761 00000 n
0000005934 00000 n
0000006008 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
6067
%%EOF

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "ic_input_stickers.pdf",
"filename" : "stickers_48.pdf",
"idiom" : "universal"
}
],

View File

@ -0,0 +1,123 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 4.270020 4.152344 cm
0.000000 0.000000 0.000000 scn
16.315697 39.500134 m
17.465136 39.695385 18.643398 39.519531 19.692223 38.997250 c
20.036819 38.810360 l
20.329382 38.633312 l
22.423059 37.333458 25.297297 34.879963 28.991110 31.253437 c
30.068821 30.185352 l
30.611883 29.639896 l
34.713905 25.501024 37.402637 22.349218 38.705769 20.126995 c
39.294643 19.122784 39.547707 17.960588 39.433071 16.802202 c
39.381195 16.416437 l
39.289803 15.914349 l
39.216812 15.566687 l
39.100006 15.079699 l
36.891888 6.359375 29.005989 0.117638 19.853556 0.117638 c
8.888740 0.117638 0.000000 9.006048 0.000000 19.970463 c
0.000000 29.125599 6.246058 37.013073 14.970581 39.218063 c
15.710625 39.388660 l
16.315697 39.500134 l
h
12.798756 35.489426 m
12.631100 35.425148 l
12.241472 35.242031 l
6.578289 32.429054 2.786009 26.584171 2.786009 19.970726 c
2.786009 10.544853 10.427470 2.903671 19.853691 2.903671 c
26.466494 2.903671 32.311123 6.694832 35.134140 12.353510 c
35.125557 12.358789 l
35.308681 12.748402 l
35.384480 12.909666 35.423779 13.085659 35.423779 13.263847 c
35.423779 13.877327 34.967880 14.384329 34.376385 14.464569 c
34.211948 14.475632 l
32.160770 14.475632 l
22.489969 14.475632 14.620218 22.186560 14.364793 31.795357 c
14.358393 32.277351 l
14.358393 34.328457 l
14.358393 34.506645 14.319095 34.682640 14.243299 34.843903 c
13.984495 35.394527 13.364433 35.659653 12.798756 35.489426 c
h
16.782263 36.753235 m
17.411953 36.860195 18.059189 36.741272 18.609720 36.417446 c
18.873783 36.257626 l
19.147327 36.083416 l
21.103546 34.807701 23.928543 32.350906 27.572746 28.738895 c
28.098915 28.215038 l
29.130342 27.174446 l
32.797047 23.440453 35.195877 20.604664 36.302593 18.717381 c
36.625195 18.167248 36.743385 17.520950 36.636360 16.892254 c
36.563797 16.495956 l
35.903824 16.976896 35.090961 17.260666 34.211819 17.260666 c
32.160641 17.260666 l
31.717884 17.267069 l
23.629288 17.501270 17.144138 24.131626 17.144138 32.276615 c
17.144138 34.327721 l
17.128656 34.679214 l
17.087467 35.145836 16.964489 35.602531 16.764452 36.028126 c
16.655125 36.260727 16.526255 36.477886 16.380722 36.678566 c
16.574390 36.716927 l
16.782263 36.753235 l
h
f*
n
Q
endstream
endobj
3 0 obj
2244
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 48.000000 48.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000002334 00000 n
0000002357 00000 n
0000002530 00000 n
0000002604 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2663
%%EOF

Some files were not shown because too many files have changed in this diff Show More