From c902031dcb3e5718e52e453da10158dca34c3960 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Thu, 10 Jan 2019 18:13:01 +0300 Subject: [PATCH] UI fixes --- .../AddItemIcon.imageset/Contents.json | 12 +- .../Item List/AddItemIcon.imageset/add.pdf | Bin 4083 -> 0 bytes .../AddItemIcon.imageset/ic_addoption@2x.png | Bin 0 -> 1202 bytes .../AddItemIcon.imageset/ic_addoption@3x.png | Bin 0 -> 1408 bytes .../RemoveItemIcon.imageset/Contents.json | 12 +- .../RemoveItemIcon.imageset/delete.pdf | Bin 4009 -> 0 bytes .../ic_deleteotion@2x.png | Bin 0 -> 1117 bytes .../ic_deleteotion@3x.png | Bin 0 -> 1432 bytes .../AuthorizationSequenceController.swift | 64 ++-- ...ationSequencePasswordEntryController.swift | 17 +- ...nSequencePasswordEntryControllerNode.swift | 6 +- TelegramUI/ChannelMembersController.swift | 3 +- TelegramUI/ChannelVisibilityController.swift | 2 +- TelegramUI/ChatController.swift | 68 +++-- TelegramUI/ChatEmptyNode.swift | 2 +- .../ChatHistorySearchContainerNode.swift | 41 +-- TelegramUI/ChatListController.swift | 275 ++++++++++-------- TelegramUI/ChatListItem.swift | 8 +- TelegramUI/ChatListNode.swift | 6 +- TelegramUI/ChatListNodeEntries.swift | 6 +- TelegramUI/ChatListSearchContainerNode.swift | 30 +- TelegramUI/ChatMessageBubbleItemNode.swift | 6 + .../ChatPresentationInterfaceState.swift | 3 +- .../ChatRecentActionsHistoryTransition.swift | 1 - TelegramUI/ChatSearchInputPanelNode.swift | 2 +- TelegramUI/ChatSearchState.swift | 2 +- TelegramUI/FFMpegMediaFrameSource.swift | 2 + .../FFMpegMediaFrameSourceContext.swift | 9 +- TelegramUI/FetchManager.swift | 10 +- TelegramUI/FetchMediaUtils.swift | 4 +- TelegramUI/FetchVideoMediaResource.swift | 14 + TelegramUI/GroupStickerPackCurrentItem.swift | 2 +- TelegramUI/HashtagSearchController.swift | 8 +- TelegramUI/ItemListStickerPackItem.swift | 2 +- TelegramUI/MergedItemListItem.swift | 2 - TelegramUI/PhotoResources.swift | 19 +- .../PresentationResourcesItemList.swift | 20 +- TelegramUI/SecureIdLocalResource.swift | 7 + TelegramUI/SharedMediaPlayer.swift | 3 + TelegramUI/StorageUsageController.swift | 44 ++- TelegramUI/UndoOverlayController.swift | 5 + TelegramUI/UndoOverlayControllerNode.swift | 10 +- .../WallpaperListPreviewControllerNode.swift | 2 +- 43 files changed, 449 insertions(+), 280 deletions(-) delete mode 100644 Images.xcassets/Item List/AddItemIcon.imageset/add.pdf create mode 100644 Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@2x.png create mode 100644 Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@3x.png delete mode 100644 Images.xcassets/Item List/RemoveItemIcon.imageset/delete.pdf create mode 100644 Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@2x.png create mode 100644 Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@3x.png diff --git a/Images.xcassets/Item List/AddItemIcon.imageset/Contents.json b/Images.xcassets/Item List/AddItemIcon.imageset/Contents.json index 2e2e9b52c0..ffe65a7ec0 100644 --- a/Images.xcassets/Item List/AddItemIcon.imageset/Contents.json +++ b/Images.xcassets/Item List/AddItemIcon.imageset/Contents.json @@ -2,7 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "add.pdf" + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_addoption@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_addoption@3x.png", + "scale" : "3x" } ], "info" : { diff --git a/Images.xcassets/Item List/AddItemIcon.imageset/add.pdf b/Images.xcassets/Item List/AddItemIcon.imageset/add.pdf deleted file mode 100644 index 931be96420bd96e81f62fba40acc0d1faaee03e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4083 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 W5{MLPd~XO$N=gbWD5$2Z4*m~PBK5-n diff --git a/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@2x.png b/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4a17bc48eb828a8df2453eda201c409507b21625 GIT binary patch literal 1202 zcmV;j1Wo&iP)Px(Y)M2xR9FesSxtx>MHK#C?aUgoMhO^_o&_(GfF1-*41^%Lc<``9L{JnJ@e)+L zcra>U(is9`z(hS56+}-W9`XapVNaSI^yX^U z;Ro#Vd9)eB`gNOQ2>#j-u*Z9&YeRPKm!6KUDU)SrJP;hP!IOq2&{^&#qgSj^0P{LPRlUzIa>2y#^EFZ>x%NTj`EC)P2!p)x97c? z_Ix{Fm3YWI%C_@TT6h!ZzhcYX&)|r3(mt~RKP#(_pEYtet+4TP?(CLr2k_oKe`fhg zW{E5bKX{$p*2g@OFKCwRxc4+>H4HL#HZnaLuu8UqLykpMVs1kQQx}*GQZHDRt9*b} zQz=hn5tW!zuB(cn6%06yt@Ts!)HTd>e!!6xWf2`Q=eVwY+?nDwz(z)U11+~}6)ZA8 z>RiHE%p|!krH{Dl{7-^}40!{d=VB6M5%_nO{(TGl&SMcZxu9KFLZ-mvf3NaM9px?7 Q1^@s607*qoM6N<$f}4y>U;qFB literal 0 HcmV?d00001 diff --git a/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@3x.png b/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..0c2b3becdf625bf052eeec90839fe087cfc75a5b GIT binary patch literal 1408 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 diff --git a/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@2x.png b/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a52d7ebf3c646df84fdfaf3ce87129854bee0887 GIT binary patch literal 1117 zcmV-j1fu(iP)Px(7fD1xR9FesSxaaWVGy3#q)`kgp0q9XVw2i~kE+1Ak3x|)$vE?;8WVdh;VTSsMB#*Ou?xW6fPI7g>6E1fOq*8)AQEa}uP#fc zEH^q%ahROMzM)5{^rZ9_1^h=L5`LC{umX`h{IWizn(}KD4BFzd^)S`=gFul>sh&1( zG{kmTSi09Ln^-=H^6XrJRnDe5`19qKl|%mXa5QEm3}}IsK!NX_!B@k#<83AYR@opN z?k`Nt#>24rju_&ivkiNPyG)M6kdV}s_iSLA*4T;MTq*BReZ9-%jsaFhA)04RnlpBy zO8ETdxCtf(SXHom6a~{#PCEr@*M>!+LIGC2!EBY=oPgN~OEdL#;#3T9w2`r4cFJue zZnn7RlRsQtM)_sStm_iN!Y<_GQ@tfG-MAJj!FsCk z9n<2?fQftWP3(*#x*{ABzHS!SqAS4JP`ojr&7X>qz{BL_saO(eIOSGDj!dGGU@0q+ zPvIBRIL9zgWai5v95vMVXubeO>@;N7H4~T4hwXB=8PTIO&$^txxDH^?ySFpdg<71B zm)qZF!59U+<4`My#ejoBPlCtYB=};-eA?{W31Mn?BxuA&p1EBVY%?Lj$YtcIric!M zjQEWkuB#@h7~tp)cLPJ8GKr4ZkuG4*hgfAz0IcS-Jo0n|Pz3_-@zm!*Je~x=LQwNr zgzNE4bMjrnbM4}@$+rIktQg9w&T%}?-3y2?1YXxZy#IPy3ot%;5|;TagT9k2koRlx ztk)gDr|-X8*@~q#E27-v6Uk`?ev^+ea-7QZ!g~@1R-QEtrv)sGJiFK`Q$z+`X$sEj zE~!hhLDCCmWy3m#lQ`9Uwrt&MK;r~^dn}2Z%jG2NYveq;QU|OU((-UTA~i*n!JN#x zh&E=UgF%j{-hj(3z8KUyqRoKF7;pmkfWK76NBrFKuUkKd4bM2p8_%N(5uX&8_KDro j39h16>ADhm^soK`cxx_6r6A8)00000NkvXXu0mjf+gKZl literal 0 HcmV?d00001 diff --git a/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@3x.png b/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a4c5861aae1296c4564eeb32a62621bcf20d9c90 GIT binary patch literal 1432 zcmV;J1!ww+P)Px)Qb|NXRCodHT)&UpL==9{%I;pIq~NF!mx{f>5-JKvA}&fGQj{(fG$6qr5F`qU zq(UpIyCOv-?hhamG!&$ZA_Wq0h(wW+Dq$hFR9r*@*93{|F7oib8T;`1yk~ptU9ast zX|=X@etn;3-u(JznPy<>WP6GJao$2!9b)%od@mF6A}KnEYx#&hA;yQ)Hm)~GIltn& zNBi9$`Br~R0!-6*8i}iJ*P-iQpCVViN37lkU;xixEX45lW9EmvMa*AOv$@VcJor6xa$W+t5TTA8X_FdUz+3wW1T^w0 z+}`Q%2(|4?;?^49e%RkTZ2Yg8h)^ew9H6IzF53Gis0I7tvztN)=6^x{Wm;%;`PRd~ zZF8_DLLEDN4&cAQ=SADhbB)LR7)|D5zWwN`O}eRxP-ojM+NMj$?7U5eGl;WJi}Wd9 z>krc8m5K=C44+;_FYtPrOlBUI={_|V&ZTjtiHT4v?N@2Y*QeJT+y8~vY? zq@0Kd_=7j#3$Ed_m?Y7;3B$Y%zi}3R<8EA;Vu{c-VZ5WHi)DT+ML&@KN8! zC!jf$ttJG)NZHY(HJ0G>(Dg(jbnh^PW1QLuF;tSN-eQysEE>~s>pCD;8nGDg`SI?lUMgRWgfm|^qq7x;zw7%}Eeyo^8>ny-Y10;MSA66+bQ)=U0;rgqIxo;@^dYtyKL>_p`V{;ayvXX!4IxY#K|`*i06>(xEmTPrX)Yl(FET z;FB8Biw#))06j-^jMc{(gAg1zTFfar#=07?3kPOEC1TFHnupyGA&SFXz36zhHMuRT3ZB_dnjCWZbiW#ahzcn5rWuLouL=0oUKPG>I7vZ5gIg8>JK+sqKO&GD#sHNoYfGhiHMMZnpL#3QaDg?jN%g# z)C9pMmY4_$tewqsbR3uS+_kO~%D9(f6f9wjb(Y}>{(aSsJEp1;J_+)z2PROtqp4gf zbfTswP$+{a?k&%JYCJ)3w%10?u65kl2c4CK8S4qMY=9YacmidE!@6liWoFKR)%cKn mn3UT8Z9 NavigationBarTheme { return NavigationBarTheme(buttonColor: theme.accentColor, disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) @@ -34,11 +39,13 @@ public final class AuthorizationSequenceController: NavigationController { super.init(mode: .single, theme: NavigationControllerTheme(navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black, emptyDetailIcon: nil)) self.stateDisposable = (account.postbox.stateView() - |> map { view -> UnauthorizedAccountStateContents in - if let state = view.state as? UnauthorizedAccountState { - return state.contents + |> map { view -> InnerState in + if let _ = view.state as? AuthorizedAccountState { + return .authorized + } else if let state = view.state as? UnauthorizedAccountState { + return .state(state.contents) } else { - return .empty + return .state(.empty) } } |> distinctUntilChanged @@ -387,7 +394,7 @@ public final class AuthorizationSequenceController: NavigationController { return controller } - private func passwordEntryController(hint: String) -> AuthorizationSequencePasswordEntryController { + private func passwordEntryController(hint: String, suggestReset: Bool) -> AuthorizationSequencePasswordEntryController { var currentController: AuthorizationSequencePasswordEntryController? for c in self.viewControllers { if let c = c as? AuthorizationSequencePasswordEntryController { @@ -446,7 +453,7 @@ public final class AuthorizationSequenceController: NavigationController { switch option { case let .email(pattern): let _ = (strongSelf.account.postbox.transaction { transaction -> Void in - if let state = transaction.getState() as? UnauthorizedAccountState, case let .passwordEntry(hint, number, code) = state.contents { + if let state = transaction.getState() as? UnauthorizedAccountState, case let .passwordEntry(hint, number, code, _) = state.contents { transaction.setState(UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .passwordRecovery(hint: hint, number: number, code: code, emailPattern: pattern))) } }).start() @@ -464,7 +471,7 @@ public final class AuthorizationSequenceController: NavigationController { } controller.reset = { [weak self, weak controller] in if let strongSelf = self, let strongController = controller { - strongController.present(standardTextAlertController(theme: AlertControllerTheme(authTheme: strongSelf.theme), title: nil, text: strongSelf.strings.TwoStepAuth_RecoveryUnavailable, actions: [ + strongController.present(standardTextAlertController(theme: AlertControllerTheme(authTheme: strongSelf.theme), title: nil, text: suggestReset ? strongSelf.strings.TwoStepAuth_RecoveryFailed : strongSelf.strings.TwoStepAuth_RecoveryUnavailable, actions: [ TextAlertAction(type: .defaultAction, title: strongSelf.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: strongSelf.strings.Login_ResetAccountProtected_Reset, action: { if let strongSelf = self, let strongController = controller { @@ -491,7 +498,7 @@ public final class AuthorizationSequenceController: NavigationController { })]), in: .window(.root)) } } - controller.updateData(hint: hint) + controller.updateData(hint: hint, suggestReset: suggestReset) return controller } @@ -548,7 +555,7 @@ public final class AuthorizationSequenceController: NavigationController { let account = strongSelf.account let _ = (strongSelf.account.postbox.transaction { transaction -> Void in if let state = transaction.getState() as? UnauthorizedAccountState, case let .passwordRecovery(hint, number, code, _) = state.contents { - transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .passwordEntry(hint: hint, number: number, code: code))) + transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .passwordEntry(hint: hint, number: number, code: code, suggestReset: true))) } }).start() } @@ -679,25 +686,30 @@ public final class AuthorizationSequenceController: NavigationController { return controller } - private func updateState(state: UnauthorizedAccountStateContents) { + private func updateState(state: InnerState) { switch state { - case .empty: - if let _ = self.viewControllers.last as? AuthorizationSequenceSplashController { - } else { - self.setViewControllers([self.splashController()], animated: !self.viewControllers.isEmpty) + case .authorized: + break + case let .state(state): + switch state { + case .empty: + if let _ = self.viewControllers.last as? AuthorizationSequenceSplashController { + } else { + self.setViewControllers([self.splashController()], animated: !self.viewControllers.isEmpty) + } + case let .phoneEntry(countryCode, number): + self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: countryCode, number: number)], animated: !self.viewControllers.isEmpty) + case let .confirmationCodeEntry(number, type, _, timeout, nextType, termsOfService): + self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: defaultCountryCode(), number: ""), self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) + case let .passwordEntry(hint, _, _, suggestReset): + self.setViewControllers([self.splashController(), self.passwordEntryController(hint: hint, suggestReset: suggestReset)], animated: !self.viewControllers.isEmpty) + case let .passwordRecovery(_, _, _, emailPattern): + self.setViewControllers([self.splashController(), self.passwordRecoveryController(emailPattern: emailPattern)], animated: !self.viewControllers.isEmpty) + case let .awaitingAccountReset(protectedUntil, number): + self.setViewControllers([self.splashController(), self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number)], animated: !self.viewControllers.isEmpty) + case let .signUp(_, _, _, firstName, lastName, termsOfService): + self.setViewControllers([self.splashController(), self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) } - case let .phoneEntry(countryCode, number): - self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: countryCode, number: number)], animated: !self.viewControllers.isEmpty) - case let .confirmationCodeEntry(number, type, _, timeout, nextType, termsOfService): - self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: defaultCountryCode(), number: ""), self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) - case let .passwordEntry(hint, _, _): - self.setViewControllers([self.splashController(), self.passwordEntryController(hint: hint)], animated: !self.viewControllers.isEmpty) - case let .passwordRecovery(_, _, _, emailPattern): - self.setViewControllers([self.splashController(), self.passwordRecoveryController(emailPattern: emailPattern)], animated: !self.viewControllers.isEmpty) - case let .awaitingAccountReset(protectedUntil, number): - self.setViewControllers([self.splashController(), self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number)], animated: !self.viewControllers.isEmpty) - case let .signUp(_, _, _, firstName, lastName, termsOfService): - self.setViewControllers([self.splashController(), self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) } } diff --git a/TelegramUI/AuthorizationSequencePasswordEntryController.swift b/TelegramUI/AuthorizationSequencePasswordEntryController.swift index 722ceba444..e7f531e044 100644 --- a/TelegramUI/AuthorizationSequencePasswordEntryController.swift +++ b/TelegramUI/AuthorizationSequencePasswordEntryController.swift @@ -19,12 +19,14 @@ final class AuthorizationSequencePasswordEntryController: ViewController { didSet { if self.didForgotWithNoRecovery != oldValue { if self.isNodeLoaded, let hint = self.hint { - self.controllerNode.updateData(hint: hint, didForgotWithNoRecovery: didForgotWithNoRecovery) + self.controllerNode.updateData(hint: hint, didForgotWithNoRecovery: didForgotWithNoRecovery, suggestReset: self.suggestReset) } } } } + var suggestReset: Bool = false + private let hapticFeedback = HapticFeedback() var inProgress: Bool = false { @@ -82,7 +84,7 @@ final class AuthorizationSequencePasswordEntryController: ViewController { } if let hint = self.hint { - self.controllerNode.updateData(hint: hint, didForgotWithNoRecovery: self.didForgotWithNoRecovery) + self.controllerNode.updateData(hint: hint, didForgotWithNoRecovery: self.didForgotWithNoRecovery, suggestReset: self.suggestReset) } } @@ -92,11 +94,12 @@ final class AuthorizationSequencePasswordEntryController: ViewController { self.controllerNode.activateInput() } - func updateData(hint: String) { - if self.hint != hint { + func updateData(hint: String, suggestReset: Bool) { + if self.hint != hint || self.suggestReset != suggestReset { self.hint = hint + self.suggestReset = suggestReset if self.isNodeLoaded { - self.controllerNode.updateData(hint: hint, didForgotWithNoRecovery: self.didForgotWithNoRecovery) + self.controllerNode.updateData(hint: hint, didForgotWithNoRecovery: self.didForgotWithNoRecovery, suggestReset: self.suggestReset) } } } @@ -123,7 +126,9 @@ final class AuthorizationSequencePasswordEntryController: ViewController { } func forgotPressed() { - if self.didForgotWithNoRecovery { + if self.suggestReset { + self.present(standardTextAlertController(theme: AlertControllerTheme(authTheme: self.theme), title: nil, text: self.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .defaultAction, title: self.strings.Common_OK, action: {})]), in: .window(.root)) + } else if self.didForgotWithNoRecovery { self.present(standardTextAlertController(theme: AlertControllerTheme(authTheme: self.theme), title: nil, text: self.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: self.strings.Common_OK, action: {})]), in: .window(.root)) } else { self.forgot?() diff --git a/TelegramUI/AuthorizationSequencePasswordEntryControllerNode.swift b/TelegramUI/AuthorizationSequencePasswordEntryControllerNode.swift index 515f23d415..5832f0def2 100644 --- a/TelegramUI/AuthorizationSequencePasswordEntryControllerNode.swift +++ b/TelegramUI/AuthorizationSequencePasswordEntryControllerNode.swift @@ -25,6 +25,7 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT var reset: (() -> Void)? var didForgotWithNoRecovery = false + var suggestReset = false private var clearOnce: Bool = false @@ -91,8 +92,9 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT self.resetNode.addTarget(self, action: #selector(self.resetPressed), forControlEvents: .touchUpInside) } - func updateData(hint: String, didForgotWithNoRecovery: Bool) { + func updateData(hint: String, didForgotWithNoRecovery: Bool, suggestReset: Bool) { self.didForgotWithNoRecovery = didForgotWithNoRecovery + self.suggestReset = suggestReset self.codeField.textField.attributedPlaceholder = NSAttributedString(string: hint, font: Font.regular(20.0), textColor: self.theme.textPlaceholderColor) if let (layout, navigationHeight) = self.layoutArguments { self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate) @@ -126,7 +128,7 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT items.append(AuthorizationLayoutItem(node: self.forgotNode, size: forgotSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 48.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - if self.didForgotWithNoRecovery { + if self.didForgotWithNoRecovery || self.suggestReset { self.resetNode.isHidden = false items.append(AuthorizationLayoutItem(node: self.resetNode, size: resetSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) } else { diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index 3076efd1f5..a3d1a59b27 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -357,7 +357,7 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo |> afterCompleted { contactsController?.dismiss() } - }).start(error: { error in + }).start(error: { [weak contactsController] error in let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let text: String switch error { @@ -369,6 +369,7 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo text = presentationData.strings.Channel_ErrorAddBlocked } presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + contactsController?.dismiss() })) presentControllerImpl?(contactsController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) diff --git a/TelegramUI/ChannelVisibilityController.swift b/TelegramUI/ChannelVisibilityController.swift index cf960ed214..051b19e691 100644 --- a/TelegramUI/ChannelVisibilityController.swift +++ b/TelegramUI/ChannelVisibilityController.swift @@ -871,7 +871,7 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode: if let link = link { UIPasteboard.general.string = link let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Username_LinkCopied, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.Username_LinkCopied)), nil) } }) }, revokePrivateLink: { diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index 3e5f1d7779..508fcc6bd2 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -2373,7 +2373,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } if let updatedIndex = updatedIndex { navigateIndex = resultsState.messageIndices[updatedIndex] - return current.updatedSearch(data.withUpdatedResultsState(ChatSearchResultsState(messageIndices: resultsState.messageIndices, currentId: resultsState.messageIndices[updatedIndex].id, totalCount: resultsState.totalCount, complete: resultsState.complete))) + return current.updatedSearch(data.withUpdatedResultsState(ChatSearchResultsState(messageIndices: resultsState.messageIndices, currentId: resultsState.messageIndices[updatedIndex].id, state: resultsState.state, totalCount: resultsState.totalCount, completed: resultsState.completed))) } } } @@ -2382,7 +2382,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if let navigateIndex = navigateIndex { switch strongSelf.chatLocation { case .peer: - strongSelf.navigateToMessage(from: nil, to: .id(navigateIndex.id)) + strongSelf.navigateToMessage(from: nil, to: .index(navigateIndex), forceInCurrentChat: true) case .group: strongSelf.navigateToMessage(from: nil, to: .index(navigateIndex)) } @@ -2603,11 +2603,15 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal strongSelf.recordingModeFeedback?.error() - let rect: CGRect? + var rect: CGRect? let isStickers: Bool = subject == .stickers switch subject { case .stickers: rect = strongSelf.chatDisplayNode.frameForStickersButton() + if var rectValue = rect, let actionRect = strongSelf.chatDisplayNode.frameForInputActionButton() { + rectValue.origin.y = actionRect.minY + rect = rectValue + } case .mediaRecording: rect = strongSelf.chatDisplayNode.frameForInputActionButton() } @@ -3281,6 +3285,12 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal self.silentPostTooltipController?.dismiss() self.mediaRecordingModeTooltipController?.dismiss() + + self.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + }) } private func saveInterfaceState(includeScrollState: Bool = true) { @@ -4558,13 +4568,13 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal var derivedSearchState: ChatSearchState? if let search = interfaceState.search { - func loadMoreIndexFromResultsState(_ resultsState: ChatSearchResultsState?) -> MessageIndex? { + func loadMoreStateFromResultsState(_ resultsState: ChatSearchResultsState?) -> SearchMessagesState? { guard let resultsState = resultsState, let currentId = resultsState.currentId else { return nil } if let index = resultsState.messageIndices.index(where: { $0.id == currentId }) { if index <= limit / 2 { - return resultsState.messageIndices.first + return resultsState.state } } return nil @@ -4573,16 +4583,16 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal case .everything: switch self.chatLocation { case let .peer(peerId): - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil), loadMoreIndex: loadMoreIndexFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) case let .group(groupId): - derivedSearchState = ChatSearchState(query: search.query, location: .group(groupId), loadMoreIndex: loadMoreIndexFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .group(groupId), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) } case .members: derivedSearchState = nil case let .member(peer): switch self.chatLocation { case let .peer(peerId): - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: peer.id, tags: nil), loadMoreIndex: loadMoreIndexFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: peer.id, tags: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) case .group: derivedSearchState = nil } @@ -4620,18 +4630,17 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal searchDisposable = MetaDisposable() self.searchDisposable = searchDisposable } - searchDisposable.set((searchMessages(account: self.account, location: searchState.location, query: searchState.query, limit: limit) - |> map { ($0.0, $0.2) } + searchDisposable.set((searchMessages(account: self.account, location: searchState.location, query: searchState.query, state: nil, limit: limit) |> delay(0.2, queue: Queue.mainQueue()) - |> deliverOnMainQueue).start(next: { [weak self] results, totalCount in + |> deliverOnMainQueue).start(next: { [weak self] results, updatedState in guard let strongSelf = self else { return } - let complete = results.count == 0 + let complete = results.completed var navigateIndex: MessageIndex? strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { current in if let data = current.search { - let messageIndices = results.map({ MessageIndex($0) }).sorted() + let messageIndices = results.messages.map({ MessageIndex($0) }).sorted() var currentIndex = messageIndices.last if let previousResultId = data.resultsState?.currentId { for index in messageIndices { @@ -4642,7 +4651,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } } navigateIndex = currentIndex - return current.updatedSearch(data.withUpdatedResultsState(ChatSearchResultsState(messageIndices: messageIndices, currentId: currentIndex?.id, totalCount: max(Int32(messageIndices.count), totalCount), complete: complete))) + return current.updatedSearch(data.withUpdatedResultsState(ChatSearchResultsState(messageIndices: messageIndices, currentId: currentIndex?.id, state: updatedState, totalCount: results.totalCount, completed: results.completed))) } else { return current } @@ -4650,7 +4659,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if let navigateIndex = navigateIndex { switch strongSelf.chatLocation { case .peer: - strongSelf.navigateToMessage(from: nil, to: .id(navigateIndex.id)) + strongSelf.navigateToMessage(from: nil, to: .index(navigateIndex), forceInCurrentChat: true) case .group: strongSelf.navigateToMessage(from: nil, to: .index(navigateIndex)) } @@ -4661,8 +4670,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } })) } - } else if previousSearchState?.loadMoreIndex != searchState.loadMoreIndex { - if let loadMoreIndex = searchState.loadMoreIndex { + } else if previousSearchState?.loadMoreState != searchState.loadMoreState { + if let loadMoreState = searchState.loadMoreState { self.searching.set(true) let searchDisposable: MetaDisposable if let current = self.searchDisposable { @@ -4671,22 +4680,17 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal searchDisposable = MetaDisposable() self.searchDisposable = searchDisposable } - searchDisposable.set((searchMessages(account: self.account, location: searchState.location, query: searchState.query, lowerBound: loadMoreIndex, limit: limit) - |> map { ($0.0, $0.2) } + searchDisposable.set((searchMessages(account: self.account, location: searchState.location, query: searchState.query, state: loadMoreState, limit: limit) |> delay(0.2, queue: Queue.mainQueue()) - |> deliverOnMainQueue).start(next: { [weak self] results, totalCount in + |> deliverOnMainQueue).start(next: { [weak self] results, updatedState in guard let strongSelf = self else { return } - let complete = results.count == 0 + let complete = results.completed strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { current in if let data = current.search, let previousResultsState = data.resultsState { - let previousSet = Set(previousResultsState.messageIndices) - let messageIndices = results.map({ MessageIndex($0) }).sorted() - var mergedIndices = messageIndices.filter({ !previousSet.contains($0) }) - mergedIndices.append(contentsOf: previousResultsState.messageIndices) - - return current.updatedSearch(data.withUpdatedResultsState(ChatSearchResultsState(messageIndices: mergedIndices, currentId: previousResultsState.currentId, totalCount: max(totalCount, Int32(mergedIndices.count)), complete: complete))) + let messageIndices = results.messages.map({ MessageIndex($0) }).sorted() + return current.updatedSearch(data.withUpdatedResultsState(ChatSearchResultsState(messageIndices: messageIndices, currentId: previousResultsState.currentId, state: updatedState, totalCount: results.totalCount, completed: results.completed))) } else { return current } @@ -4717,11 +4721,11 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal self.chatDisplayNode.historyNode.scrollToEndOfHistory() } - public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, completion: (() -> Void)? = nil) { - self.navigateToMessage(from: nil, to: messageLocation, rememberInStack: false, animated: animated, completion: completion) + public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, completion: (() -> Void)? = nil) { + self.navigateToMessage(from: nil, to: messageLocation, rememberInStack: false, forceInCurrentChat: forceInCurrentChat, animated: animated, completion: completion) } - private func navigateToMessage(from fromId: MessageId?, to messageLocation: NavigateToMessageLocation, scrollPosition: ListViewScrollPosition = .center(.bottom), rememberInStack: Bool = true, animated: Bool = true, completion: (() -> Void)? = nil) { + private func navigateToMessage(from fromId: MessageId?, to messageLocation: NavigateToMessageLocation, scrollPosition: ListViewScrollPosition = .center(.bottom), rememberInStack: Bool = true, forceInCurrentChat: Bool = false, animated: Bool = true, completion: (() -> Void)? = nil) { if self.isNodeLoaded { var fromIndex: MessageIndex? @@ -4733,11 +4737,11 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } } - if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, messageId.peerId != peerId { + if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) { if let navigationController = self.navigationController as? NavigationController { navigateToChatController(navigationController: navigationController, account: self.account, chatLocation: .peer(messageId.peerId), messageId: messageId, keepStack: .always) } - } else if case let .peer(peerId) = self.chatLocation, messageLocation.peerId == peerId { + } else if case let .peer(peerId) = self.chatLocation, (messageLocation.peerId == peerId || forceInCurrentChat) { if let fromIndex = fromIndex { if let _ = fromId, rememberInStack { self.historyNavigationStack.add(fromIndex) diff --git a/TelegramUI/ChatEmptyNode.swift b/TelegramUI/ChatEmptyNode.swift index 8d863b3165..39ffcc8987 100644 --- a/TelegramUI/ChatEmptyNode.swift +++ b/TelegramUI/ChatEmptyNode.swift @@ -457,7 +457,7 @@ final class ChatEmptyNode: ASDisplayNode { contentType = .secret } else if let group = peer as? TelegramGroup, case .creator = group.role { contentType = .group - } else if let channel = peer as? TelegramChannel, channel.flags.contains(.isCreator) { + } else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isCreator) { contentType = .group } else { contentType = .regular diff --git a/TelegramUI/ChatHistorySearchContainerNode.swift b/TelegramUI/ChatHistorySearchContainerNode.swift index 96296be76d..1154dc2ce4 100644 --- a/TelegramUI/ChatHistorySearchContainerNode.swift +++ b/TelegramUI/ChatHistorySearchContainerNode.swift @@ -174,18 +174,19 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { if let strongSelf = self { let signal: Signal<[ChatHistorySearchEntry]?, NoError> if let query = query, !query.isEmpty { - let foundRemoteMessages: Signal<[Message], NoError> = searchMessages(account: account, location: .peer(peerId: peerId, fromId: nil, tags: tagMask), query: query) |> map {$0.0} - |> delay(0.2, queue: Queue.concurrentDefaultQueue()) + let foundRemoteMessages: Signal<[Message], NoError> = searchMessages(account: account, location: .peer(peerId: peerId, fromId: nil, tags: tagMask), query: query, state: nil) + |> map { $0.0.messages } + |> delay(0.2, queue: Queue.concurrentDefaultQueue()) signal = combineLatest(foundRemoteMessages, themeAndStringsPromise.get()) - |> map { messages, themeAndStrings -> [ChatHistorySearchEntry]? in - if messages.isEmpty { - return [] - } else { - return messages.map { message -> ChatHistorySearchEntry in - return .message(message, themeAndStrings.0, themeAndStrings.1, themeAndStrings.2) - } + |> map { messages, themeAndStrings -> [ChatHistorySearchEntry]? in + if messages.isEmpty { + return [] + } else { + return messages.map { message -> ChatHistorySearchEntry in + return .message(message, themeAndStrings.0, themeAndStrings.1, themeAndStrings.2) } + } } strongSelf._isSearching.set(true) @@ -195,17 +196,17 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { } strongSelf.searchDisposable.set((signal - |> deliverOnMainQueue).start(next: { entries in - if let strongSelf = self { - let previousEntries = previousEntriesValue.swap(entries) - - let firstTime = previousEntries == nil - let transition = chatHistorySearchContainerPreparedTransition(from: previousEntries ?? [], to: entries ?? [], query: query ?? "", displayingResults: entries != nil, account: account, peerId: peerId, interaction: interfaceInteraction) - strongSelf.currentEntries = entries - strongSelf.enqueueTransition(transition, firstTime: firstTime) - strongSelf._isSearching.set(false) - } - })) + |> deliverOnMainQueue).start(next: { entries in + if let strongSelf = self { + let previousEntries = previousEntriesValue.swap(entries) + + let firstTime = previousEntries == nil + let transition = chatHistorySearchContainerPreparedTransition(from: previousEntries ?? [], to: entries ?? [], query: query ?? "", displayingResults: entries != nil, account: account, peerId: peerId, interaction: interfaceInteraction) + strongSelf.currentEntries = entries + strongSelf.enqueueTransition(transition, firstTime: firstTime) + strongSelf._isSearching.set(false) + } + })) } })) diff --git a/TelegramUI/ChatListController.swift b/TelegramUI/ChatListController.swift index 7ed60f1bc0..f4458111ab 100644 --- a/TelegramUI/ChatListController.swift +++ b/TelegramUI/ChatListController.swift @@ -321,129 +321,150 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie } self.chatListDisplayNode.chatListNode.deletePeerChat = { [weak self] peerId in - if let strongSelf = self { - let _ = (strongSelf.account.postbox.transaction { transaction -> Peer? in - return transaction.getPeer(peerId) - } |> deliverOnMainQueue).start(next: { peer in - if let strongSelf = self, let peer = peer { - let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme) - var items: [ActionSheetItem] = [] - var canClear = true - var canStop = false - var delayedDelete = true - - var deleteTitle = strongSelf.presentationData.strings.Common_Delete - if let channel = peer as? TelegramChannel { - if case .broadcast = channel.info { - delayedDelete = false - canClear = false - deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel - } - if let addressName = channel.addressName, !addressName.isEmpty { - canClear = false - } - } else if let user = peer as? TelegramUser, user.botInfo != nil { - canStop = true - } - if canClear { - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - - if let strongSelf = self { - let _ = clearHistoryInteractively(postbox: strongSelf.account.postbox, peerId: peerId).start() - } - })) - } - - items.append(ActionSheetButtonItem(title: deleteTitle, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - if let strongSelf = self { - if delayedDelete { - let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme) - var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(account: strongSelf.account, peer: peer, strings: strongSelf.presentationData.strings)) - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteChat, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - if let strongSelf = self { - strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) - strongSelf.chatListDisplayNode.chatListNode.updateState({ state in - var state = state - state.pendingRemovalPeerIds.insert(peer.id) - return state - }) - strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) - strongSelf.present(UndoOverlayController(account: strongSelf.account, text: strongSelf.presentationData.strings.Undo_ChatDeleted, action: { shouldCommit in - guard let strongSelf = self else { - return - } - if shouldCommit { - strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) - let _ = removePeerChat(postbox: strongSelf.account.postbox, peerId: peerId, reportChatSpam: false).start(completed: { - guard let strongSelf = self else { - return - } - strongSelf.chatListDisplayNode.chatListNode.updateState({ state in - var state = state - state.pendingRemovalPeerIds.remove(peer.id) - return state - }) - self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) - }) - } else { - strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) - strongSelf.chatListDisplayNode.chatListNode.updateState({ state in - var state = state - state.pendingRemovalPeerIds.remove(peer.id) - return state - }) - self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) - } - }), in: .window(.root)) - } - })) - actionSheet.setItemGroups([ - ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ]) - ]) - strongSelf.present(actionSheet, in: .window(.root)) - } else { - strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) - let _ = removePeerChat(postbox: strongSelf.account.postbox, peerId: peerId, reportChatSpam: false).start(completed: { - self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) - }) - } - } - })) - - if canStop { - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_DeleteBotConversationConfirmation, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - - if let strongSelf = self { - strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) - let _ = removePeerChat(postbox: strongSelf.account.postbox, peerId: peerId, reportChatSpam: false).start(completed: { - self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) - }) - let _ = requestUpdatePeerIsBlocked(account: strongSelf.account, peerId: peer.id, isBlocked: true).start() - } - })) - } - - actionSheet.setItemGroups([ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ]) - ]) - strongSelf.present(actionSheet, in: .window(.root)) - } - }) + guard let strongSelf = self else { + return } + let _ = (strongSelf.account.postbox.transaction { transaction -> RenderedPeer? in + guard let peer = transaction.getPeer(peerId) else { + return nil + } + if let associatedPeerId = peer.associatedPeerId { + if let associatedPeer = transaction.getPeer(associatedPeerId) { + return RenderedPeer(peerId: peerId, peers: SimpleDictionary([peer.id: peer, associatedPeer.id: associatedPeer])) + } else { + return nil + } + } else { + return RenderedPeer(peer: peer) + } + } + |> deliverOnMainQueue).start(next: { peer in + guard let strongSelf = self, let peer = peer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else { + return + } + let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme) + var items: [ActionSheetItem] = [] + var canClear = true + var canStop = false + + var deleteTitle = strongSelf.presentationData.strings.Common_Delete + if let channel = chatPeer as? TelegramChannel { + if case .broadcast = channel.info { + canClear = false + deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel + } + if let addressName = channel.addressName, !addressName.isEmpty { + canClear = false + } + } else if let user = chatPeer as? TelegramUser, user.botInfo != nil { + canStop = true + deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat + } else if let _ = chatPeer as? TelegramSecretChat { + deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat + } + items.append(DeleteChatPeerActionSheetItem(account: strongSelf.account, peer: mainPeer, strings: strongSelf.presentationData.strings)) + if canClear { + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + guard let strongSelf = self else { + return + } + strongSelf.chatListDisplayNode.chatListNode.updateState({ state in + var state = state + state.pendingClearHistoryPeerIds.insert(peer.peerId) + return state + }) + strongSelf.present(UndoOverlayController(account: strongSelf.account, text: strongSelf.presentationData.strings.Undo_MessagesDeleted, action: { shouldCommit in + guard let strongSelf = self else { + return + } + if shouldCommit { + let _ = clearHistoryInteractively(postbox: strongSelf.account.postbox, peerId: peerId).start(completed: { + guard let strongSelf = self else { + return + } + strongSelf.chatListDisplayNode.chatListNode.updateState({ state in + var state = state + state.pendingClearHistoryPeerIds.remove(peer.peerId) + return state + }) + }) + } else { + strongSelf.chatListDisplayNode.chatListNode.updateState({ state in + var state = state + state.pendingClearHistoryPeerIds.remove(peer.peerId) + return state + }) + } + }), in: .window(.root)) + })) + } + + items.append(ActionSheetButtonItem(title: deleteTitle, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + guard let strongSelf = self else { + return + } + strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) + strongSelf.chatListDisplayNode.chatListNode.updateState({ state in + var state = state + state.pendingRemovalPeerIds.insert(peer.peerId) + return state + }) + strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) + strongSelf.present(UndoOverlayController(account: strongSelf.account, text: strongSelf.presentationData.strings.Undo_ChatDeleted, action: { shouldCommit in + guard let strongSelf = self else { + return + } + if shouldCommit { + strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) + let _ = removePeerChat(postbox: strongSelf.account.postbox, peerId: peerId, reportChatSpam: false).start(completed: { + guard let strongSelf = self else { + return + } + strongSelf.chatListDisplayNode.chatListNode.updateState({ state in + var state = state + state.pendingRemovalPeerIds.remove(peer.peerId) + return state + }) + self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) + }) + } else { + strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) + strongSelf.chatListDisplayNode.chatListNode.updateState({ state in + var state = state + state.pendingRemovalPeerIds.remove(peer.peerId) + return state + }) + self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) + } + }), in: .window(.root)) + })) + + if canStop { + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_DeleteBotConversationConfirmation, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + if let strongSelf = self { + strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) + let _ = removePeerChat(postbox: strongSelf.account.postbox, peerId: peerId, reportChatSpam: false).start(completed: { + self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) + }) + let _ = requestUpdatePeerIsBlocked(account: strongSelf.account, peerId: peer.peerId, isBlocked: true).start() + } + })) + } + + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ]) + ]) + strongSelf.present(actionSheet, in: .window(.root)) + }) } self.chatListDisplayNode.chatListNode.peerSelected = { [weak self] peerId, animated, isAd in @@ -735,6 +756,16 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie } } + override public func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + self.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + }) + } + override public func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) diff --git a/TelegramUI/ChatListItem.swift b/TelegramUI/ChatListItem.swift index 761fe86321..02337c23e9 100644 --- a/TelegramUI/ChatListItem.swift +++ b/TelegramUI/ChatListItem.swift @@ -328,12 +328,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var peer: Peer? switch item.content { - case let .peer(message, peerValue, _, _, _, _, _, _, _): - if let message = message { - peer = messageMainPeer(message) - } else { - peer = peerValue.chatMainPeer - } + case let .peer(_, peerValue, _, _, _, _, _, _, _): + peer = peerValue.chatMainPeer case .groupReference: break } diff --git a/TelegramUI/ChatListNode.swift b/TelegramUI/ChatListNode.swift index 10c82983cf..146aaee603 100644 --- a/TelegramUI/ChatListNode.swift +++ b/TelegramUI/ChatListNode.swift @@ -103,6 +103,7 @@ struct ChatListNodeState: Equatable { var selectedPeerIds: Set var peerInputActivities: ChatListNodePeerInputActivities? var pendingRemovalPeerIds: Set + var pendingClearHistoryPeerIds: Set static func ==(lhs: ChatListNodeState, rhs: ChatListNodeState) -> Bool { if lhs.presentationData !== rhs.presentationData { @@ -123,6 +124,9 @@ struct ChatListNodeState: Equatable { if lhs.pendingRemovalPeerIds != rhs.pendingRemovalPeerIds { return false } + if lhs.pendingClearHistoryPeerIds != rhs.pendingClearHistoryPeerIds { + return false + } return true } } @@ -363,7 +367,7 @@ final class ChatListNode: ListView { self.controlsHistoryPreload = controlsHistoryPreload self.mode = mode - self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: false, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), peerInputActivities: nil, pendingRemovalPeerIds: Set()) + self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: false, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), peerInputActivities: nil, pendingRemovalPeerIds: Set(), pendingClearHistoryPeerIds: Set()) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) self.theme = theme diff --git a/TelegramUI/ChatListNodeEntries.swift b/TelegramUI/ChatListNodeEntries.swift index 00778d5375..0ba0c7a7be 100644 --- a/TelegramUI/ChatListNodeEntries.swift +++ b/TelegramUI/ChatListNodeEntries.swift @@ -233,7 +233,11 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, if state.pendingRemovalPeerIds.contains(index.messageIndex.id.peerId) { continue loop } - result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: message, readState: combinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: false)) + var updatedMessage = message + if state.pendingClearHistoryPeerIds.contains(index.messageIndex.id.peerId) { + updatedMessage = nil + } + result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: updatedMessage, readState: combinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: false)) case let .HoleEntry(hole): result.append(.HoleEntry(hole, theme: state.presentationData.theme)) case let .GroupReferenceEntry(groupId, index, message, topPeers, counters): diff --git a/TelegramUI/ChatListSearchContainerNode.swift b/TelegramUI/ChatListSearchContainerNode.swift index ac5a9bb3a2..62b35a41f6 100644 --- a/TelegramUI/ChatListSearchContainerNode.swift +++ b/TelegramUI/ChatListSearchContainerNode.swift @@ -527,19 +527,7 @@ private struct ChatListSearchMessagesResult { let messages: [Message] let readStates: [PeerId: CombinedPeerReadState] let hasMore: Bool - - func appending(_ other: ChatListSearchMessagesResult) -> ChatListSearchMessagesResult { - var messages: [Message] = self.messages - var ids = Set(self.messages.map { $0.id }) - for message in other.messages { - if !ids.contains(message.id) { - ids.insert(message.id) - messages.append(message) - } - } - messages.sort(by: { MessageIndex($0) > MessageIndex($1) }) - return ChatListSearchMessagesResult(query: query, messages: messages, readStates: self.readStates.merging(other.readStates, uniquingKeysWith: { left, _ in left }), hasMore: other.hasMore) - } + let state: SearchMessagesState } private struct ChatListSearchMessagesContext { @@ -700,22 +688,22 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { if filter.contains(.doNotSearchMessages) { foundRemoteMessages = .single((([], [:], 0), false)) } else { - let searchSignal = searchMessages(account: account, location: location, query: query, limit: 50) - |> map { result -> ChatListSearchMessagesResult in - return ChatListSearchMessagesResult(query: query, messages: result.0.sorted(by: { MessageIndex($0) > MessageIndex($1) }), readStates: result.1, hasMore: result.2 != 0) + let searchSignal = searchMessages(account: account, location: location, query: query, state: nil, limit: 50) + |> map { result, updatedState -> ChatListSearchMessagesResult in + return ChatListSearchMessagesResult(query: query, messages: result.messages.sorted(by: { MessageIndex($0) > MessageIndex($1) }), readStates: result.readStates, hasMore: !result.completed, state: updatedState) } let loadMore = searchContext.get() |> mapToSignal { searchContext -> Signal<(([Message], [PeerId: CombinedPeerReadState], Int32), Bool), NoError> in if let searchContext = searchContext { - if let loadMoreIndex = searchContext.loadMoreIndex { - return searchMessages(account: account, location: location, query: query, lowerBound: loadMoreIndex, limit: 80) - |> map { result -> ChatListSearchMessagesResult in - return ChatListSearchMessagesResult(query: query, messages: result.0.sorted(by: { MessageIndex($0) > MessageIndex($1) }), readStates: result.1, hasMore: result.2 != 0) + if let _ = searchContext.loadMoreIndex { + return searchMessages(account: account, location: location, query: query, state: searchContext.result.state, limit: 80) + |> map { result, updatedState -> ChatListSearchMessagesResult in + return ChatListSearchMessagesResult(query: query, messages: result.messages.sorted(by: { MessageIndex($0) > MessageIndex($1) }), readStates: result.readStates, hasMore: !result.completed, state: updatedState) } |> mapToSignal { foundMessages -> Signal<(([Message], [PeerId: CombinedPeerReadState], Int32), Bool), NoError> in updateSearchContext { previous in - let updated = ChatListSearchMessagesContext(result: previous?.result.appending(foundMessages) ?? foundMessages, loadMoreIndex: nil) + let updated = ChatListSearchMessagesContext(result: foundMessages, loadMoreIndex: nil) return (updated, true) } return .complete() diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index cbabf3c179..a0c195587a 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -1779,10 +1779,14 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { override func updateHiddenMedia() { var hasHiddenMosaicStatus = false + var hasHiddenBackground = false if let item = self.item { for contentNode in self.contentNodes { if let contentItem = contentNode.item { if contentNode.updateHiddenMedia(item.controllerInteraction.hiddenMedia[contentItem.message.id]) { + if self.contentNodes.count == 1 && self.nameNode == nil && self.adminBadgeNode == nil && self.forwardInfoNode == nil && self.replyInfoNode == nil { + hasHiddenBackground = true + } if let mosaicStatusNode = self.mosaicStatusNode, mosaicStatusNode.frame.intersects(contentNode.frame) { hasHiddenMosaicStatus = true } @@ -1801,6 +1805,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { } } } + + //self.backgroundNode.isHidden = hasHiddenBackground } override func updateAutomaticMediaDownloadSettings() { diff --git a/TelegramUI/ChatPresentationInterfaceState.swift b/TelegramUI/ChatPresentationInterfaceState.swift index 4259ee11fb..c7d99fce1e 100644 --- a/TelegramUI/ChatPresentationInterfaceState.swift +++ b/TelegramUI/ChatPresentationInterfaceState.swift @@ -191,8 +191,9 @@ enum ChatTitlePanelContext: Equatable, Comparable { struct ChatSearchResultsState: Equatable { let messageIndices: [MessageIndex] let currentId: MessageId? + let state: SearchMessagesState let totalCount: Int32 - let complete: Bool + let completed: Bool } enum ChatSearchDomain: Equatable { diff --git a/TelegramUI/ChatRecentActionsHistoryTransition.swift b/TelegramUI/ChatRecentActionsHistoryTransition.swift index 5584240349..58927b8ff7 100644 --- a/TelegramUI/ChatRecentActionsHistoryTransition.swift +++ b/TelegramUI/ChatRecentActionsHistoryTransition.swift @@ -785,7 +785,6 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { (.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls), (.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers), (.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages), - (.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls), (.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo) ] diff --git a/TelegramUI/ChatSearchInputPanelNode.swift b/TelegramUI/ChatSearchInputPanelNode.swift index 0bc0360d06..1d855a4075 100644 --- a/TelegramUI/ChatSearchInputPanelNode.swift +++ b/TelegramUI/ChatSearchInputPanelNode.swift @@ -121,7 +121,7 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { var resultsText: NSAttributedString? if let results = interfaceState.search?.resultsState { resultCount = results.messageIndices.count - let displayTotalCount = results.complete ? results.messageIndices.count : Int(results.totalCount) + let displayTotalCount = results.completed ? results.messageIndices.count : Int(results.totalCount) if let currentId = results.currentId, let index = results.messageIndices.index(where: { $0.id == currentId }) { let adjustedIndex = results.messageIndices.count - 1 - index resultIndex = index diff --git a/TelegramUI/ChatSearchState.swift b/TelegramUI/ChatSearchState.swift index 671715622c..bf886b8000 100644 --- a/TelegramUI/ChatSearchState.swift +++ b/TelegramUI/ChatSearchState.swift @@ -5,5 +5,5 @@ import TelegramCore struct ChatSearchState: Equatable { let query: String let location: SearchMessagesLocation - let loadMoreIndex: MessageIndex? + let loadMoreState: SearchMessagesState? } diff --git a/TelegramUI/FFMpegMediaFrameSource.swift b/TelegramUI/FFMpegMediaFrameSource.swift index 365a44d3cd..aa7c8da58a 100644 --- a/TelegramUI/FFMpegMediaFrameSource.swift +++ b/TelegramUI/FFMpegMediaFrameSource.swift @@ -89,6 +89,8 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource { localStorage["FFMpegMediaFrameSourceContext"] = context taskQueue.loop() + + Thread.current.threadDictionary.removeObject(forKey: "FFMpegMediaFrameSourceContext") } } diff --git a/TelegramUI/FFMpegMediaFrameSourceContext.swift b/TelegramUI/FFMpegMediaFrameSourceContext.swift index 9b7e75d745..0e94c4429a 100644 --- a/TelegramUI/FFMpegMediaFrameSourceContext.swift +++ b/TelegramUI/FFMpegMediaFrameSourceContext.swift @@ -55,6 +55,8 @@ struct FFMpegMediaFrameSourceContextInfo { let videoStream: FFMpegMediaFrameSourceStreamContextInfo? } +private var maxOffset: Int = 0 + private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer?, bufferSize: Int32) -> Int32 { let context = Unmanaged.fromOpaque(userData!).takeUnretainedValue() guard let postbox = context.postbox, let resourceReference = context.resourceReference, let streamable = context.streamable else { @@ -65,6 +67,11 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa var fetchedData: Data? + #if DEBUG + maxOffset = max(maxOffset, context.readingOffset + Int(bufferSize)) + print("maxOffset \(maxOffset)") + #endif + if streamable { let data: Signal let resourceSize: Int = resourceReference.resource.size ?? Int(Int32.max - 1) @@ -217,7 +224,7 @@ final class FFMpegMediaFrameSourceContext: NSObject { fileprivate var streamable: Bool? fileprivate var statsCategory: MediaResourceStatsCategory? - private let ioBufferSize = 64 * 1024 + private let ioBufferSize = 1 * 1024 fileprivate var readingOffset = 0 fileprivate var requestedDataOffset: Int? diff --git a/TelegramUI/FetchManager.swift b/TelegramUI/FetchManager.swift index 4236993298..6c1617e6a9 100644 --- a/TelegramUI/FetchManager.swift +++ b/TelegramUI/FetchManager.swift @@ -185,10 +185,11 @@ private final class FetchManagerCategoryContext { let entryCompleted = self.entryCompleted let storeManager = self.storeManager activeContext.disposable = (fetchedMediaResource(postbox: self.postbox, reference: entry.resourceReference, statsCategory: entry.statsCategory, reportResultStatus: true, continueInBackground: entry.userInitiated) - |> mapToSignal { type -> Signal in + |> mapToSignal { type -> Signal in if let storeManager = storeManager, let mediaReference = entry.mediaReference, case .remote = type, let peerType = entry.storeToDownloadsPeerType { return storeDownloadedMedia(storeManager: storeManager, media: mediaReference, peerType: peerType) - |> mapToSignal { _ -> Signal in + |> introduceError(FetchResourceError.self) + |> mapToSignal { _ -> Signal in return .complete() } |> then(.single(type)) @@ -266,10 +267,11 @@ private final class FetchManagerCategoryContext { let entryCompleted = self.entryCompleted let storeManager = self.storeManager activeContext.disposable = (fetchedMediaResource(postbox: self.postbox, reference: entry.resourceReference, statsCategory: entry.statsCategory, reportResultStatus: true, continueInBackground: entry.userInitiated) - |> mapToSignal { type -> Signal in + |> mapToSignal { type -> Signal in if let storeManager = storeManager, let mediaReference = entry.mediaReference, case .remote = type, let peerType = entry.storeToDownloadsPeerType { return storeDownloadedMedia(storeManager: storeManager, media: mediaReference, peerType: peerType) - |> mapToSignal { _ -> Signal in + |> introduceError(FetchResourceError.self) + |> mapToSignal { _ -> Signal in return .complete() } |> then(.single(type)) diff --git a/TelegramUI/FetchMediaUtils.swift b/TelegramUI/FetchMediaUtils.swift index 6501f1f7ee..0fc55c2b56 100644 --- a/TelegramUI/FetchMediaUtils.swift +++ b/TelegramUI/FetchMediaUtils.swift @@ -3,11 +3,11 @@ import TelegramCore import Postbox import SwiftSignalKit -public func freeMediaFileInteractiveFetched(account: Account, fileReference: FileMediaReference) -> Signal { +public func freeMediaFileInteractiveFetched(account: Account, fileReference: FileMediaReference) -> Signal { return fetchedMediaResource(postbox: account.postbox, reference: fileReference.resourceReference(fileReference.media.resource)) } -func freeMediaFileResourceInteractiveFetched(account: Account, fileReference: FileMediaReference, resource: MediaResource) -> Signal { +func freeMediaFileResourceInteractiveFetched(account: Account, fileReference: FileMediaReference, resource: MediaResource) -> Signal { return fetchedMediaResource(postbox: account.postbox, reference: fileReference.resourceReference(resource)) } diff --git a/TelegramUI/FetchVideoMediaResource.swift b/TelegramUI/FetchVideoMediaResource.swift index c51e32e85e..a4311efefb 100644 --- a/TelegramUI/FetchVideoMediaResource.swift +++ b/TelegramUI/FetchVideoMediaResource.swift @@ -2,6 +2,7 @@ import Foundation import Postbox import SwiftSignalKit import LegacyComponents +import FFMpeg private final class AVURLAssetCopyItem: MediaResourceDataFetchCopyLocalItem { private let url: URL @@ -79,6 +80,8 @@ public func fetchVideoLibraryMediaResource(resource: VideoLibraryMediaResource) if stat(asset.url.path, &value) == 0 { subscriber.putNext(.copyLocalItem(AVURLAssetCopyItem(url: asset.url))) subscriber.putCompletion() + } else { + subscriber.putError(.generic) } return } else { @@ -110,6 +113,14 @@ public func fetchVideoLibraryMediaResource(resource: VideoLibraryMediaResource) if let result = next as? TGMediaVideoConversionResult { var value = stat() if stat(result.fileURL.path, &value) == 0 { + let tempFile = TempBox.shared.tempFile(fileName: "video.mp4") + if FFMpegRemuxer.remux(result.fileURL.path, to: tempFile.path) { + let _ = try? FileManager.default.removeItem(atPath: result.fileURL.path) + subscriber.putNext(.moveTempFile(file: tempFile)) + } else { + TempBox.shared.dispose(tempFile) + subscriber.putNext(.moveLocalFile(path: result.fileURL.path)) + } /*if let data = try? Data(contentsOf: result.fileURL, options: [.mappedRead]) { var range: Range? let _ = updatedSize.modify { updatedSize in @@ -122,10 +133,13 @@ public func fetchVideoLibraryMediaResource(resource: VideoLibraryMediaResource) subscriber.putNext(.dataPart(resourceOffset: data.count, data: Data(), range: 0 ..< 0, complete: true)) }*/ subscriber.putNext(.moveLocalFile(path: result.fileURL.path)) + } else { + subscriber.putError(.generic) } subscriber.putCompletion() } }, error: { _ in + subscriber.putError(.generic) }, completed: nil) disposable.set(ActionDisposable { signalDisposable?.dispose() diff --git a/TelegramUI/GroupStickerPackCurrentItem.swift b/TelegramUI/GroupStickerPackCurrentItem.swift index a6d0ddec17..b12393fd0a 100644 --- a/TelegramUI/GroupStickerPackCurrentItem.swift +++ b/TelegramUI/GroupStickerPackCurrentItem.swift @@ -223,7 +223,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode { } var updatedImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? - var updatedFetchSignal: Signal? + var updatedFetchSignal: Signal? if fileUpdated { if let file = file { updatedImageSignal = chatMessageSticker(account: item.account, file: file, small: false) diff --git a/TelegramUI/HashtagSearchController.swift b/TelegramUI/HashtagSearchController.swift index e2c9507366..0e6ac12941 100644 --- a/TelegramUI/HashtagSearchController.swift +++ b/TelegramUI/HashtagSearchController.swift @@ -36,11 +36,11 @@ final class HashtagSearchController: TelegramController { let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations) let location: SearchMessagesLocation = .general - let search = searchMessages(account: account, location: location, query: query) + let search = searchMessages(account: account, location: location, query: query, state: nil) let foundMessages: Signal<[ChatListSearchEntry], NoError> = search - |> map { result in - return result.0.map({ .message($0, result.1[$0.id.peerId], chatListPresentationData) }) - } + |> map { result, _ in + return result.messages.map({ .message($0, result.readStates[$0.id.peerId], chatListPresentationData) }) + } let interaction = ChatListNodeInteraction(activateSearch: { }, peerSelected: { peer in }, togglePeerSelected: { _ in diff --git a/TelegramUI/ItemListStickerPackItem.swift b/TelegramUI/ItemListStickerPackItem.swift index 7578e4dcd4..db9741d040 100644 --- a/TelegramUI/ItemListStickerPackItem.swift +++ b/TelegramUI/ItemListStickerPackItem.swift @@ -333,7 +333,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { } var updatedImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? - var updatedFetchSignal: Signal? + var updatedFetchSignal: Signal? if fileUpdated { if let file = file { updatedImageSignal = chatMessageSticker(account: item.account, file: file, small: false) diff --git a/TelegramUI/MergedItemListItem.swift b/TelegramUI/MergedItemListItem.swift index 9a30829567..8b13789179 100644 --- a/TelegramUI/MergedItemListItem.swift +++ b/TelegramUI/MergedItemListItem.swift @@ -1,3 +1 @@ -import Foundation - diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index 11516c0fe1..fe24012b90 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -28,7 +28,7 @@ func chatMessagePhotoDatas(postbox: Postbox, photoReference: ImageMediaReference return .single((nil, loadedData, true)) } else { let decodedThumbnailData = photoReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) - let fetchedThumbnail: Signal + let fetchedThumbnail: Signal if let _ = decodedThumbnailData { fetchedThumbnail = .complete() } else { @@ -138,7 +138,7 @@ private func chatMessageFileDatas(account: Account, fileReference: FileMediaRefe if maybeData.complete { return .single((nil, maybeData.path, true)) } else { - let fetchedThumbnail: Signal + let fetchedThumbnail: Signal if !fetched, let _ = decodedThumbnailData { fetchedThumbnail = .single(.local) } else if let thumbnailResource = thumbnailResource { @@ -201,7 +201,7 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference: if let decodedThumbnailData = decodedThumbnailData { return .single((decodedThumbnailData, nil, false)) } else if let thumbnailResource = thumbnailResource { - let fetchedThumbnail: Signal = fetchedMediaResource(postbox: account.postbox, reference: fileReference.resourceReference(thumbnailResource)) + let fetchedThumbnail: Signal = fetchedMediaResource(postbox: account.postbox, reference: fileReference.resourceReference(thumbnailResource)) return Signal { subscriber in let fetchedDisposable = fetchedThumbnail.start() let thumbnailDisposable = account.postbox.mediaBox.resourceData(thumbnailResource, pathExtension: pathExtension).start(next: { next in @@ -233,7 +233,7 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference: if maybeData.complete { return .single((nil, maybeData.path, true)) } else { - let fetchedThumbnail: Signal + let fetchedThumbnail: Signal if let _ = fileReference.media.immediateThumbnailData { fetchedThumbnail = .complete() } else if let thumbnailResource = thumbnailResource { @@ -1690,13 +1690,14 @@ func chatMessagePhotoStatus(account: Account, messageId: MessageId, photoReferen } } -public func chatMessagePhotoInteractiveFetched(account: Account, photoReference: ImageMediaReference, storeToDownloadsPeerType: AutomaticMediaDownloadPeerType?) -> Signal { +public func chatMessagePhotoInteractiveFetched(account: Account, photoReference: ImageMediaReference, storeToDownloadsPeerType: AutomaticMediaDownloadPeerType?) -> Signal { if let largestRepresentation = largestRepresentationForPhoto(photoReference.media) { return fetchedMediaResource(postbox: account.postbox, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image, reportResultStatus: true) - |> mapToSignal { type -> Signal in + |> mapToSignal { type -> Signal in if case .remote = type, let peerType = storeToDownloadsPeerType { return storeDownloadedMedia(storeManager: account.telegramApplicationContext.mediaManager?.downloadedMediaStoreManager, media: photoReference.abstract, peerType: peerType) - |> mapToSignal { _ -> Signal in + |> introduceError(FetchResourceError.self) + |> mapToSignal { _ -> Signal in return .complete() } |> then(.single(type)) @@ -1714,7 +1715,7 @@ func chatMessagePhotoCancelInteractiveFetch(account: Account, photoReference: Im } } -func chatMessageWebFileInteractiveFetched(account: Account, image: TelegramMediaWebFile) -> Signal { +func chatMessageWebFileInteractiveFetched(account: Account, image: TelegramMediaWebFile) -> Signal { return fetchedMediaResource(postbox: account.postbox, reference: .standalone(resource: image.resource), statsCategory: .image) } @@ -2208,7 +2209,7 @@ private func avatarGalleryPhotoDatas(account: Account, fileReference: FileMediaR let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: []) return .single((nil, loadedData, true)) } else { - let fetchedThumbnail: Signal + let fetchedThumbnail: Signal if let _ = decodedThumbnailData { fetchedThumbnail = .complete() } else { diff --git a/TelegramUI/PresentationResourcesItemList.swift b/TelegramUI/PresentationResourcesItemList.swift index ef402ea17e..67f3d6be48 100644 --- a/TelegramUI/PresentationResourcesItemList.swift +++ b/TelegramUI/PresentationResourcesItemList.swift @@ -73,7 +73,15 @@ struct PresentationResourcesItemList { static func itemListDeleteIndicatorIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.itemListDeleteIndicatorIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Item List/RemoveItemIcon"), color: theme.list.itemDestructiveColor) + guard let image = generateTintedImage(image: UIImage(bundleImageName: "Item List/RemoveItemIcon"), color: theme.list.itemDestructiveColor) else { + return nil + } + return generateImage(image.size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 2, y: 2), size: CGSize(width: size.width - 4.0, height: size.height - 4.0))) + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) + }) }) } @@ -104,7 +112,15 @@ struct PresentationResourcesItemList { static func addPhoneIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.itemListAddPhoneIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Item List/AddItemIcon"), color: theme.list.itemAccentColor) + guard let image = generateTintedImage(image: UIImage(bundleImageName: "Item List/AddItemIcon"), color: theme.list.itemAccentColor) else { + return nil + } + return generateImage(image.size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 2, y: 2), size: CGSize(width: size.width - 4.0, height: size.height - 4.0))) + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) + }) }) } diff --git a/TelegramUI/SecureIdLocalResource.swift b/TelegramUI/SecureIdLocalResource.swift index 9d35536a92..0248b8d091 100644 --- a/TelegramUI/SecureIdLocalResource.swift +++ b/TelegramUI/SecureIdLocalResource.swift @@ -85,6 +85,13 @@ func fetchSecureIdLocalImageResource(postbox: Postbox, resource: SecureIdLocalIm } let _ = try? FileManager.default.removeItem(atPath: path) } + case let .moveTempFile(file): + if let data = try? Data(contentsOf: URL(fileURLWithPath: file.path)) { + let _ = buffer.with { buffer in + buffer.data = data + } + } + TempBox.shared.dispose(file) case .copyLocalItem: assertionFailure() break diff --git a/TelegramUI/SharedMediaPlayer.swift b/TelegramUI/SharedMediaPlayer.swift index d000f1a509..0331f313ee 100644 --- a/TelegramUI/SharedMediaPlayer.swift +++ b/TelegramUI/SharedMediaPlayer.swift @@ -729,6 +729,9 @@ final class SharedMediaPlayer { case let .telegramFile(file): fetchedNextSignal = fetchedMediaResource(postbox: self.postbox, reference: file.resourceReference(file.media.resource)) |> ignoreValues + |> `catch` { _ -> Signal in + return .complete() + } } self.prefetchDisposable.set((fetchedCurrentSignal |> then(fetchedNextSignal)).start()) } else { diff --git a/TelegramUI/StorageUsageController.swift b/TelegramUI/StorageUsageController.swift index 5e31d86b2d..2c908fbb1c 100644 --- a/TelegramUI/StorageUsageController.swift +++ b/TelegramUI/StorageUsageController.swift @@ -197,14 +197,24 @@ private func storageUsageControllerEntries(presentationData: PresentationData, c var peerSizes: Int64 = 0 var statsByPeerId: [(PeerId, Int64)] = [] + var peerIndices: [PeerId: Int] = [:] for (peerId, categories) in stats.media { + var updatedPeerId = peerId + if let group = stats.peers[peerId] as? TelegramGroup, let migrationReference = group.migrationReference, let channel = stats.peers[migrationReference.peerId] { + updatedPeerId = channel.id + } var combinedSize: Int64 = 0 for (_, media) in categories { for (_, size) in media { combinedSize += size } } - statsByPeerId.append((peerId, combinedSize)) + if let index = peerIndices[updatedPeerId] { + statsByPeerId[index].1 += combinedSize + } else { + peerIndices[updatedPeerId] = statsByPeerId.count + statsByPeerId.append((updatedPeerId, combinedSize)) + } peerSizes += combinedSize } @@ -527,7 +537,23 @@ func storageUsageController(account: Account, isModal: Bool = false) -> ViewCont }, openPeerMedia: { peerId in let _ = (statsPromise.get() |> take(1) |> deliverOnMainQueue).start(next: { [weak statsPromise] result in if let result = result, case let .result(stats) = result { - if let categories = stats.media[peerId] { + var additionalPeerId: PeerId? + if var categories = stats.media[peerId] { + if let channel = stats.peers[peerId] as? TelegramChannel, case .group = channel.info { + for (_, peer) in stats.peers { + if let group = peer as? TelegramGroup, let migrationReference = group.migrationReference, migrationReference.peerId == peerId { + if let additionalCategories = stats.media[group.id] { + additionalPeerId = group.id + categories.merge(additionalCategories, uniquingKeysWith: { lhs, rhs in + return lhs.merging(rhs, uniquingKeysWith: { lhs, rhs in + return lhs + rhs + }) + }) + } + } + } + } + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let controller = ActionSheetController(presentationTheme: presentationData.theme) let dismissAction: () -> Void = { [weak controller] in @@ -611,6 +637,20 @@ func storageUsageController(account: Account, isModal: Bool = false) -> ViewCont media[peerId] = categories } + if let additionalPeerId = additionalPeerId { + if var categories = media[additionalPeerId] { + for category in clearCategories { + if let contents = categories[category] { + for (mediaId, _) in contents { + clearMediaIds.insert(mediaId) + } + } + categories.removeValue(forKey: category) + } + + media[additionalPeerId] = categories + } + } var clearResourceIds = Set() for id in clearMediaIds { diff --git a/TelegramUI/UndoOverlayController.swift b/TelegramUI/UndoOverlayController.swift index 67a80cbcaf..ec621ac4c7 100644 --- a/TelegramUI/UndoOverlayController.swift +++ b/TelegramUI/UndoOverlayController.swift @@ -32,6 +32,11 @@ final class UndoOverlayController: ViewController { self.displayNodeDidLoad() } + func dismissWithCommitAction() { + self.action(true) + self.dismiss() + } + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) diff --git a/TelegramUI/UndoOverlayControllerNode.swift b/TelegramUI/UndoOverlayControllerNode.swift index a157a85555..1d077e59c7 100644 --- a/TelegramUI/UndoOverlayControllerNode.swift +++ b/TelegramUI/UndoOverlayControllerNode.swift @@ -113,17 +113,17 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { transition.updateFrame(node: self.panelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight))) let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) - let buttonTextFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.right - rightInset - buttonTextSize.width, y: floor((panelHeight - buttonTextSize.height) / 2.0)), size: buttonTextSize) + let buttonTextFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.right - rightInset - buttonTextSize.width, y: floor((contentHeight - buttonTextSize.height) / 2.0)), size: buttonTextSize) transition.updateFrame(node: self.buttonTextNode, frame: buttonTextFrame) self.buttonNode.frame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.right - rightInset - buttonTextSize.width - 8.0, y: 0.0), size: CGSize(width: layout.safeInsets.right + rightInset + buttonTextSize.width + 8.0, height: contentHeight)) - let textSize = self.textNode.updateLayout(CGSize(width: buttonTextFrame.minX - 8.0 - leftInset, height: .greatestFiniteMagnitude)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: leftInset, y: floor((panelHeight - textSize.height) / 2.0)), size: textSize)) + let textSize = self.textNode.updateLayout(CGSize(width: buttonTextFrame.minX - 8.0 - leftInset - layout.safeInsets.left - layout.safeInsets.right, height: .greatestFiniteMagnitude)) + transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + leftInset, y: floor((contentHeight - textSize.height) / 2.0)), size: textSize)) let timerTextSize = self.timerTextNode.updateLayout(CGSize(width: 100.0, height: 100.0)) - transition.updateFrame(node: self.timerTextNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((leftInset - timerTextSize.width) / 2.0), y: floor((panelHeight - timerTextSize.height) / 2.0)), size: timerTextSize)) + transition.updateFrame(node: self.timerTextNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((leftInset - timerTextSize.width) / 2.0), y: floor((contentHeight - timerTextSize.height) / 2.0)), size: timerTextSize)) let statusSize: CGFloat = 30.0 - transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((leftInset - statusSize) / 2.0), y: floor((panelHeight - statusSize) / 2.0)), size: CGSize(width: statusSize, height: statusSize))) + transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((leftInset - statusSize) / 2.0), y: floor((contentHeight - statusSize) / 2.0)), size: CGSize(width: statusSize, height: statusSize))) if firstLayout { self.statusNode.transitionToState(.secretTimeout(color: .white, icon: nil, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {}) } diff --git a/TelegramUI/WallpaperListPreviewControllerNode.swift b/TelegramUI/WallpaperListPreviewControllerNode.swift index e5b9cf4c7a..bef5728a68 100644 --- a/TelegramUI/WallpaperListPreviewControllerNode.swift +++ b/TelegramUI/WallpaperListPreviewControllerNode.swift @@ -46,7 +46,7 @@ private final class WallpaperBackgroundNode: ASDisplayNode { self.addSubnode(self.statusNode) let signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError> - let fetchSignal: Signal + let fetchSignal: Signal let statusSignal: Signal let displaySize: CGSize switch wallpaper {