mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
1411 lines
48 KiB
Objective-C
1411 lines
48 KiB
Objective-C
#import "TGLocationViewController.h"
|
|
|
|
#import "LegacyComponentsInternal.h"
|
|
|
|
#import "TGNavigationBar.h"
|
|
#import "TGUser.h"
|
|
#import "TGConversation.h"
|
|
#import "TGMessage.h"
|
|
#import "TGImageUtils.h"
|
|
#import "TGFont.h"
|
|
|
|
#import <MapKit/MapKit.h>
|
|
|
|
#import "TGLocationUtils.h"
|
|
#import "TGLocationSignals.h"
|
|
|
|
#import "TGLocationVenue.h"
|
|
#import "TGLocationAnnotation.h"
|
|
|
|
#import "TGLocationTitleView.h"
|
|
#import "TGLocationMapView.h"
|
|
#import "TGLocationTrackingButton.h"
|
|
#import "TGLocationMapModeControl.h"
|
|
#import "TGLocationPinAnnotationView.h"
|
|
|
|
#import "TGLocationOptionsView.h"
|
|
#import "TGLocationInfoCell.h"
|
|
#import "TGLocationLiveCell.h"
|
|
|
|
#import <LegacyComponents/TGMenuSheetController.h>
|
|
|
|
@interface TGLiveLocation ()
|
|
|
|
- (CLLocation *)location;
|
|
|
|
@end
|
|
|
|
@interface TGLocationViewController () <MKMapViewDelegate>
|
|
{
|
|
id<LegacyComponentsContext> _context;
|
|
bool _dismissing;
|
|
|
|
id _peer;
|
|
TGMessage *_message;
|
|
TGLocationMediaAttachment *_locationAttachment;
|
|
UIColor *_venueColor;
|
|
|
|
TGLocationAnnotation *_annotation;
|
|
|
|
UIBarButtonItem *_actionsBarItem;
|
|
bool _didSetRightBarButton;
|
|
|
|
SVariable *_reloadReady;
|
|
SMetaDisposable *_reloadDisposable;
|
|
|
|
id<SDisposable> _frequentUpdatesDisposable;
|
|
|
|
SSignal *_signal;
|
|
TGLiveLocation *_currentLiveLocation;
|
|
SMetaDisposable *_liveLocationsDisposable;
|
|
NSArray *_initialLiveLocations;
|
|
NSArray *_liveLocations;
|
|
bool _hasOwnLiveLocation;
|
|
bool _ownLocationExpired;
|
|
|
|
bool _presentedLiveLocations;
|
|
bool _selectedCurrentLiveLocation;
|
|
|
|
bool _ignoreNextUpdates;
|
|
bool _focusOnOwnLocation;
|
|
bool _throttle;
|
|
TGLocationPinAnnotationView *_ownLiveLocationView;
|
|
__weak MKAnnotationView *_userLocationView;
|
|
|
|
UIImageView *_headingArrowView;
|
|
}
|
|
@end
|
|
|
|
@implementation TGLocationViewController
|
|
|
|
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context locationAttachment:(TGLocationMediaAttachment *)locationAttachment peer:(id)peer color:(UIColor *)color
|
|
{
|
|
self = [self initWithContext:context];
|
|
if (self != nil)
|
|
{
|
|
_locationAttachment = locationAttachment;
|
|
_venueColor = color;
|
|
|
|
_reloadDisposable = [[SMetaDisposable alloc] init];
|
|
_reloadReady = [[SVariable alloc] init];
|
|
[self setReloadReady:true];
|
|
|
|
_context = context;
|
|
_peer = peer;
|
|
|
|
if (locationAttachment.period == 0)
|
|
_annotation = [[TGLocationAnnotation alloc] initWithLocation:locationAttachment color:color];
|
|
|
|
_liveLocationsDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
self.titleText = locationAttachment.period > 0 ? TGLocalized(@"Map.LiveLocationTitle") : TGLocalized(@"Map.LocationTitle");
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context liveLocation:(TGLiveLocation *)liveLocation
|
|
{
|
|
self = [self initWithContext:context];
|
|
if (self != nil)
|
|
{
|
|
_message = liveLocation.message;
|
|
_locationAttachment = liveLocation.message.locationAttachment;
|
|
_currentLiveLocation = liveLocation;
|
|
if (liveLocation)
|
|
{
|
|
_liveLocations = @[liveLocation];
|
|
_hasOwnLiveLocation = liveLocation.hasOwnSession;
|
|
if (_hasOwnLiveLocation)
|
|
_ownLocationExpired = liveLocation.isExpired;
|
|
}
|
|
_reloadDisposable = [[SMetaDisposable alloc] init];
|
|
_reloadReady = [[SVariable alloc] init];
|
|
[self setReloadReady:true];
|
|
|
|
_context = context;
|
|
_peer = liveLocation.peer;
|
|
|
|
_liveLocationsDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
self.titleText = _locationAttachment.period > 0 ? TGLocalized(@"Map.LiveLocationTitle") : TGLocalized(@"Map.LocationTitle");
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context message:(TGMessage *)message peer:(id)peer color:(UIColor *)color
|
|
{
|
|
self = [self initWithContext:context];
|
|
if (self != nil)
|
|
{
|
|
_message = message;
|
|
_locationAttachment = message.locationAttachment;
|
|
|
|
_reloadDisposable = [[SMetaDisposable alloc] init];
|
|
_reloadReady = [[SVariable alloc] init];
|
|
[self setReloadReady:true];
|
|
|
|
_context = context;
|
|
_peer = peer;
|
|
_venueColor = color;
|
|
|
|
if (_locationAttachment.period == 0)
|
|
_annotation = [[TGLocationAnnotation alloc] initWithLocation:_locationAttachment color:color];
|
|
|
|
_liveLocationsDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
self.titleText = _locationAttachment.period > 0 ? TGLocalized(@"Map.LiveLocationTitle") : TGLocalized(@"Map.LocationTitle");
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
_mapView.delegate = nil;
|
|
[_liveLocationsDisposable dispose];
|
|
[_reloadDisposable dispose];
|
|
[_frequentUpdatesDisposable dispose];
|
|
|
|
[_locationManager stopUpdatingHeading];
|
|
}
|
|
|
|
- (void)tg_setRightBarButtonItem:(UIBarButtonItem *)barButtonItem action:(bool)action animated:(bool)animated {
|
|
if (self.updateRightBarItem != nil) {
|
|
self.updateRightBarItem(barButtonItem, action, animated);
|
|
} else {
|
|
[self setRightBarButtonItem:barButtonItem animated:animated];
|
|
}
|
|
}
|
|
|
|
- (void)setFrequentUpdatesHandle:(id<SDisposable>)disposable
|
|
{
|
|
_frequentUpdatesDisposable = disposable;
|
|
}
|
|
|
|
- (void)setLiveLocationsSignal:(SSignal *)signal
|
|
{
|
|
if (_currentLiveLocation.isOwnLocation)
|
|
{
|
|
_signal = [[signal reduceLeftWithPassthrough:nil with:^id(id current, id value, void (^emit)(id))
|
|
{
|
|
if (current == nil)
|
|
{
|
|
emit([SSignal single:value]);
|
|
return @true;
|
|
}
|
|
else
|
|
{
|
|
emit([[SSignal single:value] delay:0.25 onQueue:[SQueue concurrentDefaultQueue]]);
|
|
return current;
|
|
}
|
|
}] switchToLatest];
|
|
}
|
|
else
|
|
{
|
|
__weak TGLocationViewController *weakSelf = self;
|
|
_signal = [signal mapToSignal:^SSignal *(id value)
|
|
{
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return nil;
|
|
|
|
if (strongSelf->_throttle)
|
|
return [[SSignal single:value] delay:0.25 onQueue:[SQueue concurrentDefaultQueue]];
|
|
else
|
|
return [SSignal single:value];
|
|
}];
|
|
}
|
|
|
|
[self setupSignals];
|
|
}
|
|
|
|
- (void)setLiveLocations:(NSArray *)liveLocations actual:(bool)actual
|
|
{
|
|
if (liveLocations.count == 0 && _currentLiveLocation != nil)
|
|
liveLocations = @[ _currentLiveLocation ];
|
|
|
|
TGLiveLocation *ownLiveLocation = nil;
|
|
for (TGLiveLocation *liveLocation in liveLocations)
|
|
{
|
|
if (liveLocation.hasOwnSession)
|
|
{
|
|
ownLiveLocation = liveLocation;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_hasOwnLiveLocation = ownLiveLocation != nil;
|
|
_ownLocationExpired = ownLiveLocation.isExpired;
|
|
|
|
if (_hasOwnLiveLocation && !_ownLocationExpired)
|
|
{
|
|
TGLocationAnnotation *annotation = [[TGLocationAnnotation alloc] initWithLocation:ownLiveLocation.message.locationAttachment];
|
|
annotation.peer = ownLiveLocation.peer;
|
|
annotation.isOwn = true;
|
|
|
|
if (_ownLiveLocationView == nil)
|
|
{
|
|
_ownLiveLocationView = [[TGLocationPinAnnotationView alloc] initWithAnnotation:annotation];
|
|
_ownLiveLocationView.pallete = self.pallete;
|
|
_ownLiveLocationView.frame = CGRectOffset(_ownLiveLocationView.frame, 21.0f, 22.0f);
|
|
[_userLocationView addSubview:_ownLiveLocationView];
|
|
|
|
if (_currentLiveLocation.hasOwnSession)
|
|
[self selectOwnAnnotationAnimated:false];
|
|
}
|
|
else
|
|
{
|
|
_ownLiveLocationView.annotation = annotation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
[_ownLiveLocationView removeFromSuperview];
|
|
_ownLiveLocationView = nil;
|
|
}
|
|
|
|
CGFloat previousLocationsCount = _liveLocations.count;
|
|
_liveLocations = liveLocations;
|
|
[self reloadData];
|
|
|
|
if (!_presentedLiveLocations && actual)
|
|
{
|
|
_presentedLiveLocations = true;
|
|
if (previousLocationsCount < liveLocations.count > 0)
|
|
{
|
|
CGFloat updatedHeight = [self possibleContentHeight] - [self visibleContentHeight];
|
|
if (fabs(-_tableView.contentInset.top + updatedHeight - _tableView.contentOffset.y) > FLT_EPSILON)
|
|
{
|
|
TGDispatchAfter(0.3, dispatch_get_main_queue(), ^
|
|
{
|
|
[self setReloadReady:false];
|
|
[_tableView setContentOffset:CGPointMake(0.0f, -_tableView.contentInset.top + updatedHeight) animated:true];
|
|
});
|
|
}
|
|
}
|
|
|
|
if (_currentLiveLocation.hasOwnSession && !_ownLocationExpired && !self.zoomToFitAllLocationsOnScreen)
|
|
{
|
|
[_mapView setUserTrackingMode:[TGLocationTrackingButton userTrackingModeWithLocationTrackingMode:TGLocationTrackingModeFollow] animated:false];
|
|
[_optionsView setTrackingMode:TGLocationTrackingModeFollow animated:true];
|
|
}
|
|
}
|
|
[self updateAnnotations];
|
|
|
|
if ([self isLiveLocation])
|
|
{
|
|
if ([self hasMoreThanOneLocation])
|
|
{
|
|
if (!_didSetRightBarButton)
|
|
{
|
|
_didSetRightBarButton = true;
|
|
[self tg_setRightBarButtonItem:_actionsBarItem action:false animated:true];
|
|
}
|
|
|
|
if (actual && self.zoomToFitAllLocationsOnScreen)
|
|
{
|
|
_zoomToFitAllLocationsOnScreen = false;
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
{
|
|
MKMapRect visibleMapRect = _mapView.visibleMapRect;
|
|
NSSet *visibleAnnotations = [_mapView annotationsInMapRect:visibleMapRect];
|
|
if (visibleAnnotations.count == _mapView.annotations.count)
|
|
return;
|
|
|
|
[self showAllPressed];
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_didSetRightBarButton)
|
|
{
|
|
_didSetRightBarButton = false;
|
|
[self tg_setRightBarButtonItem:nil action:false animated:true];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_focusOnOwnLocation)
|
|
{
|
|
if (_ownLiveLocationView != nil && !_ownLiveLocationView.isSelected)
|
|
{
|
|
[self selectOwnAnnotationAnimated:false];
|
|
_focusOnOwnLocation = false;
|
|
_throttle = false;
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
{
|
|
MKMapRect visibleMapRect = _mapView.visibleMapRect;
|
|
NSSet *visibleAnnotations = [_mapView annotationsInMapRect:visibleMapRect];
|
|
if (visibleAnnotations.count == _mapView.annotations.count)
|
|
return;
|
|
|
|
[self showAllPressed];
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
- (bool)handleOwnAnnotationTap:(CGPoint)location
|
|
{
|
|
if (_ownLiveLocationView == nil)
|
|
return false;
|
|
|
|
if (CGRectContainsPoint([_ownLiveLocationView.superview convertRect:CGRectInset(_ownLiveLocationView.frame, -16.0f, - 16.0f) toView:_mapView], location))
|
|
{
|
|
[self selectOwnAnnotation];
|
|
[self setMapCenterCoordinate:_ownLiveLocationView.annotation.coordinate offset:CGPointZero animated:true];
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
- (void)reloadData
|
|
{
|
|
[_reloadDisposable setDisposable:[[self reloadReadySignal] startWithNext:nil completed:^
|
|
{
|
|
[_tableView reloadData];
|
|
_edgeView.highlighted = false;
|
|
}]];
|
|
}
|
|
|
|
double DegreesToRadians(double degrees) {return degrees * M_PI / 180.0;};
|
|
double RadiansToDegrees(double radians) {return radians * 180.0/M_PI;};
|
|
|
|
- (double)bearingFromLocation:(CLLocationCoordinate2D)fromLocation toLocation:(CLLocationCoordinate2D)toLocation
|
|
{
|
|
double lat1 = DegreesToRadians(fromLocation.latitude);
|
|
double lon1 = DegreesToRadians(fromLocation.longitude);
|
|
|
|
double lat2 = DegreesToRadians(toLocation.latitude);
|
|
double lon2 = DegreesToRadians(toLocation.longitude);
|
|
|
|
double dLon = lon2 - lon1;
|
|
|
|
double y = sin(dLon) * cos(lat2);
|
|
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
|
|
double radiansBearing = atan2(y, x);
|
|
|
|
return radiansBearing;
|
|
}
|
|
|
|
- (void)updateAnnotations
|
|
{
|
|
NSMutableDictionary *liveLocations = [[NSMutableDictionary alloc] init];
|
|
for (TGLiveLocation *liveLocation in _liveLocations)
|
|
{
|
|
if (!liveLocation.hasOwnSession || liveLocation.isExpired)
|
|
liveLocations[@(liveLocation.message.mid)] = liveLocation;
|
|
}
|
|
|
|
TGLocationAnnotation *currentAnnotation = nil;
|
|
NSMutableSet *annotationsToRemove = [[NSMutableSet alloc] init];
|
|
for (TGLocationAnnotation *annotation in _mapView.annotations)
|
|
{
|
|
if (![annotation isKindOfClass:[TGLocationAnnotation class]] || annotation == _annotation)
|
|
continue;
|
|
|
|
if (liveLocations[@(annotation.messageId)] != nil)
|
|
{
|
|
[UIView animateWithDuration:0.3 animations:^{
|
|
CLLocationCoordinate2D previousCoordinate = annotation.coordinate;
|
|
annotation.coordinate = [(TGLiveLocation *)liveLocations[@(annotation.messageId)] location].coordinate;
|
|
|
|
|
|
CLLocation *previous = [[CLLocation alloc] initWithLatitude:previousCoordinate.latitude longitude:previousCoordinate.longitude];
|
|
CLLocation *new = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
|
|
// if ([new distanceFromLocation:previous] > 20) {
|
|
CGFloat hdg = [self bearingFromLocation:previousCoordinate toLocation:annotation.coordinate];
|
|
if (hdg != 0.0) {
|
|
annotation.heading = @(hdg);
|
|
}
|
|
// }
|
|
}];
|
|
annotation.isExpired = [(TGLiveLocation *)liveLocations[@(annotation.messageId)] isExpired];
|
|
[liveLocations removeObjectForKey:@(annotation.messageId)];
|
|
|
|
if (annotation.messageId == _currentLiveLocation.message.mid)
|
|
currentAnnotation = annotation;
|
|
}
|
|
else
|
|
{
|
|
[annotationsToRemove addObject:annotation];
|
|
}
|
|
}
|
|
|
|
[_mapView removeAnnotations:annotationsToRemove.allObjects];
|
|
|
|
NSMutableArray *newAnnotations = [[NSMutableArray alloc] init];
|
|
for (TGLiveLocation *liveLocation in liveLocations.allValues)
|
|
{
|
|
TGLocationAnnotation *annotation = [[TGLocationAnnotation alloc] initWithLocation:liveLocation.message.locationAttachment];
|
|
annotation.peer = liveLocation.peer;
|
|
annotation.messageId = liveLocation.message.mid;
|
|
annotation.isExpired = liveLocation.isExpired;
|
|
|
|
[newAnnotations addObject:annotation];
|
|
|
|
if (annotation.messageId == _currentLiveLocation.message.mid)
|
|
currentAnnotation = annotation;
|
|
}
|
|
|
|
[_mapView addAnnotations:newAnnotations];
|
|
|
|
NSInteger annotationsCount = _ownLiveLocationView != nil ? 1 : 0;
|
|
for (TGLocationAnnotation *annotation in _mapView.annotations)
|
|
{
|
|
if ([annotation isKindOfClass:[TGLocationAnnotation class]] && ((TGLocationAnnotation *)annotation).isLiveLocation)
|
|
annotationsCount += 1;
|
|
}
|
|
|
|
_mapView.allowAnnotationSelectionChanges = annotationsCount;
|
|
|
|
if (!_selectedCurrentLiveLocation && currentAnnotation != nil)
|
|
{
|
|
_selectedCurrentLiveLocation = true;
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
{
|
|
[_mapView setSelectedAnnotations:@[currentAnnotation]];
|
|
});
|
|
}
|
|
}
|
|
|
|
- (void)loadView
|
|
{
|
|
[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;
|
|
|
|
__weak TGLocationViewController *weakSelf = self;
|
|
_mapView.customAnnotationTap = ^bool(CGPoint location)
|
|
{
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return false;
|
|
return [strongSelf handleOwnAnnotationTap:location];
|
|
};
|
|
|
|
if (TGIsPad() || _modalMode)
|
|
{
|
|
[self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Done") style:UIBarButtonItemStyleDone target:self action:@selector(dismissButtonPressed)]];
|
|
}
|
|
|
|
if ([self isLiveLocation])
|
|
{
|
|
NSString *actionsButtonTitle = TGLocalized(@"Map.LiveLocationShowAll");
|
|
_actionsBarItem = [[UIBarButtonItem alloc] initWithTitle:actionsButtonTitle style:UIBarButtonItemStylePlain target:self action:@selector(showAllPressed)];
|
|
}
|
|
else
|
|
{
|
|
if (iosMajorVersion() >= 7)
|
|
{
|
|
_actionsBarItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(actionsButtonPressed)];
|
|
[self tg_setRightBarButtonItem:_actionsBarItem action:true animated:false];
|
|
}
|
|
else
|
|
{
|
|
NSString *actionsButtonTitle = TGLocalized(@"Common.More");
|
|
_actionsBarItem = [[UIBarButtonItem alloc] initWithTitle:actionsButtonTitle style:UIBarButtonItemStylePlain target:self action:@selector(actionsButtonPressed)];
|
|
[self tg_setRightBarButtonItem:_actionsBarItem action:true animated:false];
|
|
}
|
|
}
|
|
|
|
if (_previewMode)
|
|
_optionsView.hidden = true;
|
|
}
|
|
|
|
- (void)viewDidLoad
|
|
{
|
|
[super viewDidLoad];
|
|
|
|
[_mapView addAnnotation:_annotation];
|
|
[_mapView selectAnnotation:_annotation animated:false];
|
|
|
|
_mapView.region = MKCoordinateRegionMake([self locationCoordinate], MKCoordinateSpanMake(0.008, 0.008));
|
|
|
|
[TGLocationUtils requestWhenInUserLocationAuthorizationWithLocationManager:_locationManager];
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated
|
|
{
|
|
[super viewWillAppear:animated];
|
|
|
|
[_locationManager startUpdatingHeading];
|
|
|
|
if (self.previewMode && !animated)
|
|
{
|
|
UIView *contentView = [[_mapView subviews] firstObject];
|
|
UIView *annotationContainer = nil;
|
|
for (NSUInteger i = 1; i < contentView.subviews.count; i++)
|
|
{
|
|
UIView *view = contentView.subviews[i];
|
|
if ([NSStringFromClass(view.class) rangeOfString:@"AnnotationContainer"].location != NSNotFound)
|
|
{
|
|
annotationContainer = view;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (UIView *view in annotationContainer.subviews)
|
|
view.frame = CGRectOffset(view.frame, 0, 48.5f);
|
|
}
|
|
|
|
if ([_tableView numberOfRowsInSection:0] > 0)
|
|
[_tableView layoutSubviews];
|
|
|
|
CGFloat updatedHeight = [self possibleContentHeight] - [self visibleContentHeight];
|
|
[_tableView setContentOffset:CGPointMake(0.0f, -_tableView.contentInset.top + updatedHeight) animated:false];
|
|
|
|
if (_initialLiveLocations.count > 0)
|
|
{
|
|
[self setLiveLocations:_initialLiveLocations actual:false];
|
|
_initialLiveLocations = nil;
|
|
}
|
|
}
|
|
|
|
- (void)viewDidAppear:(BOOL)animated
|
|
{
|
|
[super viewDidAppear:animated];
|
|
if (self.onViewDidAppear != nil)
|
|
self.onViewDidAppear();
|
|
}
|
|
|
|
- (void)setupSignals
|
|
{
|
|
SSignal *combinedSignal = [SSignal combineSignals:@[ [[[self userLocationSignal] deliverOn:[SQueue concurrentBackgroundQueue]] map:^id(id location) {
|
|
if (location != nil)
|
|
return location;
|
|
else
|
|
return [NSNull null];
|
|
}], _signal ]];
|
|
|
|
__weak TGLocationViewController *weakSelf = self;
|
|
[_liveLocationsDisposable setDisposable:[combinedSignal startWithNext:^(NSArray *next)
|
|
{
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
if (strongSelf->_dismissing)
|
|
return;
|
|
|
|
CLLocation *currentLocation = [next.firstObject isKindOfClass:[CLLocation class]] ? next.firstObject : nil;
|
|
NSArray *liveLocations = next.lastObject;
|
|
bool actual = liveLocations.count > 0;
|
|
|
|
NSMutableArray *filteredLiveLocations = [[NSMutableArray alloc] init];
|
|
bool hasCurrentLocation = false;
|
|
bool currentExpiredLocationIsOwn = false;
|
|
for (TGLiveLocation *liveLocation in liveLocations)
|
|
{
|
|
if (!liveLocation.isExpired)
|
|
[filteredLiveLocations addObject:liveLocation];
|
|
if (liveLocation.message.mid == strongSelf->_currentLiveLocation.message.mid)
|
|
hasCurrentLocation = true;
|
|
}
|
|
if (!hasCurrentLocation && strongSelf->_currentLiveLocation != nil)
|
|
{
|
|
bool isChannel = [strongSelf->_currentLiveLocation.peer isKindOfClass:[TGConversation class]] && !((TGConversation *)strongSelf->_currentLiveLocation.peer).isChannelGroup;
|
|
|
|
TGLiveLocation *currentExpiredLiveLocation = [[TGLiveLocation alloc] initWithMessage:strongSelf->_currentLiveLocation.message peer:strongSelf->_currentLiveLocation.peer hasOwnSession:strongSelf->_currentLiveLocation.hasOwnSession isOwnLocation:strongSelf->_currentLiveLocation.isOwnLocation isExpired:isChannel ? strongSelf->_currentLiveLocation.isExpired : true];
|
|
[filteredLiveLocations addObject:currentExpiredLiveLocation];
|
|
|
|
if (currentExpiredLiveLocation.isOwnLocation && currentExpiredLiveLocation.isExpired)
|
|
currentExpiredLocationIsOwn = true;
|
|
}
|
|
liveLocations = filteredLiveLocations;
|
|
|
|
for (TGLiveLocation *location in filteredLiveLocations)
|
|
{
|
|
if (strongSelf->_ignoreNextUpdates && location.hasOwnSession && !location.isExpired && (currentExpiredLocationIsOwn || (strongSelf->_currentLiveLocation.isOwnLocation && strongSelf->_currentLiveLocation.isExpired)))
|
|
{
|
|
strongSelf->_dismissing = true;
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
if (strongSelf.openLocation != nil)
|
|
strongSelf.openLocation(location.message);
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (strongSelf->_ignoreNextUpdates)
|
|
return;
|
|
|
|
NSMutableArray *sortedLiveLocations = [liveLocations mutableCopy];
|
|
if (currentLocation != nil)
|
|
{
|
|
[sortedLiveLocations sortUsingComparator:^NSComparisonResult(TGLiveLocation *obj1, TGLiveLocation *obj2)
|
|
{
|
|
if (obj1.hasOwnSession)
|
|
return NSOrderedAscending;
|
|
else if (obj2.hasOwnSession)
|
|
return NSOrderedDescending;
|
|
|
|
if (obj1.message.mid == strongSelf->_currentLiveLocation.message.mid)
|
|
return NSOrderedAscending;
|
|
else if (obj2.message.mid == strongSelf->_currentLiveLocation.message.mid)
|
|
return NSOrderedDescending;
|
|
|
|
CGFloat distance1 = [obj1.location distanceFromLocation:currentLocation];
|
|
CGFloat distance2 = [obj2.location distanceFromLocation:currentLocation];
|
|
|
|
if (distance1 > distance2)
|
|
return NSOrderedDescending;
|
|
else if (distance1 < distance2)
|
|
return NSOrderedAscending;
|
|
|
|
return NSOrderedSame;
|
|
}];
|
|
}
|
|
else
|
|
{
|
|
[sortedLiveLocations sortUsingComparator:^NSComparisonResult(TGLiveLocation *obj1, TGLiveLocation *obj2)
|
|
{
|
|
if (obj1.hasOwnSession)
|
|
return NSOrderedAscending;
|
|
else if (obj2.hasOwnSession)
|
|
return NSOrderedDescending;
|
|
|
|
if (obj1.message.mid == strongSelf->_currentLiveLocation.message.mid)
|
|
return NSOrderedAscending;
|
|
else if (obj2.message.mid == strongSelf->_currentLiveLocation.message.mid)
|
|
return NSOrderedDescending;
|
|
|
|
int32_t date1 = [obj1.message actualDate];
|
|
int32_t date2 = [obj2.message actualDate];
|
|
|
|
if (date1 > date2)
|
|
return NSOrderedAscending;
|
|
else if (date1 < date2)
|
|
return NSOrderedDescending;
|
|
|
|
return NSOrderedSame;
|
|
}];
|
|
}
|
|
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
if ([strongSelf isViewLoaded])
|
|
[strongSelf setLiveLocations:sortedLiveLocations actual:actual];
|
|
else
|
|
strongSelf->_initialLiveLocations = sortedLiveLocations;
|
|
});
|
|
}]];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)setPreviewMode:(bool)previewMode
|
|
{
|
|
_previewMode = previewMode;
|
|
|
|
if (!previewMode)
|
|
{
|
|
[self setRightBarButtonItem:_actionsBarItem];
|
|
_optionsView.hidden = false;
|
|
}
|
|
}
|
|
|
|
#pragma mark - Actions
|
|
|
|
- (void)fitAllLocations:(NSArray *)locations
|
|
{
|
|
MKMapRect zoomRect = MKMapRectNull;
|
|
for (CLLocation *location in locations)
|
|
{
|
|
MKMapPoint annotationPoint = MKMapPointForCoordinate(location.coordinate);
|
|
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
|
|
zoomRect = MKMapRectUnion(zoomRect, pointRect);
|
|
}
|
|
UIEdgeInsets insets = UIEdgeInsetsMake(TGLocationMapInset + 110.0f, 80.0f, TGLocationMapInset + 110.0f, 80.0f);
|
|
zoomRect = [_mapView mapRectThatFits:zoomRect edgePadding:insets];
|
|
[_mapView setVisibleMapRect:zoomRect animated:true];
|
|
}
|
|
|
|
- (void)showAllPressed
|
|
{
|
|
NSMutableArray *locations = [[NSMutableArray alloc] init];
|
|
for (id <MKAnnotation> annotation in _mapView.annotations)
|
|
{
|
|
CLLocation *location = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
|
|
[locations addObject:location];
|
|
}
|
|
|
|
[self fitAllLocations:locations];
|
|
}
|
|
|
|
- (void)dismissButtonPressed
|
|
{
|
|
[self.presentingViewController dismissViewControllerAnimated:true completion:nil];
|
|
}
|
|
|
|
- (void)actionsButtonPressed
|
|
{
|
|
TGLocationMediaAttachment *locationAttachment = _locationAttachment;
|
|
if (locationAttachment == nil)
|
|
return;
|
|
|
|
if (self.presentActionsMenu != nil)
|
|
{
|
|
self.presentActionsMenu(locationAttachment, false);
|
|
return;
|
|
}
|
|
|
|
CGRect (^sourceRect)(void) = ^CGRect
|
|
{
|
|
return CGRectZero;
|
|
};
|
|
|
|
__weak TGLocationViewController *weakSelf = self;
|
|
if (_presentOpenInMenu && _presentOpenInMenu(self, locationAttachment, false, ^(TGMenuSheetController *menuController)
|
|
{
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_presentShareMenu) {
|
|
strongSelf->_presentShareMenu(menuController, [strongSelf locationCoordinate]);
|
|
}
|
|
}))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:false];
|
|
controller.dismissesByOutsideTap = true;
|
|
controller.hasSwipeGesture = true;
|
|
controller.narrowInLandscape = true;
|
|
controller.sourceRect = sourceRect;
|
|
controller.barButtonItem = self.navigationItem.rightBarButtonItem;
|
|
|
|
NSMutableArray *itemViews = [[NSMutableArray alloc] init];
|
|
|
|
__weak TGMenuSheetController *weakController = controller;
|
|
TGMenuSheetButtonItemView *openItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Map.OpenInMaps") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
|
{
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
__strong TGMenuSheetController *strongController = weakController;
|
|
if (strongController == nil)
|
|
return;
|
|
|
|
[strongController dismissAnimated:true];
|
|
[TGLocationUtils openMapsWithCoordinate:[strongSelf locationCoordinate] withDirections:false locationName:strongSelf->_annotation.title];
|
|
}];
|
|
[itemViews addObject:openItem];
|
|
|
|
TGMenuSheetButtonItemView *shareItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Conversation.ContextMenuShare") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
|
{
|
|
__strong TGMenuSheetController *strongController = weakController;
|
|
if (strongController == nil)
|
|
return;
|
|
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_presentShareMenu) {
|
|
strongSelf->_presentShareMenu(strongController, [strongSelf locationCoordinate]);
|
|
}
|
|
}];
|
|
[itemViews addObject:shareItem];
|
|
|
|
TGMenuSheetButtonItemView *cancelItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
|
|
{
|
|
__strong TGMenuSheetController *strongController = weakController;
|
|
if (strongController == nil)
|
|
return;
|
|
|
|
[strongController dismissAnimated:true manual:true];
|
|
}];
|
|
[itemViews addObject:cancelItem];
|
|
|
|
[controller setItemViews:itemViews];
|
|
[controller presentInViewController:self sourceView:self.view animated:true];
|
|
}
|
|
}
|
|
|
|
- (void)userLocationButtonPressed
|
|
{
|
|
if (![self hasUserLocation])
|
|
{
|
|
if (![TGLocationUtils requestWhenInUserLocationAuthorizationWithLocationManager:_locationManager])
|
|
{
|
|
if (_locationServicesDisabled)
|
|
[[[LegacyComponentsGlobals provider] accessChecker] checkLocationAuthorizationStatusForIntent:TGLocationAccessIntentTracking alertDismissComlpetion:nil];
|
|
}
|
|
|
|
[self updateLocationAvailability];
|
|
return;
|
|
}
|
|
|
|
TGLocationTrackingMode newMode = TGLocationTrackingModeNone;
|
|
switch ([TGLocationTrackingButton locationTrackingModeWithUserTrackingMode:_mapView.userTrackingMode])
|
|
{
|
|
case TGLocationTrackingModeFollow:
|
|
newMode = TGLocationTrackingModeFollowWithHeading;
|
|
break;
|
|
|
|
case TGLocationTrackingModeFollowWithHeading:
|
|
newMode = TGLocationTrackingModeNone;
|
|
break;
|
|
|
|
default:
|
|
newMode = TGLocationTrackingModeFollow;
|
|
break;
|
|
}
|
|
|
|
[_mapView setUserTrackingMode:[TGLocationTrackingButton userTrackingModeWithLocationTrackingMode:newMode] animated:true];
|
|
[_optionsView setTrackingMode:newMode animated:true];
|
|
|
|
if (newMode != TGLocationTrackingModeNone && _ownLiveLocationView != nil)
|
|
[self selectOwnAnnotation];
|
|
}
|
|
|
|
- (void)selectOwnAnnotation
|
|
{
|
|
[self selectOwnAnnotationAnimated:true];
|
|
}
|
|
|
|
- (void)selectOwnAnnotationAnimated:(bool)animated
|
|
{
|
|
if (!_ownLiveLocationView.isSelected)
|
|
[_ownLiveLocationView setSelected:true animated:animated];
|
|
[_mapView deselectAnnotation:_mapView.selectedAnnotations.firstObject animated:true];
|
|
[_ownLiveLocationView.superview.superview bringSubviewToFront:_ownLiveLocationView.superview];
|
|
}
|
|
|
|
- (void)getDirectionsPressed:(TGLocationMediaAttachment *)locationAttachment prompt:(bool)prompt
|
|
{
|
|
if (self.presentActionsMenu != nil)
|
|
{
|
|
self.presentActionsMenu(locationAttachment, true);
|
|
return;
|
|
}
|
|
|
|
if (_presentOpenInMenu && _presentOpenInMenu(self, locationAttachment, true, nil))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
void (^block)(void) = ^
|
|
{
|
|
NSString *title = @"";
|
|
if (locationAttachment.venue != nil)
|
|
title = locationAttachment.venue.title;
|
|
else if ([_peer isKindOfClass:[TGUser class]])
|
|
title = ((TGUser *)_peer).displayName;
|
|
else if ([_peer isKindOfClass:[TGConversation class]])
|
|
title = ((TGConversation *)_peer).chatTitle;
|
|
|
|
[TGLocationUtils openMapsWithCoordinate:[self locationCoordinate] withDirections:true locationName:title];
|
|
};
|
|
|
|
if (prompt)
|
|
{
|
|
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:false];
|
|
controller.dismissesByOutsideTap = true;
|
|
controller.narrowInLandscape = true;
|
|
|
|
__weak TGMenuSheetController *weakController = controller;
|
|
NSArray *items = @
|
|
[
|
|
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Map.GetDirections") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
|
{
|
|
__strong TGMenuSheetController *strongController = weakController;
|
|
if (strongController == nil)
|
|
return;
|
|
|
|
[strongController dismissAnimated:true];
|
|
block();
|
|
}],
|
|
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
|
|
{
|
|
__strong TGMenuSheetController *strongController = weakController;
|
|
if (strongController != nil)
|
|
[strongController dismissAnimated:true];
|
|
}]
|
|
];
|
|
|
|
[controller setItemViews:items];
|
|
controller.sourceRect = ^
|
|
{
|
|
return CGRectZero;
|
|
};
|
|
controller.permittedArrowDirections = UIPopoverArrowDirectionUp;
|
|
[controller presentInViewController:self sourceView:self.view animated:true];
|
|
}
|
|
else
|
|
{
|
|
block();
|
|
}
|
|
}
|
|
}
|
|
|
|
- (UIButton *)directionsButton
|
|
{
|
|
TGLocationInfoCell *infoCell = [_tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
|
|
if ([infoCell isKindOfClass:[TGLocationInfoCell class]])
|
|
return infoCell.directionsButton;
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (CGRect)_liveLocationMenuSourceRect
|
|
{
|
|
TGLocationLiveCell *cell = [_tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
|
|
if ([cell isKindOfClass:[TGLocationLiveCell class]])
|
|
return [cell convertRect:cell.bounds toView:self.view];
|
|
|
|
return CGRectZero;
|
|
}
|
|
|
|
#pragma mark - Map View Delegate
|
|
|
|
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
|
|
{
|
|
if (annotation == mapView.userLocation)
|
|
return nil;
|
|
|
|
TGLocationPinAnnotationView *view = (TGLocationPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:TGLocationPinAnnotationKind];
|
|
if (view == nil)
|
|
{
|
|
view = [[TGLocationPinAnnotationView alloc] initWithAnnotation:annotation];
|
|
view.pallete = self.pallete;
|
|
}
|
|
else
|
|
{
|
|
view.annotation = annotation;
|
|
}
|
|
view.layer.zPosition = -1;
|
|
|
|
return view;
|
|
}
|
|
|
|
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray<MKAnnotationView *> *)views
|
|
{
|
|
for (MKAnnotationView *view in views)
|
|
{
|
|
if ([view.annotation isKindOfClass:[MKUserLocation class]])
|
|
{
|
|
_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];
|
|
|
|
if (_currentLiveLocation.hasOwnSession && _mapView.selectedAnnotations.count == 0)
|
|
[_ownLiveLocationView setSelected:true animated:false];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
|
|
{
|
|
if (_ownLiveLocationView.isSelected)
|
|
[_ownLiveLocationView setSelected:false animated:true];
|
|
|
|
[self setMapCenterCoordinate:view.annotation.coordinate offset:CGPointZero animated:true];
|
|
|
|
[_optionsView setTrackingMode:TGLocationTrackingModeNone animated:true];
|
|
}
|
|
|
|
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated
|
|
{
|
|
if (mode == MKUserTrackingModeNone)
|
|
[_optionsView setTrackingMode:TGLocationTrackingModeNone animated:true];
|
|
}
|
|
|
|
- (CLLocationCoordinate2D)locationCoordinate
|
|
{
|
|
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
|
|
{
|
|
NSInteger count = 0;
|
|
if (![self isLiveLocation])
|
|
count += 1;
|
|
|
|
if (_liveLocations.count > 0)
|
|
{
|
|
count += _liveLocations.count;
|
|
if (self.allowLiveLocationSharing && !_hasOwnLiveLocation)
|
|
count += 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
__weak TGLocationViewController *weakSelf = self;
|
|
if (indexPath.row == 0 && ![self isLiveLocation])
|
|
{
|
|
TGLocationInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:TGLocationInfoCellKind];
|
|
if (cell == nil)
|
|
cell = [[TGLocationInfoCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationInfoCellKind];
|
|
cell.pallete = self.pallete;
|
|
[cell setLocation:_locationAttachment color: _venueColor messageId:_message.mid userLocationSignal:[self userLocationSignal]];
|
|
cell.locatePressed = ^
|
|
{
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
[strongSelf->_mapView deselectAnnotation:strongSelf->_mapView.selectedAnnotations.firstObject animated:true];
|
|
[strongSelf setMapCenterCoordinate:[strongSelf locationCoordinate] offset:CGPointZero animated:true];
|
|
}
|
|
};
|
|
cell.directionsPressed = ^
|
|
{
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf getDirectionsPressed:strongSelf->_locationAttachment prompt:false];
|
|
};
|
|
cell.safeInset = self.controllerSafeAreaInset;
|
|
return cell;
|
|
}
|
|
else
|
|
{
|
|
TGLocationLiveCell *cell = [tableView dequeueReusableCellWithIdentifier:TGLocationLiveCellKind];
|
|
if (cell == nil)
|
|
cell = [[TGLocationLiveCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationLiveCellKind];
|
|
cell.pallete = self.pallete;
|
|
cell.edgeView = indexPath.row == 0 ? _edgeHighlightView : nil;
|
|
cell.safeInset = self.controllerSafeAreaInset;
|
|
|
|
if (self.allowLiveLocationSharing && (indexPath.row == 0 || (![self isLiveLocation] && indexPath.row == 1)))
|
|
{
|
|
if (_hasOwnLiveLocation)
|
|
{
|
|
TGLiveLocation *liveLocation = _liveLocations.firstObject;
|
|
if (liveLocation.isExpired)
|
|
[cell configureForStart];
|
|
else
|
|
[cell configureForStopWithMessage:liveLocation.message remaining:self.remainingTimeForMessage(liveLocation.message)];
|
|
}
|
|
else
|
|
{
|
|
[cell configureForStart];
|
|
}
|
|
|
|
cell.longPressed = nil;
|
|
}
|
|
else
|
|
{
|
|
NSInteger index = indexPath.row;
|
|
if (![self isLiveLocation])
|
|
index -= 1;
|
|
if (self.allowLiveLocationSharing && !_hasOwnLiveLocation)
|
|
index -= 1;
|
|
|
|
TGLiveLocation *liveLocation = index >= 0 && index < _liveLocations.count ? _liveLocations[index] : nil;
|
|
[cell configureWithPeer:liveLocation.peer message:liveLocation.message remaining:self.remainingTimeForMessage(liveLocation.message) userLocationSignal:[self userLocationSignal]];
|
|
|
|
cell.longPressed = ^
|
|
{
|
|
__strong TGLocationViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf getDirectionsPressed:liveLocation.message.locationAttachment prompt:true];
|
|
};
|
|
}
|
|
return cell;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (indexPath.row == 0 && ![self isLiveLocation])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
[super tableView:tableView didHighlightRowAtIndexPath:indexPath];
|
|
[self setReloadReady:false];
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
[super tableView:tableView didUnhighlightRowAtIndexPath:indexPath];
|
|
if (!_tableView.isTracking)
|
|
[self setReloadReady:true];
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
bool animated = true;
|
|
if (indexPath.row == 0 && ![self isLiveLocation])
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
TGLocationLiveCell *cell = [tableView dequeueReusableCellWithIdentifier:TGLocationLiveCellKind];
|
|
if (cell == nil)
|
|
cell = [[TGLocationLiveCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationInfoCellKind];
|
|
|
|
if (self.allowLiveLocationSharing && (indexPath.row == 0 || (![self isLiveLocation] && indexPath.row == 1)))
|
|
{
|
|
if (_hasOwnLiveLocation && !_ownLocationExpired)
|
|
{
|
|
if (self.liveLocationStopped != nil)
|
|
self.liveLocationStopped();
|
|
}
|
|
else
|
|
{
|
|
[[[self userLocationSignal] take:1] startWithNext:^(CLLocation *location)
|
|
{
|
|
[self _presentLiveLocationMenu:location.coordinate dismissOnCompletion:true];
|
|
}];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NSInteger index = indexPath.row;
|
|
if (![self isLiveLocation])
|
|
index -= 1;
|
|
if (self.allowLiveLocationSharing && !_hasOwnLiveLocation)
|
|
index -= 1;
|
|
|
|
TGLiveLocation *liveLocation = _liveLocations[index];
|
|
for (TGLocationAnnotation *annotation in _mapView.annotations)
|
|
{
|
|
if (![annotation isKindOfClass:[TGLocationAnnotation class]])
|
|
continue;
|
|
|
|
if (annotation.messageId == liveLocation.message.mid)
|
|
{
|
|
if ([_mapView.selectedAnnotations containsObject:annotation])
|
|
[self setMapCenterCoordinate:annotation.coordinate offset:CGPointZero animated:true];
|
|
else
|
|
[_mapView selectAnnotation:annotation animated:true];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
[tableView deselectRowAtIndexPath:indexPath animated:animated];
|
|
}
|
|
|
|
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (indexPath.row == 0 && ![self isLiveLocation])
|
|
return TGLocationInfoCellHeight;
|
|
else
|
|
return TGLocationLiveCellHeight;
|
|
|
|
return 0;
|
|
}
|
|
|
|
- (CGFloat)visibleContentHeight
|
|
{
|
|
if (![self isLiveLocation])
|
|
return TGLocationInfoCellHeight + self.safeAreaInsetBottom;
|
|
else
|
|
return TGLocationLiveCellHeight + self.safeAreaInsetBottom;
|
|
}
|
|
|
|
- (CGFloat)possibleContentHeight
|
|
{
|
|
if (![self isLiveLocation])
|
|
{
|
|
CGFloat height = TGLocationInfoCellHeight;
|
|
if (_liveLocations.count > 0)
|
|
{
|
|
CGFloat count = _liveLocations.count;
|
|
if (self.allowLiveLocationSharing && !_hasOwnLiveLocation)
|
|
count += 1;
|
|
count = MIN(1.5f, count);
|
|
height += count * TGLocationLiveCellHeight;
|
|
}
|
|
return height + self.safeAreaInsetBottom;
|
|
}
|
|
else
|
|
{
|
|
CGFloat count = _liveLocations.count;
|
|
if (self.allowLiveLocationSharing && !_hasOwnLiveLocation)
|
|
count += 1;
|
|
count = MIN(2.5f, count);
|
|
CGFloat height = count * TGLocationLiveCellHeight;
|
|
return height + self.safeAreaInsetBottom;
|
|
}
|
|
}
|
|
|
|
- (bool)isLiveLocation
|
|
{
|
|
return _locationAttachment.period > 0;
|
|
}
|
|
|
|
- (bool)hasMoreThanOneLocation
|
|
{
|
|
return ((_hasOwnLiveLocation && _liveLocations.count > 1) || (!_hasOwnLiveLocation && _liveLocations.count > 0));
|
|
}
|
|
|
|
- (void)setReloadReady:(bool)ready
|
|
{
|
|
[_reloadReady set:[SSignal single:@(ready)]];
|
|
}
|
|
|
|
- (SSignal *)reloadReadySignal
|
|
{
|
|
return [[_reloadReady.signal filter:^bool(NSNumber *value) {
|
|
return value.boolValue;
|
|
}] take:1];
|
|
}
|
|
|
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
|
{
|
|
[super scrollViewDidScroll:scrollView];
|
|
|
|
_mapView.compassInsets = UIEdgeInsetsMake(TGLocationMapInset + 120.0f + (scrollView.contentOffset.y + scrollView.contentInset.top) / 2.0f, 0.0f, 0.0f, 10.0f + TGScreenPixel);
|
|
}
|
|
|
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
{
|
|
[self setReloadReady:false];
|
|
}
|
|
|
|
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
|
|
{
|
|
if (!scrollView.isTracking)
|
|
[self setReloadReady:true];
|
|
}
|
|
|
|
- (void)scrollViewDidEndDragging:(UIScrollView *)__unused scrollView willDecelerate:(BOOL)decelerate
|
|
{
|
|
if (!decelerate)
|
|
[self setReloadReady:true];
|
|
}
|
|
|
|
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)__unused scrollView
|
|
{
|
|
if (!scrollView.isTracking)
|
|
[self setReloadReady:true];
|
|
}
|
|
|
|
- (void)_willStartOwnLiveLocation
|
|
{
|
|
_focusOnOwnLocation = true;
|
|
|
|
if (_currentLiveLocation.isOwnLocation)
|
|
_ignoreNextUpdates = true;
|
|
else
|
|
_throttle = true;
|
|
}
|
|
|
|
- (void)layoutControllerForSize:(CGSize)size duration:(NSTimeInterval)duration
|
|
{
|
|
[super layoutControllerForSize:size duration:duration];
|
|
|
|
if (!self.isViewLoaded)
|
|
return;
|
|
|
|
for (UITableViewCell *cell in _tableView.visibleCells)
|
|
{
|
|
if ([cell isKindOfClass:[TGLocationInfoCell class]])
|
|
{
|
|
((TGLocationInfoCell *)cell).safeInset = self.controllerSafeAreaInset;
|
|
} else if ([cell isKindOfClass:[TGLocationLiveCell class]])
|
|
{
|
|
((TGLocationLiveCell *)cell).safeInset = self.controllerSafeAreaInset;
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation TGLiveLocation
|
|
|
|
- (instancetype)initWithMessage:(TGMessage *)message peer:(id)peer hasOwnSession:(bool)hasOwnSession isOwnLocation:(bool)isOwnLocation isExpired:(bool)isExpired
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
_message = message;
|
|
_peer = peer;
|
|
_hasOwnSession = hasOwnSession;
|
|
_isOwnLocation = isOwnLocation;
|
|
_isExpired = isExpired;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
|
|
- (instancetype)initWithMessage:(TGMessage *)message peer:(id)peer
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
_message = message;
|
|
_peer = peer;
|
|
_hasOwnSession = true;
|
|
_isOwnLocation = true;
|
|
_isExpired = false;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (int64_t)peerId
|
|
{
|
|
return [_peer isKindOfClass:[TGUser class]] ? ((TGUser *)_peer).uid : ((TGConversation *)_peer).conversationId;
|
|
}
|
|
|
|
- (CLLocation *)location
|
|
{
|
|
TGLocationMediaAttachment *location = _message.locationAttachment;
|
|
if (location == nil)
|
|
return nil;
|
|
|
|
return [[CLLocation alloc] initWithLatitude:location.latitude longitude:location.longitude];
|
|
}
|
|
|
|
@end
|