diff --git a/Telegram-Mac/Appearance.swift b/Telegram-Mac/Appearance.swift index 63d632023..279127510 100644 --- a/Telegram-Mac/Appearance.swift +++ b/Telegram-Mac/Appearance.swift @@ -2018,7 +2018,18 @@ private func generateIcons(from palette: ColorPalette, bubbled: Bool) -> Telegra poll_selected_incorrect_incoming: { generatePollIcon(NSImage(named: "Icon_PollSelectedIncorrect")!, backgound: palette.redBubble_incoming) }, poll_selected_outgoing: { generatePollIcon(NSImage(named: "Icon_PollSelected")!, backgound: palette.webPreviewActivityBubble_outgoing) }, poll_selected_correct_outgoing: { generatePollIcon(NSImage(named: "Icon_PollSelected")!, backgound: palette.greenBubble_outgoing) }, - poll_selected_incorrect_outgoing: { generatePollIcon(NSImage(named: "Icon_PollSelectedIncorrect")!, backgound: palette.redBubble_outgoing) } + poll_selected_incorrect_outgoing: { generatePollIcon(NSImage(named: "Icon_PollSelectedIncorrect")!, backgound: palette.redBubble_outgoing) }, + chat_filter_add: { NSImage(named: "Icon_FilterAdd")!.precomposed(palette.accentIcon) }, + chat_filter_bots: { NSImage(named: "Icon_FilterBots")!.precomposed(palette.accentIcon) }, + chat_filter_channels: { NSImage(named: "Icon_FilterChannels")!.precomposed(palette.accentIcon) }, + chat_filter_custom: { NSImage(named: "Icon_FilterCustom")!.precomposed(palette.accentIcon) }, + chat_filter_groups: { NSImage(named: "Icon_FilterGroups")!.precomposed(palette.accentIcon) }, + chat_filter_muted: { NSImage(named: "Icon_FilterMuted")!.precomposed(palette.accentIcon) }, + chat_filter_private_chats: { NSImage(named: "Icon_FilterPrivateChats")!.precomposed(palette.accentIcon) }, + chat_filter_read: { NSImage(named: "Icon_FilterRead")!.precomposed(palette.accentIcon) }, + chat_filter_secret_chats: { NSImage(named: "Icon_FilterSecretChats")!.precomposed(palette.accentIcon) }, + chat_filter_unmuted: { NSImage(named: "Icon_FilterUnmuted")!.precomposed(palette.accentIcon) }, + chat_filter_unread: { NSImage(named: "Icon_FilterUnread")!.precomposed(palette.accentIcon) } ) } diff --git a/Telegram-Mac/ApplicationContext.swift b/Telegram-Mac/ApplicationContext.swift index 74d12bb9d..e51d62dbd 100644 --- a/Telegram-Mac/ApplicationContext.swift +++ b/Telegram-Mac/ApplicationContext.swift @@ -350,15 +350,7 @@ final class AuthorizedApplicationContext: NSObject, SplitViewDelegate { let applyChatPreset:(Int?)->Void = { atIndex in - if let index = atIndex { - _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { - $0.withSelectedAtIndex(index) - }).start() - } else { - _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { - $0.withUpdatedCurrentPreset(nil) - }).start() - } + } diff --git a/Telegram-Mac/ApplicationSpecificPreferencesKeys.swift b/Telegram-Mac/ApplicationSpecificPreferencesKeys.swift index cc6af8e2b..8261b0664 100644 --- a/Telegram-Mac/ApplicationSpecificPreferencesKeys.swift +++ b/Telegram-Mac/ApplicationSpecificPreferencesKeys.swift @@ -30,7 +30,7 @@ private enum ApplicationSpecificPreferencesKeyValues: Int32 { case walletPasscodeTimeout = 37 case passcodeSettings = 38 case appConfiguration = 39 - case chatListSettings = 46 + case chatListSettings = 47 } struct ApplicationSpecificPreferencesKeys { diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/Contents.json new file mode 100644 index 000000000..32fa579a1 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_add.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_add@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/ic_add.png b/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/ic_add.png new file mode 100644 index 000000000..d1421de18 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/ic_add.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/ic_add@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/ic_add@2x.png new file mode 100644 index 000000000..fd6fdb33f Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterAdd.imageset/ic_add@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/Contents.json new file mode 100644 index 000000000..501805ac2 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_bot.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_bot@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/ic_bot.png b/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/ic_bot.png new file mode 100644 index 000000000..153161941 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/ic_bot.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/ic_bot@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/ic_bot@2x.png new file mode 100644 index 000000000..1958600b9 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterBots.imageset/ic_bot@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/Contents.json new file mode 100644 index 000000000..2595db3b7 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_channel.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_channel@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/ic_channel.png b/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/ic_channel.png new file mode 100644 index 000000000..36e6cf839 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/ic_channel.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/ic_channel@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/ic_channel@2x.png new file mode 100644 index 000000000..b757cf244 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterChannels.imageset/ic_channel@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/Contents.json new file mode 100644 index 000000000..15fc6bb37 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_customlist.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_customlist@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/ic_customlist.png b/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/ic_customlist.png new file mode 100644 index 000000000..635068a6a Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/ic_customlist.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/ic_customlist@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/ic_customlist@2x.png new file mode 100644 index 000000000..430b2793e Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterCustom.imageset/ic_customlist@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/Contents.json new file mode 100644 index 000000000..152058a20 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_group.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_group@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/ic_group.png b/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/ic_group.png new file mode 100644 index 000000000..60dec9ec1 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/ic_group.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/ic_group@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/ic_group@2x.png new file mode 100644 index 000000000..230c153ae Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterGroups.imageset/ic_group@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/Contents.json new file mode 100644 index 000000000..70063b6f2 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_muted.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_muted@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/ic_muted.png b/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/ic_muted.png new file mode 100644 index 000000000..072e34519 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/ic_muted.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/ic_muted@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/ic_muted@2x.png new file mode 100644 index 000000000..71da5d66a Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterMuted.imageset/ic_muted@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/Contents.json new file mode 100644 index 000000000..7cf411fdb --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_user.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_user@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/ic_user.png b/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/ic_user.png new file mode 100644 index 000000000..b319abe04 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/ic_user.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/ic_user@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/ic_user@2x.png new file mode 100644 index 000000000..bb0c49010 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterPrivateChats.imageset/ic_user@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/Contents.json new file mode 100644 index 000000000..39a49c5b0 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_read.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_read@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/ic_read.png b/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/ic_read.png new file mode 100644 index 000000000..76f59b0f6 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/ic_read.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/ic_read@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/ic_read@2x.png new file mode 100644 index 000000000..edb935ca5 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterRead.imageset/ic_read@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/Contents.json new file mode 100644 index 000000000..1f0d15ef0 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_secretchat.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_secretchat@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/ic_secretchat.png b/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/ic_secretchat.png new file mode 100644 index 000000000..9900b0105 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/ic_secretchat.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/ic_secretchat@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/ic_secretchat@2x.png new file mode 100644 index 000000000..65ea4cc9a Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterSecretChats.imageset/ic_secretchat@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/Contents.json new file mode 100644 index 000000000..3d4d31a17 --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_unmuted.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_unmuted@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/ic_unmuted.png b/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/ic_unmuted.png new file mode 100644 index 000000000..a534eca9b Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/ic_unmuted.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/ic_unmuted@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/ic_unmuted@2x.png new file mode 100644 index 000000000..5117cbce3 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterUnmuted.imageset/ic_unmuted@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/Contents.json new file mode 100644 index 000000000..0fc3beb1c --- /dev/null +++ b/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_unread.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_unread@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/ic_unread.png b/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/ic_unread.png new file mode 100644 index 000000000..1ff951936 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/ic_unread.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/ic_unread@2x.png b/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/ic_unread@2x.png new file mode 100644 index 000000000..4aa1df6eb Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_FilterUnread.imageset/ic_unread@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/Contents.json index a48b454e2..8f7c7eb22 100644 --- a/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/Contents.json +++ b/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_chats_1@1x.png", + "filename" : "ic_chats.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "ic_chats_1@2x.png", + "filename" : "ic_chats@2x.png", "scale" : "2x" }, { diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats.png b/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats.png new file mode 100644 index 000000000..9370cf513 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats@2x.png new file mode 100644 index 000000000..2715b079b Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats_1@1x.png b/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats_1@1x.png deleted file mode 100644 index 0ca85775b..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats_1@1x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats_1@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats_1@2x.png deleted file mode 100644 index e3829970c..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabChatList.imageset/ic_chats_1@2x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/Contents.json index ff26a5734..8f7c7eb22 100644 --- a/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/Contents.json +++ b/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_chats_2@1x.png", + "filename" : "ic_chats.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "ic_chats_2@2x.png", + "filename" : "ic_chats@2x.png", "scale" : "2x" }, { diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats.png b/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats.png new file mode 100644 index 000000000..9370cf513 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats@2x.png new file mode 100644 index 000000000..2715b079b Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats_2@1x.png b/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats_2@1x.png deleted file mode 100644 index 99cf338aa..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats_2@1x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats_2@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats_2@2x.png deleted file mode 100644 index 6a177b96a..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabChatList_Highlighted.imageset/ic_chats_2@2x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/Contents.json index 2d057742d..6d3745d5e 100644 --- a/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/Contents.json +++ b/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_contacts_1@1x.png", + "filename" : "ic_contacts.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "ic_contacts_1@2x.png", + "filename" : "ic_contacts@2x.png", "scale" : "2x" }, { diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts.png b/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts.png new file mode 100644 index 000000000..37b6eb9c9 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts@2x.png new file mode 100644 index 000000000..65e44c6a3 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts_1@1x.png b/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts_1@1x.png deleted file mode 100644 index 99e010ace..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts_1@1x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts_1@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts_1@2x.png deleted file mode 100644 index 0fd0b01ce..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabContacts.imageset/ic_contacts_1@2x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/Contents.json index 25e4e5877..6d3745d5e 100644 --- a/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/Contents.json +++ b/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_contacts_2@1x.png", + "filename" : "ic_contacts.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "ic_contacts_2@2x.png", + "filename" : "ic_contacts@2x.png", "scale" : "2x" }, { diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts.png b/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts.png new file mode 100644 index 000000000..37b6eb9c9 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts@2x.png new file mode 100644 index 000000000..65e44c6a3 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts_2@1x.png b/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts_2@1x.png deleted file mode 100644 index eda01248f..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts_2@1x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts_2@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts_2@2x.png deleted file mode 100644 index dc03a4ef2..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabContacts_Highlighted.imageset/ic_contacts_2@2x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/Contents.json index b24728a36..45a0013e5 100644 --- a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/Contents.json +++ b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_calls_1@1x.png", + "filename" : "ic_calls.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "ic_calls_1@2x.png", + "filename" : "ic_calls@2x.png", "scale" : "2x" }, { diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls.png b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls.png new file mode 100644 index 000000000..beaa6d49a Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls@2x.png new file mode 100644 index 000000000..4baf298c6 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls_1@1x.png b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls_1@1x.png deleted file mode 100644 index 83781c5aa..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls_1@1x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls_1@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls_1@2x.png deleted file mode 100644 index 95810c6a0..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCalls.imageset/ic_calls_1@2x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/Contents.json index cda2d9a02..45a0013e5 100644 --- a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/Contents.json +++ b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_calls_2@1x.png", + "filename" : "ic_calls.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "ic_calls_2@2x.png", + "filename" : "ic_calls@2x.png", "scale" : "2x" }, { diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls.png b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls.png new file mode 100644 index 000000000..beaa6d49a Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls@2x.png new file mode 100644 index 000000000..4baf298c6 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls_2@1x.png b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls_2@1x.png deleted file mode 100644 index 48c80fef5..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls_2@1x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls_2@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls_2@2x.png deleted file mode 100644 index 819b882a6..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabRecentCallsHighlighted.imageset/ic_calls_2@2x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/Contents.json index 5f66cf2b1..25de093c5 100644 --- a/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/Contents.json +++ b/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_settings_1@1x.png", + "filename" : "ic_settings.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "ic_settings_1@2x.png", + "filename" : "ic_settings@2x.png", "scale" : "2x" }, { diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings.png b/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings.png new file mode 100644 index 000000000..2dffab770 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings@2x.png new file mode 100644 index 000000000..70f119694 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings_1@1x.png b/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings_1@1x.png deleted file mode 100644 index cd0e99956..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings_1@1x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings_1@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings_1@2x.png deleted file mode 100644 index 1acafe38c..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabSettings.imageset/ic_settings_1@2x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/Contents.json b/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/Contents.json index 3f0eb9662..25de093c5 100644 --- a/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/Contents.json +++ b/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_settings_2@1x.png", + "filename" : "ic_settings.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "ic_settings_2@2x.png", + "filename" : "ic_settings@2x.png", "scale" : "2x" }, { diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings.png b/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings.png new file mode 100644 index 000000000..2dffab770 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings@2x.png new file mode 100644 index 000000000..70f119694 Binary files /dev/null and b/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings@2x.png differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings_2@1x.png b/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings_2@1x.png deleted file mode 100644 index 5a36e0cf8..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings_2@1x.png and /dev/null differ diff --git a/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings_2@2x.png b/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings_2@2x.png deleted file mode 100644 index 180d7240a..000000000 Binary files a/Telegram-Mac/Assets.xcassets/Icon_TabSettings_Highlighted.imageset/ic_settings_2@2x.png and /dev/null differ diff --git a/Telegram-Mac/ChatController.swift b/Telegram-Mac/ChatController.swift index 273007b08..72b45e939 100644 --- a/Telegram-Mac/ChatController.swift +++ b/Telegram-Mac/ChatController.swift @@ -2011,7 +2011,7 @@ class ChatController: EditableViewController, Notifable, Tab if let message = message { self?.chatInteraction.update({$0.withEditMessage(message)}) } else { - self?.chatInteraction.cancelEditing() + self?.chatInteraction.cancelEditing(true) } self?.chatInteraction.focusInputField() } diff --git a/Telegram-Mac/ChatHistoryEntry.swift b/Telegram-Mac/ChatHistoryEntry.swift index c999f46c8..8a27e6fe3 100644 --- a/Telegram-Mac/ChatHistoryEntry.swift +++ b/Telegram-Mac/ChatHistoryEntry.swift @@ -7,7 +7,7 @@ // import Cocoa - +import TGUIKit import Postbox import TelegramCore import SyncCore diff --git a/Telegram-Mac/ChatInterfaceInteraction.swift b/Telegram-Mac/ChatInterfaceInteraction.swift index c066070a0..714f0ed22 100644 --- a/Telegram-Mac/ChatInterfaceInteraction.swift +++ b/Telegram-Mac/ChatInterfaceInteraction.swift @@ -255,24 +255,27 @@ final class ChatInteraction : InterfaceObserver { return selectedRange.lowerBound ..< selectedRange.lowerBound + text.length } - func cancelEditing() { + func cancelEditing(_ force: Bool = false) { if let editState = self.presentation.interfaceState.editState { let oldState = ChatEditState(message: editState.message) - switch editState.loadingState { - case .loading, .progress: - editDisposable.set(nil) - self.update({$0.updatedInterfaceState({$0.updatedEditState({$0?.withUpdatedLoadingState(.none)})})}) - return - default: - if oldState != editState { - confirm(for: context.window, information: L10n.chatEditCancelText, okTitle: L10n.alertDiscard, cancelTitle: L10n.alertNO, successHandler: { [weak self] _ in - self?.update({$0.withoutEditMessage().updatedUrlPreview(nil)}) - }) - } else { - self.update({$0.withoutEditMessage().updatedUrlPreview(nil)}) + if force { + self.update({$0.withoutEditMessage().updatedUrlPreview(nil)}) + } else { + switch editState.loadingState { + case .loading, .progress: + editDisposable.set(nil) + self.update({$0.updatedInterfaceState({$0.updatedEditState({$0?.withUpdatedLoadingState(.none)})})}) + return + default: + if oldState != editState { + confirm(for: context.window, information: L10n.chatEditCancelText, okTitle: L10n.alertDiscard, cancelTitle: L10n.alertNO, successHandler: { [weak self] _ in + self?.update({$0.withoutEditMessage().updatedUrlPreview(nil)}) + }) + } else { + self.update({$0.withoutEditMessage().updatedUrlPreview(nil)}) + } } } - } } diff --git a/Telegram-Mac/ChatListController.swift b/Telegram-Mac/ChatListController.swift index 362c64c37..7191105fd 100644 --- a/Telegram-Mac/ChatListController.swift +++ b/Telegram-Mac/ChatListController.swift @@ -56,6 +56,13 @@ private func chatListFilterPredicate(for preset: ChatListFilterPreset?) -> ((Pee return false } } + + if !preset.includeCategories.contains(.secretChats) { + if let _ = peer as? TelegramSecretChat { + return false + } + } + if !preset.includeCategories.contains(.bots) { if let user = peer as? TelegramUser { if user.botInfo != nil { @@ -63,15 +70,27 @@ private func chatListFilterPredicate(for preset: ChatListFilterPreset?) -> ((Pee } } } - if !preset.includeCategories.contains(.groups) { + if !preset.includeCategories.contains(.privateGroups) { if let _ = peer as? TelegramGroup { return false } else if let channel = peer as? TelegramChannel { if case .group = channel.info { - return false + if channel.username == nil { + return false + } } } } + if !preset.includeCategories.contains(.publicGroups) { + if let channel = peer as? TelegramChannel { + if case .group = channel.info { + if channel.username != nil { + return false + } + } + } + } + if !preset.includeCategories.contains(.channels) { if let channel = peer as? TelegramChannel { if case .broadcast = channel.info { @@ -90,6 +109,7 @@ enum UIChatListEntryId : Hashable { case chatId(PeerId) case groupId(PeerGroupId) case reveal + case empty } struct ChatListInputActivity : Equatable { @@ -123,8 +143,8 @@ struct ChatListState: Equatable { enum UIChatListEntry : Identifiable, Comparable { case chat(ChatListEntry, [ChatListInputActivity], isSponsored: Bool) case group(Int, PeerGroupId, [ChatListGroupReferencePeer], Message?, PeerGroupUnreadCountersCombinedSummary, TotalUnreadCountDisplayCategory, Bool, HiddenArchiveStatus) - case reveal(ChatListFilterPreset?) - + case reveal([ChatListFilterPreset], ChatListFilterPreset?, [Int32: Int32]) + case empty(ChatListFilterPreset?) static func == (lhs: UIChatListEntry, rhs: UIChatListEntry) -> Bool { switch lhs { case let .chat(entry, activity, isSponsored): @@ -145,8 +165,14 @@ enum UIChatListEntry : Identifiable, Comparable { } else { return false } - case let .reveal(filter): - if case .reveal(filter) = rhs { + case let .reveal(filters, current, counters): + if case .reveal(filters, current, counters) = rhs { + return true + } else { + return false + } + case let .empty(filter): + if case .empty(filter) = rhs { return true } else { return false @@ -154,30 +180,32 @@ enum UIChatListEntry : Identifiable, Comparable { } } - - static func < (lhs: UIChatListEntry, rhs: UIChatListEntry) -> Bool { - switch lhs { - case let .chat(lhsEntry, _, _): - if case let .chat(rhsEntry, _, _) = rhs { - return lhsEntry < rhsEntry - } else { - return true - } - case let .group(lhsIndex, _, _, _, _, _, _, _): - if case let .group(rhsIndex, _, _, _, _, _, _, _) = rhs { - return lhsIndex < rhsIndex - } else { - return false + var index: ChatListIndex { + switch self { + case let .chat(entry, _, _): + switch entry { + case let .HoleEntry(hole): + return ChatListIndex(pinningIndex: nil, messageIndex: hole.index) + case let .MessageEntry(values): + return values.0 } case .reveal: - if case .reveal = rhs { - return false - } else { - return true + return ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex.absoluteUpperBound()) + case let .group(values): + var index = MessageIndex.absoluteUpperBound().predecessor() + for _ in 0 ..< values.0 { + index = index.predecessor() } + return ChatListIndex(pinningIndex: 0, messageIndex: index) + case .empty: + return ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex.absoluteUpperBound().predecessor()) } } + static func < (lhs: UIChatListEntry, rhs: UIChatListEntry) -> Bool { + return lhs.index < rhs.index + } + var stableId: UIChatListEntryId { switch self { case let .chat(entry, _, _): @@ -186,6 +214,8 @@ enum UIChatListEntry : Identifiable, Comparable { return .groupId(groupId) case .reveal: return .reveal + case .empty: + return .empty } } @@ -193,7 +223,7 @@ enum UIChatListEntry : Identifiable, Comparable { -fileprivate func prepareEntries(from:[AppearanceWrapperEntry]?, to:[AppearanceWrapperEntry], adIndex: UInt16?, context: AccountContext, initialSize:NSSize, animated:Bool, scrollState:TableScrollState? = nil, groupId: PeerGroupId) -> Signal { +fileprivate func prepareEntries(from:[AppearanceWrapperEntry]?, to:[AppearanceWrapperEntry], adIndex: UInt16?, context: AccountContext, initialSize:NSSize, animated:Bool, scrollState:TableScrollState? = nil, groupId: PeerGroupId, setupFilter: @escaping(ChatListFilterPreset?)->Void, openFilterSettings: @escaping()->Void) -> Signal { return Signal { subscriber in @@ -216,12 +246,10 @@ fileprivate func prepareEntries(from:[AppearanceWrapperEntry]?, } case let .group(_, groupId, peers, message, unreadState, unreadCountDisplayCategory, animated, archiveStatus): return ChatListRowItem(initialSize, context: context, pinnedType: .none, groupId: groupId, peers: peers, message: message, unreadState: unreadState, unreadCountDisplayCategory: unreadCountDisplayCategory, animateGroup: animated, archiveStatus: archiveStatus) - case .reveal: - return ChatListRevealItem(initialSize, context: context, action: { - _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { - $0.withUpdatedCurrentPreset(nil) - }).start() - }) + case let .reveal(tabs, selected, counters): + return ChatListRevealItem(initialSize, context: context, tabs: tabs, selected: selected, counters: counters, action: setupFilter, openSettings: openFilterSettings) + case let .empty(filter): + return ChatListEmptyRowItem(initialSize, stableId: entry.stableId, filter: filter, context: context, openFilterSettings: openFilterSettings) } } @@ -281,17 +309,32 @@ enum HiddenArchiveStatus : Equatable { } } +private struct FilterData : Equatable { + let filter: ChatListFilterPreset? + let tabs: [ChatListFilterPreset] + init(filter: ChatListFilterPreset?, tabs: [ChatListFilterPreset]) { + self.filter = filter + self.tabs = tabs + } + func withUpdatedFilter(_ filter: ChatListFilterPreset?) -> FilterData { + return FilterData(filter: filter, tabs: self.tabs) + } + func withUpdatedTabs(_ tabs: [ChatListFilterPreset]) -> FilterData { + return FilterData(filter: self.filter, tabs: tabs) + } +} + class ChatListController : PeersListController { - private let filter = ValuePromise(ignoreRepeated: true) - private let _filterValue = Atomic(value: nil) - var filterValue: ChatListFilterPreset? { + private let filter = ValuePromise(ignoreRepeated: true) + private let _filterValue = Atomic(value: FilterData(filter: nil, tabs: [])) + private var filterValue: FilterData? { return _filterValue.with { $0 } } - private func setPreset(_ value: ChatListFilterPreset?) { - filter.set(_filterValue.modify( { _ in return value })) + private func updateFilter(_ f:(FilterData)->FilterData) { + filter.set(_filterValue.modify(f)) _ = first.swap(true) - self.request.set(.single(.Initial(50, .up(true)))) + setCenterTitle(self.defaultBarTitle) } private let request = Promise() @@ -341,8 +384,6 @@ class ChatListController : PeersListController { statePromise.set(stateValue.modify(f)) } - genericView.tableView.emptyItem = ChatListEmptyRowItem(frame.size, context: context) - let postbox = context.account.postbox let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:]) @@ -436,25 +477,33 @@ class ChatListController : PeersListController { let signal = combineLatest(request.get() |> distinctUntilChanged, filter.get()) - let chatHistoryView: Signal<(ChatListView, ViewUpdateType, Bool, ChatListFilterPreset?), NoError> = signal |> mapToSignal { location, preset -> Signal<(ChatListView, ViewUpdateType, Bool, ChatListFilterPreset?), NoError> in + let chatHistoryView: Signal<(ChatListView, ViewUpdateType, Bool, FilterData), NoError> = signal |> mapToSignal { location, data -> Signal<(ChatListView, ViewUpdateType, Bool, FilterData), NoError> in var signal:Signal<(ChatListView,ViewUpdateType), NoError> var removeNextAnimation: Bool = false switch location { case let .Initial(count, st): - signal = context.account.viewTracker.tailChatListView(groupId: groupId, filterPredicate: chatListFilterPredicate(for: preset), count: count) + signal = context.account.viewTracker.tailChatListView(groupId: groupId, filterPredicate: chatListFilterPredicate(for: data.filter), count: count) scroll = st case let .Index(index, st): - signal = context.account.viewTracker.aroundChatListView(groupId: groupId, filterPredicate: chatListFilterPredicate(for: preset), index: index, count: 50) + signal = context.account.viewTracker.aroundChatListView(groupId: groupId, filterPredicate: chatListFilterPredicate(for: data.filter), index: index, count: 150) scroll = st removeNextAnimation = st != nil } - return signal |> map { ($0.0, $0.1, removeNextAnimation, preset)} + return signal |> map { ($0.0, $0.1, removeNextAnimation, data)} } - + let setupFilter:(ChatListFilterPreset?)->Void = { [weak self] filter in + self?.updateFilter { + $0.withUpdatedFilter(filter) + } + self?.scrollup() + } + let openFilterSettings:()->Void = { + context.sharedContext.bindings.rootNavigation().push(ChatListPresetListController(context: context)) + } - let list:Signal = combineLatest(queue: prepareQueue, chatHistoryView, appearanceSignal, statePromise.get(), context.chatUndoManager.allStatuses(), hiddenArchiveState.get(), appNotificationSettings(accountManager: context.sharedContext.accountManager)) |> mapToQueue { value, appearance, state, undoStatuses, archiveIsHidden, inAppSettings -> Signal in + let list:Signal = combineLatest(queue: prepareQueue, chatHistoryView, appearanceSignal, statePromise.get(), context.chatUndoManager.allStatuses(), hiddenArchiveState.get(), appNotificationSettings(accountManager: context.sharedContext.accountManager), filtersBadgeCounters(context: context)) |> mapToQueue { value, appearance, state, undoStatuses, archiveIsHidden, inAppSettings, filtersCounter -> Signal in var removeNextAnimation = value.2 @@ -486,21 +535,39 @@ class ChatListController : PeersListController { return .chat($0, state.activities.activities[$0.index.messageIndex.id.peerId] ?? [], isSponsored: $1) } - if value.3 != nil, mapped.isEmpty {} else { + if value.3.filter != nil, mapped.isEmpty {} else { if value.0.laterIndex == nil { for (i, group) in value.0.groupEntries.reversed().enumerated() { mapped.append(.group(i, group.groupId, group.renderedPeers, group.message, group.unreadState, inAppSettings.totalUnreadCountDisplayCategory, animateGroupNextTransition.swap(nil) == group.groupId, archiveIsHidden)) } } - - if value.3 != nil { - mapped.append(.reveal(value.3)) + } + + + if mapped.isEmpty { + let hasHole = !value.0.entries.filter({ value in + switch value { + case .HoleEntry: + return true + default: + return false + } + }).isEmpty + if !hasHole { + mapped.append(.empty(value.3.filter)) } } - + if !value.3.tabs.isEmpty { + let dict:[Int32: Int32] = filtersCounter.reduce([:], { current, value in + var current = current + current[value.id] = value.count + return current + }) + mapped.append(.reveal(value.3.tabs, value.3.filter, dict)) + } - let entries = mapped.compactMap { entry -> AppearanceWrapperEntry? in + let entries = mapped.sorted().compactMap { entry -> AppearanceWrapperEntry? in switch entry { case let .chat(inner, activities, isSponsored): switch inner { @@ -526,6 +593,8 @@ class ChatListController : PeersListController { return AppearanceWrapperEntry(entry: entry, appearance: appearance) case .reveal: return AppearanceWrapperEntry(entry: entry, appearance: appearance) + case .empty: + return AppearanceWrapperEntry(entry: entry, appearance: appearance) } } @@ -535,7 +604,7 @@ class ChatListController : PeersListController { // if value.3 != previousFilter.swap(value.3) { // animated = false // } - return prepareEntries(from: prev, to: entries, adIndex: nil, context: context, initialSize: initialSize.modify({$0}), animated: animated, scrollState: scroll, groupId: groupId) + return prepareEntries(from: prev, to: entries, adIndex: nil, context: context, initialSize: initialSize.with { $0 }, animated: animated, scrollState: scroll, groupId: groupId, setupFilter: setupFilter, openFilterSettings: openFilterSettings) } @@ -627,23 +696,48 @@ class ChatListController : PeersListController { let filterView = chatListFilterPreferences(postbox: context.account.postbox) |> deliverOnMainQueue switch mode { case .folder: - self.setPreset(nil) + self.updateFilter( { + $0.withUpdatedTabs([]).withUpdatedFilter(nil) + } ) + case let .filter(filterId): + filterDisposable.set(filterView.start(next: { [weak self] settings in + var shouldBack: Bool = false + self?.updateFilter { current in + var current = current + if let updated = settings.presets.first(where: { $0.uniqueId == filterId }) { + current = current.withUpdatedFilter(updated) + } else { + shouldBack = true + current = current.withUpdatedFilter(nil) + } + current = current.withUpdatedTabs([]) + return current + } + if shouldBack { + self?.navigationController?.back() + } + })) default: filterDisposable.set(filterView.start(next: { [weak self] settings in - self?.setPreset(settings.current) + self?.updateFilter( { current in + var current = current + if let filter = current.filter { + if let updated = settings.presets.first(where: { $0.uniqueId == filter.uniqueId }) { + current = current.withUpdatedFilter(updated) + } else { + current = current.withUpdatedFilter(nil) + } + } + + if settings.tabsIsEnabled { + current = current.withUpdatedTabs(settings.presets) + } else { + current = current.withUpdatedTabs([]).withUpdatedFilter(nil) + } + return current + } ) })) } - - -// return account.postbox.unreadMessageCountsView(items: items) |> map { view in -// var totalCount:Int32 = 0 -// if let total = view.count(for: .total(value, .messages)) { -// totalCount = total -// } -// -// return (view, totalCount) -// } - } func collapseOrExpandArchive() { @@ -797,14 +891,17 @@ class ChatListController : PeersListController { let view = self.previousChatList.with { $0 } if self.genericView.tableView.contentOffset.y == 0, view?.laterIndex == nil { - if filterValue != nil { - _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { - $0.withUpdatedCurrentPreset(nil) - }).start() + switch mode { + case .folder: + navigationController?.back() return + case .filter: + navigationController?.back() + return + case .plain: + break } - navigationController?.back() - return + } @@ -860,39 +957,22 @@ class ChatListController : PeersListController { return chatListFilterPreferences(postbox: context.account.postbox) |> take(1) |> deliverOnMainQueue - |> map { settings -> [SPopoverItem] in + |> map { [weak self] settings -> [SPopoverItem] in var items:[SPopoverItem] = [] + items.append(SPopoverItem(L10n.chatListFilterSetup, { context.sharedContext.bindings.rootNavigation().push(ChatListPresetListController(context: context)) - }, theme.icons.peerInfoAddMember)) + }, theme.icons.chat_filter_add)) if !settings.presets.isEmpty { - if (settings.presets.count == 1 && settings.presets.first == settings.current) || settings.presets.count > 1 { - items.append(SPopoverItem()) + if (settings.presets.count == 1 && settings.presets.first != self?.filterValue?.filter) || settings.presets.count > 1 { + items.append(SPopoverItem(false)) } } - if settings.current != nil { - - let badge = GlobalBadgeNode(context.account, sharedContext: context.sharedContext, view: View(), layoutChanged: { - - }, getColor: { isSelected in - return isSelected ? .white : theme.colors.accent - }, applyFilter: false) - let additionView: SPopoverAdditionItemView = SPopoverAdditionItemView(context: badge, view: badge.view!, updateIsSelected: { [weak badge] isSelected in - badge?.isSelected = isSelected - }) - - items.append(SPopoverItem(L10n.chatListFilterAll, { - _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { - $0.withUpdatedCurrentPreset(nil) - }).start() - }, generateTextIcon(.initialize(string: settings.shortcut(for: nil), color: theme.colors.accent, font: .medium(.text))), additionView: additionView)) - } - for preset in settings.presets { - if preset.uniqueId != settings.current?.uniqueId { + if preset.uniqueId != self?.filterValue?.filter?.uniqueId { let badge = GlobalBadgeNode(context.account, sharedContext: context.sharedContext, view: View(), layoutChanged: { }, getColor: { isSelected in @@ -902,11 +982,18 @@ class ChatListController : PeersListController { badge?.isSelected = isSelected }) - items.append(SPopoverItem(preset.title, { - _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { - $0.withUpdatedCurrentPreset(preset) - }).start() - }, generateTextIcon(.initialize(string: settings.shortcut(for: preset), color: theme.colors.accent, font: .medium(.text))), additionView: additionView)) + items.append(SPopoverItem(preset.title, { [weak self] in + guard let `self` = self else { + return + } + if settings.tabsIsEnabled { + self.updateFilter { + $0.withUpdatedFilter(preset) + } + } else { + self.navigationController?.push(ChatListController(context, modal: false, filterId: preset.uniqueId)) + } + }, preset.icon, additionView: additionView)) } } return items @@ -977,7 +1064,7 @@ class ChatListController : PeersListController { switch direction { case let .left(_state): - if self.mode.isFolder && checkFolder { + if !self.mode.isPlain && checkFolder { swipeState = nil } else { swipeState = _state @@ -1142,6 +1229,26 @@ class ChatListController : PeersListController { } } + override var removeAfterDisapper: Bool { + switch self.mode { + case .plain: + return false + default: + return true + } + } + + override func backSettings() -> (String, CGImage?) { + switch self.mode { + case .plain: + return super.backSettings() + case .folder: + return (L10n.chatListArchiveBack, super.backSettings().1) + case .filter: + return (L10n.chatListFilterBack, super.backSettings().1) + } + } + override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) context.window.removeAllHandlers(for: self) @@ -1168,29 +1275,47 @@ class ChatListController : PeersListController { override var enableBack: Bool { - return mode.groupId != .root + switch mode { + case .folder, .filter: + return true + default: + return false + } } override var defaultBarTitle: String { - switch mode.groupId { - case .root: + switch mode { + case .plain: return super.defaultBarTitle - default: + case .folder: return L10n.chatListArchivedChats + case .filter: + return _filterValue.with { $0.filter?.title ?? "Filter" } } } override func escapeKeyAction() -> KeyHandlerResult { - if mode.isFolder, let navigation = navigationController { + if !mode.isPlain, let navigation = navigationController { navigation.back() return .invoked } return super.escapeKeyAction() } - init(_ context: AccountContext, modal:Bool = false, groupId: PeerGroupId? = nil) { + + init(_ context: AccountContext, modal:Bool = false, groupId: PeerGroupId? = nil, filterId: Int32? = nil) { self.undoTooltipControl = UndoTooltipControl(context: context) - super.init(context, followGlobal:!modal, mode: groupId != nil ? .folder(groupId!) : .plain) + + let mode: PeerListMode + if let filterId = filterId { + mode = .filter(filterId) + } else if let groupId = groupId { + mode = .folder(groupId) + } else { + mode = .plain + } + + super.init(context, followGlobal: !modal, mode: mode) if groupId != nil { context.closeFolderFirst = true diff --git a/Telegram-Mac/ChatListEmptyRowItem.swift b/Telegram-Mac/ChatListEmptyRowItem.swift index 29ba75a55..7babc0dc5 100644 --- a/Telegram-Mac/ChatListEmptyRowItem.swift +++ b/Telegram-Mac/ChatListEmptyRowItem.swift @@ -12,20 +12,32 @@ import SwiftSignalKit import Postbox class ChatListEmptyRowItem: TableRowItem { - private let _stableId: UInt32 = arc4random() + private let _stableId: AnyHashable override var stableId: AnyHashable { return _stableId } let context: AccountContext - init(_ initialSize: NSSize, context: AccountContext) { + let filter: ChatListFilterPreset? + let openFilterSettings: ()->Void + init(_ initialSize: NSSize, stableId: AnyHashable, filter: ChatListFilterPreset?, context: AccountContext, openFilterSettings: @escaping()->Void) { self.context = context + self.filter = filter + self._stableId = stableId + self.openFilterSettings = openFilterSettings super.init(initialSize) } override var height: CGFloat { if let table = table { - return table.frame.height + var tableHeight: CGFloat = 0 + table.enumerateItems { item -> Bool in + if item.index < self.index { + tableHeight += item.height + } + return true + } + return table.frame.height - tableHeight } return initialSize.height } @@ -40,30 +52,34 @@ private class ChatListEmptyRowView : TableRowView { private let disposable = MetaDisposable() private let textView = TextView() private let separator = View() - private var preset: ChatListFilterPreset? = nil { - didSet { - if preset != oldValue { - needsLayout = true - } - } - } + private let sticker: MediaAnimatedStickerView = MediaAnimatedStickerView(frame: NSZeroRect) required init(frame frameRect: NSRect) { super.init(frame: frameRect) addSubview(textView) textView.isSelectable = false addSubview(separator) + addSubview(sticker) } override func set(item: TableRowItem, animated: Bool = false) { super.set(item: item, animated: animated) - if let item = item as? ChatListEmptyRowItem { - let signal = chatListFilterPreferences(postbox: item.context.account.postbox) |> deliverOnMainQueue - disposable.set(signal.start(next: { [weak self] settings in - self?.preset = settings.current - })) + + guard let item = item as? ChatListEmptyRowItem else { + return } + + let animatedSticker: LocalAnimatedSticker + + if let _ = item.filter { + animatedSticker = .think_spectacular + } else { + animatedSticker = .chiken_born + } + sticker.update(with: animatedSticker.file, size: NSMakeSize(112, 112), context: item.context, parent: nil, table: item.table, parameters: animatedSticker.parameters, animated: animated, positionFlags: nil, approximateSynchronousValue: false) + + needsLayout = true } deinit { @@ -76,27 +92,30 @@ private class ChatListEmptyRowView : TableRowView { separator.background = theme.colors.border + guard let item = item as? ChatListEmptyRowItem else { + return + } + + let text: String - if let _ = self.preset { + if let _ = item.filter { text = L10n.chatListFilterEmpty } else { text = L10n.chatListEmptyText } - let attr = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: .normal(.text), textColor: theme.colors.grayText), bold: MarkdownAttributeSet(font: .bold(.text), textColor: theme.colors.grayText), link: MarkdownAttributeSet(font: .normal(.text), textColor: theme.colors.link), linkAttribute: { [weak self] contents in - return (NSAttributedString.Key.link.rawValue, inAppLink.callback(contents, { [weak self] _ in - guard let item = self?.item as? ChatListEmptyRowItem else { - return + let attr = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: .normal(.title), textColor: theme.colors.text), bold: MarkdownAttributeSet(font: .medium(.title), textColor: theme.colors.text), link: MarkdownAttributeSet(font: .normal(.title), textColor: theme.colors.link), linkAttribute: { [weak item] contents in + return (NSAttributedString.Key.link.rawValue, inAppLink.callback(contents, { [weak item] value in + if value == "filter" { + item?.openFilterSettings() } - _ = updateChatListFilterPreferencesInteractively(postbox: item.context.account.postbox, { - $0.withUpdatedCurrentPreset(nil) - }).start() + })) })).mutableCopy() as! NSMutableAttributedString - attr.detectBoldColorInString(with: .bold(.text)) + attr.detectBoldColorInString(with: .medium(.title)) let layout = TextViewLayout(attr, alignment: .center) @@ -106,8 +125,11 @@ private class ChatListEmptyRowView : TableRowView { textView.center() textView.isHidden = frame.width <= 70 + sticker.isHidden = frame.width <= 70 separator.frame = NSMakeRect(frame.width - .borderSize, 0, .borderSize, frame.height) + + sticker.centerX(y: textView.frame.minY - sticker.frame.height - 20) } required init?(coder: NSCoder) { diff --git a/Telegram-Mac/ChatListFilterPreferences.swift b/Telegram-Mac/ChatListFilterPreferences.swift index fe38123d5..b2663a5d2 100644 --- a/Telegram-Mac/ChatListFilterPreferences.swift +++ b/Telegram-Mac/ChatListFilterPreferences.swift @@ -16,7 +16,9 @@ public enum ChatListFilterPresetName: Equatable, Hashable, PostboxCoding { case unmuted case unread case channels - case groups + case publicGroups + case privateGroups + case secretChats case privateChats case bots case custom(String) @@ -30,10 +32,14 @@ public enum ChatListFilterPresetName: Equatable, Hashable, PostboxCoding { case 2: self = .privateChats case 3: - self = .groups + self = .publicGroups case 4: - self = .bots + self = .privateGroups case 5: + self = .secretChats + case 6: + self = .bots + case 7: self = .unread case 10: self = .custom(decoder.decodeStringForKey("title", orElse: "Preset")) @@ -51,12 +57,16 @@ public enum ChatListFilterPresetName: Equatable, Hashable, PostboxCoding { encoder.encodeInt32(1, forKey: "_t") case .privateChats: encoder.encodeInt32(2, forKey: "_t") - case .groups: + case .publicGroups: encoder.encodeInt32(3, forKey: "_t") - case .bots: + case .privateGroups: encoder.encodeInt32(4, forKey: "_t") - case .unread: + case .secretChats: encoder.encodeInt32(5, forKey: "_t") + case .bots: + encoder.encodeInt32(6, forKey: "_t") + case .unread: + encoder.encodeInt32(7, forKey: "_t") case let .custom(title): encoder.encodeInt32(10, forKey: "_t") encoder.encodeString(title, forKey: "title") @@ -70,8 +80,12 @@ public enum ChatListFilterPresetName: Equatable, Hashable, PostboxCoding { return L10n.chatListFilterUnmutedChats case .channels: return L10n.chatListFilterChannels - case .groups: - return L10n.chatListFilterGroups + case .publicGroups: + return L10n.chatListFilterPublicGroups + case .privateGroups: + return L10n.chatListFilterPrivateGroups + case .secretChats: + return L10n.chatListFilterSecretChat case .privateChats: return L10n.chatListFilterPrivateChats case .unread: @@ -118,7 +132,54 @@ struct ChatListFilterPreset: Equatable, PostboxCoding { return name.title } var desc: String { - return self.includeCategories.string + var text: String = L10n.chatListFilterDescCustomized + if includeCategories == [.muted, .read] { + return "\(self.additionallyIncludePeers.count)" + } else if includeCategories == ._unmuted { + text = L10n.chatListFilterDescUnmuted + } else if includeCategories == ._unread { + text = L10n.chatListFilterDescUnread + } else if includeCategories == ._privateChats { + text = L10n.chatListFilterDescPrivateChats + } else if includeCategories == ._groups { + text = L10n.chatListFilterDescGroups + } else if includeCategories == ._privateGroups { + text = L10n.chatListFilterDescPrivateGroups + } else if includeCategories == ._publicGroups { + text = L10n.chatListFilterDescPublicGroups + } else if includeCategories == ._channels { + text = L10n.chatListFilterDescChannels + } else if includeCategories == ._bots { + text = L10n.chatListFilterDescBots + } else if includeCategories == ._secretChats { + text = L10n.chatListFilterDescSecretChats + } + + if !self.additionallyIncludePeers.isEmpty { + text = ", +\(self.additionallyIncludePeers.count)" + } + return text + } + + var icon: CGImage { + + if includeCategories == ._unmuted { + return theme.icons.chat_filter_unmuted + } else if includeCategories == ._unread { + return theme.icons.chat_filter_unread + } else if includeCategories == ._groups || includeCategories == ._publicGroups || includeCategories == ._privateGroups { + return theme.icons.chat_filter_groups + } else if includeCategories == ._channels { + return theme.icons.chat_filter_channels + } else if includeCategories == ._privateChats { + return theme.icons.chat_filter_private_chats + } else if includeCategories == ._bots { + return theme.icons.chat_filter_bots + } else if includeCategories == ._secretChats { + return theme.icons.chat_filter_secret_chats + } + + return theme.icons.chat_filter_custom } static var new: ChatListFilterPreset { @@ -163,21 +224,27 @@ struct ChatListFilter: OptionSet { static let muted = ChatListFilter(rawValue: 1 << 1) static let privateChats = ChatListFilter(rawValue: 1 << 2) - static let groups = ChatListFilter(rawValue: 1 << 3) - static let bots = ChatListFilter(rawValue: 1 << 4) - static let channels = ChatListFilter(rawValue: 1 << 5) - static let read = ChatListFilter(rawValue: 1 << 6) + static let publicGroups = ChatListFilter(rawValue: 1 << 3) + static let privateGroups = ChatListFilter(rawValue: 1 << 4) + static let secretChats = ChatListFilter(rawValue: 1 << 5) + static let bots = ChatListFilter(rawValue: 1 << 6) + static let channels = ChatListFilter(rawValue: 1 << 7) + static let read = ChatListFilter(rawValue: 1 << 8) static let all: ChatListFilter = [ .muted, .privateChats, - .groups, + .privateGroups, + .publicGroups, + .secretChats, .bots, .channels, .read ] - static let _workMode: ChatListFilter = [ + static let _unmuted: ChatListFilter = [ .privateChats, - .groups, + .privateGroups, + .publicGroups, + .secretChats, .bots, .channels, .read] @@ -185,7 +252,9 @@ struct ChatListFilter: OptionSet { static let _unread: ChatListFilter = [ .muted, .privateChats, - .groups, + .privateChats, + .privateGroups, + .secretChats, .bots, .channels] @@ -196,7 +265,23 @@ struct ChatListFilter: OptionSet { ] static let _groups: ChatListFilter = [ .muted, - .groups, + .publicGroups, + .privateGroups, + .read + ] + static let _privateGroups: ChatListFilter = [ + .muted, + .privateGroups, + .read + ] + static let _publicGroups: ChatListFilter = [ + .muted, + .publicGroups, + .read + ] + static let _secretChats: ChatListFilter = [ + .muted, + .secretChats, .read ] static let _privateChats: ChatListFilter = [ @@ -215,9 +300,9 @@ struct ChatListFilter: OptionSet { } struct ChatListFilterPreferences: PreferencesEntry, Equatable { - let current: ChatListFilterPreset? let presets: [ChatListFilterPreset] let needShowTooltip: Bool + let tabsIsEnabled: Bool static var defaultSettings: ChatListFilterPreferences { var presets: [ChatListFilterPreset] = [] @@ -226,31 +311,27 @@ struct ChatListFilterPreferences: PreferencesEntry, Equatable { // presets.append(ChatListFilterPreset(name: .groups, includeCategories: ._groups, additionallyIncludePeers: [], uniqueId: 2)) // presets.append(ChatListFilterPreset(name: .bots, includeCategories: ._bots, additionallyIncludePeers: [], uniqueId: 3)) presets.append(ChatListFilterPreset(name: .unread, includeCategories: ._unread, additionallyIncludePeers: [], applyReadMutedForExceptions: false, uniqueId: 4)) - presets.append(ChatListFilterPreset(name: .unmuted, includeCategories: ._workMode, additionallyIncludePeers: [], applyReadMutedForExceptions: false, uniqueId: 5)) + presets.append(ChatListFilterPreset(name: .unmuted, includeCategories: ._unmuted, additionallyIncludePeers: [], applyReadMutedForExceptions: false, uniqueId: 5)) - return ChatListFilterPreferences(current: nil, presets: presets, needShowTooltip: true) + return ChatListFilterPreferences(presets: presets, needShowTooltip: true, tabsIsEnabled: false) } - init(current: ChatListFilterPreset?, presets: [ChatListFilterPreset], needShowTooltip: Bool) { - self.current = current + init(presets: [ChatListFilterPreset], needShowTooltip: Bool, tabsIsEnabled: Bool) { self.presets = presets self.needShowTooltip = needShowTooltip + self.tabsIsEnabled = tabsIsEnabled } init(decoder: PostboxDecoder) { - self.current = decoder.decodeObjectForKey("current") as? ChatListFilterPreset self.presets = decoder.decodeObjectArrayWithDecoderForKey("presets") self.needShowTooltip = decoder.decodeBoolForKey("needShowTooltip", orElse: true) + self.tabsIsEnabled = decoder.decodeBoolForKey("tabsIsEnabled", orElse: true) } func encode(_ encoder: PostboxEncoder) { - if let current = current { - encoder.encodeObject(current, forKey: "current") - } else { - encoder.encodeNil(forKey: "current") - } encoder.encodeObjectArray(self.presets, forKey: "presets") encoder.encodeBool(self.needShowTooltip, forKey: "needShowTooltip") + encoder.encodeBool(self.tabsIsEnabled, forKey: "tabsIsEnabled") } func isEqual(to: PreferencesEntry) -> Bool { @@ -262,12 +343,9 @@ struct ChatListFilterPreferences: PreferencesEntry, Equatable { } static func ==(lhs: ChatListFilterPreferences, rhs: ChatListFilterPreferences) -> Bool { - return lhs.current == rhs.current && lhs.presets == rhs.presets && lhs.needShowTooltip == rhs.needShowTooltip + return lhs.presets == rhs.presets && lhs.needShowTooltip == rhs.needShowTooltip && lhs.tabsIsEnabled == rhs.tabsIsEnabled } - func withUpdatedCurrentPreset(_ current: ChatListFilterPreset?) -> ChatListFilterPreferences { - return ChatListFilterPreferences(current: current, presets: self.presets, needShowTooltip: false) - } func withAddedPreset(_ preset: ChatListFilterPreset, onlyReplace: Bool = false) -> ChatListFilterPreferences { var presets = self.presets if let index = presets.firstIndex(where: {$0.uniqueId == preset.uniqueId}) { @@ -275,40 +353,30 @@ struct ChatListFilterPreferences: PreferencesEntry, Equatable { } else if !onlyReplace { presets.append(preset) } - var current = self.current - if current?.uniqueId == preset.uniqueId { - current = preset - } - return ChatListFilterPreferences(current: current, presets: presets, needShowTooltip: false) + return ChatListFilterPreferences(presets: presets, needShowTooltip: false, tabsIsEnabled: self.tabsIsEnabled) } func withRemovedPreset(_ preset: ChatListFilterPreset) -> ChatListFilterPreferences { var presets = self.presets presets.removeAll(where: {$0.uniqueId == preset.uniqueId }) - var current = self.current - if current?.uniqueId == preset.uniqueId { - current = nil - } - return ChatListFilterPreferences(current: current, presets: presets, needShowTooltip: false) + return ChatListFilterPreferences(presets: presets, needShowTooltip: false, tabsIsEnabled: self.tabsIsEnabled) } func withMovePreset(_ from: Int, _ to: Int) -> ChatListFilterPreferences { var presets = self.presets presets.insert(presets.remove(at: from), at: to) - return ChatListFilterPreferences(current: self.current, presets: presets, needShowTooltip: false) + return ChatListFilterPreferences(presets: presets, needShowTooltip: false, tabsIsEnabled: self.tabsIsEnabled) } func withSelectedAtIndex(_ index: Int) -> ChatListFilterPreferences { - var current = self.current - if index < self.presets.count { - current = self.presets[index] - } - return ChatListFilterPreferences(current: current, presets: self.presets, needShowTooltip: false) + return ChatListFilterPreferences(presets: self.presets, needShowTooltip: false, tabsIsEnabled: self.tabsIsEnabled) } func withUpdatedNeedShowTooltip(_ needShowTooltip: Bool) -> ChatListFilterPreferences { - return ChatListFilterPreferences(current: self.current, presets: self.presets, needShowTooltip: needShowTooltip) + return ChatListFilterPreferences(presets: self.presets, needShowTooltip: needShowTooltip, tabsIsEnabled: self.tabsIsEnabled) + } + func withUpdatedTabEnable(_ tabsIsEnabled: Bool) -> ChatListFilterPreferences { + return ChatListFilterPreferences(presets: self.presets, needShowTooltip: self.needShowTooltip, tabsIsEnabled: tabsIsEnabled) } - func shortcut(for preset: ChatListFilterPreset?) -> String { if let preset = preset { for (i, value) in self.presets.enumerated() { @@ -347,3 +415,134 @@ func updateChatListFilterPreferencesInteractively(postbox: Postbox, _ f: @escapi }) } } + + + +func filtersBadgeCounters(context: AccountContext) -> Signal<[(id: Int32, count: Int32)], NoError> { + return chatListFilterPreferences(postbox: context.account.postbox) |> map { $0.presets } |> mapToSignal { filters -> Signal<[(id: Int32, count: Int32)], NoError> in + + var signals:[Signal<(id: Int32, count: Int32), NoError>] = [] + for current in filters { + + var unreadCountItems: [UnreadMessageCountsItem] = [] + unreadCountItems.append(.total(nil)) + var keys: [PostboxViewKey] = [] + let unreadKey: PostboxViewKey + + if !current.additionallyIncludePeers.isEmpty { + for peerId in current.additionallyIncludePeers { + unreadCountItems.append(.peer(peerId)) + } + } + unreadKey = .unreadCounts(items: unreadCountItems) + keys.append(unreadKey) + for peerId in current.additionallyIncludePeers { + keys.append(.basicPeer(peerId)) + + } + keys.append(.peerNotificationSettings(peerIds: Set(current.additionallyIncludePeers))) + + let s:Signal<(id: Int32, count: Int32), NoError> = combineLatest(context.account.postbox.combinedView(keys: keys), appNotificationSettings(accountManager: context.sharedContext.accountManager)) |> map { keysView, inAppSettings -> (id: Int32, count: Int32) in + + if let unreadCounts = keysView.views[unreadKey] as? UnreadMessageCountsView { + var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int)] = [:] + var totalState: ChatListTotalUnreadState? + for entry in unreadCounts.entries { + switch entry { + case let .total(_, totalStateValue): + totalState = totalStateValue + case let .peer(peerId, state): + if let state = state, state.isUnread { + let notificationSettings = keysView.views[.peerNotificationSettings(peerIds: Set(current.additionallyIncludePeers))] as? PeerNotificationSettingsView + if let peerView = keysView.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer { + let tag = context.account.postbox.seedConfiguration.peerSummaryCounterTags(peer) + var peerCount = Int(state.count) + let isRemoved = notificationSettings?.notificationSettings[peerId]?.isRemovedFromTotalUnreadCount ?? false + var removable = false + switch inAppSettings.totalUnreadCountDisplayStyle { + case .raw: + removable = true + case .filtered: + if !isRemoved { + removable = true + } + } + if !current.includeCategories.contains(.muted), isRemoved { + removable = false + } + if removable, state.isUnread { + switch inAppSettings.totalUnreadCountDisplayCategory { + case .chats: + peerCount = 1 + case .messages: + peerCount = max(1, peerCount) + } + peerTagAndCount[peerId] = (tag, peerCount) + } + + } + } + } + } + + var tags: [PeerSummaryCounterTags] = [] + if current.includeCategories.contains(.privateChats) { + tags.append(.privateChat) + } + + if current.includeCategories.contains(.publicGroups) { + tags.append(.publicGroup) + } + if current.includeCategories.contains(.privateGroups) { + tags.append(.privateGroup) + } + if current.includeCategories.contains(.secretChats) { + tags.append(.secretChat) + } + if current.includeCategories.contains(.bots) { + tags.append(.bot) + } + if current.includeCategories.contains(.channels) { + tags.append(.channel) + } + + var count:Int32 = 0 + if let totalState = totalState { + for tag in tags { + + if let value = totalState.filteredCounters[tag] { + var removable = false + switch inAppSettings.totalUnreadCountDisplayStyle { + case .raw: + removable = true + case .filtered: + removable = true + } + if removable { + switch inAppSettings.totalUnreadCountDisplayCategory { + case .chats: + count += value.chatCount + case .messages: + count += value.messageCount + } + } + } + } + } + for peerId in current.additionallyIncludePeers { + if let (tag, peerCount) = peerTagAndCount[peerId] { + if !tags.contains(tag) { + count += Int32(peerCount) + } + } + } + return (id: current.uniqueId, count: count) + } else { + return (id: current.uniqueId, count: 0) + } + } + signals.append(s) + } + return combineLatest(signals) + } +} diff --git a/Telegram-Mac/ChatListPresetController.swift b/Telegram-Mac/ChatListPresetController.swift index 001baebc6..ba1a031fb 100644 --- a/Telegram-Mac/ChatListPresetController.swift +++ b/Telegram-Mac/ChatListPresetController.swift @@ -69,7 +69,12 @@ private final class ChatListPresetArguments { private let _id_name_input = InputDataIdentifier("_id_name_input") private let _id_private_chats = InputDataIdentifier("_id_private_chats") -private let _id_groups = InputDataIdentifier("_id_groups") + +private let _id_public_groups = InputDataIdentifier("_id_public_groups") +private let _id_private_groups = InputDataIdentifier("_id_private_groups") +private let _id_secret_chats = InputDataIdentifier("_id_secret_chats") + + private let _id_channels = InputDataIdentifier("_id_channels") private let _id_bots = InputDataIdentifier("_id_bots") private let _id_exclude_muted = InputDataIdentifier("_id_exclude_muted") @@ -91,22 +96,33 @@ private func chatListPresetEntries(state: ChatListPresetState, peers: [Peer], ar entries.append(.sectionId(sectionId, type: .normal)) sectionId += 1 - entries.append(.desc(sectionId: sectionId, index: index, text: .plain("NAME"), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textTopItem))) + entries.append(.desc(sectionId: sectionId, index: index, text: .plain("FILTER NAME"), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textTopItem))) index += 1 - entries.append(.input(sectionId: sectionId, index: index, value: .string(state.preset.title), error: nil, identifier: _id_name_input, mode: .plain, data: .init(viewType: .singleItem), placeholder: nil, inputPlaceholder: "preset name", filter: { $0 }, limit: 144)) + entries.append(.input(sectionId: sectionId, index: index, value: .string(state.preset.title), error: nil, identifier: _id_name_input, mode: .plain, data: .init(viewType: .singleItem), placeholder: nil, inputPlaceholder: "Filter Name", filter: { $0 }, limit: 144)) index += 1 entries.append(.sectionId(sectionId, type: .normal)) sectionId += 1 + entries.append(.desc(sectionId: sectionId, index: index, text: .plain("INCLUDE CHAT TYPES"), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textTopItem))) + index += 1 + entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_private_chats, data: .init(name: L10n.chatListFilterPrivateChats, color: theme.colors.text, type: .selectable(state.preset.includeCategories.contains(.privateChats)), viewType: .firstItem, enabled: true, action: { arguments.toggleOption(.privateChats) }))) index += 1 - entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_groups, data: .init(name: L10n.chatListFilterGroups, color: theme.colors.text, type: .selectable(state.preset.includeCategories.contains(.groups)), viewType: .innerItem, enabled: true, action: { - arguments.toggleOption(.groups) + entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_secret_chats, data: .init(name: L10n.chatListFilterSecretChat, color: theme.colors.text, type: .selectable(state.preset.includeCategories.contains(.secretChats)), viewType: .innerItem, enabled: true, action: { + arguments.toggleOption(.secretChats) + }))) + index += 1 + entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_public_groups, data: .init(name: L10n.chatListFilterPublicGroups, color: theme.colors.text, type: .selectable(state.preset.includeCategories.contains(.publicGroups)), viewType: .innerItem, enabled: true, action: { + arguments.toggleOption(.publicGroups) + }))) + index += 1 + entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_private_groups, data: .init(name: L10n.chatListFilterPrivateGroups, color: theme.colors.text, type: .selectable(state.preset.includeCategories.contains(.privateGroups)), viewType: .innerItem, enabled: true, action: { + arguments.toggleOption(.privateGroups) }))) index += 1 entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_channels, data: .init(name: L10n.chatListFilterChannels, color: theme.colors.text, type: .selectable(state.preset.includeCategories.contains(.channels)), viewType: .innerItem, enabled: true, action: { @@ -127,55 +143,27 @@ private func chatListPresetEntries(state: ChatListPresetState, peers: [Peer], ar }))) index += 1 - entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_exclude_read, data: .init(name: "Exclude Read", color: theme.colors.text, type: .switchable(!state.preset.includeCategories.contains(.read)), viewType: .innerItem, enabled: true, action: { + entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_exclude_read, data: .init(name: "Exclude Read", color: theme.colors.text, type: .switchable(!state.preset.includeCategories.contains(.read)), viewType: .lastItem, enabled: true, action: { arguments.toggleOption(.read) }))) index += 1 - entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_apply_exception, data: .init(name: "Apply for Exceptions", color: theme.colors.text, type: .switchable(state.preset.applyReadMutedForExceptions), viewType: .lastItem, enabled: true, action: { - arguments.toggleApplyForExceptions(!state.preset.applyReadMutedForExceptions) - }))) - index += 1 - - var applyText: String? = nil - if state.preset.applyReadMutedForExceptions { - if !state.preset.includeCategories.contains(.read) { - applyText = "All **Read** chats including the exceptions will be excluded from exceptions list." - if !state.preset.includeCategories.contains(.muted) { - applyText = "All **Read** and **Muted** chats including the exceptions will be excluded from chat list." - } - } else if !state.preset.includeCategories.contains(.muted) { - applyText = "All **Muted** chats including the exceptions will be excluded from chat list." - } - if applyText == nil { - applyText = "You can exclude **Read** or **Muted** chats even if a chat in the exceptions." - } - } else { - applyText = "You can exclude **Read** or **Muted** chats even if a chat in the exceptions." - } - if let applyText = applyText { - entries.append(.desc(sectionId: sectionId, index: index, text: .plain(applyText), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textBottomItem))) - index += 1 - } entries.append(.sectionId(sectionId, type: .normal)) sectionId += 1 - entries.append(.desc(sectionId: sectionId, index: index, text: .plain("EXCEPTIONS"), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textTopItem))) + entries.append(.desc(sectionId: sectionId, index: index, text: .plain("ALWAYS INCLUDE"), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textTopItem))) index += 1 entries.append(.custom(sectionId: sectionId, index: index, value: .none, identifier: _id_add_exception, equatable: InputDataEquatable(state), item: { initialSize, stableId in - return GeneralInteractedRowItem(initialSize, stableId: stableId.hashValue, name: "Add", nameStyle: blueActionButton, type: .none, viewType: peers.isEmpty ? .singleItem : .firstItem, action: arguments.addPeer, thumb: GeneralThumbAdditional(thumb: theme.icons.peerInfoAddMember, textInset: 46, thumbInset: 5)) + return GeneralInteractedRowItem(initialSize, stableId: stableId.hashValue, name: "Add Chats", nameStyle: blueActionButton, type: .none, viewType: peers.isEmpty ? .singleItem : .firstItem, action: arguments.addPeer, thumb: GeneralThumbAdditional(thumb: theme.icons.peerInfoAddMember, textInset: 46, thumbInset: 5)) })) index += 1 - - - var fake:[Int] = [] fake.append(0) for (i, _) in peers.enumerated() { diff --git a/Telegram-Mac/ChatListPresetListController.swift b/Telegram-Mac/ChatListPresetListController.swift index f6a504fa4..547abc67d 100644 --- a/Telegram-Mac/ChatListPresetListController.swift +++ b/Telegram-Mac/ChatListPresetListController.swift @@ -18,16 +18,19 @@ private final class ChatListPresetArguments { let context: AccountContext let openPreset:(ChatListFilterPreset)->Void let removePreset: (ChatListFilterPreset)->Void - init(context: AccountContext, openPreset: @escaping(ChatListFilterPreset)->Void, removePreset: @escaping(ChatListFilterPreset)->Void) { + let toggleTabsIsEnabled:(Bool)->Void + init(context: AccountContext, openPreset: @escaping(ChatListFilterPreset)->Void, removePreset: @escaping(ChatListFilterPreset)->Void, toggleTabsIsEnabled: @escaping(Bool)->Void) { self.context = context self.openPreset = openPreset self.removePreset = removePreset + self.toggleTabsIsEnabled = toggleTabsIsEnabled } } private func _id_preset(_ preset: ChatListFilterPreset) -> InputDataIdentifier { return InputDataIdentifier("_id_preset_\(preset.uniqueId)") } private let _id_add_new = InputDataIdentifier("_id_add_new") +private let _id_add_tabs = InputDataIdentifier("_id_add_tabs") private func chatListPresetEntries(state: ChatListFilterPreferences, arguments: ChatListPresetArguments) -> [InputDataEntry] { var entries: [InputDataEntry] = [] @@ -38,18 +41,31 @@ private func chatListPresetEntries(state: ChatListFilterPreferences, arguments: entries.append(.sectionId(sectionId, type: .normal)) sectionId += 1 - for (i, preset) in state.presets.enumerated() { + + if !state.presets.isEmpty { + entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_add_tabs, data: InputDataGeneralData(name: "Show Tabs", color: theme.colors.text, type: .switchable(state.tabsIsEnabled), viewType: .singleItem, action: { + arguments.toggleTabsIsEnabled(!state.tabsIsEnabled) + }))) + index += 1 + + entries.append(.desc(sectionId: sectionId, index: index, text: .plain("Display filter tabs on main screen for quick switching."), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textBottomItem))) + index += 1 + + entries.append(.sectionId(sectionId, type: .normal)) + sectionId += 1 + } + + entries.append(.desc(sectionId: sectionId, index: index, text: .plain("FILTERS"), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textTopItem))) + index += 1 + + for preset in state.presets { var viewType = bestGeneralViewType(state.presets, for: preset) if state.presets.count == 1 { viewType = .firstItem } else if preset == state.presets.last, state.presets.count < 10 { viewType = .innerItem } - var shortCut: String = "⌃⌘\(i + 2)" - if i + 2 == 11 { - shortCut = "⌃⌘-" - } - entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_preset(preset), data: .init(name: preset.title, color: theme.colors.text, type: .nextContext(shortCut), viewType: viewType, enabled: true, description: nil, justUpdate: arc4random64(), action: { + entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_preset(preset), data: .init(name: preset.title, color: theme.colors.text, type: .nextContext(preset.desc), viewType: viewType, enabled: true, description: nil, justUpdate: arc4random64(), action: { arguments.openPreset(preset) }, menuItems: { return [ContextMenuItem("Remove", handler: { @@ -60,9 +76,10 @@ private func chatListPresetEntries(state: ChatListFilterPreferences, arguments: } if state.presets.count < 10 { - entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_add_new, data: InputDataGeneralData(name: L10n.chatListFilterListAddNew, color: theme.colors.accent, icon: theme.icons.peerInfoAddMember, type: .next, viewType: .lastItem, action: { + entries.append(.general(sectionId: sectionId, index: index, value: .none, error: nil, identifier: _id_add_new, data: InputDataGeneralData(name: L10n.chatListFilterListAddNew, color: theme.colors.accent, icon: theme.icons.peerInfoAddMember, type: .next, viewType: state.presets.isEmpty ? .singleItem : .lastItem, action: { arguments.openPreset(ChatListFilterPreset.new) }))) + index += 1 entries.append(.desc(sectionId: sectionId, index: index, text: .plain("You can add up to 10 filters. Drag and drop filter to sort it."), data: .init(color: theme.colors.listGrayText, detectBold: true, viewType: .textBottomItem))) index += 1 @@ -85,6 +102,10 @@ func ChatListPresetListController(context: AccountContext) -> InputDataControlle _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { $0.withRemovedPreset(preset) }).start() + }, toggleTabsIsEnabled: { value in + _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { + $0.withUpdatedTabEnable(value) + }).start() }) let dataSignal = chatListFilterPreferences(postbox: context.account.postbox) |> deliverOnPrepareQueue |> map { state in @@ -109,15 +130,35 @@ func ChatListPresetListController(context: AccountContext) -> InputDataControlle controller.afterTransaction = { controller in - let count = controller.tableView.count - 3 - if count > 0 { - controller.tableView.resortController = TableResortController(resortRange: NSMakeRange(1, controller.tableView.count - 4), start: { row in + var range: NSRange = NSMakeRange(NSNotFound, 0) + + controller.tableView.enumerateItems(with: { item in + if let stableId = item.stableId.base as? InputDataEntryId { + switch stableId { + case let .general(identifier): + if identifier.identifier.hasPrefix("_id_preset") { + if range.location == NSNotFound { + range.location = item.index + } + range.length += 1 + } + default: + if range.location != NSNotFound { + return false + } + } + } + return true + }) + + if range.location != NSNotFound { + controller.tableView.resortController = TableResortController(resortRange: range, start: { row in }, resort: { row in }, complete: { from, to in _ = updateChatListFilterPreferencesInteractively(postbox: context.account.postbox, { - $0.withMovePreset(from - 1, to - 1) + $0.withMovePreset(from - range.location, to - range.location) }).start() }) } else { diff --git a/Telegram-Mac/ChatListRevealItem.swift b/Telegram-Mac/ChatListRevealItem.swift index 258d1806c..26c0f7ef4 100644 --- a/Telegram-Mac/ChatListRevealItem.swift +++ b/Telegram-Mac/ChatListRevealItem.swift @@ -14,17 +14,29 @@ import SwiftSignalKit import SyncCore class ChatListRevealItem: TableStickItem { - fileprivate let action:(()->Void)? + fileprivate let action:((ChatListFilterPreset?)->Void)? fileprivate let context: AccountContext? - init(_ initialSize: NSSize, context: AccountContext, action: (()->Void)? = nil) { + fileprivate let tabs: [ChatListFilterPreset] + fileprivate let selected: ChatListFilterPreset? + fileprivate let openSettings: (()->Void)? + fileprivate let counters: [Int32: Int32] + init(_ initialSize: NSSize, context: AccountContext, tabs: [ChatListFilterPreset], selected: ChatListFilterPreset?, counters: [Int32 : Int32], action: ((ChatListFilterPreset?)->Void)? = nil, openSettings: (()->Void)? = nil) { self.action = action self.context = context + self.tabs = tabs + self.selected = selected + self.openSettings = openSettings + self.counters = counters super.init(initialSize) } required init(_ initialSize: NSSize) { self.action = nil self.context = nil + self.tabs = [] + self.selected = nil + self.openSettings = nil + self.counters = [:] super.init(initialSize) } @@ -35,25 +47,30 @@ class ChatListRevealItem: TableStickItem { override func viewClass() -> AnyClass { return ChatListRevealView.self } + + override var identifier: String { + return "ChatListRevealView" + } + + override var height: CGFloat { + return 36 + } } private final class ChatListRevealView : TableStickView { - private let textView: TextView = TextView() - private let separator: View = View() - private var badgeNode: GlobalBadgeNode? private let containerView = View() + private var animated: Bool = false + private let segmentView: ScrollableSegmentView = ScrollableSegmentView(frame: NSZeroRect) required init(frame frameRect: NSRect) { super.init(frame: frameRect) addSubview(containerView) - containerView.addSubview(textView) - containerView.addSubview(separator) - textView.userInteractionEnabled = false - textView.isSelectable = false - textView.isEventLess = true - containerView.isEventLess = true + containerView.addSubview(segmentView) border = [.Right] - separator.change(opacity: 0, animated: false) + } + + override func scrollWheel(with event: NSEvent) { + segmentView.scrollWheel(with: event) } override func mouseUp(with event: NSEvent) { @@ -61,19 +78,17 @@ private final class ChatListRevealView : TableStickView { } override func mouseDown(with event: NSEvent) { if mouseInside() { - if let item = item as? ChatListRevealItem { - item.action?() - } } } + override func updateIsVisible(_ visible: Bool, animated: Bool) { super.updateIsVisible(visible, animated: animated) - var visible = visible - if let table = item?.table { - visible = visible && table.documentOffset.y > 0 - } - separator.change(opacity: visible ? 1 : 0, animated: false) +// var visible = visible +// if let table = item?.table { +// visible = visible && table.documentOffset.y > 0 +// } +// separator.change(opacity: visible ? 1 : 0, animated: false) } override var backdorColor: NSColor { @@ -82,7 +97,6 @@ private final class ChatListRevealView : TableStickView { override func updateColors() { super.updateColors() - separator.backgroundColor = theme.colors.border } override func set(item: TableRowItem, animated: Bool = false) { @@ -91,36 +105,101 @@ private final class ChatListRevealView : TableStickView { guard let item = item as? ChatListRevealItem else { return } - if let context = item.context, self.badgeNode == nil { - self.badgeNode = GlobalBadgeNode(context.account, sharedContext: context.sharedContext, view: View(), layoutChanged: { [weak self] in - self?.needsLayout = true - }, getColor: { _ in return theme.colors.accent }, fontSize: 9, applyFilter: false) - containerView.addSubview(self.badgeNode!.view!) - } + let animated = self.animated || animated + self.animated = true + + let segmentTheme = ScrollableSegmentTheme(border: presentation.colors.border, selector: presentation.colors.accent, inactiveText: presentation.colors.grayText, activeText: presentation.colors.accent, textFont: .normal(.title)) + var index: Int = 0 + let insets = NSEdgeInsets(left: 10, right: 10, bottom: 6) + var items:[ScrollableSegmentItem] = [.init(title: L10n.chatListFilterAllChats, index: 0, uniqueId: -1, selected: item.selected == nil, insets: insets, icon: nil, theme: segmentTheme, equatable: UIEquatable(0))] + index += 1 + for tab in item.tabs { + + let unreadCount = item.counters[tab.uniqueId] + let icon: CGImage? + if let unreadCount = unreadCount, unreadCount > 0 { + let attributedString = NSAttributedString.initialize(string: "\(Int(unreadCount).prettyNumber)", color: theme.colors.background, font: .medium(.text), coreText: true) + let textLayout = TextNode.layoutText(maybeNode: nil, attributedString, nil, 1, .start, NSMakeSize(CGFloat.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitude), nil, false, .center) + + icon = generateImage(NSMakeSize(textLayout.0.size.width + 12, textLayout.0.size.height + 4), rotatedContext: { size, ctx in + let rect = NSMakeRect(0, 0, size.width, size.height) + ctx.clear(rect) + + ctx.setFillColor(theme.colors.accent.cgColor) + + + ctx.round(size, size.height/2.0) + ctx.fill(rect) + + let focus = NSMakePoint((rect.width - textLayout.0.size.width) / 2, (rect.height - textLayout.0.size.height) / 2) + textLayout.1.draw(NSMakeRect(focus.x, 2, textLayout.0.size.width, textLayout.0.size.height), in: ctx, backingScaleFactor: 2.0, backgroundColor: .white) + + })! + } else { + icon = nil + } + + + items.append(ScrollableSegmentItem(title: tab.title, index: index, uniqueId: tab.uniqueId, selected: item.selected == tab, insets: insets, icon: icon, theme: segmentTheme, equatable: UIEquatable(unreadCount ?? 0))) + index += 1 + } + if let _ = item.openSettings { + items.append(.init(title: "", index: index, uniqueId: -2, selected: false, insets: NSEdgeInsets(left: 5, right: 10, bottom: 6), icon: theme.icons.chat_filter_add, theme: segmentTheme, equatable: UIEquatable(0))) + index += 1 + } + + + + segmentView.updateItems(items, animated: animated) + + segmentView.didChangeSelectedItem = { [weak item] selected in + if let item = item { + if selected.uniqueId == -1 { + item.action?(nil) + } else if selected.uniqueId == -2 { + item.openSettings?() + } else { + item.action?(item.tabs[selected.index - 1]) + } + } + } } + override var isHidden: Bool { + didSet { + if isHidden { + var bp:Int = 0 + bp += 1 + } + } + } + + override var isAlwaysUp: Bool { + return true + } + + override func removeFromSuperview() { + super.removeFromSuperview() + } + + public override func setFrameOrigin(_ newOrigin: NSPoint) { + super.setFrameOrigin(newOrigin) + } + + deinit { + var bp:Int = 0 + bp += 1 + } + override func layout() { super.layout() - containerView.frame = bounds + containerView.frame = NSMakeRect(0, 0, bounds.width - 1, bounds.height) - - let layout = TextViewLayout(.initialize(string: self.frame.width < 200 ? L10n.chatListCloseFilterShort : L10n.chatListCloseFilter, color: theme.colors.accent, font: .normal(.text)), maximumNumberOfLines: 1) - layout.measure(width: frame.width - 40) - textView.update(layout) - - let badgeSize = self.frame.width < 200 ? .zero : self.badgeNode?.view?.frame.size ?? .zero - if badgeSize == .zero { - textView.center() - } else { - textView.centerY(x: floorToScreenPixels(backingScaleFactor, (frame.width - (textView.frame.width + badgeSize.width + 10)) / 2)) - } - self.badgeNode?.view?.isHidden = self.frame.width < 200 - separator.frame = NSMakeRect(0, frame.height - .borderSize, frame.width, .borderSize) - - self.badgeNode?.view?.setFrameOrigin(NSMakePoint(textView.frame.maxX + 10, 6)) + segmentView.frame = containerView.bounds + } required init?(coder: NSCoder) { diff --git a/Telegram-Mac/ChatSelectText.swift b/Telegram-Mac/ChatSelectText.swift index 1ec0699cf..25bfae848 100644 --- a/Telegram-Mac/ChatSelectText.swift +++ b/Telegram-Mac/ChatSelectText.swift @@ -214,6 +214,7 @@ class ChatSelectText : NSObject { private var startMessageId:MessageId? = nil private var lastPressureEventStage = 0 private var inPressedState = false + private var locationInWindow: NSPoint? = nil private var lastSelectdMessageId: MessageId? @@ -249,6 +250,7 @@ class ChatSelectText : NSObject { self?.started = false self?.inPressedState = false + self?.locationInWindow = event.locationInWindow if let table = self?.table, let superview = table.superview, let documentView = table.documentView { let point = superview.convert(event.locationInWindow, from: nil) @@ -297,6 +299,7 @@ class ChatSelectText : NSObject { window.set(mouseHandler: { [weak self] event -> KeyHandlerResult in self?.beginInnerLocation = NSZeroPoint + self?.locationInWindow = nil Queue.mainQueue().justDispatch { guard let table = self?.table else {return} @@ -337,7 +340,15 @@ class ChatSelectText : NSObject { window.set(mouseHandler: { [weak self] event -> KeyHandlerResult in guard let `self` = self else {return .rejected} - + + if let locationInWindow = self.locationInWindow { + let old = (ceil(locationInWindow.x), ceil(locationInWindow.y)) + let new = (ceil(event.locationInWindow.x), round(event.locationInWindow.y)) + if abs(old.0 - new.0) <= 1 && abs(old.1 - new.1) <= 1 { + return .rejected + } + } + self.endInnerLocation = self.table.documentView?.convert(window.mouseLocationOutsideOfEventStream, from: nil) ?? NSZeroPoint // if let overView = window.contentView?.hitTest(window.mouseLocationOutsideOfEventStream) as? Control { diff --git a/Telegram-Mac/GeneralRowItem.swift b/Telegram-Mac/GeneralRowItem.swift index 9d124da9c..0fe96a16d 100644 --- a/Telegram-Mac/GeneralRowItem.swift +++ b/Telegram-Mac/GeneralRowItem.swift @@ -139,11 +139,6 @@ enum GeneralViewItemPosition : Equatable { } -extension NSEdgeInsets : Equatable { - public static func ==(lhs: NSEdgeInsets, rhs: NSEdgeInsets) -> Bool { - return lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right && lhs.bottom == rhs.bottom - } -} enum GeneralViewType : Equatable { case legacy diff --git a/Telegram-Mac/GlobalBadgeNode.swift b/Telegram-Mac/GlobalBadgeNode.swift index c12ac22b7..e1023011f 100644 --- a/Telegram-Mac/GlobalBadgeNode.swift +++ b/Telegram-Mac/GlobalBadgeNode.swift @@ -95,68 +95,184 @@ class GlobalBadgeNode: Node { let total:Int32 } - let signal:Signal = chatListFilterPreferences(postbox: account.postbox) |> mapToSignal { settings -> Signal in - var items:[UnreadMessageCountsItem] = [] - let peerSignal: Signal<(Peer, Bool)?, NoError> - - - - - if let peerId = excludePeerId { - items.append(.peer(peerId)) - let notificationKeyView: PostboxViewKey = .peerNotificationSettings(peerIds: Set([peerId])) - peerSignal = combineLatest(account.postbox.loadedPeerWithId(peerId), account.postbox.combinedView(keys: [notificationKeyView]) |> map { view in - return ((view.views[notificationKeyView] as? PeerNotificationSettingsView)?.notificationSettings[peerId])?.isRemovedFromTotalUnreadCount ?? false + var items:[UnreadMessageCountsItem] = [] + let peerSignal: Signal<(Peer, Bool)?, NoError> + + + + + if let peerId = excludePeerId { + items.append(.peer(peerId)) + let notificationKeyView: PostboxViewKey = .peerNotificationSettings(peerIds: Set([peerId])) + peerSignal = combineLatest(account.postbox.loadedPeerWithId(peerId), account.postbox.combinedView(keys: [notificationKeyView]) |> map { view in + return ((view.views[notificationKeyView] as? PeerNotificationSettingsView)?.notificationSettings[peerId])?.isRemovedFromTotalUnreadCount ?? false }) |> map {Optional($0)} - } else { - peerSignal = .single(nil) + } else { + peerSignal = .single(nil) + } + + let signal: Signal<[(Int32, RenderedTotalUnreadCountType)], NoError> + if collectAllAccounts { + signal = sharedContext.activeAccountsWithInfo |> mapToSignal { primaryId, accounts in + return combineLatest(accounts.filter { $0.account.id != account.id }.map { renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: $0.account.postbox) }) } - - let signal: Signal<[(Int32, RenderedTotalUnreadCountType)], NoError> - if collectAllAccounts { - signal = sharedContext.activeAccountsWithInfo |> mapToSignal { primaryId, accounts in - return combineLatest(accounts.filter { $0.account.id != account.id }.map { renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: $0.account.postbox) }) - } - } else { - signal = renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: account.postbox) |> map { [$0] } - } - - var unreadCountItems: [UnreadMessageCountsItem] = [] - unreadCountItems.append(.total(nil)) - var keys: [PostboxViewKey] = [] - let unreadKey: PostboxViewKey - if let current = preset ?? settings.current, applyFilter { - if !current.additionallyIncludePeers.isEmpty { - for peerId in current.additionallyIncludePeers { - unreadCountItems.append(.peer(peerId)) - } - } - unreadKey = .unreadCounts(items: unreadCountItems) - keys.append(unreadKey) + } else { + signal = renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: account.postbox) |> map { [$0] } + } + + var unreadCountItems: [UnreadMessageCountsItem] = [] + unreadCountItems.append(.total(nil)) + var keys: [PostboxViewKey] = [] + let unreadKey: PostboxViewKey + if let current = preset, applyFilter { + if !current.additionallyIncludePeers.isEmpty { for peerId in current.additionallyIncludePeers { - keys.append(.basicPeer(peerId)) - + unreadCountItems.append(.peer(peerId)) } - keys.append(.peerNotificationSettings(peerIds: Set(current.additionallyIncludePeers))) - } else { - unreadKey = .unreadCounts(items: []) } - - return combineLatest(signal, account.postbox.unreadMessageCountsView(items: items), account.postbox.combinedView(keys: keys), appNotificationSettings(accountManager: sharedContext.accountManager), peerSignal) |> map { (counts, view, keysView, inAppSettings, peerSettings) in + unreadKey = .unreadCounts(items: unreadCountItems) + keys.append(unreadKey) + for peerId in current.additionallyIncludePeers { + keys.append(.basicPeer(peerId)) - if !applyFilter || (settings.current == nil && preset == nil) { - var excludeTotal: Int32 = 0 - - var dockText: String? - let totalValue = !inAppSettings.badgeEnabled ? 0 : (collectAllAccounts && !inAppSettings.notifyAllAccounts ? 0 : max(0, counts.reduce(0, { $0 + $1.0 }))) - if totalValue > 0 { - dockText = "\(totalValue)" + } + keys.append(.peerNotificationSettings(peerIds: Set(current.additionallyIncludePeers))) + } else { + unreadKey = .unreadCounts(items: []) + } + + let s:Signal = combineLatest(signal, account.postbox.unreadMessageCountsView(items: items), account.postbox.combinedView(keys: keys), appNotificationSettings(accountManager: sharedContext.accountManager), peerSignal) |> map { (counts, view, keysView, inAppSettings, peerSettings) in + + if !applyFilter || preset == nil { + var excludeTotal: Int32 = 0 + + var dockText: String? + let totalValue = !inAppSettings.badgeEnabled ? 0 : (collectAllAccounts && !inAppSettings.notifyAllAccounts ? 0 : max(0, counts.reduce(0, { $0 + $1.0 }))) + if totalValue > 0 { + dockText = "\(totalValue)" + } + + excludeTotal = totalValue + + if items.count == 1, let peerSettings = peerSettings { + if let count = view.count(for: items[0]), inAppSettings.totalUnreadCountIncludeTags.contains(peerSettings.0.peerSummaryTags), count > 0 { + var removable = false + switch inAppSettings.totalUnreadCountDisplayStyle { + case .raw: + removable = true + case .filtered: + if !peerSettings.1 { + removable = true + } + } + if removable { + switch inAppSettings.totalUnreadCountDisplayCategory { + case .chats: + excludeTotal -= 1 + case .messages: + excludeTotal -= count + } + } + } + } + return Result(dockText: dockText, total: excludeTotal) + } else if let preset = preset { + if let unreadCounts = keysView.views[unreadKey] as? UnreadMessageCountsView { + var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int)] = [:] + var totalState: ChatListTotalUnreadState? + for entry in unreadCounts.entries { + switch entry { + case let .total(_, totalStateValue): + totalState = totalStateValue + case let .peer(peerId, state): + if let state = state, state.isUnread { + let notificationSettings = keysView.views[.peerNotificationSettings(peerIds: Set(preset.additionallyIncludePeers))] as? PeerNotificationSettingsView + if let peerView = keysView.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer { + let tag = account.postbox.seedConfiguration.peerSummaryCounterTags(peer) + var peerCount = Int(state.count) + let isRemoved = notificationSettings?.notificationSettings[peerId]?.isRemovedFromTotalUnreadCount ?? false + var removable = false + switch inAppSettings.totalUnreadCountDisplayStyle { + case .raw: + removable = true + case .filtered: + if !isRemoved { + removable = true + } + } + if !preset.includeCategories.contains(.muted), isRemoved { + removable = false + } + if removable, state.isUnread { + switch inAppSettings.totalUnreadCountDisplayCategory { + case .chats: + peerCount = 1 + case .messages: + peerCount = max(1, peerCount) + } + peerTagAndCount[peerId] = (tag, peerCount) + } + + } + } + } } - excludeTotal = totalValue + var tags: [PeerSummaryCounterTags] = [] + if preset.includeCategories.contains(.privateChats) { + tags.append(.privateChat) + } + + if preset.includeCategories.contains(.publicGroups) { + tags.append(.publicGroup) + } + if preset.includeCategories.contains(.privateGroups) { + tags.append(.privateGroup) + } + if preset.includeCategories.contains(.secretChats) { + tags.append(.secretChat) + } + if preset.includeCategories.contains(.bots) { + tags.append(.bot) + } + if preset.includeCategories.contains(.channels) { + tags.append(.channel) + } + + + var count:Int32 = 0 + if let totalState = totalState { + for tag in tags { + + if let value = totalState.filteredCounters[tag] { + var removable = false + switch inAppSettings.totalUnreadCountDisplayStyle { + case .raw: + removable = true + case .filtered: + removable = true + } + if removable { + switch inAppSettings.totalUnreadCountDisplayCategory { + case .chats: + count += value.chatCount + case .messages: + count += value.messageCount + } + } + } + } + } + for peerId in preset.additionallyIncludePeers { + if let (tag, peerCount) = peerTagAndCount[peerId] { + if !tags.contains(tag) { + count += Int32(peerCount) + } + } + } if items.count == 1, let peerSettings = peerSettings { - if let count = view.count(for: items[0]), inAppSettings.totalUnreadCountIncludeTags.contains(peerSettings.0.peerSummaryTags), count > 0 { + if let current = view.count(for: items[0]), inAppSettings.totalUnreadCountIncludeTags.contains(peerSettings.0.peerSummaryTags), current > 0 { var removable = false switch inAppSettings.totalUnreadCountDisplayStyle { case .raw: @@ -169,136 +285,23 @@ class GlobalBadgeNode: Node { if removable { switch inAppSettings.totalUnreadCountDisplayCategory { case .chats: - excludeTotal -= 1 + count -= 1 case .messages: - excludeTotal -= count + count -= current } } } } - return Result(dockText: dockText, total: excludeTotal) - } else if let preset = preset ?? settings.current { - if let unreadCounts = keysView.views[unreadKey] as? UnreadMessageCountsView { - var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int)] = [:] - var totalState: ChatListTotalUnreadState? - for entry in unreadCounts.entries { - switch entry { - case let .total(_, totalStateValue): - totalState = totalStateValue - case let .peer(peerId, state): - if let state = state, state.isUnread { - let notificationSettings = keysView.views[.peerNotificationSettings(peerIds: Set(preset.additionallyIncludePeers))] as? PeerNotificationSettingsView - if let peerView = keysView.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer { - let tag = account.postbox.seedConfiguration.peerSummaryCounterTags(peer) - var peerCount = Int(state.count) - let isRemoved = notificationSettings?.notificationSettings[peerId]?.isRemovedFromTotalUnreadCount ?? false - var removable = false - switch inAppSettings.totalUnreadCountDisplayStyle { - case .raw: - removable = true - case .filtered: - if !isRemoved { - removable = true - } - } - if !preset.includeCategories.contains(.muted), isRemoved { - removable = false - } - if removable, state.isUnread { - switch inAppSettings.totalUnreadCountDisplayCategory { - case .chats: - peerCount = 1 - case .messages: - peerCount = max(1, peerCount) - } - peerTagAndCount[peerId] = (tag, peerCount) - } - - } - } - } - } - - var tags: [PeerSummaryCounterTags] = [] - if preset.includeCategories.contains(.privateChats) { - tags.append(.privateChat) - } - - if preset.includeCategories.contains(.groups) { - tags.append(.privateGroup) - tags.append(.publicGroup) - } - if preset.includeCategories.contains(.bots) { - tags.append(.bot) - } - if preset.includeCategories.contains(.channels) { - tags.append(.channel) - } - - - var count:Int32 = 0 - if let totalState = totalState { - for tag in tags { - - if let value = totalState.filteredCounters[tag] { - var removable = false - switch inAppSettings.totalUnreadCountDisplayStyle { - case .raw: - removable = true - case .filtered: - removable = true - } - if removable { - switch inAppSettings.totalUnreadCountDisplayCategory { - case .chats: - count += value.chatCount - case .messages: - count += value.messageCount - } - } - } - } - } - for peerId in preset.additionallyIncludePeers { - if let (tag, peerCount) = peerTagAndCount[peerId] { - if !tags.contains(tag) { - count += Int32(peerCount) - } - } - } - - if items.count == 1, let peerSettings = peerSettings { - if let current = view.count(for: items[0]), inAppSettings.totalUnreadCountIncludeTags.contains(peerSettings.0.peerSummaryTags), current > 0 { - var removable = false - switch inAppSettings.totalUnreadCountDisplayStyle { - case .raw: - removable = true - case .filtered: - if !peerSettings.1 { - removable = true - } - } - if removable { - switch inAppSettings.totalUnreadCountDisplayCategory { - case .chats: - count -= 1 - case .messages: - count -= current - } - } - } - } - - return Result(dockText: nil, total: count) - } + + return Result(dockText: nil, total: count) } - return Result(dockText: nil, total: 0) } + return Result(dockText: nil, total: 0) } |> deliverOnMainQueue - self.disposable.set(signal.start(next: { [weak self] result in + self.disposable.set(s.start(next: { [weak self] result in if let strongSelf = self { if result.total == 0 { diff --git a/Telegram-Mac/Info.plist b/Telegram-Mac/Info.plist index a04057dc4..14f430302 100644 --- a/Telegram-Mac/Info.plist +++ b/Telegram-Mac/Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 190408 + 190681 LSApplicationCategoryType public.app-category.social-networking LSFileQuarantineEnabled diff --git a/Telegram-Mac/InstantPageMedia.swift b/Telegram-Mac/InstantPageMedia.swift index f6892385f..4a4418fc2 100644 --- a/Telegram-Mac/InstantPageMedia.swift +++ b/Telegram-Mac/InstantPageMedia.swift @@ -10,7 +10,7 @@ import Cocoa import Postbox import TelegramCore import SyncCore - +import TGUIKit diff --git a/Telegram-Mac/Localizable.swift b/Telegram-Mac/Localizable.swift index c324b464a..7f7d12c56 100644 --- a/Telegram-Mac/Localizable.swift +++ b/Telegram-Mac/Localizable.swift @@ -1867,9 +1867,9 @@ internal final class L10n { internal static var chatInputShowNext: String { return L10n.tr("Localizable", "ChatInput.ShowNext") } /// Archived Chats internal static var chatListArchivedChats: String { return L10n.tr("Localizable", "ChatList.ArchivedChats") } - /// Show All Chats - internal static var chatListCloseFilter: String { return L10n.tr("Localizable", "ChatList.CloseFilter") } /// Show All + internal static var chatListCloseFilter: String { return L10n.tr("Localizable", "ChatList.CloseFilter") } + /// All internal static var chatListCloseFilterShort: String { return L10n.tr("Localizable", "ChatList.CloseFilterShort") } /// Draft: internal static var chatListDraft: String { return L10n.tr("Localizable", "ChatList.Draft") } @@ -1895,6 +1895,8 @@ internal final class L10n { internal static var chatListAddPlaceholder: String { return L10n.tr("Localizable", "ChatList.Add.Placeholder") } /// Add internal static var chatListAddSave: String { return L10n.tr("Localizable", "ChatList.Add.Save") } + /// Chats + internal static var chatListArchiveBack: String { return L10n.tr("Localizable", "ChatList.Archive.Back") } /// Call internal static var chatListContextCall: String { return L10n.tr("Localizable", "ChatList.Context.Call") } /// Clear History @@ -1927,25 +1929,53 @@ internal final class L10n { internal static var chatListContextUnpin: String { return L10n.tr("Localizable", "ChatList.Context.Unpin") } /// All internal static var chatListFilterAll: String { return L10n.tr("Localizable", "ChatList.Filter.All") } + /// All Chats + internal static var chatListFilterAllChats: String { return L10n.tr("Localizable", "ChatList.Filter.AllChats") } + /// Chats + internal static var chatListFilterBack: String { return L10n.tr("Localizable", "ChatList.Filter.Back") } /// Bots internal static var chatListFilterBots: String { return L10n.tr("Localizable", "ChatList.Filter.Bots") } /// Channels internal static var chatListFilterChannels: String { return L10n.tr("Localizable", "ChatList.Filter.Channels") } /// The filter is empty.\n[Show All Chats]() internal static var chatListFilterEmpty: String { return L10n.tr("Localizable", "ChatList.Filter.Empty") } - /// Groups - internal static var chatListFilterGroups: String { return L10n.tr("Localizable", "ChatList.Filter.Groups") } /// Private Chats internal static var chatListFilterPrivateChats: String { return L10n.tr("Localizable", "ChatList.Filter.PrivateChats") } + /// Private Groups + internal static var chatListFilterPrivateGroups: String { return L10n.tr("Localizable", "ChatList.Filter.PrivateGroups") } + /// Public Groups + internal static var chatListFilterPublicGroups: String { return L10n.tr("Localizable", "ChatList.Filter.PublicGroups") } + /// Secret Chats + internal static var chatListFilterSecretChat: String { return L10n.tr("Localizable", "ChatList.Filter.SecretChat") } /// Filters internal static var chatListFilterSetup: String { return L10n.tr("Localizable", "ChatList.Filter.Setup") } /// Unmuted Chats internal static var chatListFilterUnmutedChats: String { return L10n.tr("Localizable", "ChatList.Filter.UnmutedChats") } /// Unread Chats internal static var chatListFilterUnreadChats: String { return L10n.tr("Localizable", "ChatList.Filter.UnreadChats") } - /// Add New + /// All Bots + internal static var chatListFilterDescBots: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.Bots") } + /// All Channels + internal static var chatListFilterDescChannels: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.Channels") } + /// Customized + internal static var chatListFilterDescCustomized: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.Customized") } + /// All Groups + internal static var chatListFilterDescGroups: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.Groups") } + /// All Private Chats + internal static var chatListFilterDescPrivateChats: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.PrivateChats") } + /// All Private Groups + internal static var chatListFilterDescPrivateGroups: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.PrivateGroups") } + /// All Public Groups + internal static var chatListFilterDescPublicGroups: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.PublicGroups") } + /// All SecretChats + internal static var chatListFilterDescSecretChats: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.SecretChats") } + /// All Unmuted + internal static var chatListFilterDescUnmuted: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.Unmuted") } + /// All Unread + internal static var chatListFilterDescUnread: String { return L10n.tr("Localizable", "ChatList.Filter.Desc.Unread") } + /// Create New Filter internal static var chatListFilterListAddNew: String { return L10n.tr("Localizable", "ChatList.Filter.List.AddNew") } - /// Chat List + /// Filters internal static var chatListFilterListTitle: String { return L10n.tr("Localizable", "ChatList.Filter.List.Title") } /// New Filter internal static var chatListFilterPresetNewName: String { return L10n.tr("Localizable", "ChatList.Filter.Preset.NewName") } diff --git a/Telegram-Mac/LottiePlayer.swift b/Telegram-Mac/LottiePlayer.swift index d0d85d848..8a3c379ff 100644 --- a/Telegram-Mac/LottiePlayer.swift +++ b/Telegram-Mac/LottiePlayer.swift @@ -406,7 +406,7 @@ private final class PlayerRenderer { framesTask?.cancel() } case .onceEnd: - if current.frame == currentState(stateValue)?.endFrame { + if let state = currentState(stateValue), state.endFrame - current.frame <= 1 { renderer.finished = true renderer.timer?.invalidate() framesTask?.cancel() diff --git a/Telegram-Mac/MainViewController.swift b/Telegram-Mac/MainViewController.swift index 69c9f412b..8259e6750 100644 --- a/Telegram-Mac/MainViewController.swift +++ b/Telegram-Mac/MainViewController.swift @@ -368,10 +368,10 @@ class MainViewController: TelegramViewController { private func _showFastChatSettings(_ control: Control, items: [SPopoverItem]) { switch self.chatList.mode { - case .plain: + case .plain, .filter: if self.tabController.current == chatListNavigation, !items.isEmpty { showPopover(for: control, with: SPopoverViewController(items: items, visibility: 10), edge: .maxX, inset: NSMakePoint(control.frame.width + 40, 0)) - } + } default: break } diff --git a/Telegram-Mac/PeersListController.swift b/Telegram-Mac/PeersListController.swift index 7f82b376f..f768c387c 100644 --- a/Telegram-Mac/PeersListController.swift +++ b/Telegram-Mac/PeersListController.swift @@ -231,6 +231,23 @@ final class RevealAllChatsView : Control { } } +final class FilterTabsView : View { + let tabs: ScrollableSegmentView = ScrollableSegmentView(frame: NSZeroRect) + required init(frame frameRect: NSRect) { + super.init(frame: frameRect) + addSubview(tabs) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layout() { + super.layout() + tabs.frame = bounds + } +} + class PeerListContainerView : View { private let backgroundView = BackgroundView(frame: NSZeroRect) var tableView = TableView(frame:NSZeroRect, drawBorder: true) { @@ -239,55 +256,15 @@ class PeerListContainerView : View { addSubview(tableView) } } + private let searchContainer: View = View() + let searchView:SearchView = SearchView(frame:NSMakeRect(10, 0, 0, 0)) let compose:ImageButton = ImageButton() fileprivate let proxyButton:ImageButton = ImageButton() private let proxyConnecting: ProgressIndicator = ProgressIndicator(frame: NSMakeRect(0, 0, 11, 11)) private var searchState: SearchFieldState = .None -// private var revealView: RevealAllChatsView? -// -// -// func setNeedShowReveal(_ showReveal: Bool, animated: Bool, callback: @escaping()->Void) -> Void { -// if showReveal { -// if self.revealView == nil { -// self.revealView = RevealAllChatsView(frame: NSMakeRect(0, 0, frame.width - 60, 30)) -// self.addSubview(self.revealView!) -// -// guard let revealView = self.revealView else { -// return -// } -// revealView.frame = NSMakeRect(30, frame.height - revealView.frame.height - 10, revealView.textView.frame.width + 40, 30) -// -// -// if animated { -// revealView.layer?.animatePosition(from: NSMakePoint(revealView.frame.minX, frame.height), to: revealView.frame.origin, timingFunction: .spring) -// revealView.layer?.animateAlpha(from: 0, to: 0.1, duration: 0.4, timingFunction: .spring) -// } -// } -// -// revealView?.removeAllHandlers() -// revealView?.set(handler: { _ in -// callback() -// }, for: .Click) -// -// } else { -// if animated { -// if let revealView = self.revealView { -// self.revealView = nil -// revealView.change(pos: NSMakePoint(revealView.frame.minX, frame.height), animated: true, timingFunction: .spring) -// revealView.layer?.animateAlpha(from: 1, to: 0, duration: 0.4, timingFunction: .spring, removeOnCompletion: false, completion: { [weak revealView] _ in -// revealView?.removeFromSuperview() -// -// }) -// } -// } else { -// self.revealView?.removeFromSuperview() -// self.revealView = nil -// } -// } -// } -// + var mode: PeerListMode = .plain { didSet { switch mode { @@ -295,6 +272,8 @@ class PeerListContainerView : View { compose.isHidden = true case .plain: compose.isHidden = false + case .filter: + compose.isHidden = true } needsLayout = true } @@ -304,10 +283,11 @@ class PeerListContainerView : View { self.border = [.Right] compose.autohighlight = false autoresizesSubviews = false + addSubview(searchContainer) addSubview(tableView) - addSubview(compose) - addSubview(proxyButton) - addSubview(searchView) + searchContainer.addSubview(compose) + searchContainer.addSubview(proxyButton) + searchContainer.addSubview(searchView) proxyButton.addSubview(proxyConnecting) setFrameSize(frameRect.size) updateLocalizationAndTheme(theme: theme) @@ -343,7 +323,7 @@ class PeerListContainerView : View { func searchStateChanged(_ state: SearchFieldState, animated: Bool) { self.searchState = state - searchView.change(size: NSMakeSize(state == .Focus || mode.isFolder ? frame.width - searchView.frame.minX * 2 : (frame.width - (36 + compose.frame.width) - (proxyButton.isHidden ? 0 : proxyButton.frame.width + 12)), 30), animated: animated) + searchView.change(size: NSMakeSize(state == .Focus || !mode.isPlain ? frame.width - searchView.frame.minX * 2 : (frame.width - (36 + compose.frame.width) - (proxyButton.isHidden ? 0 : proxyButton.frame.width + 12)), 30), animated: animated) compose.change(opacity: state == .Focus ? 0 : 1, animated: animated) proxyButton.change(opacity: state == .Focus ? 0 : 1, animated: animated) } @@ -391,16 +371,21 @@ class PeerListContainerView : View { } } - searchView.setFrameSize(NSMakeSize(searchState == .Focus || mode.isFolder ? frame.width - searchView.frame.minX * 2 : (frame.width - (36 + compose.frame.width) - (proxyButton.isHidden ? 0 : proxyButton.frame.width + 12)), 30)) + searchContainer.frame = NSMakeRect(0, 0, frame.width, offset) + + + searchView.setFrameSize(NSMakeSize(searchState == .Focus || !mode.isPlain ? frame.width - searchView.frame.minX * 2 : (frame.width - (36 + compose.frame.width) - (proxyButton.isHidden ? 0 : proxyButton.frame.width + 12)), 30)) + + tableView.setFrameSize(frame.width, frame.height - offset) searchView.isHidden = frame.width < 200 if searchView.isHidden { - compose.centerX(y: floorToScreenPixels(backingScaleFactor, (49 - compose.frame.height)/2.0)) + compose.center() proxyButton.setFrameOrigin(-proxyButton.frame.width, 0) } else { - compose.setFrameOrigin(frame.width - 12 - compose.frame.width, floorToScreenPixels(backingScaleFactor, (offset - compose.frame.height)/2.0)) - proxyButton.setFrameOrigin(frame.width - 12 - compose.frame.width - proxyButton.frame.width - 6, floorToScreenPixels(backingScaleFactor, (offset - proxyButton.frame.height)/2.0)) + compose.setFrameOrigin(searchContainer.frame.width - 12 - compose.frame.width, floorToScreenPixels(backingScaleFactor, (searchContainer.frame.height - compose.frame.height)/2.0)) + proxyButton.setFrameOrigin(searchContainer.frame.width - 12 - compose.frame.width - proxyButton.frame.width - 6, floorToScreenPixels(backingScaleFactor, (searchContainer.frame.height - proxyButton.frame.height)/2.0)) } searchView.setFrameOrigin(10, floorToScreenPixels(backingScaleFactor, (offset - searchView.frame.height)/2.0)) tableView.setFrameOrigin(0, offset) @@ -419,10 +404,11 @@ class PeerListContainerView : View { enum PeerListMode { case plain case folder(PeerGroupId) + case filter(Int32) - var isFolder:Bool { + var isPlain:Bool { switch self { - case .folder: + case .plain: return true default: return false @@ -436,6 +422,14 @@ enum PeerListMode { return .root } } + var filterId: Int32? { + switch self { + case let .filter(id): + return id + default: + return nil + } + } } @@ -471,7 +465,7 @@ class PeersListController: TelegramGenericViewController, self.mode = mode self.searchOptions = searchOptions super.init(context) - self.bar = .init(height: mode.isFolder ? 50 : 0) + self.bar = .init(height: !mode.isPlain ? 50 : 0) } override var redirectUserInterfaceCalls: Bool { @@ -680,6 +674,7 @@ class PeersListController: TelegramGenericViewController, self.genericView.tableView.change(opacity: 0, animated: animated, completion: { [weak self] _ in self?.genericView.tableView.isHidden = true }) + searchController.defaultQuery = self.genericView.searchView.query searchController.navigationController = self.navigationController searchController.viewWillAppear(true) @@ -780,6 +775,8 @@ class PeersListController: TelegramGenericViewController, self.navigationController?.push(ChatListController(context, modal: false, groupId: groupId)) case .reveal: break + case .empty: + break } if close { self.genericView.searchView.cancel(true) diff --git a/Telegram-Mac/PreparedChatHistoryViewTransition.swift b/Telegram-Mac/PreparedChatHistoryViewTransition.swift index 4946b62db..d6991f0af 100644 --- a/Telegram-Mac/PreparedChatHistoryViewTransition.swift +++ b/Telegram-Mac/PreparedChatHistoryViewTransition.swift @@ -11,6 +11,7 @@ import SwiftSignalKit import Postbox import TelegramCore import SyncCore +import TGUIKit struct ChatHistoryViewTransition { let historyView: ChatHistoryView diff --git a/Telegram-Mac/SImageView.swift b/Telegram-Mac/SImageView.swift index 865d9dc01..94debc373 100644 --- a/Telegram-Mac/SImageView.swift +++ b/Telegram-Mac/SImageView.swift @@ -24,6 +24,10 @@ class SImageView: NSView { fatalError("init(frame:) has not been implemented") } + override func hitTest(_ point: NSPoint) -> NSView? { + return nil + } + var data: (CGImage, NSEdgeInsets)? { didSet { if let image = data { diff --git a/Telegram-Mac/SearchController.swift b/Telegram-Mac/SearchController.swift index e85f28b70..14faa4c7f 100644 --- a/Telegram-Mac/SearchController.swift +++ b/Telegram-Mac/SearchController.swift @@ -419,6 +419,8 @@ class SearchController: GenericViewController,TableViewDelegate { } + var defaultQuery: String? = nil + private let context:AccountContext private var marked: Bool = false private let arguments:SearchControllerArguments @@ -928,7 +930,7 @@ class SearchController: GenericViewController,TableViewDelegate { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - request(with: nil) + request(with: self.defaultQuery) } override func viewDidDisappear(_ animated: Bool) { diff --git a/Telegram-Mac/Spotlight.swift b/Telegram-Mac/Spotlight.swift index 67d97066c..eba38912b 100644 --- a/Telegram-Mac/Spotlight.swift +++ b/Telegram-Mac/Spotlight.swift @@ -12,6 +12,7 @@ import TelegramCore import SyncCore import Postbox import SwiftSignalKit +import TGUIKit enum SpotlightIdentifierSource : Equatable { case peerId(PeerId) diff --git a/Telegram-Mac/TelegramIconsTheme.swift b/Telegram-Mac/TelegramIconsTheme.swift index bc0bdb30c..56c26e881 100644 --- a/Telegram-Mac/TelegramIconsTheme.swift +++ b/Telegram-Mac/TelegramIconsTheme.swift @@ -5763,6 +5763,149 @@ final class TelegramIconsTheme { return image } } + var chat_filter_add: CGImage { + if let image = cached.with({ $0["chat_filter_add"] }) { + return image + } else { + let image = _chat_filter_add() + _ = cached.modify { current in + var current = current + current["chat_filter_add"] = image + return current + } + return image + } + } + var chat_filter_bots: CGImage { + if let image = cached.with({ $0["chat_filter_bots"] }) { + return image + } else { + let image = _chat_filter_bots() + _ = cached.modify { current in + var current = current + current["chat_filter_bots"] = image + return current + } + return image + } + } + var chat_filter_channels: CGImage { + if let image = cached.with({ $0["chat_filter_channels"] }) { + return image + } else { + let image = _chat_filter_channels() + _ = cached.modify { current in + var current = current + current["chat_filter_channels"] = image + return current + } + return image + } + } + var chat_filter_custom: CGImage { + if let image = cached.with({ $0["chat_filter_custom"] }) { + return image + } else { + let image = _chat_filter_custom() + _ = cached.modify { current in + var current = current + current["chat_filter_custom"] = image + return current + } + return image + } + } + var chat_filter_groups: CGImage { + if let image = cached.with({ $0["chat_filter_groups"] }) { + return image + } else { + let image = _chat_filter_groups() + _ = cached.modify { current in + var current = current + current["chat_filter_groups"] = image + return current + } + return image + } + } + var chat_filter_muted: CGImage { + if let image = cached.with({ $0["chat_filter_muted"] }) { + return image + } else { + let image = _chat_filter_muted() + _ = cached.modify { current in + var current = current + current["chat_filter_muted"] = image + return current + } + return image + } + } + var chat_filter_private_chats: CGImage { + if let image = cached.with({ $0["chat_filter_private_chats"] }) { + return image + } else { + let image = _chat_filter_private_chats() + _ = cached.modify { current in + var current = current + current["chat_filter_private_chats"] = image + return current + } + return image + } + } + var chat_filter_read: CGImage { + if let image = cached.with({ $0["chat_filter_read"] }) { + return image + } else { + let image = _chat_filter_read() + _ = cached.modify { current in + var current = current + current["chat_filter_read"] = image + return current + } + return image + } + } + var chat_filter_secret_chats: CGImage { + if let image = cached.with({ $0["chat_filter_secret_chats"] }) { + return image + } else { + let image = _chat_filter_secret_chats() + _ = cached.modify { current in + var current = current + current["chat_filter_secret_chats"] = image + return current + } + return image + } + } + var chat_filter_unmuted: CGImage { + if let image = cached.with({ $0["chat_filter_unmuted"] }) { + return image + } else { + let image = _chat_filter_unmuted() + _ = cached.modify { current in + var current = current + current["chat_filter_unmuted"] = image + return current + } + return image + } + } + var chat_filter_unread: CGImage { + if let image = cached.with({ $0["chat_filter_unread"] }) { + return image + } else { + let image = _chat_filter_unread() + _ = cached.modify { current in + var current = current + current["chat_filter_unread"] = image + return current + } + return image + } + } private let _dialogMuteImage: ()->CGImage private let _dialogMuteImageSelected: ()->CGImage @@ -6207,6 +6350,17 @@ final class TelegramIconsTheme { private let _poll_selected_outgoing: ()->CGImage private let _poll_selected_correct_outgoing: ()->CGImage private let _poll_selected_incorrect_outgoing: ()->CGImage + private let _chat_filter_add: ()->CGImage + private let _chat_filter_bots: ()->CGImage + private let _chat_filter_channels: ()->CGImage + private let _chat_filter_custom: ()->CGImage + private let _chat_filter_groups: ()->CGImage + private let _chat_filter_muted: ()->CGImage + private let _chat_filter_private_chats: ()->CGImage + private let _chat_filter_read: ()->CGImage + private let _chat_filter_secret_chats: ()->CGImage + private let _chat_filter_unmuted: ()->CGImage + private let _chat_filter_unread: ()->CGImage init( dialogMuteImage: @escaping()->CGImage, @@ -6651,7 +6805,18 @@ final class TelegramIconsTheme { poll_selected_incorrect_incoming: @escaping()->CGImage, poll_selected_outgoing: @escaping()->CGImage, poll_selected_correct_outgoing: @escaping()->CGImage, - poll_selected_incorrect_outgoing: @escaping()->CGImage + poll_selected_incorrect_outgoing: @escaping()->CGImage, + chat_filter_add: @escaping()->CGImage, + chat_filter_bots: @escaping()->CGImage, + chat_filter_channels: @escaping()->CGImage, + chat_filter_custom: @escaping()->CGImage, + chat_filter_groups: @escaping()->CGImage, + chat_filter_muted: @escaping()->CGImage, + chat_filter_private_chats: @escaping()->CGImage, + chat_filter_read: @escaping()->CGImage, + chat_filter_secret_chats: @escaping()->CGImage, + chat_filter_unmuted: @escaping()->CGImage, + chat_filter_unread: @escaping()->CGImage ) { self._dialogMuteImage = dialogMuteImage self._dialogMuteImageSelected = dialogMuteImageSelected @@ -7096,5 +7261,16 @@ final class TelegramIconsTheme { self._poll_selected_outgoing = poll_selected_outgoing self._poll_selected_correct_outgoing = poll_selected_correct_outgoing self._poll_selected_incorrect_outgoing = poll_selected_incorrect_outgoing + self._chat_filter_add = chat_filter_add + self._chat_filter_bots = chat_filter_bots + self._chat_filter_channels = chat_filter_channels + self._chat_filter_custom = chat_filter_custom + self._chat_filter_groups = chat_filter_groups + self._chat_filter_muted = chat_filter_muted + self._chat_filter_private_chats = chat_filter_private_chats + self._chat_filter_read = chat_filter_read + self._chat_filter_secret_chats = chat_filter_secret_chats + self._chat_filter_unmuted = chat_filter_unmuted + self._chat_filter_unread = chat_filter_unread } } \ No newline at end of file diff --git a/Telegram-Mac/WalletBalanceItem.swift b/Telegram-Mac/WalletBalanceItem.swift index 74c15f2e7..fa0164a3a 100644 --- a/Telegram-Mac/WalletBalanceItem.swift +++ b/Telegram-Mac/WalletBalanceItem.swift @@ -324,7 +324,7 @@ private final class WalletBalanceView : TableRowView { return } - crystalView.update(with: WalletAnimatedSticker.brilliant_static.file, size: NSMakeSize(44, 44), context: item.context, parent: nil, table: nil, parameters: WalletAnimatedSticker.brilliant_static.parameters, animated: animated, positionFlags: nil, approximateSynchronousValue: true) + crystalView.update(with: LocalAnimatedSticker.brilliant_static.file, size: NSMakeSize(44, 44), context: item.context, parent: nil, table: nil, parameters: LocalAnimatedSticker.brilliant_static.parameters, animated: animated, positionFlags: nil, approximateSynchronousValue: true) sendButton.isHidden = item.walletState?.balance == -1 diff --git a/Telegram-Mac/WalletInfoCreatedItem.swift b/Telegram-Mac/WalletInfoCreatedItem.swift index fffe352e5..c4ab7ad5c 100644 --- a/Telegram-Mac/WalletInfoCreatedItem.swift +++ b/Telegram-Mac/WalletInfoCreatedItem.swift @@ -17,7 +17,7 @@ class WalletInfoCreatedItem: GeneralRowItem { fileprivate let headerView: TextViewLayout fileprivate let yourWalletAddress: TextViewLayout fileprivate let context: AccountContext - fileprivate let animation: TelegramMediaFile = WalletAnimatedSticker.chiken_born.file + fileprivate let animation: TelegramMediaFile = LocalAnimatedSticker.chiken_born.file private var _h: CGFloat = 0 init(_ initialSize: NSSize, stableId: AnyHashable, context: AccountContext, address: String, viewType: GeneralViewType) { diff --git a/Telegram-Mac/WalletInfoTransactionItem.swift b/Telegram-Mac/WalletInfoTransactionItem.swift index 27ab9a43f..b81860d43 100644 --- a/Telegram-Mac/WalletInfoTransactionItem.swift +++ b/Telegram-Mac/WalletInfoTransactionItem.swift @@ -306,9 +306,9 @@ private final class WalletInfoTransactionView: TableRowView { } } - let parameters = ChatAnimatedStickerMediaLayoutParameters(playPolicy: .once, media: WalletAnimatedSticker.brilliant_static.file) + let parameters = ChatAnimatedStickerMediaLayoutParameters(playPolicy: .once, media: LocalAnimatedSticker.brilliant_static.file) - crystalView.update(with: WalletAnimatedSticker.brilliant_static.file, size: NSMakeSize(16, 16), context: item.context, parent: nil, table: nil, parameters: parameters, animated: animated, positionFlags: nil, approximateSynchronousValue: true) + crystalView.update(with: LocalAnimatedSticker.brilliant_static.file, size: NSMakeSize(16, 16), context: item.context, parent: nil, table: nil, parameters: parameters, animated: animated, positionFlags: nil, approximateSynchronousValue: true) needsLayout = true diff --git a/Telegram-Mac/WalletProcessTransactionController.swift b/Telegram-Mac/WalletProcessTransactionController.swift index a9322f83f..a501ee1af 100644 --- a/Telegram-Mac/WalletProcessTransactionController.swift +++ b/Telegram-Mac/WalletProcessTransactionController.swift @@ -103,15 +103,15 @@ private func entries(state: WalletTransactionState, arguments: WalletTransaction return DynamicHeightRowItem(initialSize, stableId: stableId, side: .top) })) - let animation: WalletAnimatedSticker? + let animation: LocalAnimatedSticker? switch state.mode { case .passcode: - animation = WalletAnimatedSticker.keychain + animation = LocalAnimatedSticker.keychain case .sending: - animation = WalletAnimatedSticker.fly_dollar + animation = LocalAnimatedSticker.fly_dollar case .sent: - animation = WalletAnimatedSticker.gift + animation = LocalAnimatedSticker.gift case .none: animation = nil } diff --git a/Telegram-Mac/WalletSendController.swift b/Telegram-Mac/WalletSendController.swift index c875610ce..b1fccb187 100644 --- a/Telegram-Mac/WalletSendController.swift +++ b/Telegram-Mac/WalletSendController.swift @@ -128,7 +128,7 @@ private func WalletSendEntries(state: WalletSendState, arguments: WalletSendArgu let range = balance.nsstring.range(of: ":") if range.location != NSNotFound { - text?.addAttributes([InputDataTextInsertAnimatedViewData.attributeKey : InputDataTextInsertAnimatedViewData(context: arguments.context, file: WalletAnimatedSticker.brilliant_static.file)], range: NSMakeRange(range.location + 1, 3)) + text?.addAttributes([InputDataTextInsertAnimatedViewData.attributeKey : InputDataTextInsertAnimatedViewData(context: arguments.context, file: LocalAnimatedSticker.brilliant_static.file)], range: NSMakeRange(range.location + 1, 3)) } } else { text = nil diff --git a/Telegram-Mac/WalletSendProccessingView.swift b/Telegram-Mac/WalletSendProccessingView.swift index 06146c110..d1458abd5 100644 --- a/Telegram-Mac/WalletSendProccessingView.swift +++ b/Telegram-Mac/WalletSendProccessingView.swift @@ -32,7 +32,7 @@ final class WalletSendProccessingView : Control { } func setup(context: AccountContext) { - self.animationView.update(with: WalletAnimatedSticker.fly_dollar.file, size: NSMakeSize(200, 200), context: context, parent: nil, table: nil, parameters: WalletAnimatedSticker.fly_dollar.parameters, animated: false, positionFlags: nil, approximateSynchronousValue: true) + self.animationView.update(with: LocalAnimatedSticker.fly_dollar.file, size: NSMakeSize(200, 200), context: context, parent: nil, table: nil, parameters: LocalAnimatedSticker.fly_dollar.parameters, animated: false, positionFlags: nil, approximateSynchronousValue: true) needsLayout = true } @@ -101,7 +101,7 @@ final class WalletSentView : Control { func setup(context: AccountContext, amount: String, callback: @escaping()->Void) { self.amount = amount - self.animationView.update(with: WalletAnimatedSticker.success.file, size: NSMakeSize(150, 150), context: context, parent: nil, table: nil, parameters: WalletAnimatedSticker.success.parameters, animated: false, positionFlags: nil, approximateSynchronousValue: true) + self.animationView.update(with: LocalAnimatedSticker.success.file, size: NSMakeSize(150, 150), context: context, parent: nil, table: nil, parameters: LocalAnimatedSticker.success.parameters, animated: false, positionFlags: nil, approximateSynchronousValue: true) walletButton.removeAllHandlers() @@ -187,7 +187,7 @@ final class WalletPasscodeView : Control { } func setup(context: AccountContext, decryptedKey: @escaping(Data)->Void) { - self.animationView.update(with: WalletAnimatedSticker.keychain.file, size: NSMakeSize(150, 150), context: context, parent: nil, table: nil, parameters: WalletAnimatedSticker.success.parameters, animated: false, positionFlags: nil, approximateSynchronousValue: true) + self.animationView.update(with: LocalAnimatedSticker.keychain.file, size: NSMakeSize(150, 150), context: context, parent: nil, table: nil, parameters: LocalAnimatedSticker.success.parameters, animated: false, positionFlags: nil, approximateSynchronousValue: true) let inputItem = InputDataRowItem(NSMakeSize(350, 40), stableId: 0, mode: .secure, error: nil, viewType: .singleItem, currentText: "", placeholder: nil, inputPlaceholder: "Passcode", filter: { $0 }, updated: { updated in diff --git a/Telegram-Mac/WalletSplashController.swift b/Telegram-Mac/WalletSplashController.swift index 03e45e66f..a45ef0ab8 100644 --- a/Telegram-Mac/WalletSplashController.swift +++ b/Telegram-Mac/WalletSplashController.swift @@ -105,14 +105,14 @@ private func splashEntries(state: WalletSplashState, arguments: WalletSplashArgu - let animation: WalletAnimatedSticker? + let animation: LocalAnimatedSticker? switch state.mode { case .createPasscode: switch state.passcodeState { case .secure: - animation = WalletAnimatedSticker.keychain + animation = LocalAnimatedSticker.keychain case .plain: - animation = WalletAnimatedSticker.keychain + animation = LocalAnimatedSticker.keychain } default: animation = state.mode.animation @@ -125,7 +125,7 @@ private func splashEntries(state: WalletSplashState, arguments: WalletSplashArgu struct WalletSplashEquatable : Equatable { let mode: WalletSplashMode - let animation: WalletAnimatedSticker? + let animation: LocalAnimatedSticker? } let splashEquatable = WalletSplashEquatable(mode: state.mode, animation: animation) diff --git a/Telegram-Mac/WalletSplashHelps.swift b/Telegram-Mac/WalletSplashHelps.swift index f545c1b6b..84f27cedd 100644 --- a/Telegram-Mac/WalletSplashHelps.swift +++ b/Telegram-Mac/WalletSplashHelps.swift @@ -63,46 +63,46 @@ extension WalletSplashMode { var animationFile: TelegramMediaFile? { switch self { case .intro: - return WalletAnimatedSticker.brilliant_loading.file + return LocalAnimatedSticker.brilliant_loading.file case .success: - return WalletAnimatedSticker.success.file + return LocalAnimatedSticker.success.file case .save24Words: - return WalletAnimatedSticker.write_words.file + return LocalAnimatedSticker.write_words.file case .created: - return WalletAnimatedSticker.gift.file + return LocalAnimatedSticker.gift.file case .restoreFailed: - return WalletAnimatedSticker.sad.file + return LocalAnimatedSticker.sad.file case .testWords: - return WalletAnimatedSticker.write_words.file + return LocalAnimatedSticker.write_words.file case .importExist: return nil case .createPasscode: - return WalletAnimatedSticker.keychain.file + return LocalAnimatedSticker.keychain.file case .unavailable: - return WalletAnimatedSticker.sad.file + return LocalAnimatedSticker.sad.file } } - var animation: WalletAnimatedSticker? { + var animation: LocalAnimatedSticker? { switch self { case .intro: - return WalletAnimatedSticker.brilliant_loading + return LocalAnimatedSticker.brilliant_loading case .success: - return WalletAnimatedSticker.success + return LocalAnimatedSticker.success case .save24Words: - return WalletAnimatedSticker.write_words + return LocalAnimatedSticker.write_words case .created: - return WalletAnimatedSticker.gift + return LocalAnimatedSticker.gift case .restoreFailed: - return WalletAnimatedSticker.sad + return LocalAnimatedSticker.sad case .testWords: - return WalletAnimatedSticker.smart_guy + return LocalAnimatedSticker.smart_guy case .importExist: return nil case .createPasscode: - return WalletAnimatedSticker.keychain + return LocalAnimatedSticker.keychain case .unavailable: - return WalletAnimatedSticker.sad + return LocalAnimatedSticker.sad } } @@ -304,7 +304,7 @@ func formatBalanceText(_ value: Int64) -> String { } -enum WalletAnimatedSticker { +enum LocalAnimatedSticker { case brilliant_static case brilliant_loading case smart_guy @@ -319,6 +319,7 @@ enum WalletAnimatedSticker { case success case monkey_unsee case monkey_see + case think_spectacular var file: TelegramMediaFile { let resource:LocalBundleResource switch self { @@ -350,6 +351,8 @@ enum WalletAnimatedSticker { resource = LocalBundleResource(name: "monkey_unsee", ext: "tgs") case .monkey_see: resource = LocalBundleResource(name: "monkey_see", ext: "tgs") + case .think_spectacular: + resource = LocalBundleResource(name: "think_spectacular", ext: "tgs") } return TelegramMediaFile(fileId: MediaId(namespace: 0, id: MediaId.Id(resource.name.hashValue)), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/x-tgsticker", size: nil, attributes: [.Sticker(displayText: "", packReference: nil, maskData: nil), .Animated, .FileName(fileName: "telegram-animoji.tgs")]) } @@ -378,7 +381,7 @@ enum WalletAnimatedSticker { case .write_words: playPolicy = .once case .chiken_born: - playPolicy = .loop + playPolicy = .onceEnd case .sad: playPolicy = .once case .success: @@ -387,6 +390,8 @@ enum WalletAnimatedSticker { playPolicy = .once case .monkey_see: playPolicy = .once + case .think_spectacular: + playPolicy = .once } return ChatAnimatedStickerMediaLayoutParameters(playPolicy: playPolicy, alwaysAccept: alwaysAccept, media: self.file) } diff --git a/Telegram-Mac/WalletSplashRowItem.swift b/Telegram-Mac/WalletSplashRowItem.swift index b31bac59b..8b9410e4e 100644 --- a/Telegram-Mac/WalletSplashRowItem.swift +++ b/Telegram-Mac/WalletSplashRowItem.swift @@ -16,10 +16,10 @@ import SyncCore class WalletSplashRowItem: GeneralRowItem { fileprivate let descLayout: TextViewLayout fileprivate let titleLayout: TextViewLayout - fileprivate let animation: WalletAnimatedSticker? + fileprivate let animation: LocalAnimatedSticker? fileprivate let context: AccountContext private var h: CGFloat = 0 - init(_ initialSize: NSSize, stableId: AnyHashable, context: AccountContext, title: String, desc: String, animation: WalletAnimatedSticker?, viewType: GeneralViewType, action:@escaping(String)->Void) { + init(_ initialSize: NSSize, stableId: AnyHashable, context: AccountContext, title: String, desc: String, animation: LocalAnimatedSticker?, viewType: GeneralViewType, action:@escaping(String)->Void) { self.context = context self.animation = animation diff --git a/Telegram-Mac/WalletTransactionTextItem.swift b/Telegram-Mac/WalletTransactionTextItem.swift index bf5f355ce..ccc06e2f0 100644 --- a/Telegram-Mac/WalletTransactionTextItem.swift +++ b/Telegram-Mac/WalletTransactionTextItem.swift @@ -114,7 +114,7 @@ private final class WalletTransactionTextView : TableRowView { self.textView.update(item.textLayout) self.subTextView.update(item.subTextLayout) - crystalView.update(with: WalletAnimatedSticker.brilliant_static.file, size: NSMakeSize(40, 40), context: item.context, parent: nil, table: nil, parameters: WalletAnimatedSticker.brilliant_static.parameters, animated: animated, positionFlags: nil, approximateSynchronousValue: true) + crystalView.update(with: LocalAnimatedSticker.brilliant_static.file, size: NSMakeSize(40, 40), context: item.context, parent: nil, table: nil, parameters: LocalAnimatedSticker.brilliant_static.parameters, animated: animated, positionFlags: nil, approximateSynchronousValue: true) needsLayout = true } diff --git a/Telegram-Mac/en.lproj/Localizable.strings b/Telegram-Mac/en.lproj/Localizable.strings index 287dc4fbc..133e91987 100644 Binary files a/Telegram-Mac/en.lproj/Localizable.strings and b/Telegram-Mac/en.lproj/Localizable.strings differ diff --git a/Telegram-Mac/wallet/brilliant_loading.tgs b/Telegram-Mac/tgs/brilliant_loading.tgs similarity index 100% rename from Telegram-Mac/wallet/brilliant_loading.tgs rename to Telegram-Mac/tgs/brilliant_loading.tgs diff --git a/Telegram-Mac/wallet/brilliant_static.tgs b/Telegram-Mac/tgs/brilliant_static.tgs similarity index 100% rename from Telegram-Mac/wallet/brilliant_static.tgs rename to Telegram-Mac/tgs/brilliant_static.tgs diff --git a/Telegram-Mac/wallet/chiken_born.tgs b/Telegram-Mac/tgs/chiken_born.tgs similarity index 100% rename from Telegram-Mac/wallet/chiken_born.tgs rename to Telegram-Mac/tgs/chiken_born.tgs diff --git a/Telegram-Mac/wallet/fly_dollar.tgs b/Telegram-Mac/tgs/fly_dollar.tgs similarity index 100% rename from Telegram-Mac/wallet/fly_dollar.tgs rename to Telegram-Mac/tgs/fly_dollar.tgs diff --git a/Telegram-Mac/wallet/gift.tgs b/Telegram-Mac/tgs/gift.tgs similarity index 100% rename from Telegram-Mac/wallet/gift.tgs rename to Telegram-Mac/tgs/gift.tgs diff --git a/Telegram-Mac/wallet/keyboard_typing.tgs b/Telegram-Mac/tgs/keyboard_typing.tgs similarity index 100% rename from Telegram-Mac/wallet/keyboard_typing.tgs rename to Telegram-Mac/tgs/keyboard_typing.tgs diff --git a/Telegram-Mac/wallet/keychain.tgs b/Telegram-Mac/tgs/keychain.tgs similarity index 100% rename from Telegram-Mac/wallet/keychain.tgs rename to Telegram-Mac/tgs/keychain.tgs diff --git a/Telegram-Mac/wallet/monkey_see.tgs b/Telegram-Mac/tgs/monkey_see.tgs similarity index 100% rename from Telegram-Mac/wallet/monkey_see.tgs rename to Telegram-Mac/tgs/monkey_see.tgs diff --git a/Telegram-Mac/wallet/monkey_unsee.tgs b/Telegram-Mac/tgs/monkey_unsee.tgs similarity index 100% rename from Telegram-Mac/wallet/monkey_unsee.tgs rename to Telegram-Mac/tgs/monkey_unsee.tgs diff --git a/Telegram-Mac/wallet/sad_man.tgs b/Telegram-Mac/tgs/sad_man.tgs similarity index 100% rename from Telegram-Mac/wallet/sad_man.tgs rename to Telegram-Mac/tgs/sad_man.tgs diff --git a/Telegram-Mac/wallet/smart_guy.tgs b/Telegram-Mac/tgs/smart_guy.tgs similarity index 100% rename from Telegram-Mac/wallet/smart_guy.tgs rename to Telegram-Mac/tgs/smart_guy.tgs diff --git a/Telegram-Mac/wallet/swap_money.tgs b/Telegram-Mac/tgs/swap_money.tgs similarity index 100% rename from Telegram-Mac/wallet/swap_money.tgs rename to Telegram-Mac/tgs/swap_money.tgs diff --git a/Telegram-Mac/tgs/think_spectacular.tgs b/Telegram-Mac/tgs/think_spectacular.tgs new file mode 100644 index 000000000..2dd379959 Binary files /dev/null and b/Telegram-Mac/tgs/think_spectacular.tgs differ diff --git a/Telegram-Mac/wallet/wallet_success_created.tgs b/Telegram-Mac/tgs/wallet_success_created.tgs similarity index 100% rename from Telegram-Mac/wallet/wallet_success_created.tgs rename to Telegram-Mac/tgs/wallet_success_created.tgs diff --git a/Telegram-Mac/wallet/write_words.tgs b/Telegram-Mac/tgs/write_words.tgs similarity index 100% rename from Telegram-Mac/wallet/write_words.tgs rename to Telegram-Mac/tgs/write_words.tgs diff --git a/Telegram.xcodeproj/project.pbxproj b/Telegram.xcodeproj/project.pbxproj index 1de243221..10a37655b 100644 --- a/Telegram.xcodeproj/project.pbxproj +++ b/Telegram.xcodeproj/project.pbxproj @@ -104,8 +104,6 @@ 9F4EEF8821D515C5002C3B33 /* InstantPageStoredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4EEF8721D515C5002C3B33 /* InstantPageStoredState.swift */; }; 9F52F51A2130286E006FC0B5 /* LocationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F52F5192130286E006FC0B5 /* LocationRequest.swift */; }; 9F580BE520A0AA7B00F6D56C /* ChatRecorderOverlayWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F580BE420A0AA7B00F6D56C /* ChatRecorderOverlayWindow.swift */; }; - 9F5B6E792011460000C58B2A /* MergeLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5B6E78201145FA00C58B2A /* MergeLists.swift */; }; - 9F5B6E7A2011460000C58B2A /* MergeLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5B6E78201145FA00C58B2A /* MergeLists.swift */; }; 9F62AE7E202D85B7007FB557 /* FetchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F62AE7D202D85B7007FB557 /* FetchManager.swift */; }; 9F62AE81202D85E7007FB557 /* FetchManagerLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F62AE80202D85E7007FB557 /* FetchManagerLocation.swift */; }; 9F62AE83202D8759007FB557 /* FetchMediaUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F62AE82202D8759007FB557 /* FetchMediaUtils.swift */; }; @@ -221,6 +219,7 @@ A789E09723E427CE00AEB34A /* ChatListFilterPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7C1379123DB00D900803ED3 /* ChatListFilterPreferences.swift */; }; A7B5031023B62E0400C9838E /* nanosvg.c in Sources */ = {isa = PBXBuildFile; fileRef = A7B5030E23B62E0000C9838E /* nanosvg.c */; }; A7B5031323B62E1A00C9838E /* Svg.m in Sources */ = {isa = PBXBuildFile; fileRef = A7B5031123B62E1700C9838E /* Svg.m */; }; + A7B6DDD723ED935100B8E01C /* think_spectacular.tgs in Resources */ = {isa = PBXBuildFile; fileRef = A7B6DDD623ED8FDF00B8E01C /* think_spectacular.tgs */; }; A7C1377D23D1A62800803ED3 /* PeerEmptyHolderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7C1377C23D1A62700803ED3 /* PeerEmptyHolderItem.swift */; }; A7C1379223DB00D900803ED3 /* ChatListFilterPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7C1379123DB00D900803ED3 /* ChatListFilterPreferences.swift */; }; A7C1379E23DF21EA00803ED3 /* ChatListRevealItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7C1379D23DF21EA00803ED3 /* ChatListRevealItem.swift */; }; @@ -1038,7 +1037,6 @@ 9F52E7522074C1710048791C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/MainMenu.strings; sourceTree = ""; }; 9F52F5192130286E006FC0B5 /* LocationRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRequest.swift; sourceTree = ""; }; 9F580BE420A0AA7B00F6D56C /* ChatRecorderOverlayWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRecorderOverlayWindow.swift; sourceTree = ""; }; - 9F5B6E78201145FA00C58B2A /* MergeLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MergeLists.swift; sourceTree = ""; }; 9F62AE7D202D85B7007FB557 /* FetchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchManager.swift; sourceTree = ""; }; 9F62AE80202D85E7007FB557 /* FetchManagerLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchManagerLocation.swift; sourceTree = ""; }; 9F62AE82202D8759007FB557 /* FetchMediaUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchMediaUtils.swift; sourceTree = ""; }; @@ -1263,6 +1261,7 @@ A7B5030F23B62E0400C9838E /* nanosvg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nanosvg.h; sourceTree = ""; }; A7B5031123B62E1700C9838E /* Svg.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Svg.m; sourceTree = ""; }; A7B5031223B62E1A00C9838E /* Svg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Svg.h; sourceTree = ""; }; + A7B6DDD623ED8FDF00B8E01C /* think_spectacular.tgs */ = {isa = PBXFileReference; lastKnownFileType = file; path = think_spectacular.tgs; sourceTree = ""; }; A7C1377C23D1A62700803ED3 /* PeerEmptyHolderItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerEmptyHolderItem.swift; sourceTree = ""; }; A7C1379123DB00D900803ED3 /* ChatListFilterPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListFilterPreferences.swift; sourceTree = ""; }; A7C1379D23DF21EA00803ED3 /* ChatListRevealItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListRevealItem.swift; sourceTree = ""; }; @@ -3452,7 +3451,7 @@ isa = PBXGroup; children = ( A778DC2C23C77ACB00DD307B /* sounds */, - D07450DB233D617B00769D7F /* wallet */, + D07450DB233D617B00769D7F /* tgs */, 9F0367E622707B6400456348 /* lottie */, 9F0AE6B42199904400A8B53A /* sound_a.caf */, 9F3EAB3F20A5ED2F003FE7E3 /* ocr_nn.bin */, @@ -4019,7 +4018,6 @@ 9FFAE508205A92B9000C028E /* RingBuffer.m */, 9F0E6F79203EFE870086699C /* Preferences.swift */, 9F62AE7F202D85CC007FB557 /* fetch */, - 9F5B6E78201145FA00C58B2A /* MergeLists.swift */, C234A7F91ED7104500EBBECE /* localization */, C271EB981EB9DEDA0034792D /* calls */, C210742A1E780CA8006EE5EF /* cache */, @@ -4482,9 +4480,10 @@ name = send; sourceTree = ""; }; - D07450DB233D617B00769D7F /* wallet */ = { + D07450DB233D617B00769D7F /* tgs */ = { isa = PBXGroup; children = ( + A7B6DDD623ED8FDF00B8E01C /* think_spectacular.tgs */, D07C6D7F2346A42F00468B1A /* monkey_see.tgs */, D07C6D7E2346A42E00468B1A /* monkey_unsee.tgs */, D07450F42340F28D00769D7F /* wallet_success_created.tgs */, @@ -4500,7 +4499,7 @@ D07450E2233D61B500769D7F /* swap_money.tgs */, D07450E0233D61B300769D7F /* write_words.tgs */, ); - path = wallet; + path = tgs; sourceTree = ""; }; D076F86E2295B1F6004F895A /* inline-auth */ = { @@ -4826,6 +4825,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + A7B6DDD723ED935100B8E01C /* think_spectacular.tgs in Resources */, 9F1668C62008A97000DD39FB /* builtin-wallpaper-0.jpg in Resources */, C2057FA51EBBCEA7000423DC /* voip_busy.caf in Resources */, 9F3EAB4520A5ED2F003FE7E3 /* ocr_nn.bin in Resources */, @@ -4962,7 +4962,6 @@ C23BC3711E9ACD1700D79F92 /* SearchEmptyRowItem.swift in Sources */, C23BC36C1E9ACC0800D79F92 /* MimeTypes.swift in Sources */, C23BC3701E9ACCF900D79F92 /* SEPasslockController.swift in Sources */, - 9F5B6E7A2011460000C58B2A /* MergeLists.swift in Sources */, C291E2741E8B051100D397BA /* ImageUtils.swift in Sources */, C232EABF1E1D13F500C4D38C /* ChatListNothingItem.swift in Sources */, C232EABC1E1D134C00C4D38C /* AvatarLayer.swift in Sources */, @@ -5613,7 +5612,6 @@ C2B1A1271DA3D84900ACB1DD /* ChatInputAccessory.swift in Sources */, C218FF961F3DAD7200DD7D35 /* InstantPageSelectText.swift in Sources */, C236ADDE1F7D318700E8C71A /* TGVideoCameraMovieRecorder.m in Sources */, - 9F5B6E792011460000C58B2A /* MergeLists.swift in Sources */, C2DE5D3E1F3CAF2B0081EC1E /* InstantPageShapeItem.swift in Sources */, 9F18908D2237B5A400665EF5 /* InputURLFormatterModalController.swift in Sources */, C221ED5C1EA69AA300471C65 /* StorageUsageController.swift in Sources */, diff --git a/TelegramShare/Info.plist b/TelegramShare/Info.plist index 6cbf44a13..ec5029ff8 100644 --- a/TelegramShare/Info.plist +++ b/TelegramShare/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 5.9.1 CFBundleVersion - 190408 + 190681 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSExtension diff --git a/submodules/TGUIKit/TGUIKit.xcodeproj/project.pbxproj b/submodules/TGUIKit/TGUIKit.xcodeproj/project.pbxproj index e1e964fdc..70b01e45d 100644 --- a/submodules/TGUIKit/TGUIKit.xcodeproj/project.pbxproj +++ b/submodules/TGUIKit/TGUIKit.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 9F6281292037389200B2D3C9 /* CheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F6281282037389200B2D3C9 /* CheckBox.swift */; }; 9F9B5EAF2255FEDA00728CDC /* TooltipController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9B5EAE2255FEDA00728CDC /* TooltipController.swift */; }; 9FC76CAD20DBDDA30050CE3B /* SegmentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC76CAC20DBDDA30050CE3B /* SegmentController.swift */; }; + A789ECFE23EAE99100AEB34A /* ScrollableSegmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A789ECFD23EAE99100AEB34A /* ScrollableSegmentView.swift */; }; + A789ED0023EAFDE600AEB34A /* MergeLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = A789ECFF23EAFDE600AEB34A /* MergeLists.swift */; }; A7C41DB92359E69400CF9402 /* CatalinaStyledSegmentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7C41DB82359E69400CF9402 /* CatalinaStyledSegmentController.swift */; }; A7D28203236C3B5D0000A9BF /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7D28202236C3B5D0000A9BF /* SwiftSignalKit.framework */; }; A7D49478235F226C003E9A25 /* SpinningProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D49477235F226C003E9A25 /* SpinningProgressView.swift */; }; @@ -117,6 +119,8 @@ 9F6281282037389200B2D3C9 /* CheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBox.swift; sourceTree = ""; }; 9F9B5EAE2255FEDA00728CDC /* TooltipController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipController.swift; sourceTree = ""; }; 9FC76CAC20DBDDA30050CE3B /* SegmentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentController.swift; sourceTree = ""; }; + A789ECFD23EAE99100AEB34A /* ScrollableSegmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollableSegmentView.swift; sourceTree = ""; }; + A789ECFF23EAFDE600AEB34A /* MergeLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MergeLists.swift; sourceTree = ""; }; A7C41DB82359E69400CF9402 /* CatalinaStyledSegmentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalinaStyledSegmentController.swift; sourceTree = ""; }; A7D28202236C3B5D0000A9BF /* SwiftSignalKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSignalKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A7D49477235F226C003E9A25 /* SpinningProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpinningProgressView.swift; sourceTree = ""; }; @@ -248,6 +252,14 @@ name = segment; sourceTree = ""; }; + A789ECFC23EAE97800AEB34A /* scrollable-segment */ = { + isa = PBXGroup; + children = ( + A789ECFD23EAE99100AEB34A /* ScrollableSegmentView.swift */, + ); + name = "scrollable-segment"; + sourceTree = ""; + }; A7C41DB72359E66800CF9402 /* catalina */ = { isa = PBXGroup; children = ( @@ -333,6 +345,7 @@ C22E062A1D7F3C2700A11C88 /* utils */ = { isa = PBXGroup; children = ( + A789ECFF23EAFDE600AEB34A /* MergeLists.swift */, C2271DEC1DAFE4D0001792B6 /* TransformImageArguments.swift */, C2CBCAC11D81593B00142EC0 /* extensions */, C22E062D1D7F3CC000A11C88 /* TGColor.swift */, @@ -465,6 +478,7 @@ C2E0DEA91D7EF51C00EF1C8D /* TGUIKit */ = { isa = PBXGroup; children = ( + A789ECFC23EAE97800AEB34A /* scrollable-segment */, D05F391E22FB377E0040F341 /* date */, 9FC76CAB20DBDD370050CE3B /* segment */, C2449A0F1F0E475200DF5650 /* thrid-party */, @@ -636,6 +650,7 @@ C234CA8F1D97DF26003023F7 /* Image.swift in Sources */, C2B1A1181DA1673300ACB1DD /* TableAnimationInterface.swift in Sources */, C2271F331DB4CEB30045E719 /* HorizontalTableView.swift in Sources */, + A789ED0023EAFDE600AEB34A /* MergeLists.swift in Sources */, C2E0DEBB1D7F05AB00EF1C8D /* View.swift in Sources */, C2271DD41DAF766A001792B6 /* WeakReference.swift in Sources */, C22E06351D804C8200A11C88 /* TableView.swift in Sources */, @@ -671,6 +686,7 @@ C230B9181DD3A6350057F596 /* ProgressModal.swift in Sources */, C21178021F17E78E00AC706D /* TimableProgressView.swift in Sources */, C2E0DEB91D7EF7F300EF1C8D /* ViewController.swift in Sources */, + A789ECFE23EAE99100AEB34A /* ScrollableSegmentView.swift in Sources */, C2303E841D9A69BD00098E12 /* TabBarView.swift in Sources */, C22E063B1D8067A000A11C88 /* TableRowView.swift in Sources */, C2303E7A1D9987FE00098E12 /* Popover.swift in Sources */, diff --git a/submodules/TGUIKit/TGUIKit/Control.swift b/submodules/TGUIKit/TGUIKit/Control.swift index 492997a9f..27f9457bb 100644 --- a/submodules/TGUIKit/TGUIKit/Control.swift +++ b/submodules/TGUIKit/TGUIKit/Control.swift @@ -67,6 +67,7 @@ open class Control: View { private(set) internal var backgroundState:[ControlState:NSColor] = [:] private var mouseMovedInside: Bool = true private var longInvoked: Bool = false + public var handleLongEvent: Bool = true open override var backgroundColor: NSColor { get{ return self.style.backgroundColor @@ -265,14 +266,19 @@ open class Control: View { updateState() send(event: .Down) let point = event.locationInWindow - let disposable = (Signal.single(Void()) |> delay(0.35, queue: Queue.mainQueue())).start(next: { [weak self] in - if let inside = self?.mouseInside(), inside, let wPoint = self?.window?.mouseLocationOutsideOfEventStream, NSPointInRect(point, NSMakeRect(wPoint.x - 2, wPoint.y - 2, 4, 4)) { - self?.longInvoked = true - self?.send(event: .LongMouseDown) - } - }) - - longHandleDisposable.set(disposable) + if handleLongEvent { + let disposable = (Signal.single(Void()) |> delay(0.35, queue: Queue.mainQueue())).start(next: { [weak self] in + if let inside = self?.mouseInside(), inside, let wPoint = self?.window?.mouseLocationOutsideOfEventStream, NSPointInRect(point, NSMakeRect(wPoint.x - 2, wPoint.y - 2, 4, 4)) { + self?.longInvoked = true + self?.send(event: .LongMouseDown) + } + }) + + longHandleDisposable.set(disposable) + } else { + longHandleDisposable.set(nil) + } + } else { super.mouseDown(with: event) diff --git a/submodules/TGUIKit/TGUIKit/Extensions.swift b/submodules/TGUIKit/TGUIKit/Extensions.swift index 912851ee8..f9fa087b1 100644 --- a/submodules/TGUIKit/TGUIKit/Extensions.swift +++ b/submodules/TGUIKit/TGUIKit/Extensions.swift @@ -1934,3 +1934,8 @@ public extension String { return result } } +extension NSEdgeInsets : Equatable { + public static func ==(lhs: NSEdgeInsets, rhs: NSEdgeInsets) -> Bool { + return lhs.left == rhs.left && lhs.right == rhs.right && lhs.bottom == rhs.bottom && lhs.top == rhs.top + } +} diff --git a/Telegram-Mac/MergeLists.swift b/submodules/TGUIKit/TGUIKit/MergeLists.swift similarity index 100% rename from Telegram-Mac/MergeLists.swift rename to submodules/TGUIKit/TGUIKit/MergeLists.swift diff --git a/submodules/TGUIKit/TGUIKit/SPopoverRowItem.swift b/submodules/TGUIKit/TGUIKit/SPopoverRowItem.swift index 60b807b49..a9003544f 100644 --- a/submodules/TGUIKit/TGUIKit/SPopoverRowItem.swift +++ b/submodules/TGUIKit/TGUIKit/SPopoverRowItem.swift @@ -157,14 +157,18 @@ private class SPopoverRowView: TableRowView { public final class SPopoverSeparatorItem : TableRowItem { + let drawBorder: Bool + override public var stableId: AnyHashable { return arc4random() } override public init(_ initialSize: NSSize) { + self.drawBorder = true super.init(initialSize) } - public init() { + public init(_ drawBorder: Bool = true) { + self.drawBorder = drawBorder super.init(NSZeroSize) } @@ -173,7 +177,7 @@ public final class SPopoverSeparatorItem : TableRowItem { } override public var height: CGFloat { - return 10 + return drawBorder ? 10 : 1 } } @@ -189,6 +193,15 @@ private final class SPopoverSeparatorView : TableRowView { separator.backgroundColor = presentation.colors.border } + override func set(item: TableRowItem, animated: Bool = false) { + super.set(item: item, animated: animated) + + guard let item = item as? SPopoverSeparatorItem else { + return + } + // separator.isHidden = !item.drawBorder + } + override func layout() { super.layout() separator.setFrameSize(NSMakeSize(frame.width, .borderSize)) diff --git a/submodules/TGUIKit/TGUIKit/SPopoverViewController.swift b/submodules/TGUIKit/TGUIKit/SPopoverViewController.swift index cab64315b..f97f922cb 100644 --- a/submodules/TGUIKit/TGUIKit/SPopoverViewController.swift +++ b/submodules/TGUIKit/TGUIKit/SPopoverViewController.swift @@ -28,9 +28,10 @@ public struct SPopoverItem : Equatable { let height: CGFloat let handler:()->Void let isSeparator: Bool + let drawSeparatorBorder: Bool let additionView: SPopoverAdditionItemView? - public init(_ title:String, _ handler:@escaping ()->Void, _ image:CGImage? = nil, _ textColor: NSColor = presentation.colors.text, height: CGFloat = 40.0, isSeparator: Bool = false, additionView: SPopoverAdditionItemView? = nil) { + public init(_ title:String, _ handler:@escaping ()->Void, _ image:CGImage? = nil, _ textColor: NSColor = presentation.colors.text, height: CGFloat = 40.0, isSeparator: Bool = false, drawSeparatorBorder: Bool = true, additionView: SPopoverAdditionItemView? = nil) { self.title = title self.image = image self.textColor = textColor @@ -38,15 +39,17 @@ public struct SPopoverItem : Equatable { self.height = height self.isSeparator = false self.additionView = additionView + self.drawSeparatorBorder = drawSeparatorBorder } - public init() { + public init(_ drawSeparatorBorder: Bool = true) { self.title = "" self.image = nil self.textColor = presentation.colors.text self.handler = {} self.height = 10 self.isSeparator = true + self.drawSeparatorBorder = drawSeparatorBorder self.additionView = nil } @@ -75,7 +78,7 @@ public class SPopoverViewController: GenericViewController { let alignAsImage = !items.filter({$0.image != nil}).isEmpty let items = items.map { item -> TableRowItem in if item.isSeparator { - return SPopoverSeparatorItem() + return SPopoverSeparatorItem(item.drawSeparatorBorder) } else { return SPopoverRowItem(NSZeroSize, height: item.height, image: item.image, alignAsImage: alignAsImage, title: item.title, textColor: item.textColor, additionView: item.additionView, clickHandler: { Queue.mainQueue().justDispatch { diff --git a/submodules/TGUIKit/TGUIKit/ScrollableSegmentController.swift b/submodules/TGUIKit/TGUIKit/ScrollableSegmentController.swift new file mode 100644 index 000000000..f552321e5 --- /dev/null +++ b/submodules/TGUIKit/TGUIKit/ScrollableSegmentController.swift @@ -0,0 +1,13 @@ +// +// ScrollableSegmentController.swift +// TGUIKit +// +// Created by Mikhail Filimonov on 05.02.2020. +// Copyright © 2020 Telegram. All rights reserved. +// + +import Cocoa + +class ScrollableSegmentView: View { + +} diff --git a/submodules/TGUIKit/TGUIKit/ScrollableSegmentView.swift b/submodules/TGUIKit/TGUIKit/ScrollableSegmentView.swift new file mode 100644 index 000000000..6a50f8d99 --- /dev/null +++ b/submodules/TGUIKit/TGUIKit/ScrollableSegmentView.swift @@ -0,0 +1,542 @@ +// +// ScrollableSegmentController.swift +// TGUIKit +// +// Created by Mikhail Filimonov on 05.02.2020. +// Copyright © 2020 Telegram. All rights reserved. +// + +import Cocoa + + + + +public protocol _HasCustomUIEquatableRepresentation { + func _toCustomUIEquatable() -> UIEquatable? +} + +internal protocol _UIEquatableBox { + var _typeID: ObjectIdentifier { get } + func _unbox() -> T? + + func _isEqual(to: _UIEquatableBox) -> Bool? + + var _base: Any { get } + func _downCastConditional(into result: UnsafeMutablePointer) -> Bool +} + +internal struct _ConcreteEquatableBox : _UIEquatableBox { + internal var _baseEquatable: Base + + internal init(_ base: Base) { + self._baseEquatable = base + } + + + internal var _typeID: ObjectIdentifier { + return ObjectIdentifier(type(of: self)) + } + + internal func _unbox() -> T? { + return (self as _UIEquatableBox as? _ConcreteEquatableBox)?._baseEquatable + } + + internal func _isEqual(to rhs: _UIEquatableBox) -> Bool? { + if let rhs: Base = rhs._unbox() { + return _baseEquatable == rhs + } + return nil + } + + internal var _base: Any { + return _baseEquatable + } + + internal + func _downCastConditional(into result: UnsafeMutablePointer) -> Bool { + guard let value = _baseEquatable as? T else { return false } + result.initialize(to: value) + return true + } +} + + +public struct UIEquatable { + internal var _box: _UIEquatableBox + internal var _usedCustomRepresentation: Bool + + + public init(_ base: H) { + if let customRepresentation = + (base as? _HasCustomUIEquatableRepresentation)?._toCustomUIEquatable() { + self = customRepresentation + self._usedCustomRepresentation = true + return + } + + self._box = _ConcreteEquatableBox(base) + self._usedCustomRepresentation = false + } + + internal init(_usingDefaultRepresentationOf base: H) { + self._box = _ConcreteEquatableBox(base) + self._usedCustomRepresentation = false + } + + public var base: Any { + return _box._base + } + internal + func _downCastConditional(into result: UnsafeMutablePointer) -> Bool { + // Attempt the downcast. + if _box._downCastConditional(into: result) { return true } + + + + return false + } +} + +extension UIEquatable : Equatable { + public static func == (lhs: UIEquatable, rhs: UIEquatable) -> Bool { + if let result = lhs._box._isEqual(to: rhs._box) { return result } + + return false + } +} + +extension UIEquatable : CustomStringConvertible { + public var description: String { + return String(describing: base) + } +} + +extension UIEquatable : CustomDebugStringConvertible { + public var debugDescription: String { + return "UIEquatable(" + String(reflecting: base) + ")" + } +} + +extension UIEquatable : CustomReflectable { + public var customMirror: Mirror { + return Mirror( + self, + children: ["value": base]) + } +} + +public final class ScrollableSegmentItem : Equatable, Comparable, Identifiable { + let title: String + let selected: Bool + let theme: ScrollableSegmentTheme + let insets: NSEdgeInsets + let icon: CGImage? + let equatable: UIEquatable? + public let index: Int + public let uniqueId: Int32 + + public init(title: String, index: Int, uniqueId: Int32, selected: Bool, insets: NSEdgeInsets, icon: CGImage?, theme: ScrollableSegmentTheme, equatable: UIEquatable?) { + self.title = title + self.index = index + self.uniqueId = uniqueId + self.selected = selected + self.theme = theme + self.insets = insets + self.icon = icon + self.equatable = equatable + } + + public var stableId: Int32 { + return uniqueId + } + public static func <(lhs: ScrollableSegmentItem, rhs: ScrollableSegmentItem) -> Bool { + return lhs.index < rhs.index + } + public static func ==(lhs: ScrollableSegmentItem, rhs: ScrollableSegmentItem) -> Bool { + return lhs.index == rhs.index && lhs.title == rhs.title && lhs.uniqueId == rhs.uniqueId && lhs.selected == rhs.selected && lhs.theme == rhs.theme && lhs.insets == rhs.insets && lhs.equatable == rhs.equatable + } + + fileprivate var view:SegmentItemView? +} + +private func buildText(for item: ScrollableSegmentItem) -> TextViewLayout { + let layout = TextViewLayout.init(.initialize(string: item.title, color: item.selected ? item.theme.activeText : item.theme.inactiveText, font: item.theme.textFont)) + layout.measure(width: .greatestFiniteMagnitude) + return layout +} + +private final class SegmentItemView : Control { + private(set) var item: ScrollableSegmentItem + private var textLayout: TextViewLayout + private var imageView: ImageView? + private let textView = TextView() + init(item: ScrollableSegmentItem) { + self.item = item + self.textLayout = buildText(for: item) + super.init(frame: NSZeroRect) + self.handleLongEvent = false + addSubview(textView) + textView.userInteractionEnabled = false + textView.isSelectable = false + textView.isEventLess = true + self.updateItem(item, animated: false) + + set(handler: { [weak self] _ in + if self?.item.selected == false { + self?.textView.layer?.opacity = 0.8 + self?.imageView?.layer?.opacity = 0.8 + } + }, for: .Highlight) + + set(handler: { [weak self] _ in + self?.textView.layer?.opacity = 1.0 + self?.imageView?.layer?.opacity = 1.0 + }, for: .Normal) + + set(handler: { [weak self] _ in + self?.textView.layer?.opacity = 1.0 + self?.imageView?.layer?.opacity = 1.0 + }, for: .Hover) + } + + override func scrollWheel(with event: NSEvent) { + super.scrollWheel(with: event) + } + + var size: NSSize { + var width: CGFloat = self.textLayout.layoutSize.width + item.insets.left + item.insets.right + if let imageView = imageView { + width += item.insets.left + imageView.frame.width + } + if self.textLayout.layoutSize.width == 0 { + width -= (item.insets.left * 2) + } + return NSMakeSize(width, frame.height) + } + + func updateItem(_ item: ScrollableSegmentItem, animated: Bool) { + self.item = item + self.textLayout = buildText(for: item) + textView.update(self.textLayout) + + if let image = item.icon { + if imageView == nil { + imageView = ImageView() + addSubview(imageView!) + if animated { + // imageView?.layer?.animateAlpha(from: 0, to: 1, duration: duration) + } + } + imageView?.image = image + imageView?.sizeToFit() + } else { + if animated, let imageView = imageView { + self.imageView = nil + imageView.removeFromSuperview() + +// imageView.layer?.animateAlpha(from: 1, to: 0, duration: duration, removeOnCompletion: false, completion: { [weak imageView] _ in +// imageView?.removeFromSuperview() +// }) + } else { + imageView?.removeFromSuperview() + imageView = nil + } + + } + + change(size: size, animated: animated, duration: duration) + needsLayout = true + } + + override func layout() { + super.layout() + if textView.frame.width > 0 { + textView.centerY(x: item.insets.left) + textView.setFrameOrigin(NSMakePoint(textView.frame.minX, textView.frame.minY - item.insets.bottom + item.insets.top)) + } else { + textView.centerY(x: 0) + } + if let imageView = imageView { + imageView.centerY(x: textView.frame.maxX + item.insets.left) + imageView.setFrameOrigin(NSMakePoint(imageView.frame.minX, imageView.frame.minY - item.insets.bottom + item.insets.top)) + } + + } + + required init(frame frameRect: NSRect) { + fatalError("init(coder:) has not been implemented") + } + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +private final class SelectorView : View { + required init(frame frameRect: NSRect) { + super.init(frame: frameRect) + layerContentsRedrawPolicy = .duringViewResize + } + var theme: ScrollableSegmentTheme? = nil { + didSet { + needsDisplay = true + } + } + + + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func draw(_ layer: CALayer, in ctx: CGContext) { + super.draw(layer, in: ctx) + ctx.round(frame.size, 2.0) + if let theme = self.theme { + ctx.setFillColor(theme.selector.cgColor) + ctx.fill(bounds) + } + } +} + +public struct ScrollableSegmentTheme : Equatable { + let border: NSColor + let selector: NSColor + let inactiveText: NSColor + let activeText: NSColor + let textFont: NSFont + public init(border: NSColor, selector: NSColor, inactiveText: NSColor, activeText: NSColor, textFont: NSFont) { + self.border = border + self.selector = selector + self.inactiveText = inactiveText + self.activeText = activeText + self.textFont = textFont + } +} + +private let duration: Double = 0.2 + + +private class Scroll : ScrollView { + override func scrollWheel(with event: NSEvent) { + + var scrollPoint = contentView.bounds.origin + let isInverted: Bool = System.isScrollInverted + if event.scrollingDeltaY != 0 { + if isInverted { + scrollPoint.x += -event.scrollingDeltaY + } else { + scrollPoint.x -= event.scrollingDeltaY + } + } + if event.scrollingDeltaX != 0 { + if !isInverted { + scrollPoint.x += -event.scrollingDeltaX + } else { + scrollPoint.x -= event.scrollingDeltaX + } + } + if documentView!.frame.width > frame.width { + scrollPoint.x = min(max(0, scrollPoint.x), documentView!.frame.width - frame.width) + clipView.scroll(to: scrollPoint) + } + } +} + + +public class ScrollableSegmentView: View { + private let scrollView:Scroll = Scroll() + + private let selectorView: SelectorView = SelectorView(frame: NSZeroRect) + private let borderView = View() + + private let documentView = View() + private var items: [ScrollableSegmentItem] = [] + private var selected:Int = 0 + + + + public var didChangeSelectedItem:((ScrollableSegmentItem)->Void)? + + public var theme: ScrollableSegmentTheme = ScrollableSegmentTheme(border: presentation.colors.border, selector: presentation.colors.accent, inactiveText: presentation.colors.grayText, activeText: presentation.colors.accent, textFont: .medium(.title)) + { + didSet { + if theme != oldValue { + redraw() + } + } + } + + public override func scrollWheel(with event: NSEvent) { + scrollView.scrollWheel(with: event) + } + + public required init(frame frameRect: NSRect) { + super.init(frame: frameRect) + addSubview(scrollView) + addSubview(borderView) + addSubview(selectorView) + scrollView.documentView = documentView + +// scrollView.backgroundColor = .clear +// scrollView.background = .clear +// +// documentView.backgroundColor = .clear +// + NotificationCenter.default.addObserver(forName: NSView.boundsDidChangeNotification, object: scrollView.clipView, queue: OperationQueue.main, using: { [weak self] notification in + self?.needsLayout = true + }) + layout() + redraw() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + public override func updateLocalizationAndTheme(theme presentation: PresentationTheme) { + self.theme = ScrollableSegmentTheme(border: presentation.colors.border, selector: presentation.colors.accent, inactiveText: presentation.colors.grayText, activeText: presentation.colors.accent, textFont: .normal(.title)) + } + + private var selectorFrame: CGRect { + + let selectedItem = self.items.first(where: { $0.selected }) + + var x: CGFloat = 0 + var width: CGFloat = 20 + if let selectedItem = selectedItem, let view = selectedItem.view { + let point: NSPoint + if scrollView.clipView.isAnimateScrolling { + point = NSMakePoint(min(max(view.frame.midX - frame.width / 2, 0), max(documentView.frame.width - frame.width, 0)), 0) + } else { + point = scrollView.documentOffset + } + + + x = view.frame.minX + selectedItem.insets.left - point.x + width = view.size.width - selectedItem.insets.left - selectedItem.insets.right + } + + return CGRect(origin: NSMakePoint(x, frame.height - 6 / 2), size: CGSize(width: width, height: 6)) + } + + + private func moveSelector(_ animated: Bool = false) { + self.selectorView.change(pos: selectorFrame.origin, animated: animated) + self.selectorView.change(size: selectorFrame.size, animated: animated) + } + + public override func layout() { + super.layout() + scrollView.frame = bounds + borderView.frame = NSMakeRect(0, frame.height - .borderSize, frame.width, .borderSize) + selectorView.frame = selectorFrame + for item in self.items { + if let view = item.view { + view.setFrameSize(NSMakeSize(view.size.width, frame.height)) + } + } + layoutItems(animated: false) + } + + public func updateItems(_ items:[ScrollableSegmentItem], animated: Bool, autoscroll: Bool = true) -> Void { + assertOnMainThread() + + let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: self.items, rightList: items) + for rdx in deleteIndices.reversed() { + self.removeItem(at: rdx, animated: animated) + } + for (idx, item, _) in indicesAndItems { + self.insertItem(item, at: idx, animated: animated, callback: { [weak self] item in + self?.didChangeSelectedItem?(item) + }) + } + for (idx, item, _) in updateIndices { + self.updateItem(item, at: idx, animated: animated, callback: { [weak self] item in + self?.didChangeSelectedItem?(item) + }) + } + layoutItems(animated: animated) + + + if let selectedItem = items.first(where: { $0.selected }), let selectedView = selectedItem.view { + let point = NSMakePoint(min(max(selectedView.frame.midX - frame.width / 2, 0), max(documentView.frame.width - frame.width, 0)), 0) + if point != scrollView.documentOffset, frame != .zero { + scrollView.clipView.scroll(to: point, animated: animated) + } + } + moveSelector(animated) + } + + private func removeItem(at index: Int, animated: Bool) { + let view = self.items[index].view + self.items[index].view = nil + if animated, let view = view { + view.layer?.animateAlpha(from: 1, to: 0, duration: duration, removeOnCompletion: false, completion: { [weak view] _ in + view?.removeFromSuperview() + }) + } else { + view?.removeFromSuperview() + } + + self.items.remove(at: index) + } + private func updateItem(_ item: ScrollableSegmentItem, at index: Int, animated: Bool, callback: @escaping(ScrollableSegmentItem)->Void) { + item.view = self.items[index].view + item.view?.updateItem(item, animated: animated) + + _ = item.view?.removeLastHandler() + + item.view?.set(handler: { _ in + callback(item) + }, for: .Click) + + self.items[index] = item + } + private func insertItem(_ item: ScrollableSegmentItem, at index: Int, animated: Bool, callback: @escaping(ScrollableSegmentItem)->Void) { + let view = SegmentItemView(item: item) + view.setFrameSize(NSMakeSize(view.frame.width, frame.height)) + view.set(handler: { _ in + callback(item) + }, for: .Click) + + item.view = view + + + var subviews = self.documentView.subviews + + subviews.insert(view, at: index) + self.documentView.subviews = subviews + self.items.insert(item, at: index) + + for (i, item) in self.items.enumerated() { + if i == index - 1, let v = item.view { + view.setFrameOrigin(NSMakePoint(v.frame.maxX, 0)) + } + } + } + + + + private func layoutItems(animated: Bool) { + var x: CGFloat = 0 + for item in self.items { + if let view = item.view { + view._change(pos: NSMakePoint(x, 0), animated: animated) + x += view.size.width + } + } + documentView.change(size: NSMakeSize(x, frame.height), animated: animated) + } + + private func redraw() { + selectorView.theme = self.theme + borderView.backgroundColor = self.theme.border + backgroundColor = presentation.colors.background + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + +} diff --git a/submodules/TGUIKit/TGUIKit/TGClipView.swift b/submodules/TGUIKit/TGUIKit/TGClipView.swift index 46d97209a..cdc31591c 100644 --- a/submodules/TGUIKit/TGUIKit/TGClipView.swift +++ b/submodules/TGUIKit/TGUIKit/TGClipView.swift @@ -231,12 +231,17 @@ public class TGClipView: NSClipView,CALayerDelegate { } else { _ = destination.x - o.x let ydif = destination.y - o.y - + let xdif = destination.x - o.x + if ydif != 0 { let incY = abs(ydif) - abs(ydif + 1) o.y -= incY } + if xdif != 0 { + let incX = abs(xdif) - abs(xdif + 1) + o.x -= incX + } super.scroll(to: o) } diff --git a/submodules/TGUIKit/TGUIKit/TableStickView.swift b/submodules/TGUIKit/TGUIKit/TableStickView.swift index 14b9c801d..ad815e480 100644 --- a/submodules/TGUIKit/TGUIKit/TableStickView.swift +++ b/submodules/TGUIKit/TGUIKit/TableStickView.swift @@ -18,6 +18,10 @@ open class TableStickView: TableRowView { } } + open var isAlwaysUp: Bool { + return false + } + open func updateIsVisible(_ visible: Bool, animated: Bool) { } diff --git a/submodules/TGUIKit/TGUIKit/TableView.swift b/submodules/TGUIKit/TGUIKit/TableView.swift index 512cc094c..c342dca39 100644 --- a/submodules/TGUIKit/TGUIKit/TableView.swift +++ b/submodules/TGUIKit/TGUIKit/TableView.swift @@ -642,6 +642,8 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele public var beforeSetupItem:((TableRowView, TableRowItem)->Void)? public var afterSetupItem:((TableRowView, TableRowItem)->Void)? + + private var nextScrollEventIsAnimated: Bool = false public var emptyItem:TableRowItem? { didSet { @@ -945,8 +947,8 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele if let strongSelf = self { let reqCount = strongSelf.count / 6 - strongSelf.updateStickAfterScroll(false) - + strongSelf.updateStickAfterScroll(strongSelf.nextScrollEventIsAnimated) + strongSelf.nextScrollEventIsAnimated = false let scroll = strongSelf.scrollPosition(strongSelf.visibleRows()) if (!strongSelf.updating && !strongSelf.clipView.isAnimateScrolling) { @@ -1168,13 +1170,13 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele if stickView?.item != item { - stickView?.set(item: someItem, animated: false) - stickView?.updateIsVisible(!firstTime, animated: false) + stickView?.set(item: someItem, animated: animated) + stickView?.updateIsVisible(!firstTime, animated: animated) } if let item = stickItem { if let view = (viewNecessary(at: item.index) as? TableStickView) { - view.updateIsVisible((!firstTime || !view.header), animated: false) + view.updateIsVisible((!firstTime || !view.header), animated: animated) } } @@ -1208,7 +1210,6 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele yTopOffset = 0 } - stickView.change(pos: NSMakePoint(0, yTopOffset), animated: animated) stickView.header = abs(dif) <= item.heightValue @@ -1248,7 +1249,8 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele } if tableView.isFlipped { - stickView.isHidden = documentOffset.y <= 0 + + stickView.isHidden = documentOffset.y <= 0// && !stickView.isAlwaysUp } stickTimeoutDisposable.set((Signal.single(Void()) |> delay(2.0, queue: Queue.mainQueue())).start(next: { [weak stickView] in @@ -1262,10 +1264,17 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele } else { if index == -1 { - stickView?.removeFromSuperview() + if animated, let stickView = self.stickView { + stickView.change(pos: NSMakePoint(0, -stickView.frame.height), animated: animated, removeOnCompletion: false, completion: { [weak stickView] _ in + stickView?.removeFromSuperview() + }) + } else { + stickView?.removeFromSuperview() + } + } else { + stickView?.setFrameOrigin(0, 0) + stickView?.header = true } - stickView?.setFrameOrigin(0, 0) - stickView?.header = true self.enumerateViews(with: { view in (view as? TableStickView)?.updateIsVisible(true, animated: false) @@ -2076,6 +2085,7 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele } var view: NSView? = item.isUniqueView ? nil : self.tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: identifier), owner: self.tableView) + if(view == nil) { view = makeView(at: item.index) view?.identifier = NSUserInterfaceItemIdentifier(rawValue: identifier) @@ -2470,7 +2480,7 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele } first = false - performScrollEvent() + performScrollEvent(transition.animated) } public func updateEmpties(animated: Bool = false) { @@ -2951,7 +2961,8 @@ open class TableView: ScrollView, NSTableViewDelegate,NSTableViewDataSource,Sele } } - public func performScrollEvent() -> Void { + public func performScrollEvent(_ animated: Bool = false) -> Void { + self.nextScrollEventIsAnimated = animated self.updateScroll(visibleRows()) NotificationCenter.default.post(name: NSView.boundsDidChangeNotification, object: self.contentView) } diff --git a/submodules/TGUIKit/TGUIKit/TextView.swift b/submodules/TGUIKit/TGUIKit/TextView.swift index af53f4216..1fd3e6922 100644 --- a/submodules/TGUIKit/TGUIKit/TextView.swift +++ b/submodules/TGUIKit/TGUIKit/TextView.swift @@ -992,7 +992,12 @@ public enum CursorSelectAlignment { public struct TextSelectedRange: Equatable { - public var range:NSRange = NSMakeRange(NSNotFound, 0) + public var range:NSRange = NSMakeRange(NSNotFound, 0) { + didSet { + var bp:Int = 0 + bp += 1 + } + } public var color:NSColor = presentation.colors.selectText public var def:Bool = true @@ -1170,6 +1175,13 @@ public class TextView: Control, NSViewToolTipOwner { width = endOffset - startOffset; + + if beginLineIndex == -1 { + beginLineIndex = 0 + } else if beginLineIndex >= layout.attributedString.length { + beginLineIndex = layout.attributedString.length - 1 + } + let blockValue:CGFloat = layout.mayBlocked ? CGFloat((layout.attributedString.attribute(.preformattedPre, at: beginLineIndex, effectiveRange: nil) as? NSNumber)?.floatValue ?? 0) : 0 @@ -1430,8 +1442,12 @@ public class TextView: Control, NSViewToolTipOwner { needsDisplay = true } + private var locationInWindow:NSPoint? = nil + func _mouseDown(with event: NSEvent) -> Void { + self.locationInWindow = event.locationInWindow + if !isSelectable || !userInteractionEnabled || event.modifierFlags.contains(.shift) { super.mouseDown(with: event) return @@ -1456,6 +1472,13 @@ public class TextView: Control, NSViewToolTipOwner { if !isSelectable || !userInteractionEnabled { return } + if let locationInWindow = self.locationInWindow { + let old = (ceil(locationInWindow.x), ceil(locationInWindow.y)) + let new = (ceil(event.locationInWindow.x), round(event.locationInWindow.y)) + if abs(old.0 - new.0) <= 1 && abs(old.1 - new.1) <= 1 { + return + } + } endSelect = self.convert(event.locationInWindow, from: nil) if let layout = layout { @@ -1495,6 +1518,8 @@ public class TextView: Control, NSViewToolTipOwner { public override func mouseUp(with event: NSEvent) { + self.locationInWindow = nil + if let layout = layout, userInteractionEnabled { let point = self.convert(event.locationInWindow, from: nil) if event.clickCount == 3, isSelectable { diff --git a/submodules/telegram-ios b/submodules/telegram-ios index a8353cce2..e2b36c872 160000 --- a/submodules/telegram-ios +++ b/submodules/telegram-ios @@ -1 +1 @@ -Subproject commit a8353cce2543cea17c682cdea8548bcb40afa871 +Subproject commit e2b36c872de8140baee44c9413a4aa86018ab05d diff --git a/tools/generate-images.swift b/tools/generate-images.swift index 9e97697fa..337d2ad51 100644 --- a/tools/generate-images.swift +++ b/tools/generate-images.swift @@ -458,6 +458,18 @@ func initialize() -> [String] { array.append("poll_selected_correct_outgoing") array.append("poll_selected_incorrect_outgoing") + array.append("chat_filter_add") + array.append("chat_filter_bots") + array.append("chat_filter_channels") + array.append("chat_filter_custom") + array.append("chat_filter_groups") + array.append("chat_filter_muted") + array.append("chat_filter_private_chats") + array.append("chat_filter_read") + array.append("chat_filter_secret_chats") + array.append("chat_filter_unmuted") + array.append("chat_filter_unread") + return array }