From 2c52ee06dee0a0752a0480053f29db26628c8a2e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 26 Jul 2020 12:50:06 +0300 Subject: [PATCH 1/9] Allow inline media playback in games --- .../GameUI/Sources/GameControllerNode.swift | 10 ++++++++++ .../Sources/WebEmbedPlayerNode.swift | 16 ++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/submodules/GameUI/Sources/GameControllerNode.swift b/submodules/GameUI/Sources/GameControllerNode.swift index 91d89e57bf..668c683ccc 100644 --- a/submodules/GameUI/Sources/GameControllerNode.swift +++ b/submodules/GameUI/Sources/GameControllerNode.swift @@ -62,6 +62,16 @@ final class GameControllerNode: ViewControllerTracingNode { }, name: "performAction") configuration.userContentController = userController + + configuration.allowsInlineMediaPlayback = true + if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { + configuration.mediaTypesRequiringUserActionForPlayback = [] + } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { + configuration.requiresUserActionForMediaPlayback = false + } else { + configuration.mediaPlaybackRequiresUserAction = false + } + let webView = WKWebView(frame: CGRect(), configuration: configuration) if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { webView.allowsLinkPreview = false diff --git a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift index aa1329692a..40cf0fec67 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift @@ -88,24 +88,24 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate { let userContentController = WKUserContentController() userContentController.addUserScript(WKUserScript(source: "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta)", injectionTime: .atDocumentEnd, forMainFrameOnly: true)) - let config = WKWebViewConfiguration() - config.allowsInlineMediaPlayback = true - config.userContentController = userContentController + let configuration = WKWebViewConfiguration() + configuration.allowsInlineMediaPlayback = true + configuration.userContentController = userContentController if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - config.mediaTypesRequiringUserActionForPlayback = [] + configuration.mediaTypesRequiringUserActionForPlayback = [] } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - config.requiresUserActionForMediaPlayback = false + configuration.requiresUserActionForMediaPlayback = false } else { - config.mediaPlaybackRequiresUserAction = false + configuration.mediaPlaybackRequiresUserAction = false } if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - config.allowsPictureInPictureMediaPlayback = false + configuration.allowsPictureInPictureMediaPlayback = false } let frame = CGRect(origin: CGPoint.zero, size: intrinsicDimensions) - self.webView = WKWebView(frame: frame, configuration: config) + self.webView = WKWebView(frame: frame, configuration: configuration) super.init() self.frame = frame From 95b4d581cd3c174587492e00ef4a75c0e4ff510e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 27 Jul 2020 18:26:36 +0300 Subject: [PATCH 2/9] Fix item list scrolling for touches outside section blocks --- submodules/Display/Source/ListView.swift | 106 ++++++++++-------- .../Sources/ItemListControllerNode.swift | 10 ++ 2 files changed, 67 insertions(+), 49 deletions(-) diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index a548fd1bf5..faf1fefcfb 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -184,6 +184,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture public final var keepMinimalScrollHeightWithTopInset: CGFloat? + public final var itemNodeHitTest: ((CGPoint) -> Bool)? + public final var stackFromBottom: Bool = false public final var stackFromBottomInsetItemFactor: CGFloat = 0.0 public final var limitHitTestToNodes: Bool = false @@ -3876,67 +3878,73 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } self.touchesPosition = touchesPosition - self.selectionTouchLocation = touches.first!.location(in: self.view) - self.selectionTouchDelayTimer?.invalidate() - self.selectionLongTapDelayTimer?.invalidate() - self.selectionLongTapDelayTimer = nil - let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in - if let strongSelf = self, strongSelf.selectionTouchLocation != nil { - strongSelf.clearHighlightAnimated(false) - - if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) { - var canBeSelectedOrLongTapped = false - for itemNode in strongSelf.itemNodes { - if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped { - canBeSelectedOrLongTapped = true - } - } + var processSelection = true + if let itemNodeHitTest = self.itemNodeHitTest, !itemNodeHitTest(touchesPosition) { + processSelection = false + } + + if processSelection { + self.selectionTouchLocation = touches.first!.location(in: self.view) + self.selectionTouchDelayTimer?.invalidate() + self.selectionLongTapDelayTimer?.invalidate() + self.selectionLongTapDelayTimer = nil + let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in + if let strongSelf = self, strongSelf.selectionTouchLocation != nil { + strongSelf.clearHighlightAnimated(false) - if canBeSelectedOrLongTapped { - strongSelf.highlightedItemIndex = index + if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) { + var canBeSelectedOrLongTapped = false for itemNode in strongSelf.itemNodes { - if itemNode.index == index && itemNode.canBeSelected { - if true { - if !itemNode.isLayerBacked { - strongSelf.reorderItemNodeToFront(itemNode) - for (_, headerNode) in strongSelf.itemHeaderNodes { - strongSelf.reorderHeaderNodeToFront(headerNode) + if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped { + canBeSelectedOrLongTapped = true + } + } + + if canBeSelectedOrLongTapped { + strongSelf.highlightedItemIndex = index + for itemNode in strongSelf.itemNodes { + if itemNode.index == index && itemNode.canBeSelected { + if true { + if !itemNode.isLayerBacked { + strongSelf.reorderItemNodeToFront(itemNode) + for (_, headerNode) in strongSelf.itemHeaderNodes { + strongSelf.reorderHeaderNodeToFront(headerNode) + } } - } - let itemNodeFrame = itemNode.frame - let itemNodeBounds = itemNode.bounds - if strongSelf.items[index].selectable { - itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false) - } - - if itemNode.canBeLongTapped { - let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy { - if let strongSelf = self, strongSelf.highlightedItemIndex == index { - for itemNode in strongSelf.itemNodes { - if itemNode.index == index && itemNode.canBeLongTapped { - itemNode.longTapped() - strongSelf.clearHighlightAnimated(true) - strongSelf.selectionTouchLocation = nil - break + let itemNodeFrame = itemNode.frame + let itemNodeBounds = itemNode.bounds + if strongSelf.items[index].selectable { + itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false) + } + + if itemNode.canBeLongTapped { + let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy { + if let strongSelf = self, strongSelf.highlightedItemIndex == index { + for itemNode in strongSelf.itemNodes { + if itemNode.index == index && itemNode.canBeLongTapped { + itemNode.longTapped() + strongSelf.clearHighlightAnimated(true) + strongSelf.selectionTouchLocation = nil + break + } } } - } - }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) - strongSelf.selectionLongTapDelayTimer = timer - RunLoop.main.add(timer, forMode: RunLoop.Mode.common) + }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) + strongSelf.selectionLongTapDelayTimer = timer + RunLoop.main.add(timer, forMode: RunLoop.Mode.common) + } } + break } - break } } } } - } - }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) - self.selectionTouchDelayTimer = timer - RunLoop.main.add(timer, forMode: RunLoop.Mode.common) - + }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) + self.selectionTouchDelayTimer = timer + RunLoop.main.add(timer, forMode: RunLoop.Mode.common) + } super.touchesBegan(touches, with: event) self.updateScroller(transition: .immediate) diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index 21666e9305..59d3be2803 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -238,7 +238,9 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate { self.listNode = ListView() self.leftOverlayNode = ASDisplayNode() + self.leftOverlayNode.isUserInteractionEnabled = false self.rightOverlayNode = ASDisplayNode() + self.rightOverlayNode.isUserInteractionEnabled = false super.init() @@ -302,6 +304,14 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate { let _ = strongSelf.contentScrollingEnded?(strongSelf.listNode) } } + + self.listNode.itemNodeHitTest = { [weak self] point in + if let strongSelf = self { + return point.x > strongSelf.leftOverlayNode.frame.maxX && point.x < strongSelf.rightOverlayNode.frame.minX + } else { + return true + } + } let previousState = Atomic(value: nil) self.transitionDisposable.set(((state From 3fbb7bf303d5a868d83716a47d321062ab4807cc Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jul 2020 16:57:06 +0300 Subject: [PATCH 3/9] Add attribution for Google Places search provider and pass place id when opening Google Maps --- .../Sources/LocationAttributionItem.swift | 22 ++++++++++-- .../LocationPickerControllerNode.swift | 32 +++++++++++------- .../Sources/OpenInOptions.swift | 6 +++- .../GoogleAttribution.imageset/Contents.json | 22 ++++++++++++ .../powered_by_google_on_white@2x.png | Bin 0 -> 4550 bytes .../powered_by_google_on_white@3x.png | Bin 0 -> 6898 bytes 6 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png create mode 100644 submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@3x.png diff --git a/submodules/LocationUI/Sources/LocationAttributionItem.swift b/submodules/LocationUI/Sources/LocationAttributionItem.swift index 6f62515c4c..96f74ee56d 100644 --- a/submodules/LocationUI/Sources/LocationAttributionItem.swift +++ b/submodules/LocationUI/Sources/LocationAttributionItem.swift @@ -8,11 +8,18 @@ import TelegramPresentationData import ItemListUI import AppBundle +enum LocationAttribution: Equatable { + case foursquare + case google +} + class LocationAttributionItem: ListViewItem { let presentationData: ItemListPresentationData + let attribution: LocationAttribution - public init(presentationData: ItemListPresentationData) { + public init(presentationData: ItemListPresentationData, attribution: LocationAttribution) { self.presentationData = presentationData + self.attribution = attribution } public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -93,11 +100,20 @@ private class LocationAttributionItemNode: ListViewItemNode { strongSelf.layoutParams = params if let _ = updatedTheme { - strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/FoursquareAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor) + switch item.attribution { + case .foursquare: + strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/FoursquareAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor) + case .google: + if item.presentationData.theme.overallDarkAppearance { + strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/GoogleAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor) + } else { + strongSelf.imageNode.image = UIImage(bundleImageName: "Location/GoogleAttribution") + } + } } if let image = strongSelf.imageNode.image { - strongSelf.imageNode.frame = CGRect(x: floor((params.width - image.size.width) / 2.0), y: 0.0, width: image.size.width, height: image.size.height) + strongSelf.imageNode.frame = CGRect(x: floor((params.width - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0), width: image.size.width, height: image.size.height) } } }) diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index 03b0ebcdb3..77e5a5db64 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -40,7 +40,7 @@ private enum LocationPickerEntry: Comparable, Identifiable { case liveLocation(PresentationTheme, String, String, CLLocationCoordinate2D?) case header(PresentationTheme, String) case venue(PresentationTheme, TelegramMediaMap, Int) - case attribution(PresentationTheme) + case attribution(PresentationTheme, LocationAttribution) var stableId: LocationPickerEntryId { switch self { @@ -83,8 +83,8 @@ private enum LocationPickerEntry: Comparable, Identifiable { } else { return false } - case let .attribution(lhsTheme): - if case let .attribution(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .attribution(lhsTheme, lhsAttribution): + if case let .attribution(rhsTheme, rhsAttribution) = rhs, lhsTheme === rhsTheme, lhsAttribution == rhsAttribution { return true } else { return false @@ -131,7 +131,7 @@ private enum LocationPickerEntry: Comparable, Identifiable { func item(account: Account, presentationData: PresentationData, interaction: LocationPickerInteraction?) -> ListViewItem { switch self { - case let .location(theme, title, subtitle, venue, coordinate): + case let .location(_, title, subtitle, venue, coordinate): let icon: LocationActionListItemIcon if let venue = venue { icon = .venue(venue) @@ -147,23 +147,23 @@ private enum LocationPickerEntry: Comparable, Identifiable { }, highlighted: { highlighted in interaction?.updateSendActionHighlight(highlighted) }) - case let .liveLocation(theme, title, subtitle, coordinate): + case let .liveLocation(_, title, subtitle, coordinate): return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: .liveLocation, action: { if let coordinate = coordinate { interaction?.sendLiveLocation(coordinate) } }) - case let .header(theme, title): + case let .header(_, title): return LocationSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), title: title) - case let .venue(theme, venue, _): + case let .venue(_, venue, _): let venueType = venue.venue?.type ?? "" return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), account: account, venue: venue, style: .plain, action: { interaction?.sendVenue(venue) }, infoAction: ["home", "work"].contains(venueType) ? { interaction?.openHomeWorkInfo() } : nil) - case let .attribution(theme): - return LocationAttributionItem(presentationData: ItemListPresentationData(presentationData)) + case let .attribution(_, attribution): + return LocationAttributionItem(presentationData: ItemListPresentationData(presentationData), attribution: attribution) } } } @@ -496,15 +496,21 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { entries.append(.header(presentationData.theme, presentationData.strings.Map_ChooseAPlace.uppercased())) - var displayedVenues = foundVenues != nil || state.searchingVenuesAround ? foundVenues : venues + let displayedVenues = foundVenues != nil || state.searchingVenuesAround ? foundVenues : venues if let venues = displayedVenues { var index: Int = 0 + var attribution: LocationAttribution? for venue in venues { + if venue.venue?.provider == "foursquare" { + attribution = .foursquare + } else if venue.venue?.provider == "gplaces" { + attribution = .google + } entries.append(.venue(presentationData.theme, venue, index)) index += 1 } - if !venues.isEmpty { - entries.append(.attribution(presentationData.theme)) + if let attribution = attribution { + entries.append(.attribution(presentationData.theme, attribution)) } } let previousEntries = previousEntries.swap(entries) @@ -574,7 +580,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { if let (layout, navigationBarHeight) = strongSelf.validLayout { var updateLayout = false - var transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring) + let transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring) if previousState.displayingMapModeOptions != state.displayingMapModeOptions { updateLayout = true diff --git a/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift b/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift index 8d77aeeec4..e855eae738 100644 --- a/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift +++ b/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift @@ -181,7 +181,11 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope if withDirections { return .openUrl(url: "comgooglemaps-x-callback://?daddr=\(coordinates)&directionsmode=driving&x-success=telegram://?resume=true&x-source=Telegram") } else { - return .openUrl(url: "comgooglemaps-x-callback://?center=\(coordinates)&q=\(coordinates)&x-success=telegram://?resume=true&x-source=Telegram") + if let venue = location.venue, let venueId = venue.id, let provider = venue.provider, provider == "gplaces" { + return .openUrl(url: "https://www.google.com/maps/search/?api=1&query=\(venue.address ?? "")&query_place_id=\(venueId)") + } else { + return .openUrl(url: "comgooglemaps-x-callback://?center=\(coordinates)&q=\(coordinates)&x-success=telegram://?resume=true&x-source=Telegram") + } } })) diff --git a/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json new file mode 100644 index 0000000000..67b12b6b3f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "powered_by_google_on_white@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "powered_by_google_on_white@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4676cb59bed6aed1afddbf1450e3364a889c578a GIT binary patch literal 4550 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 sR~}1 Date: Tue, 28 Jul 2020 16:58:19 +0300 Subject: [PATCH 4/9] Display user heading in location picker and location view --- .../Sources/TGLocationViewController.m | 47 +++++++++++++++++++ .../LocationUI/Sources/LocationMapNode.swift | 39 +++++++++++++++ .../Sources/LocationPickerController.swift | 2 +- .../LocationPickerControllerNode.swift | 20 ++++++-- 4 files changed, 103 insertions(+), 5 deletions(-) diff --git a/submodules/LegacyComponents/Sources/TGLocationViewController.m b/submodules/LegacyComponents/Sources/TGLocationViewController.m index 97489ab445..dd3ec6ea8c 100644 --- a/submodules/LegacyComponents/Sources/TGLocationViewController.m +++ b/submodules/LegacyComponents/Sources/TGLocationViewController.m @@ -71,6 +71,8 @@ bool _throttle; TGLocationPinAnnotationView *_ownLiveLocationView; __weak MKAnnotationView *_userLocationView; + + UIImageView *_headingArrowView; } @end @@ -162,6 +164,8 @@ [_liveLocationsDisposable dispose]; [_reloadDisposable dispose]; [_frequentUpdatesDisposable dispose]; + + [_locationManager stopUpdatingHeading]; } - (void)tg_setRightBarButtonItem:(UIBarButtonItem *)barButtonItem action:(bool)action animated:(bool)animated { @@ -438,6 +442,36 @@ { [super loadView]; + static UIImage *headingArrowImage = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^ + { + UIGraphicsBeginImageContextWithOptions(CGSizeMake(28.0f, 28.0f), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextClearRect(context, CGRectMake(0, 0, 28, 28)); + + CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor); + + CGContextMoveToPoint(context, 14, 0); + CGContextAddLineToPoint(context, 19, 7); + CGContextAddLineToPoint(context, 9, 7); + CGContextClosePath(context); + CGContextFillPath(context); + + CGContextSetBlendMode(context, kCGBlendModeClear); + CGContextFillEllipseInRect(context, CGRectMake(5.0, 5.0, 18.0, 18.0)); + + headingArrowImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + }); + + _headingArrowView = [[UIImageView alloc] init]; + _headingArrowView.hidden = true; + _headingArrowView.frame = CGRectMake(0.0, 0.0, 28.0, 28.0); + _headingArrowView.image = headingArrowImage; + _tableView.scrollsToTop = false; _mapView.tapEnabled = false; @@ -495,6 +529,8 @@ { [super viewWillAppear:animated]; + [_locationManager startUpdatingHeading]; + if (self.previewMode && !animated) { UIView *contentView = [[_mapView subviews] firstObject]; @@ -950,6 +986,9 @@ { _userLocationView = view; + [_userLocationView addSubview:_headingArrowView]; + _headingArrowView.center = CGPointMake(view.frame.size.width / 2.0, view.frame.size.height / 2.0); + if (_ownLiveLocationView != nil) { [_userLocationView addSubview:_ownLiveLocationView]; @@ -982,6 +1021,14 @@ return CLLocationCoordinate2DMake(_locationAttachment.latitude, _locationAttachment.longitude); } +- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading +{ + if (newHeading != nil) { + _headingArrowView.hidden = false; + _headingArrowView.transform = CGAffineTransformMakeRotation(newHeading.magneticHeading / 180.0 * M_PI); + } +} + #pragma mark - - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section diff --git a/submodules/LocationUI/Sources/LocationMapNode.swift b/submodules/LocationUI/Sources/LocationMapNode.swift index 0576883bb4..c330c2017e 100644 --- a/submodules/LocationUI/Sources/LocationMapNode.swift +++ b/submodules/LocationUI/Sources/LocationMapNode.swift @@ -68,11 +68,30 @@ private class LocationMapView: MKMapView, UIGestureRecognizerDelegate { } } +private func generateHeadingArrowImage() -> UIImage? { + return generateImage(CGSize(width: 28.0, height: 28.0)) { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + context.setFillColor(UIColor(rgb: 0x3393fe).cgColor) + + context.move(to: CGPoint(x: 14.0, y: 0.0)) + context.addLine(to: CGPoint(x: 19.0, y: 7.0)) + context.addLine(to: CGPoint(x: 9.0, y: 7.0)) + context.closePath() + context.fillPath() + + context.setBlendMode(.clear) + context.fillEllipse(in: bounds.insetBy(dx: 5.0, dy: 5.0)) + } +} + final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { private let locationPromise = Promise(nil) private let pickerAnnotationContainerView: PickerAnnotationContainerView private weak var userLocationAnnotationView: MKAnnotationView? + private var headingArrowView: UIImageView? private let pinDisposable = MetaDisposable() @@ -103,6 +122,10 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { override func didLoad() { super.didLoad() + self.headingArrowView = UIImageView() + self.headingArrowView?.frame = CGRect(origin: CGPoint(), size: CGSize(width: 28.0, height: 28.0)) + self.headingArrowView?.image = generateHeadingArrowImage() + self.mapView?.interactiveTransitionGestureRecognizerTest = { p in if p.x > 44.0 { return true @@ -232,6 +255,10 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { for view in views { if view.annotation is MKUserLocation { self.userLocationAnnotationView = view + if let headingArrowView = self.headingArrowView { + view.addSubview(headingArrowView) + headingArrowView.center = CGPoint(x: view.frame.width / 2.0, y: view.frame.height / 2.0) + } if let annotationView = self.customUserLocationAnnotationView { view.addSubview(annotationView) } @@ -347,6 +374,18 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { } } + var userHeading: CGFloat? = nil { + didSet { + if let heading = self.userHeading { + self.headingArrowView?.isHidden = false + self.headingArrowView?.transform = CGAffineTransform(rotationAngle: CGFloat(heading / 180.0 * CGFloat.pi)) + } else { + self.headingArrowView?.isHidden = true + self.headingArrowView?.transform = CGAffineTransform.identity + } + } + } + var annotations: [LocationPinAnnotation] = [] { didSet { guard let mapView = self.mapView else { diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index 1a150f9c18..3602c194f3 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -289,7 +289,7 @@ public final class LocationPickerController: ViewController { return } - self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction) + self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction, locationManager: self.locationManager) self.displayNodeDidLoad() self.permissionDisposable = (DeviceAccess.authorizationStatus(subject: .location(.send)) diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index 77e5a5db64..4aee790229 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -17,6 +17,7 @@ import AppBundle import CoreLocation import Geocoding import PhoneNumberFormat +import DeviceAccess private struct LocationPickerTransaction { let deletions: [ListViewDeleteItem] @@ -240,12 +241,13 @@ struct LocationPickerState { } } -final class LocationPickerControllerNode: ViewControllerTracingNode { +final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationManagerDelegate { private let context: AccountContext private var presentationData: PresentationData private let presentationDataPromise: Promise private let mode: LocationPickerMode private let interaction: LocationPickerInteraction + private let locationManager: LocationManager private let listNode: ListView private let emptyResultsTextNode: ImmediateTextNode @@ -269,12 +271,13 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? private var listOffset: CGFloat? - init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction) { + init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction, locationManager: LocationManager) { self.context = context self.presentationData = presentationData self.presentationDataPromise = Promise(presentationData) self.mode = mode self.interaction = interaction + self.locationManager = locationManager self.state = LocationPickerState() self.statePromise = Promise(self.state) @@ -539,7 +542,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { switch previousState.selectedLocation { case .none, .venue: updateMap = true - case let .location(previousCoordinate, address): + case let .location(previousCoordinate, _): if previousCoordinate != coordinate { updateMap = true } @@ -691,11 +694,20 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { strongSelf.goToUserLocation() } } + + self.locationManager.manager.startUpdatingHeading() + self.locationManager.manager.delegate = self } deinit { self.disposable?.dispose() self.geocodingDisposable.dispose() + + self.locationManager.manager.stopUpdatingHeading() + } + + func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { + self.headerNode.mapNode.userHeading = CGFloat(newHeading.magneticHeading) } func updatePresentationData(_ presentationData: PresentationData) { @@ -727,7 +739,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { } private func dequeueTransition() { - guard let layout = self.validLayout, let transition = self.enqueuedTransitions.first else { + guard let _ = self.validLayout, let transition = self.enqueuedTransitions.first else { return } self.enqueuedTransitions.remove(at: 0) From 45b123de450ccba50120c41cec57bd642ff15324 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jul 2020 17:02:31 +0300 Subject: [PATCH 5/9] Don't display "delete for everyone" on dices sent in previous 24 hours Show single "Delete" button for "joined" and "deleted" peers --- .../Sources/ChatListController.swift | 2 +- .../ChatListUI/Sources/ChatContextMenus.swift | 6 +- .../Sources/ChatListController.swift | 108 +++++++++++------- .../Sources/ChatListControllerNode.swift | 8 +- .../Sources/ChatListSearchContainerNode.swift | 2 +- .../Sources/Node/ChatListItem.swift | 10 +- .../Sources/Node/ChatListNode.swift | 10 +- .../Sources/HashtagSearchController.swift | 2 +- .../Sources/ChannelInfoController.swift | 4 +- .../Sources/GroupInfoController.swift | 2 +- .../TextSizeSelectionController.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 2 +- .../Themes/ThemePreviewControllerNode.swift | 2 +- .../ChatInterfaceStateContextMenus.swift | 9 ++ .../ChatSearchResultsContollerNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 2 +- 16 files changed, 109 insertions(+), 64 deletions(-) diff --git a/submodules/AccountContext/Sources/ChatListController.swift b/submodules/AccountContext/Sources/ChatListController.swift index ad9ac4ee69..ec9fb811e8 100644 --- a/submodules/AccountContext/Sources/ChatListController.swift +++ b/submodules/AccountContext/Sources/ChatListController.swift @@ -10,5 +10,5 @@ public protocol ChatListController: ViewController { func activateSearch() func deactivateSearch(animated: Bool) func activateCompose() - func maybeAskForPeerChatRemoval(peer: RenderedPeer, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) + func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) } diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 0aaee436a0..7743bc1ad4 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -46,7 +46,7 @@ enum ChatContextMenuSource { case search(ChatListSearchContextActionSource) } -func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: ChatListNodeEntryPromoInfo?, source: ChatContextMenuSource, chatListController: ChatListControllerImpl?) -> Signal<[ContextMenuItem], NoError> { +func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: ChatListNodeEntryPromoInfo?, source: ChatContextMenuSource, chatListController: ChatListControllerImpl?, joined: Bool) -> Signal<[ContextMenuItem], NoError> { let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) let strings = presentationData.strings return context.account.postbox.transaction { [weak chatListController] transaction -> [ContextMenuItem] in @@ -233,7 +233,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController)) + c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined)) }))) return updatedItems @@ -379,7 +379,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch if case .chatList = source, groupAndIndex != nil { items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in if let chatListController = chatListController { - chatListController.deletePeerChat(peerId: peerId) + chatListController.deletePeerChat(peerId: peerId, joined: joined) } f(.default) }))) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index a949a737a9..44e06dca1e 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -575,11 +575,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, strongSelf.hidePsa(peerId) } - self.chatListDisplayNode.containerNode.deletePeerChat = { [weak self] peerId in + self.chatListDisplayNode.containerNode.deletePeerChat = { [weak self] peerId, joined in guard let strongSelf = self else { return } - strongSelf.deletePeerChat(peerId: peerId) + strongSelf.deletePeerChat(peerId: peerId, joined: joined) } self.chatListDisplayNode.containerNode.peerSelected = { [weak self] peer, animated, promoInfo in @@ -801,6 +801,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, gesture?.cancel() return } + + var joined = false + if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { + for media in message.media { + if let action = media as? TelegramMediaAction, action.action == .peerJoined { + joined = true + } + } + } + switch item.content { case let .groupReference(groupReference): let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupReference.groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) @@ -810,7 +820,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _): let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf), reactionItems: [], gesture: gesture) + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined), reactionItems: [], gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } } @@ -823,7 +833,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf), reactionItems: [], gesture: gesture) + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false), reactionItems: [], gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } @@ -2069,7 +2079,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, let _ = hideAccountPromoInfoChat(account: self.context.account, peerId: id).start() } - func deletePeerChat(peerId: PeerId) { + func deletePeerChat(peerId: PeerId, joined: Bool) { let _ = (self.context.account.postbox.transaction { transaction -> RenderedPeer? in guard let peer = transaction.getPeer(peerId) else { return nil @@ -2099,7 +2109,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } if let user = chatPeer as? TelegramUser, user.botInfo == nil, canRemoveGlobally { - strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in }, removed: {}) + strongSelf.maybeAskForPeerChatRemoval(peer: peer, joined: joined, completion: { _ in }, removed: {}) } else { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] @@ -2189,16 +2199,24 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, if canRemoveGlobally { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] - + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in - beginClear(.forEveryone) - actionSheet?.dismissAnimated() - })) - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in - beginClear(.forLocalPeer) - actionSheet?.dismissAnimated() - })) + + if joined || mainPeer.isDeleted { + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in + beginClear(.forEveryone) + actionSheet?.dismissAnimated() + })) + } else { + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in + beginClear(.forEveryone) + actionSheet?.dismissAnimated() + })) + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in + beginClear(.forLocalPeer) + actionSheet?.dismissAnimated() + })) + } actionSheet.setItemGroups([ ActionSheetItemGroup(items: items), @@ -2258,7 +2276,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, }) } - public func maybeAskForPeerChatRemoval(peer: RenderedPeer, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) { + public func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) { guard let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else { completion(false) return @@ -2279,31 +2297,41 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, var items: [ActionSheetItem] = [] items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in - actionSheet?.dismissAnimated() - guard let strongSelf = self else { - return - } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { - completion(false) - }), - TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: { - self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { - removed() - }) - completion(true) - }) - ], parseMarkdown: true), in: .window(.root)) - })) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in - actionSheet?.dismissAnimated() - self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { - removed() - }) - completion(true) - })) + if joined || mainPeer.isDeleted { + items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { + removed() + }) + completion(true) + })) + } else { + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + guard let strongSelf = self else { + return + } + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { + completion(false) + }), + TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: { + self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { + removed() + }) + completion(true) + }) + ], parseMarkdown: true), in: .window(.root)) + })) + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { + removed() + }) + completion(true) + })) + } actionSheet.setItemGroups([ ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 6f4eb396bf..d336726443 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -195,7 +195,7 @@ private final class ChatListShimmerNode: ASDisplayNode { let timestamp1: Int32 = 100000 let peers = SimpleDictionary() let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in - }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in + }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() }, present: { _ in }) @@ -478,8 +478,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { itemNode.listNode.hidePsa = { [weak self] peerId in self?.hidePsa?(peerId) } - itemNode.listNode.deletePeerChat = { [weak self] peerId in - self?.deletePeerChat?(peerId) + itemNode.listNode.deletePeerChat = { [weak self] peerId, joined in + self?.deletePeerChat?(peerId, joined) } itemNode.listNode.peerSelected = { [weak self] peerId, a, b in self?.peerSelected?(peerId, a, b) @@ -527,7 +527,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { var present: ((ViewController) -> Void)? var toggleArchivedFolderHiddenByDefault: (() -> Void)? var hidePsa: ((PeerId) -> Void)? - var deletePeerChat: ((PeerId) -> Void)? + var deletePeerChat: ((PeerId, Bool) -> Void)? var peerSelected: ((Peer, Bool, ChatListNodeEntryPromoInfo?) -> Void)? var groupSelected: ((PeerGroupId) -> Void)? var updatePeerGrouping: ((PeerId, Bool) -> Void)? diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 33af650006..96189597db 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1028,7 +1028,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in - }, deletePeer: { _ in + }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in }, toggleArchivedFolderHiddenByDefault: { diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 7edf5afe3b..f29befbd66 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -1946,7 +1946,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { item.interaction.setPeerMuted(item.index.messageIndex.id.peerId, false) close = false case RevealOptionKey.delete.rawValue: - item.interaction.deletePeer(item.index.messageIndex.id.peerId) + var joined = false + if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { + for media in message.media { + if let action = media as? TelegramMediaAction, action.action == .peerJoined { + joined = true + } + } + } + item.interaction.deletePeer(item.index.messageIndex.id.peerId, joined) case RevealOptionKey.archive.rawValue: item.interaction.updatePeerGrouping(item.index.messageIndex.id.peerId, true) close = false diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 326c5bcb5a..0506f6deb4 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -59,7 +59,7 @@ public final class ChatListNodeInteraction { let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void let setItemPinned: (PinnedItemId, Bool) -> Void let setPeerMuted: (PeerId, Bool) -> Void - let deletePeer: (PeerId) -> Void + let deletePeer: (PeerId, Bool) -> Void let updatePeerGrouping: (PeerId, Bool) -> Void let togglePeerMarkedUnread: (PeerId, Bool) -> Void let toggleArchivedFolderHiddenByDefault: () -> Void @@ -70,7 +70,7 @@ public final class ChatListNodeInteraction { public var searchTextHighightState: String? var highlightedChatLocation: ChatListHighlightedLocation? - public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) { + public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId, Bool) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) { self.activateSearch = activateSearch self.peerSelected = peerSelected self.disabledPeerSelected = disabledPeerSelected @@ -430,7 +430,7 @@ public final class ChatListNode: ListView { public var groupSelected: ((PeerGroupId) -> Void)? public var addContact: ((String) -> Void)? public var activateSearch: (() -> Void)? - public var deletePeerChat: ((PeerId) -> Void)? + public var deletePeerChat: ((PeerId, Bool) -> Void)? public var updatePeerGrouping: ((PeerId, Bool) -> Void)? public var presentAlert: ((String) -> Void)? public var present: ((ViewController) -> Void)? @@ -628,8 +628,8 @@ public final class ChatListNode: ListView { } self?.setCurrentRemovingPeerId(nil) }) - }, deletePeer: { [weak self] peerId in - self?.deletePeerChat?(peerId) + }, deletePeer: { [weak self] peerId, joined in + self?.deletePeerChat?(peerId, joined) }, updatePeerGrouping: { [weak self] peerId, group in self?.updatePeerGrouping?(peerId, group) }, togglePeerMarkedUnread: { [weak self, weak context] peerId, animated in diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index a2fcb6fda9..78442b6a65 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -66,7 +66,7 @@ public final class HashtagSearchController: TelegramBaseController { }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in - }, deletePeer: { _ in + }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in }, toggleArchivedFolderHiddenByDefault: { diff --git a/submodules/PeerInfoUI/Sources/ChannelInfoController.swift b/submodules/PeerInfoUI/Sources/ChannelInfoController.swift index 685be43fc3..369bbcc8e7 100644 --- a/submodules/PeerInfoUI/Sources/ChannelInfoController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelInfoController.swift @@ -1076,8 +1076,8 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi } for childController in tabController.controllers { if let chatListController = childController as? ChatListController { - chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] deleted in - if deleted { + chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in + if removed { navigationController?.popToRoot(animated: true) } }, removed: { diff --git a/submodules/PeerInfoUI/Sources/GroupInfoController.swift b/submodules/PeerInfoUI/Sources/GroupInfoController.swift index 7b25fb885f..90bbff79e5 100644 --- a/submodules/PeerInfoUI/Sources/GroupInfoController.swift +++ b/submodules/PeerInfoUI/Sources/GroupInfoController.swift @@ -2439,7 +2439,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId: } for childController in tabController.controllers { if let chatListController = childController as? ChatListController { - chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in + chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in if removed { navigationController?.popToRoot(animated: true) } diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index f2b4aafdf5..9c01ad1baa 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -214,7 +214,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView var items: [ChatListItem] = [] let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in - }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in + }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() }, present: { _ in }) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index d30215f04e..1c834b6617 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -767,7 +767,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate var items: [ChatListItem] = [] let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in - }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in + }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() }, present: { _ in diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index c273166376..7eb50d2710 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -351,7 +351,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { var items: [ChatListItem] = [] let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in - }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in + }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() }, present: { _ in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index ceaa5643f0..5d66223437 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -974,10 +974,14 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me optionsMap[id]!.insert(.deleteLocally) } else if let peer = transaction.getPeer(id.peerId) { var isAction = false + var isDice = false for media in message.media { if media is TelegramMediaAction || media is TelegramMediaExpiredContent { isAction = true } + if media is TelegramMediaDice { + isDice = true + } } if let channel = peer as? TelegramChannel { if message.flags.contains(.Incoming) { @@ -1064,6 +1068,11 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me } else if limitsConfiguration.canRemoveIncomingMessagesInPrivateChats { canDeleteGlobally = true } + + let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + if isDice && Int64(message.timestamp) + 60 * 60 * 24 > Int64(timestamp) { + canDeleteGlobally = false + } if message.flags.contains(.Incoming) { hadPersonalIncoming = true } diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 399e715d5a..a69e9c64c8 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -183,7 +183,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in - }, deletePeer: { _ in + }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in }, toggleArchivedFolderHiddenByDefault: { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index c112f505e9..c2bcb22f2b 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3837,7 +3837,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } for childController in tabController.controllers { if let chatListController = childController as? ChatListController { - chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in + chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in if deleted { navigationController?.popToRoot(animated: true) } From 3ba0fa3a6ff3ab2a5b122f8c297e47b7ea220561 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jul 2020 19:11:01 +0300 Subject: [PATCH 6/9] Fix group stats layout and chart display --- submodules/StatisticsUI/Sources/StatsMessageItem.swift | 6 +++--- submodules/TelegramCore/Sources/PeerStatistics.swift | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/submodules/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift index 4d5c8fde37..0a8b31facc 100644 --- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift +++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift @@ -173,7 +173,7 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { let leftInset = 16.0 + params.leftInset let rightInset = 16.0 + params.rightInset var totalLeftInset = leftInset - let additionalRightInset: CGFloat = 93.0 + let additionalRightInset: CGFloat = 128.0 let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize) @@ -229,9 +229,9 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: label, font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - totalLeftInset - rightInset - additionalRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let (viewsLayout, viewsApply) = makeViewsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageViews(item.views), font: labelFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) + let (viewsLayout, viewsApply) = makeViewsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageViews(item.views), font: labelFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) - let (forwardsLayout, forwardsApply) = makeForwardsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageForwards(item.forwards), font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) + let (forwardsLayout, forwardsApply) = makeForwardsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageForwards(item.forwards), font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) let verticalInset: CGFloat = 11.0 let titleSpacing: CGFloat = 3.0 diff --git a/submodules/TelegramCore/Sources/PeerStatistics.swift b/submodules/TelegramCore/Sources/PeerStatistics.swift index fd713c698c..83f9383e9a 100644 --- a/submodules/TelegramCore/Sources/PeerStatistics.swift +++ b/submodules/TelegramCore/Sources/PeerStatistics.swift @@ -30,8 +30,8 @@ public enum StatsGraph: Equatable { switch self { case .Empty: return true - case let .Failed(error): - return error.lowercased().contains("not enough data") + case .Failed: + return true default: return false } From 26a4d282f4f4613565781d5fdbcc79be71b528f0 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jul 2020 19:14:24 +0300 Subject: [PATCH 7/9] Immediately dismiss all tooltips when opening context menu --- .../TelegramUI/Sources/ChatController.swift | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f8eff54f07..9fa9f93627 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -570,6 +570,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil { return } + + strongSelf.dismissAllTooltips() + let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer let gesture: ContextGesture? = anyRecognizer as? ContextGesture if let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) { @@ -1985,7 +1988,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) }, displaySwipeToReplyHint: { [weak self] in if let strongSelf = self, let validLayout = strongSelf.validLayout, min(validLayout.size.width, validLayout.size.height) > 320.0 { - strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: true, action: { _ in return false }), in: .window(.root)) + strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: false, action: { _ in return false }), in: .current) } }, dismissReplyMarkupMessage: { [weak self] message in guard let strongSelf = self, strongSelf.presentationInterfaceState.keyboardButtonsMessage?.id == message.id else { @@ -2044,6 +2047,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } + + if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil { + return + } + + strongSelf.dismissAllTooltips() + let context = strongSelf.context let _ = (context.account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(peer.id) @@ -5228,18 +5238,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.dismissAllTooltips() - self.window?.forEachController({ controller in - if let controller = controller as? UndoOverlayController { - controller.dismissWithCommitAction() - } - }) - self.forEachController({ controller in - if let controller = controller as? TooltipScreen { - controller.dismiss() - } - return true - }) - self.sendMessageActionsController?.dismiss() if let _ = self.peekData { @@ -6077,7 +6075,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G disposable.set((signal |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self, let _ = strongSelf.validLayout { - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: true, action: { _ in return false }), in: .current) + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in return false }), in: .current) } })) @@ -7118,12 +7116,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if let value = value { - self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: true, action: { [weak self] action in + self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: false, action: { [weak self] action in if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState), action == .undo { strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), replyToMessageId: nil, localGroupingKey: nil)]) } return false - }), in: .window(.root)) + }), in: .current) } } @@ -9179,6 +9177,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.silentPostTooltipController?.dismiss() self.mediaRecordingModeTooltipController?.dismiss() self.mediaRestrictedTooltipController?.dismiss() + + self.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + }) + self.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + if let controller = controller as? TooltipScreen { + controller.dismiss() + } + return true + }) } private func commitPurposefulAction() { From b9e9208011c0c51e31f665d257f98ec55baabdce Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jul 2020 19:15:57 +0300 Subject: [PATCH 8/9] Fix modal screens presentation in landscape --- .../Display/Source/Navigation/NavigationController.swift | 5 +++-- .../Display/Source/Navigation/NavigationModalContainer.swift | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 26a12835c0..815b56521d 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -579,6 +579,7 @@ open class NavigationController: UINavigationController, ContainableController, var previousModalContainer: NavigationModalContainer? var visibleModalCount = 0 var topModalIsFlat = false + var isLandscape = layout.orientation == .landscape var hasVisibleStandaloneModal = false var topModalDismissProgress: CGFloat = 0.0 @@ -784,7 +785,7 @@ open class NavigationController: UINavigationController, ContainableController, let visibleRootModalDismissProgress: CGFloat var additionalModalFrameProgress: CGFloat if visibleModalCount == 1 { - effectiveRootModalDismissProgress = topModalIsFlat ? 1.0 : topModalDismissProgress + effectiveRootModalDismissProgress = (topModalIsFlat || isLandscape) ? 1.0 : topModalDismissProgress visibleRootModalDismissProgress = effectiveRootModalDismissProgress additionalModalFrameProgress = 0.0 } else if visibleModalCount >= 2 { @@ -851,7 +852,7 @@ open class NavigationController: UINavigationController, ContainableController, } let maxScale: CGFloat let maxOffset: CGFloat - if topModalIsFlat { + if topModalIsFlat || isLandscape { maxScale = 1.0 maxOffset = 0.0 } else if visibleModalCount <= 1 { diff --git a/submodules/Display/Source/Navigation/NavigationModalContainer.swift b/submodules/Display/Source/Navigation/NavigationModalContainer.swift index aa40c78ee3..247d077368 100644 --- a/submodules/Display/Source/Navigation/NavigationModalContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationModalContainer.swift @@ -328,6 +328,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes self.scrollNode.view.isScrollEnabled = !isStandaloneModal + let isLandscape = layout.orientation == .landscape let containerLayout: ContainerViewLayout let containerFrame: CGRect let containerScale: CGFloat @@ -336,7 +337,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes self.panRecognizer?.isEnabled = true self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25) self.container.clipsToBounds = true - if isStandaloneModal { + if isStandaloneModal || isLandscape { self.container.cornerRadius = 0.0 } else { self.container.cornerRadius = 10.0 @@ -351,7 +352,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes } var topInset: CGFloat - if isStandaloneModal { + if isStandaloneModal || isLandscape { topInset = 0.0 containerLayout = layout From 22db9c74fa4e18182d1629d0b78da8911716e381 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jul 2020 19:18:33 +0300 Subject: [PATCH 9/9] Update media picker video gallery view design --- .../Sources/TGMediaPickerCaptionInputPanel.m | 16 +-- .../TGMediaPickerGalleryInterfaceView.m | 45 +++---- .../TGMediaPickerGalleryVideoItemView.m | 126 ++++++++++-------- .../TGMediaPickerGalleryVideoScrubber.h | 2 - .../TGMediaPickerGalleryVideoScrubber.m | 46 +------ .../Sources/TGMediaPickerPhotoCounterButton.m | 2 +- .../Sources/TGPhotoEditorInterfaceAssets.m | 2 +- .../Sources/TGPhotoPaintController.m | 2 +- 8 files changed, 98 insertions(+), 143 deletions(-) diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m b/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m index f5a1ca7097..dbf7f8f746 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m @@ -86,7 +86,7 @@ static void setViewFrame(UIView *view, CGRect frame) { localizationPlaceholderText = TGLocalized(@"MediaPicker.AddCaption"); NSString *placeholderText = TGLocalized(@"MediaPicker.AddCaption"); - UIFont *placeholderFont = TGSystemFontOfSize(16); + UIFont *placeholderFont = TGSystemFontOfSize(17); CGSize placeholderSize = [placeholderText sizeWithFont:placeholderFont]; placeholderSize.width += 2.0f; placeholderSize.height += 2.0f; @@ -121,7 +121,7 @@ static void setViewFrame(UIView *view, CGRect frame) _placeholderLabel = [[UILabel alloc] init]; _placeholderLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; _placeholderLabel.backgroundColor = [UIColor clearColor]; - _placeholderLabel.font = TGSystemFontOfSize(16); + _placeholderLabel.font = TGSystemFontOfSize(17); _placeholderLabel.textColor = UIColorRGB(0x7f7f7f); _placeholderLabel.text = TGLocalized(@"MediaPicker.AddCaption"); _placeholderLabel.userInteractionEnabled = true; @@ -130,7 +130,7 @@ static void setViewFrame(UIView *view, CGRect frame) _inputFieldOnelineLabel = [[UILabel alloc] init]; _inputFieldOnelineLabel.backgroundColor = [UIColor clearColor]; - _inputFieldOnelineLabel.font = TGSystemFontOfSize(16); + _inputFieldOnelineLabel.font = TGSystemFontOfSize(17); _inputFieldOnelineLabel.hidden = true; _inputFieldOnelineLabel.numberOfLines = 1; _inputFieldOnelineLabel.textColor = [UIColor whiteColor]; @@ -169,7 +169,7 @@ static void setViewFrame(UIView *view, CGRect frame) _inputField.textColor = [UIColor whiteColor]; _inputField.disableFormatting = !_allowEntities; _inputField.placeholderView = _placeholderLabel; - _inputField.font = TGSystemFontOfSize(16); + _inputField.font = TGSystemFontOfSize(17); _inputField.accentColor = UIColorRGB(0x78b1f9); _inputField.clipsToBounds = true; _inputField.backgroundColor = nil; @@ -188,7 +188,7 @@ static void setViewFrame(UIView *view, CGRect frame) _inputField.internalTextView.scrollIndicatorInsets = UIEdgeInsetsMake(-inputFieldInternalEdgeInsets.top, 0, 5 - TGRetinaPixel, 0); - [_inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:16.0f] keepFormatting:true animated:false]; + [_inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:17.0f] keepFormatting:true animated:false]; [_inputFieldClippingContainer addSubview:_inputField]; } @@ -439,7 +439,7 @@ static void setViewFrame(UIView *view, CGRect frame) _fieldBackground.alpha = _placeholderLabel.hidden ? 1.0f : 0.0f; } - [self.inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:16.0f] keepFormatting:true animated:false]; + [self.inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:17.0f] keepFormatting:true animated:false]; } + (NSAttributedString *)attributedStringForText:(NSString *)text entities:(NSArray *)entities fontSize:(CGFloat)fontSize { @@ -777,14 +777,14 @@ static void setViewFrame(UIView *view, CGRect frame) if (text == nil) return nil; - NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:[TGMediaPickerCaptionInputPanel attributedStringForText:text entities:entities fontSize:16.0f]]; + NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:[TGMediaPickerCaptionInputPanel attributedStringForText:text entities:entities fontSize:17.0f]]; for (NSUInteger i = 0; i < string.length; i++) { unichar c = [text characterAtIndex:i]; if (c == '\t' || c == '\n') { - [string insertAttributedString:[[NSAttributedString alloc] initWithString:tokenString attributes:@{NSFontAttributeName:TGSystemFontOfSize(16.0f)}] atIndex:i]; + [string insertAttributedString:[[NSAttributedString alloc] initWithString:tokenString attributes:@{NSFontAttributeName:TGSystemFontOfSize(17.0f)}] atIndex:i]; break; } } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m index b93165a11e..ef165a5e1e 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m @@ -167,13 +167,14 @@ if (recipientName.length > 0) { _arrowView = [[UIImageView alloc] initWithImage: TGComponentsImageNamed(@"PhotoPickerArrow")]; - _arrowView.alpha = 0.45f; + _arrowView.alpha = 0.6f; [_wrapperView addSubview:_arrowView]; _recipientLabel = [[UILabel alloc] init]; + _recipientLabel.alpha = 0.6; _recipientLabel.backgroundColor = [UIColor clearColor]; _recipientLabel.font = TGBoldSystemFontOfSize(13.0f); - _recipientLabel.textColor = UIColorRGBA(0xffffff, 0.45f); + _recipientLabel.textColor = UIColorRGB(0xffffff); _recipientLabel.text = recipientName; _recipientLabel.userInteractionEnabled = false; [_recipientLabel sizeToFit]; @@ -510,13 +511,7 @@ UIEdgeInsets screenEdges = [self screenEdges]; __weak TGMediaPickerGalleryInterfaceView *weakSelf = self; - - if ([itemView.headerView isKindOfClass:[TGMediaPickerScrubberHeaderView class]]) - { - TGMediaPickerScrubberHeaderView *headerView = (TGMediaPickerScrubberHeaderView *)itemView.headerView; - [headerView.scrubberView setRecipientName:_recipientLabel.text]; - } - + [self _layoutRecipientLabelForOrientation:[self interfaceOrientation] screenEdges:screenEdges hasHeaderView:(itemView.headerView != nil)]; if (_selectionContext != nil) @@ -1039,8 +1034,8 @@ { _checkButton.alpha = alpha; _muteButton.alpha = alpha; - _arrowView.alpha = alpha * 0.45f; - _recipientLabel.alpha = alpha; + _arrowView.alpha = alpha * 0.6f; + _recipientLabel.alpha = alpha * 0.6; } completion:^(BOOL finished) { if (finished) @@ -1070,8 +1065,8 @@ _muteButton.alpha = alpha; _muteButton.userInteractionEnabled = !hidden; - _arrowView.alpha = alpha * 0.45f; - _recipientLabel.alpha = alpha; + _arrowView.alpha = alpha * 0.6f; + _recipientLabel.alpha = alpha * 0.6; } if (hidden) @@ -1095,7 +1090,7 @@ { _checkButton.alpha = alpha; _muteButton.alpha = alpha; - _arrowView.alpha = alpha * 0.45f; + _arrowView.alpha = alpha * 0.6; _recipientLabel.alpha = alpha; _portraitToolbarView.alpha = alpha; _landscapeToolbarView.alpha = alpha; @@ -1132,7 +1127,7 @@ _muteButton.alpha = alpha; _muteButton.userInteractionEnabled = !hidden; - _arrowView.alpha = alpha * 0.45f; + _arrowView.alpha = alpha * 0.6; _recipientLabel.alpha = alpha; _portraitToolbarView.alpha = alpha; @@ -1391,6 +1386,8 @@ screenEdges.left += _safeAreaInset.left; screenEdges.right -= _safeAreaInset.right; + CGFloat panelInset = 0.0f; + switch (orientation) { case UIInterfaceOrientationLandscapeLeft: @@ -1402,13 +1399,10 @@ break; default: - frame = CGRectMake(screenEdges.left + 5, screenEdges.top + 6, _muteButton.frame.size.width, _muteButton.frame.size.height); + frame = CGRectMake(screenEdges.left + 5, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 45 - _safeAreaInset.bottom - panelInset - (hasHeaderView ? 64.0 : 0.0), _muteButton.frame.size.width, _muteButton.frame.size.height); break; } - if (hasHeaderView) - frame.origin.y += 64; - return frame; } @@ -1462,9 +1456,6 @@ break; } - if (hasHeaderView) - frame.origin.y += 64; - return frame; } @@ -1526,9 +1517,6 @@ _arrowView.frame = frame; _recipientLabel.frame = CGRectMake(CGRectGetMaxX(_arrowView.frame) + 6.0f, _arrowView.frame.origin.y - 2.0f, recipientWidth, _recipientLabel.frame.size.height); - - _arrowView.hidden = hasHeaderView; - _recipientLabel.hidden = hasHeaderView; } - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)__unused duration @@ -1670,15 +1658,14 @@ { [UIView performWithoutAnimation:^ { - _photoCounterButton.frame = CGRectMake(screenEdges.right - 56 - _safeAreaInset.right, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 40 - _safeAreaInset.bottom, 64, 38); + _photoCounterButton.frame = CGRectMake(screenEdges.right - 56 - _safeAreaInset.right, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 40 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), 64, 38); - _selectedPhotosView.frame = CGRectMake(screenEdges.left + 4, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - photosViewSize - 54 - _safeAreaInset.bottom, self.frame.size.width - 4 * 2 - _safeAreaInset.right, photosViewSize); + _selectedPhotosView.frame = CGRectMake(screenEdges.left + 4, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - photosViewSize - 54 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), self.frame.size.width - 4 * 2 - _safeAreaInset.right, photosViewSize); }]; _landscapeToolbarView.frame = CGRectMake(_landscapeToolbarView.frame.origin.x, screenEdges.top, TGPhotoEditorToolbarSize, self.frame.size.height); - CGFloat topInset = _safeAreaInset.top > FLT_EPSILON ? _safeAreaInset.top - 14.0 : 0.0f; - _headerWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.top + topInset, self.frame.size.width, 64); + _headerWrapperView.frame = CGRectMake(screenEdges.left, _portraitToolbarView.frame.origin.y - 64.0 - [_captionMixin.inputPanel baseHeight], self.frame.size.width, 64.0); } break; } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m index 4e8c6bcaea..84c7f381d7 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m @@ -71,6 +71,8 @@ bool _scrubbingPanelPresented; bool _scrubbingPanelLocked; bool _shouldResetScrubber; + NSArray *_cachedThumbnails; + UIImage *_immediateThumbnail; UILabel *_fileInfoLabel; @@ -215,13 +217,12 @@ _headerView = headerView; _headerView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - _scrubberPanelView = [[UIView alloc] initWithFrame:CGRectMake(0, -64, _headerView.frame.size.width, 64)]; + _scrubberPanelView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _headerView.frame.size.width, 64)]; _scrubberPanelView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - _scrubberPanelView.hidden = true; headerView.panelView = _scrubberPanelView; [_headerView addSubview:_scrubberPanelView]; - UIView *scrubberBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, -100.0f, _headerView.frame.size.width, 164.0f)]; + UIView *scrubberBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _headerView.frame.size.width, 64.0f)]; scrubberBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth; scrubberBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor]; [_scrubberPanelView addSubview:scrubberBackgroundView]; @@ -301,7 +302,6 @@ _appeared = false; [self setScrubbingPanelApperanceLocked:false]; - [self setScrubbingPanelHidden:true animated:false]; [_positionTimer invalidate]; _positionTimer = nil; @@ -386,9 +386,14 @@ - (void)setItem:(TGMediaPickerGalleryVideoItem *)item synchronously:(bool)synchronously { bool itemChanged = ![item isEqual:self.item]; + bool itemIdChanged = item.uniqueId != self.item.uniqueId; [super setItem:item synchronously:synchronously]; + if (itemIdChanged) { + _immediateThumbnail = item.immediateThumbnailImage; + } + if (itemChanged) { [self _playerCleanup]; @@ -618,17 +623,15 @@ void (^changeBlock)(void) = ^ { - _scrubberPanelView.frame = CGRectMake(0.0f, -64.0f - _safeAreaInset.top, _scrubberPanelView.frame.size.width, _scrubberPanelView.frame.size.height); + _scrubberPanelView.alpha = 0.0f; }; void (^completionBlock)(BOOL) = ^(BOOL finished) { - if (finished) - _scrubberPanelView.hidden = true; }; if (animated) { - [UIView animateWithDuration:0.3f delay:0.0f options:(7 << 16) animations:changeBlock completion:completionBlock]; + [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:changeBlock completion:completionBlock]; } else { @@ -642,18 +645,17 @@ return; _scrubbingPanelPresented = true; - - _scrubberPanelView.hidden = false; + [_scrubberPanelView layoutSubviews]; [_scrubberView layoutSubviews]; void (^changeBlock)(void) = ^ { - _scrubberPanelView.frame = CGRectMake(0.0f, 0.0f, _scrubberPanelView.frame.size.width, _scrubberPanelView.frame.size.height); + _scrubberPanelView.alpha = 1.0f; }; if (animated) - [UIView animateWithDuration:0.3f delay:0.0f options:(7 << 16) animations:changeBlock completion:nil]; + [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:changeBlock completion:nil]; else changeBlock(); } @@ -708,7 +710,6 @@ { [_scrubberView resetThumbnails]; - [self setScrubbingPanelHidden:true animated:false]; [_scrubberPanelView setNeedsLayout]; [_scrubberPanelView layoutIfNeeded]; @@ -722,11 +723,14 @@ if (_containerView == nil) return; - _containerView.frame = self.bounds; + if (self.bounds.size.width > self.bounds.size.height) + _containerView.frame = self.bounds; + else + _containerView.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height - 44.0); [self _layoutPlayerView]; - _videoContentView.frame = (CGRect){CGPointZero, frame.size}; + _videoContentView.frame = (CGRect){CGPointZero, _containerView.frame.size}; if (_tooltipContainerView != nil && frame.size.width > frame.size.height) { @@ -753,8 +757,6 @@ mirrored = adjustments.cropMirrored; } -// _scrubberView.maximumLength = adjustments.sendAsGif ? TGVideoEditMaximumGifDuration : 0.0; - [self _layoutPlayerViewWithCropRect:cropRect videoFrameSize:videoFrameSize orientation:orientation mirrored:mirrored]; } @@ -1440,40 +1442,8 @@ trimEndValue = adjustments.trimEndValue; } } -// NSTimeInterval trimDuration = trimEndValue - trimStartValue; bool sendAsGif = !adjustments.sendAsGif; -// if (sendAsGif && _scrubberView.allowsTrimming) -// { -// if (trimDuration > TGVideoEditMaximumGifDuration) -// { -// trimEndValue = trimStartValue + TGVideoEditMaximumGifDuration; -// -// if (_scrubberView.value > trimEndValue) -// { -// [self stop]; -// [_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true]; -// [self _seekToPosition:_scrubberView.value manual:true]; -// } -// -// _scrubberView.trimStartValue = trimStartValue; -// _scrubberView.trimEndValue = trimEndValue; -// [_scrubberView setTrimApplied:true]; -// [self updatePlayerRange:trimEndValue]; -// } -// } -// else if (_shouldResetScrubber) -// { -// trimStartValue = 0.0; -// trimEndValue = _videoDuration; -// -// _scrubberView.trimStartValue = trimStartValue; -// _scrubberView.trimEndValue = trimEndValue; -// -// [_scrubberView setTrimApplied:false]; -// [self updatePlayerRange:trimEndValue]; -// } - TGVideoEditAdjustments *updatedAdjustments = [TGVideoEditAdjustments editAdjustmentsWithOriginalSize:_videoDimensions cropRect:cropRect cropOrientation:adjustments.cropOrientation cropRotation:adjustments.cropRotation cropLockedAspectRatio:adjustments.cropLockedAspectRatio cropMirrored:adjustments.cropMirrored trimStartValue:trimStartValue trimEndValue:trimEndValue toolValues:adjustments.toolValues paintingData:adjustments.paintingData sendAsGif:sendAsGif preset:adjustments.preset]; [self.item.editingContext setAdjustments:updatedAdjustments forItem:self.item.editableMediaItem]; @@ -1596,6 +1566,23 @@ return timestamps; } +- (SSignal *)_placeholderThumbnails:(NSArray *)timestamps { + NSMutableArray *thumbnails = [[NSMutableArray alloc] init]; + + UIImage *image = _immediateThumbnail; + if (image == nil) + return [SSignal complete]; + + UIImage *blurredImage = TGBlurredRectangularImage(image, true, image.size, image.size, NULL, nil); + for (__unused NSNumber *value in timestamps) { + if (thumbnails.count == 0) + [thumbnails addObject:image]; + else + [thumbnails addObject:blurredImage]; + } + return [SSignal single:thumbnails]; +} + - (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails { if (timestamps.count == 0) @@ -1605,17 +1592,42 @@ TGMediaEditingContext *editingContext = self.item.editingContext; id editableItem = self.editableMediaItem; - SSignal *thumbnailsSignal = nil; - if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto]) - thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:self.item.asset size:size timestamps:timestamps]; - else if (avAsset != nil) - thumbnailsSignal = [avAsset mapToSignal:^SSignal *(AVAsset *avAsset) { - return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps]; - }]; +// SSignal *thumbnailsSignal = nil; +// if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto]) +// thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:self.item.asset size:size timestamps:timestamps]; +// else if (avAsset != nil) +// thumbnailsSignal = [avAsset mapToSignal:^SSignal *(AVAsset *avAsset) { +// return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps]; +// }]; + __strong TGMediaPickerGalleryVideoItemView *weakSelf = self; + SSignal *thumbnailsSignal = nil; + if (_cachedThumbnails != nil) { + thumbnailsSignal = [SSignal single:_cachedThumbnails]; + } else if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto]) { + thumbnailsSignal = [[self _placeholderThumbnails:timestamps] then:[[TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item.asset size:size timestamps:timestamps] onNext:^(NSArray *images) { + __strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + if (strongSelf->_cachedThumbnails == nil) + strongSelf->_cachedThumbnails = images; + }]]; + } else if ([self.item.asset isKindOfClass:[TGCameraCapturedVideo class]]) { + thumbnailsSignal = [[((TGCameraCapturedVideo *)self.item.asset).avAsset takeLast] mapToSignal:^SSignal *(AVAsset *avAsset) { + return [[self _placeholderThumbnails:timestamps] then:[[TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps] onNext:^(NSArray *images) { + __strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + if (strongSelf->_cachedThumbnails == nil) + strongSelf->_cachedThumbnails = images; + }]]; + }]; + } + _requestingThumbnails = true; - __weak TGMediaPickerGalleryVideoItemView *weakSelf = self; [_thumbnailsDisposable setDisposable:[[[thumbnailsSignal map:^NSArray *(NSArray *images) { id adjustments = [editingContext adjustmentsForItem:editableItem]; if (adjustments.toolsApplied) { diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h index 1903dfcb0e..07e3a8613b 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h @@ -45,8 +45,6 @@ - (void)setThumbnailImage:(UIImage *)image forTimestamp:(NSTimeInterval)timestamp index:(NSInteger)index isSummaryThubmnail:(bool)isSummaryThumbnail; -- (void)setRecipientName:(NSString *)recipientName; - - (CGPoint)scrubberPositionForPosition:(NSTimeInterval)position; - (void)_updateScrubberAnimationsAndResetCurrentPosition:(bool)resetCurrentPosition; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m index 476c89aae4..c60928ba7e 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m @@ -81,9 +81,6 @@ typedef enum NSInteger _zoomedPivotTimestampIndex; NSArray *_zoomedTimestamps; NSMutableArray *_zoomedThumbnailViews; - - UIImageView *_arrowView; - UILabel *_recipientLabel; } @end @@ -417,33 +414,10 @@ typedef enum _tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; _tapGestureRecognizer.enabled = false; [_trimView addGestureRecognizer:_tapGestureRecognizer]; - - _arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")]; - _arrowView.alpha = 0.45f; - _arrowView.hidden = true; - [self addSubview:_arrowView]; - - _recipientLabel = [[UILabel alloc] init]; - _recipientLabel.backgroundColor = [UIColor clearColor]; - _recipientLabel.font = TGBoldSystemFontOfSize(13.0f); - _recipientLabel.textColor = UIColorRGBA(0xffffff, 0.45f); - _recipientLabel.hidden = true; - [self addSubview:_recipientLabel]; } return self; } -- (void)setRecipientName:(NSString *)recipientName -{ - _recipientLabel.text = recipientName; - _recipientLabel.hidden = recipientName.length == 0; - _arrowView.hidden = _recipientLabel.hidden; - - [_recipientLabel sizeToFit]; - - [self _layoutRecipientLabel]; -} - - (void)setHasDotPicker:(bool)hasDotPicker { _hasDotPicker = hasDotPicker; _tapGestureRecognizer.enabled = hasDotPicker; @@ -1052,11 +1026,9 @@ typedef enum - (void)_updateTimeLabels { - _currentTimeLabel.text = @""; + _currentTimeLabel.text = self.disableTimeDisplay ? @"" : [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.value]; - NSString *text = [NSString stringWithFormat:@"%@ / %@", [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.value], [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.duration]]; - - _inverseTimeLabel.text = self.disableTimeDisplay ? @"" : text; + _inverseTimeLabel.text = self.disableTimeDisplay ? @"" : [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.duration]; } #pragma mark - Scrubber Handle @@ -1466,18 +1438,6 @@ typedef enum } } -- (void)_layoutRecipientLabel -{ - if (self.frame.size.width < FLT_EPSILON) - return; - - CGFloat screenWidth = MAX(self.frame.size.width, self.frame.size.height); - CGFloat recipientWidth = MIN(_recipientLabel.frame.size.width, screenWidth - 100.0f); - - _arrowView.frame = CGRectMake(14.0f, 6.0f, _arrowView.frame.size.width, _arrowView.frame.size.height); - _recipientLabel.frame = CGRectMake(CGRectGetMaxX(_arrowView.frame) + 6.0f, _arrowView.frame.origin.y - 2.0f, recipientWidth, _recipientLabel.frame.size.height); -} - - (void)setFrame:(CGRect)frame { if (isnan(frame.origin.x) || isnan(frame.origin.y) || isnan(frame.size.width) || isnan(frame.size.height)) @@ -1501,8 +1461,6 @@ typedef enum _zoomedThumbnailWrapperView.frame = _summaryThumbnailWrapperView.frame; [self _updateScrubberAnimationsAndResetCurrentPosition:true]; - - [self _layoutRecipientLabel]; } + (NSString *)_stringFromTotalSeconds:(NSInteger)totalSeconds diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m index ebc05148dc..8a94b4c4e2 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m @@ -48,7 +48,7 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18; { UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.7f).CGColor); + CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor); CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f)); diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m b/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m index 8beac5ce0e..67f1ec0008 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m @@ -126,7 +126,7 @@ CGRect rect = CGRectMake(0, 0, 39.0f, 39.0f); UIGraphicsBeginImageContextWithOptions(rect.size, false, 0); CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.6f).CGColor); + CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor); CGContextFillEllipseInRect(context, CGRectInset(rect, 3, 3)); muteBackground = UIGraphicsGetImageFromCurrentImageContext(); diff --git a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m index 3fe8874b3e..abf25402a4 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m @@ -1216,7 +1216,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; if (![editorController isKindOfClass:[TGPhotoEditorController class]]) return; - if (hasStickers) { + if (!hasStickers) { [editorController setMinimalVideoDuration:duration]; }