Cleanup legacy location viewer & picker

This commit is contained in:
Ilya Laktyushin 2020-11-03 10:51:31 +04:00
parent 5547abb639
commit c392c94d9d
55 changed files with 39 additions and 11129 deletions

View File

@ -1228,7 +1228,15 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
if let map = media.media as? TelegramMediaMap {
let controller = legacyLocationController(message: nil, mapMedia: map, context: self.context, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: { }, openUrl: { _ in })
let controllerParams = LocationViewParams(sendLiveLocation: { _ in
}, stopLiveLocation: { _ in
}, openUrl: { _ in }, openPeer: { _ in
}, showAll: false)
let peer = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: "", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let controller = LocationViewController(context: self.context, subject: message, params: controllerParams)
self.pushController(controller)
return
}

View File

@ -136,15 +136,7 @@
#import <LegacyComponents/TGLiveUploadInterface.h>
#import <LegacyComponents/TGLocalMessageMetaMediaAttachment.h>
#import <LegacyComponents/TGLocalization.h>
#import <LegacyComponents/TGLocationLiveElapsedView.h>
#import <LegacyComponents/TGLocationLiveSessionItemView.h>
#import <LegacyComponents/TGLocationMapViewController.h>
#import <LegacyComponents/TGLocationMediaAttachment.h>
#import <LegacyComponents/TGLocationPickerController.h>
#import <LegacyComponents/TGLocationPulseView.h>
#import <LegacyComponents/TGLocationVenue.h>
#import <LegacyComponents/TGLocationViewController.h>
#import <LegacyComponents/TGLocationWavesView.h>
#import <LegacyComponents/TGMediaAsset.h>
#import <LegacyComponents/TGMediaAssetFetchResult.h>
#import <LegacyComponents/TGMediaAssetFetchResultChange.h>

View File

@ -1,8 +0,0 @@
#import <UIKit/UIKit.h>
@interface TGLocationLiveElapsedView : UIView
- (void)setColor:(UIColor *)color;
- (void)setRemaining:(int32_t)remaining period:(int32_t)period;
@end

View File

@ -1,12 +0,0 @@
#import <LegacyComponents/LegacyComponents.h>
#import <LegacyComponents/TGMenuSheetButtonItemView.h>
#import <SSignalKit/SSignalKit.h>
@class TGUser;
@class TGMessage;
@interface TGLocationLiveSessionItemView : TGMenuSheetButtonItemView
- (instancetype)initWithMessage:(TGMessage *)message peer:(id)peer remaining:(SSignal *)remaining action:(void (^)(void))action;
@end

View File

@ -1,84 +0,0 @@
#import <LegacyComponents/TGViewController.h>
#import <LegacyComponents/LegacyComponentsContext.h>
#import <MapKit/MapKit.h>
#import <SSignalKit/SSignalKit.h>
@class TGLocationMapView;
@class TGLocationOptionsView;
@class TGSearchBarPallete;
@interface TGLocationPallete : NSObject
@property (nonatomic, readonly) UIColor *backgroundColor;
@property (nonatomic, readonly) UIColor *selectionColor;
@property (nonatomic, readonly) UIColor *separatorColor;
@property (nonatomic, readonly) UIColor *textColor;
@property (nonatomic, readonly) UIColor *secondaryTextColor;
@property (nonatomic, readonly) UIColor *accentColor;
@property (nonatomic, readonly) UIColor *destructiveColor;
@property (nonatomic, readonly) UIColor *locationColor;
@property (nonatomic, readonly) UIColor *liveLocationColor;
@property (nonatomic, readonly) UIColor *iconColor;
@property (nonatomic, readonly) UIColor *sectionHeaderBackgroundColor;
@property (nonatomic, readonly) UIColor *sectionHeaderTextColor;
@property (nonatomic, readonly) TGSearchBarPallete *searchBarPallete;
@property (nonatomic, readonly) UIImage *avatarPlaceholder;
+ (instancetype)palleteWithBackgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor destructiveColor:(UIColor *)destructiveColor locationColor:(UIColor *)locationColor liveLocationColor:(UIColor *)liveLocationColor iconColor:(UIColor *)iconColor sectionHeaderBackgroundColor:(UIColor *)sectionHeaderBackgroundColor sectionHeaderTextColor:(UIColor *)sectionHeaderTextColor searchBarPallete:(TGSearchBarPallete *)searchBarPallete avatarPlaceholder:(UIImage *)avatarPlaceholder;
@end
@interface TGLocationMapViewController : TGViewController <UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate, MKMapViewDelegate>
{
CLLocationManager *_locationManager;
bool _locationServicesDisabled;
CGFloat _tableViewTopInset;
CGFloat _tableViewBottomInset;
UITableView *_tableView;
UIActivityIndicatorView *_activityIndicator;
UILabel *_messageLabel;
UIView *_mapViewWrapper;
TGLocationMapView *_mapView;
TGLocationOptionsView *_optionsView;
UIImageView *_edgeView;
UIImageView *_edgeHighlightView;
}
@property (nonatomic, copy) void (^liveLocationStarted)(CLLocationCoordinate2D coordinate, int32_t period);
@property (nonatomic, copy) void (^liveLocationStopped)(void);
@property (nonatomic, strong) TGLocationPallete *pallete;
@property (nonatomic, readonly, strong) UIView *locationMapView;
- (void)userLocationButtonPressed;
- (void)setMapCenterCoordinate:(CLLocationCoordinate2D)coordinate offset:(CGPoint)offset animated:(bool)animated;
- (void)setMapCenterCoordinate:(CLLocationCoordinate2D)coordinate span:(MKCoordinateSpan)span offset:(CGPoint)offset animated:(bool)animated;
- (void)updateInsets;
- (void)updateMapHeightAnimated:(bool)animated;
- (CGFloat)visibleContentHeight;
- (CGFloat)mapHeight;
- (CGFloat)safeAreaInsetBottom;
- (bool)hasUserLocation;
- (SSignal *)userLocationSignal;
- (bool)locationServicesDisabled;
- (void)updateLocationAvailability;
@property (nonatomic, strong) id receivingPeer;
- (void)_presentLiveLocationMenu:(CLLocationCoordinate2D)coordinate dismissOnCompletion:(bool)dismissOnCompletion;
- (CGRect)_liveLocationMenuSourceRect;
- (void)_willStartOwnLiveLocation;
@end
extern const CGFloat TGLocationMapInset;
extern const CGFloat TGLocationMapClipHeight;
extern const MKCoordinateSpan TGLocationDefaultSpan;

View File

@ -1,29 +0,0 @@
#import <LegacyComponents/TGLocationMapViewController.h>
#import <LegacyComponents/LegacyComponentsContext.h>
#import <CoreLocation/CoreLocation.h>
@class TGVenueAttachment;
@class TGUser;
@class TGMessage;
typedef enum {
TGLocationPickerControllerDefaultIntent,
TGLocationPickerControllerCustomLocationIntent
} TGLocationPickerControllerIntent;
@interface TGLocationPickerController : TGLocationMapViewController
@property (nonatomic, copy) void (^locationPicked)(CLLocationCoordinate2D coordinate, TGVenueAttachment *venue, NSString *address);
@property (nonatomic, copy) SSignal *(^nearbyPlacesSignal)(NSString *query, CLLocation *coordinate);
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context intent:(TGLocationPickerControllerIntent)intent;
- (void)setLiveLocationsSignal:(SSignal *)signal;
@property (nonatomic, copy) SSignal *(^remainingTimeForMessage)(TGMessage *message);
@property (nonatomic, strong) id peer;
@property (nonatomic, assign) bool allowLiveLocationSharing;
@end

View File

@ -1,8 +0,0 @@
#import <UIKit/UIKit.h>
@interface TGLocationPulseView : UIView
- (void)start;
- (void)stop;
@end

View File

@ -1,34 +0,0 @@
#import <CoreLocation/CoreLocation.h>
@class TGVenueAttachment;
@class TGLocationMediaAttachment;
@interface TGLocationVenue : NSObject
@property (nonatomic, readonly) NSString *identifier;
@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) NSString *displayAddress;
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, readonly) NSString *categoryName;
@property (nonatomic, readonly) NSURL *categoryIconUrl;
@property (nonatomic, readonly) NSString *provider;
@property (readonly, nonatomic) NSString *country;
@property (readonly, nonatomic) NSString *state;
@property (readonly, nonatomic) NSString *city;
@property (readonly, nonatomic) NSString *address;
@property (readonly, nonatomic) NSString *crossStreet;
@property (readonly, nonatomic) NSString *street;
- (TGVenueAttachment *)venueAttachment;
+ (TGLocationVenue *)venueWithFoursquareDictionary:(NSDictionary *)dictionary;
+ (TGLocationVenue *)venueWithGooglePlacesDictionary:(NSDictionary *)dictionary;
+ (TGLocationVenue *)venueWithLocationAttachment:(TGLocationMediaAttachment *)attachment;
@end
extern NSString *const TGLocationGooglePlacesVenueProvider;
extern NSString *const TGLocationFoursquareVenueProvider;

View File

@ -1,59 +0,0 @@
#import <LegacyComponents/LegacyComponentsContext.h>
#import <LegacyComponents/TGLocationMapViewController.h>
#import <CoreLocation/CoreLocation.h>
@class TGLocationMediaAttachment;
@class TGVenueAttachment;
@class TGMenuSheetController;
@class TGMessage;
@class TGUser;
@interface TGLiveLocation : NSObject
@property (nonatomic, strong, readonly) TGMessage *message;
@property (nonatomic, strong, readonly) id peer;
@property (nonatomic, readonly) bool hasOwnSession;
@property (nonatomic, readonly) bool isOwnLocation;
@property (nonatomic, readonly) bool isExpired;
- (instancetype)initWithMessage:(TGMessage *)message peer:(id)peer hasOwnSession:(bool)hasOwnSession isOwnLocation:(bool)isOwnLocation isExpired:(bool)isExpired;
- (instancetype)initWithMessage:(TGMessage *)message peer:(id)peer;
- (int64_t)peerId;
@end
@interface TGLocationViewController : TGLocationMapViewController;
@property (nonatomic, assign) bool modalMode;
@property (nonatomic, assign) bool previewMode;
@property (nonatomic, assign) bool allowLiveLocationSharing;
@property (nonatomic, assign) bool zoomToFitAllLocationsOnScreen;
@property (nonatomic, copy) void (^presentActionsMenu)(TGLocationMediaAttachment *, bool);
@property (nonatomic, copy) bool (^presentShareMenu)(TGMenuSheetController *, CLLocationCoordinate2D);
@property (nonatomic, copy) bool (^presentOpenInMenu)(TGLocationViewController *, TGLocationMediaAttachment *, bool, void (^)(TGMenuSheetController *));
@property (nonatomic, copy) void (^shareAction)(NSArray *peerIds, NSString *caption);
@property (nonatomic, copy) void (^openLocation)(TGMessage *message);
@property (nonatomic, copy) void (^onViewDidAppear)(void);
@property (nonatomic, copy) void (^updateRightBarItem)(UIBarButtonItem *, bool, bool);
@property (nonatomic, readonly) UIButton *directionsButton;
@property (nonatomic, copy) SSignal *(^remainingTimeForMessage)(TGMessage *message);
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context liveLocation:(TGLiveLocation *)liveLocation;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context locationAttachment:(TGLocationMediaAttachment *)locationAttachment peer:(id)peer color:(UIColor *)color;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context message:(TGMessage *)message peer:(id)peer color:(UIColor *)color;
- (void)actionsButtonPressed;
- (void)setLiveLocationsSignal:(SSignal *)signal;
- (void)setFrequentUpdatesHandle:(id<SDisposable>)disposable;
@end

View File

@ -1,11 +0,0 @@
#import <UIKit/UIKit.h>
@interface TGLocationWavesView : UIView
@property (nonatomic, strong) UIColor *color;
- (void)invalidate;
- (void)start;
- (void)stop;
@end

View File

@ -1,33 +0,0 @@
#import <MapKit/MapKit.h>
#import <SSignalKit/SSignalKit.h>
@class TGLocationMediaAttachment;
@class TGUser;
@interface TGLocationPickerAnnotation: NSObject <MKAnnotation>
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, strong) id peer;
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate;
@end
@interface TGLocationAnnotation : NSObject <MKAnnotation>
@property (nonatomic, readonly) TGLocationMediaAttachment *location;
@property (nonatomic, readonly) bool isLiveLocation;
@property (nonatomic, strong) id peer;
@property (nonatomic, strong) UIColor *color;
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, assign) int32_t messageId;
@property (nonatomic, assign) bool isOwn;
@property (nonatomic, assign) bool hasSession;
@property (nonatomic, assign) bool isExpired;
@property (nonatomic, strong) NSNumber *heading;
- (instancetype)initWithLocation:(TGLocationMediaAttachment *)location;
- (instancetype)initWithLocation:(TGLocationMediaAttachment *)location color:(UIColor *)color;
@end

View File

@ -1,164 +0,0 @@
#import "TGLocationAnnotation.h"
#import "TGLocationMediaAttachment.h"
@interface TGLocationAnnotation ()
{
CLLocationCoordinate2D _coordinate;
NSMutableSet *_observers;
}
@end
@implementation TGLocationAnnotation
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
NSString *observerId = [NSString stringWithFormat:@"%lu%@", observer.hash, keyPath];
[_observers addObject:observerId];
[super addObserver:observer forKeyPath:keyPath options:options context:context];
}
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
NSString *observerId = [NSString stringWithFormat:@"%lu%@", observer.hash, keyPath];
if ([_observers containsObject:observerId])
{
[_observers removeObject:observerId];
[super removeObserver:observer forKeyPath:keyPath];
}
}
- (instancetype)initWithLocation:(TGLocationMediaAttachment *)location
{
return [self initWithLocation:location color:nil];
}
- (instancetype)initWithLocation:(TGLocationMediaAttachment *)location color:(UIColor *)color
{
self = [super init];
if (self != nil)
{
_coordinate = CLLocationCoordinate2DMake(location.latitude, location.longitude);
_color = color;
_location = location;
_observers = [[NSMutableSet alloc] init];
}
return self;
}
- (NSString *)title
{
return @"";
}
- (NSString *)subtitle
{
return @"";
}
- (CLLocationCoordinate2D)coordinate
{
return _coordinate;
}
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate
{
if (fabs(newCoordinate.latitude - _coordinate.latitude) > DBL_EPSILON || fabs(newCoordinate.longitude - _coordinate.longitude) > DBL_EPSILON)
{
[self willChangeValueForKey:@"coordinate"];
_coordinate = newCoordinate;
[self didChangeValueForKey:@"coordinate"];
}
}
- (void)setIsExpired:(bool)isExpired
{
if (isExpired != _isExpired)
{
[self willChangeValueForKey:@"isExpired"];
_isExpired = isExpired;
[self didChangeValueForKey:@"isExpired"];
}
}
- (void)setHeading:(NSNumber *)heading
{
[self willChangeValueForKey:@"heading"];
_heading = heading;
[self didChangeValueForKey:@"heading"];
}
- (void)setHasSession:(bool)hasSession
{
if (hasSession != _hasSession)
{
[self willChangeValueForKey:@"hasSession"];
_hasSession = hasSession;
[self didChangeValueForKey:@"hasSession"];
}
}
- (bool)isLiveLocation
{
return _location.period > 0;
}
@end
@interface TGLocationPickerAnnotation ()
{
CLLocationCoordinate2D _coordinate;
NSMutableSet *_observers;
}
@end
@implementation TGLocationPickerAnnotation
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate
{
self = [super init];
if (self != nil)
{
_coordinate = coordinate;
_observers = [[NSMutableSet alloc] init];
}
return self;
}
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
NSString *observerId = [NSString stringWithFormat:@"%lu%@", observer.hash, keyPath];
[_observers addObject:observerId];
[super addObserver:observer forKeyPath:keyPath options:options context:context];
}
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
NSString *observerId = [NSString stringWithFormat:@"%lu%@", observer.hash, keyPath];
if ([_observers containsObject:observerId])
{
[_observers removeObject:observerId];
[super removeObserver:observer forKeyPath:keyPath];
}
}
- (CLLocationCoordinate2D)coordinate
{
return _coordinate;
}
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate
{
if (fabs(newCoordinate.latitude - _coordinate.latitude) > DBL_EPSILON || fabs(newCoordinate.longitude - _coordinate.longitude) > DBL_EPSILON)
{
[self willChangeValueForKey:@"coordinate"];
_coordinate = newCoordinate;
[self didChangeValueForKey:@"coordinate"];
}
}
@end

View File

@ -1,22 +0,0 @@
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <SSignalKit/SSignalKit.h>
@class TGMessage;
@class TGLocationPallete;
@interface TGLocationCurrentLocationCell : UITableViewCell
@property (nonatomic, strong) TGLocationPallete *pallete;
@property (nonatomic, weak) UIImageView *edgeView;
- (void)configureForCurrentLocationWithAccuracy:(CLLocationAccuracy)accuracy;
- (void)configureForCustomLocationWithAddress:(NSString *)address;
- (void)configureForGroupLocationWithAddress:(NSString *)address;
- (void)configureForLiveLocationWithAccuracy:(CLLocationAccuracy)accuracy;
- (void)configureForStopWithMessage:(TGMessage *)message remaining:(SSignal *)remaining;
@end
extern NSString *const TGLocationCurrentLocationCellKind;
extern const CGFloat TGLocationCurrentLocationCellHeight;

View File

@ -1,409 +0,0 @@
#import "TGLocationCurrentLocationCell.h"
#import "TGLocationVenueCell.h"
#import "TGLocationMapViewController.h"
#import "LegacyComponentsInternal.h"
#import "TGColor.h"
#import "TGImageUtils.h"
#import "TGFont.h"
#import "TGLocationUtils.h"
#import "TGDateUtils.h"
#import "TGMessage.h"
#import "TGLocationWavesView.h"
#import "TGLocationLiveElapsedView.h"
NSString *const TGLocationCurrentLocationCellKind = @"TGLocationCurrentLocationCellKind";
const CGFloat TGLocationCurrentLocationCellHeight = 68;
@interface TGLocationCurrentLocationCell ()
{
int32_t _messageId;
bool _isCurrentLocation;
UIView *_highlightView;
UIImageView *_circleView;
UIImageView *_iconView;
TGLocationWavesView *_wavesView;
UILabel *_titleLabel;
UILabel *_subtitleLabel;
TGLocationLiveElapsedView *_elapsedView;
UIView *_separatorView;
SMetaDisposable *_remainingDisposable;
}
@end
@implementation TGLocationCurrentLocationCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self != nil)
{
self.selectedBackgroundView = [[UIView alloc] init];
self.selectedBackgroundView.backgroundColor = [UIColor clearColor];
_highlightView = [[UIView alloc] initWithFrame:self.bounds];
_highlightView.alpha = 0.0f;
_highlightView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_highlightView.backgroundColor = TGSelectionColor();
_highlightView.userInteractionEnabled = false;
[self.contentView addSubview:_highlightView];
_circleView = [[UIImageView alloc] initWithFrame:CGRectMake(12.0f, 10.0f, 48.0f, 48.0f)];
[self.contentView addSubview:_circleView];
_iconView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 48.0f, 48.0f)];
_iconView.contentMode = UIViewContentModeCenter;
[_circleView addSubview:_iconView];
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.font = TGBoldSystemFontOfSize(16.0);
_titleLabel.text = TGLocalized(@"Map.SendMyCurrentLocation");
_titleLabel.textColor = TGAccentColor();
[self.contentView addSubview:_titleLabel];
_subtitleLabel = [[UILabel alloc] init];
_subtitleLabel.backgroundColor = [UIColor clearColor];
_subtitleLabel.font = TGSystemFontOfSize(13);
_subtitleLabel.text = TGLocalized(@"Map.Locating");
_subtitleLabel.textColor = UIColorRGB(0xa6a6a6);
[self.contentView addSubview:_subtitleLabel];
_elapsedView = [[TGLocationLiveElapsedView alloc] init];
[self.contentView addSubview:_elapsedView];
_separatorView = [[UIView alloc] init];
_separatorView.backgroundColor = TGSeparatorColor();
[self addSubview:_separatorView];
_wavesView = [[TGLocationWavesView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 48.0f, 48.0f)];
[_circleView addSubview:_wavesView];
_isCurrentLocation = true;
}
return self;
}
- (void)dealloc
{
[_wavesView invalidate];
}
- (void)setPallete:(TGLocationPallete *)pallete
{
if (pallete == nil || _pallete == pallete)
return;
_pallete = pallete;
self.backgroundColor = pallete.backgroundColor;
_highlightView.backgroundColor = pallete.selectionColor;
_titleLabel.textColor = pallete.accentColor;
_subtitleLabel.textColor = pallete.secondaryTextColor;
_separatorView.backgroundColor = pallete.separatorColor;
[_elapsedView setColor:pallete.accentColor];
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
if (animated)
{
[UIView animateWithDuration:0.2 animations:^
{
_highlightView.alpha = highlighted ? 1.0f : 0.0f;
_edgeView.alpha = highlighted ? 1.0f : 0.0f;
}];
}
else
{
_highlightView.alpha = highlighted ? 1.0f : 0.0f;
_edgeView.alpha = highlighted ? 1.0f : 0.0f;
}
}
- (void)setCircleColor:(UIColor *)color
{
UIImage *circleImage = [TGLocationVenueCell circleImage];
_circleView.image = TGTintedImage(circleImage, color);
}
- (void)configureForCurrentLocationWithAccuracy:(CLLocationAccuracy)accuracy
{
_messageId = 0;
UIImage *icon = TGComponentsImageNamed(@"LocationMessagePinIcon");
if (_pallete != nil)
icon = TGTintedImage(icon, _pallete.iconColor);
_iconView.image = icon;
_titleLabel.textColor = self.pallete != nil ? self.pallete.accentColor : TGAccentColor();
_elapsedView.hidden = true;
if (!_isCurrentLocation)
{
[UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^
{
_titleLabel.text = TGLocalized(@"Map.SendMyCurrentLocation");
if (accuracy > DBL_EPSILON)
{
NSString *accuracyString = [TGLocationUtils stringFromAccuracy:(NSInteger)accuracy];
_subtitleLabel.text = [NSString stringWithFormat:TGLocalized(@"Map.AccurateTo"), accuracyString];
_circleView.alpha = 1.0f;
_titleLabel.alpha = 1.0f;
_subtitleLabel.alpha = 1.0f;
}
else
{
_subtitleLabel.text = TGLocalized(@"Map.Locating");
_circleView.alpha = 0.5f;
_titleLabel.alpha = 0.5f;
_subtitleLabel.alpha = 0.5f;
}
} completion:nil];
_isCurrentLocation = true;
}
else
{
if (accuracy > DBL_EPSILON)
{
NSString *accuracyString = [TGLocationUtils stringFromAccuracy:(NSInteger)accuracy];
_subtitleLabel.text = [NSString stringWithFormat:TGLocalized(@"Map.AccurateTo"), accuracyString];
[UIView animateWithDuration:0.2f animations:^
{
_circleView.alpha = 1.0f;
_titleLabel.alpha = 1.0f;
_subtitleLabel.alpha = 1.0f;
}];
}
else
{
_subtitleLabel.text = TGLocalized(@"Map.Locating");
_circleView.alpha = 0.5f;
_titleLabel.alpha = 0.5f;
_subtitleLabel.alpha = 0.5f;
}
}
[self setCircleColor:_pallete != nil ? _pallete.locationColor : UIColorRGB(0x008df2)];
_separatorView.hidden = false;
[_wavesView stop];
_wavesView.hidden = true;
[self setNeedsLayout];
}
- (void)configureForLiveLocationWithAccuracy:(CLLocationAccuracy)accuracy
{
_messageId = 0;
UIImage *icon = TGComponentsImageNamed(@"LocationMessageLiveIcon");
if (_pallete != nil)
icon = TGTintedImage(icon, _pallete.iconColor);
_iconView.image = icon;
_titleLabel.textColor = self.pallete != nil ? self.pallete.accentColor : TGAccentColor();
_elapsedView.hidden = true;
[UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^
{
_titleLabel.text = TGLocalized(@"Map.ShareLiveLocation");
_subtitleLabel.text = TGLocalized(@"Map.ShareLiveLocationHelp");
if (accuracy > DBL_EPSILON)
{
_circleView.alpha = 1.0f;
_titleLabel.alpha = 1.0f;
_subtitleLabel.alpha = 1.0f;
}
else
{
_circleView.alpha = 0.5f;
_titleLabel.alpha = 0.5f;
_subtitleLabel.alpha = 0.5f;
}
} completion:nil];
[self setCircleColor:_pallete != nil ? _pallete.liveLocationColor : UIColorRGB(0xff6464)];
_separatorView.hidden = true;
[_wavesView stop];
_wavesView.hidden = true;
[self setNeedsLayout];
}
- (void)configureForStopWithMessage:(TGMessage *)message remaining:(SSignal *)remaining
{
bool changed = message.mid != _messageId;
_messageId = message.mid;
UIImage *icon = TGComponentsImageNamed(@"LocationMessagePinIcon");
if (_pallete != nil)
icon = TGTintedImage(icon, _pallete.iconColor);
_iconView.image = icon;
_titleLabel.textColor = self.pallete != nil ? self.pallete.destructiveColor : UIColorRGB(0xff3b2f);
_titleLabel.text = TGLocalized(@"Map.StopLiveLocation");
_subtitleLabel.text = [TGDateUtils stringForRelativeUpdate:[message actualDate]];
_circleView.alpha = 1.0f;
_titleLabel.alpha = 1.0f;
_subtitleLabel.alpha = 1.0f;
[self setCircleColor:_pallete != nil ? _pallete.liveLocationColor : UIColorRGB(0xff6464)];
_separatorView.hidden = true;
_wavesView.hidden = false;
_wavesView.color = self.pallete != nil ? _pallete.iconColor : [UIColor whiteColor];
[_wavesView start];
if (changed)
{
_elapsedView.hidden = false;
[self setNeedsLayout];
TGLocationMediaAttachment *locationAttachment = message.locationAttachment;
if (_remainingDisposable == nil)
_remainingDisposable = [[SMetaDisposable alloc] init];
__weak TGLocationCurrentLocationCell *weakSelf = self;
[_remainingDisposable setDisposable:[remaining startWithNext:^(NSNumber *next)
{
__strong TGLocationCurrentLocationCell *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_elapsedView setRemaining:next.intValue period:locationAttachment.period];
} completed:^
{
__strong TGLocationCurrentLocationCell *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf->_elapsedView.hidden = true;
[strongSelf setNeedsLayout];
}
}]];
}
}
- (void)configureForCustomLocationWithAddress:(NSString *)address
{
_messageId = 0;
UIImage *icon = TGComponentsImageNamed(@"LocationMessagePinIcon");
if (_pallete != nil)
icon = TGTintedImage(icon, _pallete.iconColor);
_iconView.image = icon;
_titleLabel.textColor = self.pallete != nil ? self.pallete.accentColor : TGAccentColor();
_elapsedView.hidden = true;
if (_isCurrentLocation)
{
[UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^
{
_titleLabel.text = TGLocalized(@"Map.SendThisLocation");
_subtitleLabel.text = [self _subtitleForAddress:address];
_circleView.alpha = 1.0f;
_titleLabel.alpha = 1.0f;
_subtitleLabel.alpha = 1.0f;
} completion:nil];
_isCurrentLocation = false;
}
else
{
[UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^
{
_subtitleLabel.text = [self _subtitleForAddress:address];
} completion:nil];
}
[self setCircleColor:_pallete != nil ? _pallete.locationColor : UIColorRGB(0x008df2)];
_separatorView.hidden = false;
[_wavesView stop];
_wavesView.hidden = true;
[self setNeedsLayout];
}
- (void)configureForGroupLocationWithAddress:(NSString *)address
{
_messageId = 0;
UIImage *icon = TGComponentsImageNamed(@"LocationMessagePinIcon");
if (_pallete != nil)
icon = TGTintedImage(icon, _pallete.iconColor);
_iconView.image = icon;
_titleLabel.textColor = self.pallete != nil ? self.pallete.accentColor : TGAccentColor();
_elapsedView.hidden = true;
if (_isCurrentLocation)
{
[UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^
{
_titleLabel.text = TGLocalized(@"Map.SetThisLocation");
_subtitleLabel.text = [self _subtitleForAddress:address];
_circleView.alpha = 1.0f;
_titleLabel.alpha = 1.0f;
_subtitleLabel.alpha = 1.0f;
} completion:nil];
_isCurrentLocation = false;
}
else
{
[UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^
{
_subtitleLabel.text = [self _subtitleForAddress:address];
} completion:nil];
}
[self setCircleColor:_pallete != nil ? _pallete.locationColor : UIColorRGB(0x008df2)];
_separatorView.hidden = true;
[_wavesView stop];
_wavesView.hidden = true;
[self setNeedsLayout];
}
- (NSString *)_subtitleForAddress:(NSString *)address
{
if (address != nil && address.length == 0)
return TGLocalized(@"Map.Unknown");
else if (address == nil)
return TGLocalized(@"Map.Locating");
return address;
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat padding = 76.0f;
CGFloat separatorThickness = TGScreenPixel;
_titleLabel.frame = CGRectMake(padding, 14, self.frame.size.width - padding - 14 - (_elapsedView.hidden ? 0.0f : 38.0f), 20);
_subtitleLabel.frame = CGRectMake(padding, 36, self.frame.size.width - padding - 14 - (_elapsedView.hidden ? 0.0f : 38.0f), 20);
_separatorView.frame = CGRectMake(padding, self.frame.size.height - separatorThickness, self.frame.size.width - padding, separatorThickness);
_elapsedView.frame = CGRectMake(self.frame.size.width - 30.0f - 15.0f, floor((self.frame.size.height - 30.0f) / 2.0f), 30.0f, 30.0f);
}
@end

View File

@ -1,23 +0,0 @@
#import <UIKit/UIKit.h>
#import <SSignalKit/SSignalKit.h>
@class TGLocationMediaAttachment;
@class TGLocationPallete;
@interface TGLocationInfoCell : UITableViewCell
@property (nonatomic, strong) TGLocationPallete *pallete;
@property (nonatomic, assign) UIEdgeInsets safeInset;
@property (nonatomic, copy) void (^locatePressed)(void);
@property (nonatomic, copy) void (^directionsPressed)(void);
@property (nonatomic, readonly) UIButton *directionsButton;
- (void)setLocation:(TGLocationMediaAttachment *)location color:(UIColor *)color messageId:(int32_t)messageId userLocationSignal:(SSignal *)userLocationSignal;
@end
extern NSString *const TGLocationInfoCellKind;
extern const CGFloat TGLocationInfoCellHeight;

View File

@ -1,357 +0,0 @@
#import "TGLocationInfoCell.h"
#import "TGLocationVenueCell.h"
#import "TGLocationMapViewController.h"
#import "TGLocationSignals.h"
#import "TGLocationUtils.h"
#import "TGLocationReverseGeocodeResult.h"
#import "TGLocationMediaAttachment.h"
#import "LegacyComponentsInternal.h"
#import "TGColor.h"
#import "TGFont.h"
#import "TGImageUtils.h"
#import "TGImageView.h"
#import "TGModernButton.h"
NSString *const TGLocationInfoCellKind = @"TGLocationInfoCell";
const CGFloat TGLocationInfoCellHeight = 134.0f;
@interface TGLocationInfoCell ()
{
TGModernButton *_locateButton;
UIImageView *_circleView;
TGImageView *_iconView;
UILabel *_titleLabel;
UILabel *_addressLabel;
TGModernButton *_directionsButton;
UILabel *_directionsButtonLabel;
UILabel *_etaLabel;
SMetaDisposable *_addressDisposable;
int32_t _messageId;
}
@end
@implementation TGLocationInfoCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self != nil)
{
_messageId = -1;
_locateButton = [[TGModernButton alloc] init];
[_locateButton addTarget:self action:@selector(locateButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:_locateButton];
_circleView = [[UIImageView alloc] init];
[_circleView setImage:TGTintedImage([TGLocationVenueCell circleImage], UIColorRGB(0x008df2))];
[_locateButton addSubview:_circleView];
_iconView = [[TGImageView alloc] init];
_iconView.contentMode = UIViewContentModeCenter;
_iconView.image = TGComponentsImageNamed(@"LocationMessagePinIcon");
[_circleView addSubview:_iconView];
_titleLabel = [[UILabel alloc] init];
_titleLabel.font = TGBoldSystemFontOfSize(17.0f);
_titleLabel.textColor = [UIColor blackColor];
[_locateButton addSubview:_titleLabel];
_addressLabel = [[UILabel alloc] init];
_addressLabel.font = TGSystemFontOfSize(13);
_addressLabel.textColor = UIColorRGB(0x8e8e93);
[_locateButton addSubview:_addressLabel];
static dispatch_once_t onceToken;
static UIImage *buttonImage = nil;
dispatch_once(&onceToken, ^
{
CGSize size = CGSizeMake(16.0f, 16.0f);
UIGraphicsBeginImageContextWithOptions(size, false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, TGAccentColor().CGColor);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeEllipseInRect(context, CGRectMake(0.5f, 0.5f, size.width - 1.0f, size.height - 1.0f));
buttonImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(size.width / 2.0f) topCapHeight:(NSInteger)(size.height / 2.0f)];
UIGraphicsEndImageContext();
});
_directionsButton = [[TGModernButton alloc] init];
_directionsButton.adjustsImageWhenHighlighted = false;
[_directionsButton setBackgroundImage:buttonImage forState:UIControlStateNormal];
[_directionsButton addTarget:self action:@selector(directionsButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:_directionsButton];
_directionsButtonLabel = [[UILabel alloc] init];
_directionsButtonLabel.backgroundColor = [UIColor clearColor];
_directionsButtonLabel.font = TGBoldSystemFontOfSize(17.0f);
_directionsButtonLabel.text = TGLocalized(@"Map.Directions");
_directionsButtonLabel.textAlignment = NSTextAlignmentCenter;
_directionsButtonLabel.textColor = TGAccentColor();
_directionsButtonLabel.userInteractionEnabled = false;
[_directionsButtonLabel sizeToFit];
[_directionsButton addSubview:_directionsButtonLabel];
_etaLabel = [[UILabel alloc] init];
_etaLabel.alpha = 0.0f;
_etaLabel.font = TGSystemFontOfSize(13);
_etaLabel.textAlignment = NSTextAlignmentCenter;
_etaLabel.textColor = TGAccentColor();
_etaLabel.userInteractionEnabled = false;
[_directionsButton addSubview:_etaLabel];
}
return self;
}
- (void)dealloc
{
[_addressDisposable dispose];
}
- (void)setPallete:(TGLocationPallete *)pallete
{
if (pallete == nil || _pallete == pallete)
return;
_pallete = pallete;
self.backgroundColor = pallete.backgroundColor;
[_circleView setImage:TGTintedImage([TGLocationVenueCell circleImage], _pallete.locationColor)];
_titleLabel.textColor = pallete.textColor;
_addressLabel.textColor = pallete.secondaryTextColor;
_directionsButtonLabel.textColor = pallete.accentColor;
_etaLabel.textColor = pallete.accentColor;
CGSize size = CGSizeMake(16.0f, 16.0f);
UIGraphicsBeginImageContextWithOptions(size, false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, pallete.accentColor.CGColor);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeEllipseInRect(context, CGRectMake(0.5f, 0.5f, size.width - 1.0f, size.height - 1.0f));
UIImage *buttonImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(size.width / 2.0f) topCapHeight:(NSInteger)(size.height / 2.0f)];
UIGraphicsEndImageContext();
[_directionsButton setBackgroundImage:buttonImage forState:UIControlStateNormal];
}
- (void)locateButtonPressed
{
if (self.locatePressed != nil)
self.locatePressed();
}
- (void)directionsButtonPressed
{
if (self.directionsPressed != nil)
self.directionsPressed();
}
- (UIButton *)directionsButton
{
return _directionsButton;
}
- (void)setLocation:(TGLocationMediaAttachment *)location color:(UIColor *)color messageId:(int32_t)messageId userLocationSignal:(SSignal *)userLocationSignal
{
if (_messageId == messageId)
return;
_messageId = messageId;
_titleLabel.text = location.venue.title.length > 0 ? location.venue.title : TGLocalized(@"Map.Location");
UIColor *pinColor = _pallete != nil ? _pallete.iconColor : [UIColor whiteColor];
if (color != nil) {
[_circleView setImage:TGTintedImage([TGLocationVenueCell circleImage], color)];
pinColor = [UIColor whiteColor];
}
if (location.venue.type.length > 0 && [location.venue.provider isEqualToString:@"foursquare"])
[_iconView loadUri:[NSString stringWithFormat:@"location-venue-icon://type=%@&width=%d&height=%d&color=%d", location.venue.type, 48, 48, TGColorHexCode(pinColor)] withOptions:nil];
SSignal *addressSignal = [SSignal single:@""];
if (location.venue.address.length > 0)
{
addressSignal = [SSignal single:location.venue.address];
}
else
{
addressSignal = [[[TGLocationSignals reverseGeocodeCoordinate:CLLocationCoordinate2DMake(location.latitude, location.longitude)] map:^id(TGLocationReverseGeocodeResult *result)
{
return [result displayAddress];
}] catch:^SSignal *(__unused id error)
{
return [SSignal single:[TGLocationUtils stringForCoordinate:CLLocationCoordinate2DMake(location.latitude, location.longitude)]];
}];
addressSignal = [[SSignal single:TGLocalized(@"Map.Locating")] then:addressSignal];
}
CLLocation *pointLocation = [[CLLocation alloc] initWithLatitude:location.latitude longitude:location.longitude];
if (_addressDisposable == nil)
_addressDisposable = [[SMetaDisposable alloc] init];
SSignal *updatedLocationSignal = [userLocationSignal reduceLeftWithPassthrough:nil with:^id(CLLocation *previous, CLLocation *next, void (^emit)(id))
{
if (next == nil)
return nil;
if (previous == nil && next != nil)
{
emit(@{@"location":next, @"update":@true});
return next;
}
else
{
bool update = [next distanceFromLocation:previous] > 100;
emit(@{@"location":next, @"update":@(update)});
return update ? next : previous;
}
}];
SSignal *signal = [[SSignal combineSignals:@[addressSignal, updatedLocationSignal] withInitialStates:@[ TGLocalized(@"Map.Locating"), [NSNull null] ]] mapToSignal:^SSignal *(NSArray *results)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"address"] = results.firstObject;
if (![results.lastObject isKindOfClass:[NSNull class]])
{
CLLocation *newLocation = ((NSDictionary *)results.lastObject)[@"location"];
bool updateEta = [((NSDictionary *)results.lastObject)[@"update"] boolValue];
dict[@"distance"] = @([pointLocation distanceFromLocation:newLocation]);
if (updateEta)
{
return [[SSignal single:dict] then:[[TGLocationSignals driveEta:pointLocation.coordinate] map:^id(NSNumber *eta)
{
NSMutableDictionary *newDict = [dict mutableCopy];
newDict[@"eta"] = eta;
return newDict;
}]];
}
}
return [SSignal single:dict];
}];
__weak TGLocationInfoCell *weakSelf = self;
[_addressDisposable setDisposable:[[signal deliverOn:[SQueue mainQueue]] startWithNext:^(NSDictionary *next)
{
__strong TGLocationInfoCell *strongSelf = weakSelf;
if (strongSelf != nil)
{
NSString *address = next[@"address"];
CGFloat distanceValue = [next[@"distance"] doubleValue];
NSString *distance = next[@"distance"] ? [NSString stringWithFormat:TGLocalized(@"Map.DistanceAway"), [TGLocationUtils stringFromDistance:distanceValue]] : nil;
if (next[@"distance"] != nil && distanceValue < 10)
distance = TGLocalized(@"Map.YouAreHere");
if (next[@"eta"] != nil)
[strongSelf setDrivingETA:[next[@"eta"] doubleValue]];
NSMutableArray *components = [[NSMutableArray alloc] init];
if (address.length > 0)
[components addObject:address];
if (distance.length > 0)
[components addObject:distance];
NSString *string = [components componentsJoinedByString:@" • "];
if ([strongSelf->_addressLabel.text isEqualToString:string])
return;
if (strongSelf->_addressLabel.text.length == 0)
{
strongSelf->_addressLabel.text = string;
}
else
{
[UIView transitionWithView:strongSelf->_addressLabel duration:0.2 options:UIViewAnimationOptionTransitionCrossDissolve animations:^
{
strongSelf->_addressLabel.text = string;
} completion:nil];
}
}
}]];
}
- (void)setDrivingETA:(NSTimeInterval)drivingETA
{
if (drivingETA > 0 && drivingETA < 60 * 60 * 10)
{
drivingETA = MAX(drivingETA, 60);
NSInteger minutes = (NSInteger)(drivingETA / 60) % 60;
NSInteger hours = (NSInteger)(drivingETA / 3600.0f);
NSString *string = nil;
if (hours < 1)
{
string = [NSString stringWithFormat:TGLocalized(@"Map.ETAMinutes_any"), [NSString stringWithFormat:@"%d", (int)minutes]];
}
else
{
if (hours == 1 && minutes == 0)
{
string = [NSString stringWithFormat:TGLocalized(@"Map.ETAHours_1"), @"1"];
}
else
{
string = [NSString stringWithFormat:TGLocalized(@"Map.ETAHours_any"), [NSString stringWithFormat:@"%d:%02d", (int)hours, (int)minutes]];
}
}
string = [NSString stringWithFormat:TGLocalized(@"Map.DirectionsDriveEta"), string];
if ([_etaLabel.text isEqualToString:string])
return;
if (_etaLabel.text.length == 0)
{
_etaLabel.text = string;
[UIView animateWithDuration:0.3 animations:^
{
_etaLabel.alpha = 1.0f;
[self layoutSubviews];
}];
}
else
{
[UIView transitionWithView:_etaLabel duration:0.2 options:UIViewAnimationOptionTransitionCrossDissolve animations:^
{
_etaLabel.text = string;
} completion:nil];
}
}
}
- (void)setSafeInset:(UIEdgeInsets)safeInset
{
_safeInset = safeInset;
[self setNeedsLayout];
}
- (void)layoutSubviews
{
_locateButton.frame = CGRectMake(0.0f, 0.0f, self.frame.size.width, 60.0f);
_circleView.frame = CGRectMake(12.0f + self.safeInset.left, 12.0f, 48.0f, 48.0f);
_iconView.frame = _circleView.bounds;
_titleLabel.frame = CGRectMake(76.0f + self.safeInset.left, 15.0f, self.frame.size.width - 76.0f - 12.0f - self.safeInset.left - self.safeInset.right, 20.0f);
_addressLabel.frame = CGRectMake(76.0f + self.safeInset.left, 38.0f, self.frame.size.width - 76.0f - 12.0f - self.safeInset.left - self.safeInset.right, 20.0f);
_directionsButton.frame = CGRectMake(12.0f + self.safeInset.left, 72.0f, self.frame.size.width - 12.0f * 2.0f - self.safeInset.left - self.safeInset.right, 50.0f);
bool hasEta = _etaLabel.text.length > 0;
_directionsButtonLabel.frame = CGRectMake(0.0f, hasEta ? 6.0f : 14.0f, _directionsButton.frame.size.width, _directionsButtonLabel.frame.size.height);
_etaLabel.frame = CGRectMake(0.0f, hasEta ? 25.0f : 20.0f, _directionsButton.frame.size.width, 20.0f);
}
@end

View File

@ -1,24 +0,0 @@
#import <UIKit/UIKit.h>
#import <SSignalKit/SSignalKit.h>
@class TGUser;
@class TGMessage;
@class TGLocationPallete;
@interface TGLocationLiveCell : UITableViewCell
@property (nonatomic, strong) TGLocationPallete *pallete;
@property (nonatomic, assign) UIEdgeInsets safeInset;
@property (nonatomic, copy) void (^longPressed)(void);
@property (nonatomic, readonly) int32_t messageId;
@property (nonatomic, weak) UIImageView *edgeView;
- (void)configureWithPeer:(id)peer message:(TGMessage *)message remaining:(SSignal *)remaining userLocationSignal:(SSignal *)userLocationSignal;
- (void)configureForStart;
- (void)configureForStopWithMessage:(TGMessage *)message remaining:(SSignal *)remaining;
@end
extern NSString *const TGLocationLiveCellKind;
extern const CGFloat TGLocationLiveCellHeight;

View File

@ -1,361 +0,0 @@
#import "TGLocationLiveCell.h"
#import "TGLocationVenueCell.h"
#import "TGLocationMapViewController.h"
#import "LegacyComponentsInternal.h"
#import "TGFont.h"
#import "TGColor.h"
#import "TGImageUtils.h"
#import "TGDateUtils.h"
#import "TGLocationUtils.h"
#import "TGUser.h"
#import "TGMessage.h"
#import "TGConversation.h"
#import "TGLocationMediaAttachment.h"
#import "TGLetteredAvatarView.h"
#import "TGLocationWavesView.h"
#import "TGLocationLiveElapsedView.h"
NSString *const TGLocationLiveCellKind = @"TGLocationLiveCell";
const CGFloat TGLocationLiveCellHeight = 68;
@interface TGLocationLiveCell ()
{
UIView *_highlightView;
UIImageView *_circleView;
UIImageView *_iconView;
TGLocationWavesView *_wavesView;
TGLetteredAvatarView *_avatarView;
UILabel *_titleLabel;
UILabel *_subtitleLabel;
TGLocationLiveElapsedView *_elapsedView;
UIView *_separatorView;
SMetaDisposable *_locationDisposable;
SMetaDisposable *_remainingDisposable;
UILongPressGestureRecognizer *_longPressGestureRecognizer;
}
@end
@implementation TGLocationLiveCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self != nil)
{
self.selectedBackgroundView = [[UIView alloc] init];
self.selectedBackgroundView.backgroundColor = [UIColor clearColor];
_highlightView = [[UIView alloc] initWithFrame:self.bounds];
_highlightView.alpha = 0.0f;
_highlightView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_highlightView.backgroundColor = TGSelectionColor();
_highlightView.userInteractionEnabled = false;
[self.contentView addSubview:_highlightView];
_circleView = [[UIImageView alloc] init];
[self.contentView addSubview:_circleView];
_iconView = [[UIImageView alloc] init];
_iconView.contentMode = UIViewContentModeCenter;
[_circleView addSubview:_iconView];
_avatarView = [[TGLetteredAvatarView alloc] init];
[_avatarView setSingleFontSize:22.0f doubleFontSize:22.0f useBoldFont:false];
[self.contentView addSubview:_avatarView];
_titleLabel = [[UILabel alloc] init];
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.font = TGBoldSystemFontOfSize(16.0);
_titleLabel.text = TGLocalized(@"Map.SendMyCurrentLocation");
_titleLabel.textColor = TGAccentColor();
[self.contentView addSubview:_titleLabel];
_subtitleLabel = [[UILabel alloc] init];
_subtitleLabel.backgroundColor = [UIColor clearColor];
_subtitleLabel.font = TGSystemFontOfSize(13);
_subtitleLabel.text = TGLocalized(@"Map.Locating");
_subtitleLabel.textColor = UIColorRGB(0xa6a6a6);
[self.contentView addSubview:_subtitleLabel];
_elapsedView = [[TGLocationLiveElapsedView alloc] init];
[self.contentView addSubview:_elapsedView];
_separatorView = [[UIView alloc] init];
_separatorView.backgroundColor = TGSeparatorColor();
[self addSubview:_separatorView];
_wavesView = [[TGLocationWavesView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 48.0f, 48.0f)];
[_circleView addSubview:_wavesView];
_longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePress:)];
[self addGestureRecognizer:_longPressGestureRecognizer];
}
return self;
}
- (void)dealloc
{
[_locationDisposable dispose];
[_wavesView invalidate];
}
- (void)setPallete:(TGLocationPallete *)pallete
{
if (pallete == nil || _pallete == pallete)
return;
_pallete = pallete;
self.backgroundColor = pallete.backgroundColor;
_highlightView.backgroundColor = pallete.selectionColor;
_titleLabel.textColor = pallete.accentColor;
_subtitleLabel.textColor = pallete.secondaryTextColor;
_separatorView.backgroundColor = pallete.separatorColor;
_wavesView.color = pallete.iconColor;
[_elapsedView setColor:pallete.accentColor];
}
- (void)handlePress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
if (self.longPressed != nil)
self.longPressed();
}
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
if (animated)
{
[UIView animateWithDuration:0.2 animations:^
{
_highlightView.alpha = highlighted ? 1.0f : 0.0f;
_edgeView.alpha = highlighted ? 1.0f : 0.0f;
}];
}
else
{
_highlightView.alpha = highlighted ? 1.0f : 0.0f;
_edgeView.alpha = highlighted ? 1.0f : 0.0f;
}
}
- (void)configureWithPeer:(id)peer message:(TGMessage *)message remaining:(SSignal *)remaining userLocationSignal:(SSignal *)userLocationSignal
{
bool changed = message.mid != _messageId;
_messageId = message.mid;
_circleView.hidden = true;
_avatarView.hidden = false;
CGFloat diameter = 48.0f;
static UIImage *staticPlaceholder = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
//!placeholder
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
CGContextSetStrokeColorWithColor(context, UIColorRGB(0xd9d9d9).CGColor);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeEllipseInRect(context, CGRectMake(0.5f, 0.5f, diameter - 1.0f, diameter - 1.0f));
staticPlaceholder = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
UIImage *placeholder = _pallete != nil ? _pallete.avatarPlaceholder : staticPlaceholder;
bool isUser = [peer isKindOfClass:[TGUser class]];
NSString *avatarUrl = isUser ? ((TGUser *)peer).photoFullUrlSmall : ((TGConversation *)peer).chatPhotoFullSmall;
if (avatarUrl.length != 0)
{
_avatarView.fadeTransitionDuration = 0.3;
if (![avatarUrl isEqualToString:_avatarView.currentUrl])
[_avatarView loadImage:avatarUrl filter:@"circle:48x48" placeholder:placeholder];
}
else
{
if (isUser)
{
[_avatarView loadUserPlaceholderWithSize:CGSizeMake(diameter, diameter) uid:((TGUser *)peer).uid firstName:((TGUser *)peer).firstName lastName:((TGUser *)peer).lastName placeholder:placeholder];
}
else
{
[_avatarView loadGroupPlaceholderWithSize:CGSizeMake(diameter, diameter) conversationId:((TGConversation *)peer).conversationId title:((TGConversation *)peer).chatTitle placeholder:placeholder];
}
}
_titleLabel.textColor = _pallete != nil ? _pallete.textColor : [UIColor blackColor];
_titleLabel.text = isUser ? ((TGUser *)peer).displayName : ((TGConversation *)peer).chatTitle;
NSString *subtitle = [TGDateUtils stringForRelativeUpdate:[message actualDate]];
_subtitleLabel.text = subtitle;
TGLocationMediaAttachment *locationAttachment = message.locationAttachment;
CLLocation *location = [[CLLocation alloc] initWithLatitude:locationAttachment.latitude longitude:locationAttachment.longitude];
__weak TGLocationLiveCell *weakSelf = self;
if (_locationDisposable == nil)
_locationDisposable = [[SMetaDisposable alloc] init];
[_locationDisposable setDisposable:[userLocationSignal startWithNext:^(CLLocation *next)
{
__strong TGLocationLiveCell *strongSelf = weakSelf;
if (strongSelf != nil && next != nil)
{
CGFloat distance = [next distanceFromLocation:location];
NSString *distanceString = [NSString stringWithFormat:TGLocalized(@"Map.DistanceAway"), [TGLocationUtils stringFromDistance:distance]];
strongSelf->_subtitleLabel.text = [NSString stringWithFormat:@"%@ • %@", subtitle, distanceString];
}
}]];
if (changed)
{
_elapsedView.hidden = false;
_avatarView.alpha = 1.0f;
[self setNeedsLayout];
if (_remainingDisposable == nil)
_remainingDisposable = [[SMetaDisposable alloc] init];
[_remainingDisposable setDisposable:[remaining startWithNext:^(NSNumber *next)
{
__strong TGLocationLiveCell *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_elapsedView setRemaining:next.intValue period:locationAttachment.period];
} completed:^
{
__strong TGLocationLiveCell *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf->_elapsedView.hidden = true;
strongSelf->_avatarView.alpha = 0.5f;
[strongSelf setNeedsLayout];
}
}]];
}
}
- (void)configureForStart
{
_messageId = 0;
_avatarView.hidden = true;
_circleView.hidden = false;
_elapsedView.hidden = true;
UIImage *icon = TGComponentsImageNamed(@"LocationMessageLiveIcon");
if (_pallete != nil)
icon = TGTintedImage(icon, _pallete.iconColor);
_iconView.image = icon;
[self setCircleColor:_pallete != nil ? _pallete.liveLocationColor : UIColorRGB(0xff6464)];
_titleLabel.textColor = _pallete != nil ? _pallete.accentColor : TGAccentColor();
_titleLabel.text = TGLocalized(@"Map.ShareLiveLocation");
_subtitleLabel.text = TGLocalized(@"Map.ShareLiveLocationHelp");
[_wavesView stop];
_wavesView.hidden = true;
[_locationDisposable setDisposable:nil];
[_remainingDisposable setDisposable:nil];
[self setNeedsLayout];
}
- (void)configureForStopWithMessage:(TGMessage *)message remaining:(SSignal *)remaining
{
bool changed = message.mid != _messageId;
_messageId = message.mid;
_avatarView.hidden = true;
_circleView.hidden = false;
UIImage *icon = TGComponentsImageNamed(@"LocationMessagePinIcon");
if (_pallete != nil)
icon = TGTintedImage(icon, _pallete.iconColor);
_iconView.image = icon;
[self setCircleColor:_pallete != nil ? _pallete.liveLocationColor : UIColorRGB(0xff6464)];
_titleLabel.textColor = _pallete != nil ? _pallete.destructiveColor : UIColorRGB(0xff3b2f);
_titleLabel.text = TGLocalized(@"Map.StopLiveLocation");
_subtitleLabel.text = [TGDateUtils stringForRelativeUpdate:[message actualDate]];
_wavesView.hidden = false;
_wavesView.color = _pallete != nil ? _pallete.iconColor : [UIColor whiteColor];
[_wavesView start];
[_locationDisposable setDisposable:nil];
if (changed)
{
_elapsedView.hidden = false;
[self setNeedsLayout];
TGLocationMediaAttachment *locationAttachment = message.locationAttachment;
if (_remainingDisposable == nil)
_remainingDisposable = [[SMetaDisposable alloc] init];
__weak TGLocationLiveCell *weakSelf = self;
[_remainingDisposable setDisposable:[remaining startWithNext:^(NSNumber *next)
{
__strong TGLocationLiveCell *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_elapsedView setRemaining:next.intValue period:locationAttachment.period];
} completed:^
{
__strong TGLocationLiveCell *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf->_elapsedView.hidden = true;
[strongSelf setNeedsLayout];
}
}]];
}
}
- (void)setCircleColor:(UIColor *)color
{
UIImage *circleImage = [TGLocationVenueCell circleImage];
_circleView.image = TGTintedImage(circleImage, color);
}
- (void)setSafeInset:(UIEdgeInsets)safeInset
{
_safeInset = safeInset;
[self setNeedsLayout];
}
- (void)layoutSubviews
{
[super layoutSubviews];
_circleView.frame = CGRectMake(12.0f + self.safeInset.left, 12.0f, 48.0f, 48.0f);
_iconView.frame = _circleView.bounds;
_avatarView.frame = _circleView.frame;
CGFloat padding = 76.0f + self.safeInset.left;
CGFloat separatorThickness = TGScreenPixel;
_titleLabel.frame = CGRectMake(padding, 14, self.frame.size.width - padding - 14 - (_elapsedView.hidden ? 0.0f : 38.0f) - self.safeInset.right, 20);
_subtitleLabel.frame = CGRectMake(padding, 36, self.frame.size.width - padding - 14 - (_elapsedView.hidden ? 0.0f : 38.0f) - self.safeInset.right, 20);
_separatorView.frame = CGRectMake(padding, self.frame.size.height - separatorThickness, self.frame.size.width - padding, separatorThickness);
_elapsedView.frame = CGRectMake(self.frame.size.width - 30.0f - 15.0f - self.safeInset.right, floor((self.frame.size.height - 30.0f) / 2.0f), 30.0f, 30.0f);
}
@end

View File

@ -1,101 +0,0 @@
#import "TGLocationLiveElapsedView.h"
#import "LegacyComponentsInternal.h"
#import "TGColor.h"
#import "TGFont.h"
@interface TGLocationLiveElapsedView ()
{
UIColor *_color;
CGFloat _progress;
NSString *_string;
}
@end
@implementation TGLocationLiveElapsedView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
_color = TGAccentColor();
self.backgroundColor = [UIColor clearColor];
self.contentMode = UIViewContentModeRedraw;
}
return self;
}
- (void)setColor:(UIColor *)color
{
_color = color;
[self setNeedsDisplay];
}
- (void)setRemaining:(int32_t)remaining period:(int32_t)period;
{
NSString *string = nil;
int32_t minutes = ceil(remaining / 60.0f);
if (minutes >= 60)
{
int32_t hours = ceil(remaining / 3600.0f);
string = [[NSString alloc] initWithFormat:TGLocalized(@"Map.LiveLocationShortHour"), [[NSString alloc] initWithFormat:@"%d", hours]];
}
else
{
string = [[NSString alloc] initWithFormat:@"%d", minutes];
}
_progress = remaining / (CGFloat)period;
if (_progress > 1.0f - FLT_EPSILON)
_progress = 0.999f;
_string = string;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CGRect allRect = self.bounds;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, _color.CGColor);
CGContextSetStrokeColorWithColor(context, _color.CGColor);
CGContextSetLineWidth(context, 1.5f);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinMiter);
CGContextSetMiterLimit(context, 10);
CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2);
CGFloat radius = 13.0f;
CGContextSetAlpha(context, 0.2f);
CGContextStrokeEllipseInRect(context, CGRectMake(center.x - radius, center.y - radius, radius * 2, radius * 2));
CGContextSetAlpha(context, 1.0f);
CGFloat startAngle = -M_PI_2;
CGFloat endAngle = -M_PI_2 + 2 * M_PI * (1.0f - _progress);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, center.x, center.y, radius, startAngle, endAngle, true);
CGContextAddPath(context, path);
CGPathRelease(path);
CGContextStrokePath(context);
UIFont *font = [TGFont roundedFontOfSize:14.0f];
if (font == nil) {
font = [UIFont systemFontOfSize:14.0];
}
NSDictionary *attributes = @{ NSFontAttributeName: font, NSForegroundColorAttributeName: _color };
CGSize size = iosMajorVersion() >= 7 ? [_string sizeWithAttributes:attributes] : [_string sizeWithFont:attributes[NSFontAttributeName]];
if (iosMajorVersion() >= 7)
{
[_string drawAtPoint:CGPointMake((allRect.size.width - size.width) / 2.0f, floor((allRect.size.height - size.height) / 2.0f)) withAttributes:attributes];
}
else
{
CGContextSetFillColorWithColor(context, _color.CGColor);
[_string drawAtPoint:CGPointMake((allRect.size.width - size.width) / 2.0f, floor((allRect.size.height - size.height) / 2.0f)) forWidth:FLT_MAX withFont:font lineBreakMode:NSLineBreakByWordWrapping];
}
}
@end

View File

@ -1,127 +0,0 @@
#import "TGLocationLiveSessionItemView.h"
#import "LegacyComponentsInternal.h"
#import "TGUser.h"
#import "TGConversation.h"
#import "TGLetteredAvatarView.h"
#import "TGLocationLiveElapsedView.h"
@interface TGLocationLiveSessionItemView ()
{
TGLetteredAvatarView *_avatarView;
TGLocationLiveElapsedView *_elapsedView;
UILabel *_label;
id<SDisposable> _disposable;
}
@end
@implementation TGLocationLiveSessionItemView
- (instancetype)initWithMessage:(TGMessage *)message peer:(id)peer remaining:(SSignal *)remaining action:(void (^)(void))action
{
bool isUser = [peer isKindOfClass:[TGUser class]];
NSString *title = isUser ? ((TGUser *)peer).displayName : ((TGConversation *)peer).chatTitle;
self = [super initWithTitle:@"" type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:action];
if (self != nil)
{
_label = [[UILabel alloc] init];
_label.backgroundColor = [UIColor clearColor];
_label.font = _button.titleLabel.font;
_label.textColor = [UIColor blackColor];
_label.text = title;
[_label sizeToFit];
[_button addSubview:_label];
_avatarView = [[TGLetteredAvatarView alloc] init];
[_avatarView setSingleFontSize:18.0f doubleFontSize:18.0f useBoldFont:false];
[self addSubview:_avatarView];
CGFloat diameter = 36.0f;
static UIImage *placeholder = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
//!placeholder
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
CGContextSetStrokeColorWithColor(context, UIColorRGB(0xd9d9d9).CGColor);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeEllipseInRect(context, CGRectMake(0.5f, 0.5f, diameter - 1.0f, diameter - 1.0f));
placeholder = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
NSString *avatarUrl = isUser ? ((TGUser *)peer).photoFullUrlSmall : ((TGConversation *)peer).chatPhotoFullSmall;
if (avatarUrl.length != 0)
{
_avatarView.fadeTransitionDuration = 0.3;
if (![avatarUrl isEqualToString:_avatarView.currentUrl])
[_avatarView loadImage:avatarUrl filter:@"circle:36x36" placeholder:placeholder];
}
else
{
if (isUser)
{
[_avatarView loadUserPlaceholderWithSize:CGSizeMake(diameter, diameter) uid:((TGUser *)peer).uid firstName:((TGUser *)peer).firstName lastName:((TGUser *)peer).lastName placeholder:placeholder];
}
else
{
[_avatarView loadGroupPlaceholderWithSize:CGSizeMake(diameter, diameter) conversationId:((TGConversation *)peer).conversationId title:((TGConversation *)peer).chatTitle placeholder:placeholder];
}
}
_elapsedView = [[TGLocationLiveElapsedView alloc] init];
[self addSubview:_elapsedView];
TGLocationMediaAttachment *location = nil;
for (TGMediaAttachment *attachment in message.mediaAttachments)
{
if (attachment.type == TGLocationMediaAttachmentType)
{
location = (TGLocationMediaAttachment *)attachment;
break;
}
}
__weak TGLocationLiveSessionItemView *weakSelf = self;
_disposable = [remaining startWithNext:^(NSNumber *next)
{
__strong TGLocationLiveSessionItemView *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_elapsedView setRemaining:next.intValue period:location.period];
}];
}
return self;
}
- (void)dealloc
{
[_disposable dispose];
}
- (void)setPallete:(TGMenuSheetPallete *)pallete
{
[super setPallete:pallete];
_label.textColor = pallete.textColor;
[_elapsedView setColor:pallete.accentColor];
}
- (void)layoutSubviews
{
[super layoutSubviews];
_label.frame = CGRectMake(74.0f, (self.frame.size.height - ceil(_label.frame.size.height)) / 2.0f, self.frame.size.width - 74.0f - 52.0f - 10.0f, ceil(_label.frame.size.height));
_avatarView.frame = CGRectMake(23.0f, 11.0f, 36.0f, 36.0f);
_elapsedView.frame = CGRectMake(self.frame.size.width - 30.0f - 22.0f, round((self.frame.size.height - 30.0f) / 2.0f), 30.0f, 30.0f);
}
@end

View File

@ -1,5 +0,0 @@
#import <UIKit/UIKit.h>
@interface TGLocationMapModeControl : UISegmentedControl
@end

View File

@ -1,27 +0,0 @@
#import "TGLocationMapModeControl.h"
#import "LegacyComponentsInternal.h"
#import "TGFont.h"
#import "TGColor.h"
@implementation TGLocationMapModeControl
- (instancetype)init
{
self = [super initWithItems:@[TGLocalized(@"Map.Map"), TGLocalized(@"Map.Satellite"), TGLocalized(@"Map.Hybrid")]];
if (self != nil)
{
[self setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlBackground.png") forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[self setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlSelected.png") forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
[self setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlSelected.png") forState:UIControlStateSelected | UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[self setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlHighlighted.png") forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
UIImage *dividerImage = TGComponentsImageNamed(@"ModernSegmentedControlDivider.png");
[self setDividerImage:dividerImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[self setTitleTextAttributes:@{UITextAttributeTextColor: TGAccentColor(), UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateNormal];
[self setTitleTextAttributes:@{UITextAttributeTextColor: [UIColor whiteColor], UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateSelected];
}
return self;
}
@end

View File

@ -1,17 +0,0 @@
#import <MapKit/MapKit.h>
@interface TGLocationMapView : MKMapView
@property (nonatomic, copy) void(^singleTap)(void);
@property (nonatomic, copy) bool(^customAnnotationTap)(CGPoint);
@property (nonatomic, assign) bool longPressAsTapEnabled;
@property (nonatomic, assign) bool tapEnabled;
@property (nonatomic, assign) bool manipulationEnabled;
@property (nonatomic, assign) bool allowAnnotationSelectionChanges;
@property (nonatomic, assign) UIEdgeInsets compassInsets;
@end

View File

@ -1,114 +0,0 @@
#import "TGLocationMapView.h"
@interface TGLocationMapView () <UIGestureRecognizerDelegate>
{
UITapGestureRecognizer *_tapGestureRecognizer;
UILongPressGestureRecognizer *_longPressGestureRecognizer;
}
@end
@implementation TGLocationMapView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
_manipulationEnabled = true;
_allowAnnotationSelectionChanges = true;
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tg_handleTap:)];
_tapGestureRecognizer.numberOfTapsRequired = 1;
_tapGestureRecognizer.numberOfTouchesRequired = 1;
[self addGestureRecognizer:_tapGestureRecognizer];
_longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(tg_handleLongPress:)];
_longPressGestureRecognizer.enabled = false;
_longPressGestureRecognizer.minimumPressDuration = 0.2f;
[self addGestureRecognizer:_longPressGestureRecognizer];
}
return self;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
NSString *viewClass = NSStringFromClass([gestureRecognizer.view class]);
if ([viewClass rangeOfString:@"Compass"].location != NSNotFound)
return true;
if (self.customAnnotationTap && self.customAnnotationTap([gestureRecognizer locationInView:self]))
return false;
return self.allowAnnotationSelectionChanges;
}
- (bool)tapEnabled
{
return _tapGestureRecognizer.enabled;
}
- (void)setTapEnabled:(bool)enabled
{
_tapGestureRecognizer.enabled = enabled;
}
- (bool)longPressAsTapEnabled
{
return _longPressGestureRecognizer.enabled;
}
- (void)setLongPressAsTapEnabled:(bool)enabled
{
_longPressGestureRecognizer.enabled = enabled;
}
- (void)tg_handleTap:(UITapGestureRecognizer *)__unused gestureRecognizer
{
if (self.singleTap != nil)
self.singleTap();
}
- (void)tg_handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
if (self.singleTap != nil)
self.singleTap();
}
}
- (void)setManipulationEnabled:(bool)enabled
{
_manipulationEnabled = enabled;
self.scrollEnabled = enabled;
self.zoomEnabled = enabled;
if ([self respondsToSelector:@selector(setRotateEnabled:)])
self.rotateEnabled = enabled;
if ([self respondsToSelector:@selector(setPitchEnabled:)])
self.pitchEnabled = enabled;
}
- (void)setCompassInsets:(UIEdgeInsets)compassInsets
{
_compassInsets = compassInsets;
[self setNeedsLayout];
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (UIEdgeInsetsEqualToEdgeInsets(self.compassInsets, UIEdgeInsetsZero))
return;
for (UIView *view in self.subviews)
{
if ([NSStringFromClass([view class]) rangeOfString:@"Compass"].location != NSNotFound)
{
view.frame = CGRectMake(self.frame.size.width - self.compassInsets.right - view.frame.size.width, self.compassInsets.top, view.frame.size.width, view.frame.size.height);
}
}
}
@end

View File

@ -1,642 +0,0 @@
#import "TGLocationMapViewController.h"
#import "Freedom.h"
#import "LegacyComponentsInternal.h"
#import "TGColor.h"
#import "TGImageUtils.h"
#import "TGFont.h"
#import "TGLocationUtils.h"
#import "TGUser.h"
#import "TGLocationMapView.h"
#import "TGLocationOptionsView.h"
#import <LegacyComponents/TGMenuSheetController.h>
#import "TGSearchBar.h"
const MKCoordinateSpan TGLocationDefaultSpan = { 0.008, 0.008 };
const CGFloat TGLocationMapClipHeight = 1600.0f;
const CGFloat TGLocationMapInset = 100.0f;
@interface TGLocationTableView : UITableView
@end
@interface TGLocationMapViewController () <CLLocationManagerDelegate>
{
id<LegacyComponentsContext> _context;
UIView *_mapClipView;
SVariable *_userLocation;
SPipe *_userLocationPipe;
MKPolygon *_darkPolygon;
void (^_openLiveLocationMenuBlock)(void);
}
@end
@implementation TGLocationMapViewController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context
{
self = [super initWithContext:context];
if (self != nil)
{
_context = context;
_userLocationPipe = [[SPipe alloc] init];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_userLocation = [[SVariable alloc] init];
[_userLocation set:[[SSignal single:nil] then:_userLocationPipe.signalProducer()]];
}
return self;
}
- (void)dealloc
{
_locationManager.delegate = nil;
_mapView.delegate = nil;
_tableView.dataSource = nil;
_tableView.delegate = nil;
}
- (void)setPallete:(TGLocationPallete *)pallete {
_pallete = pallete;
if ([self isViewLoaded]) {
self.view.backgroundColor = pallete.backgroundColor;
}
}
- (void)loadView
{
[super loadView];
self.view.backgroundColor = self.pallete.backgroundColor;
self.alwaysUseTallNavigationBarHeight = true;
_tableView = [[TGLocationTableView alloc] initWithFrame:self.view.bounds];
if (iosMajorVersion() >= 11)
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
_tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_tableView.backgroundColor = self.view.backgroundColor;
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.delaysContentTouches = false;
_tableView.canCancelContentTouches = true;
[self.view addSubview:_tableView];
_activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
if (self.pallete != nil)
_activityIndicator.color = self.pallete.secondaryTextColor;
_activityIndicator.userInteractionEnabled = false;
[_tableView addSubview:_activityIndicator];
_messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
_messageLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_messageLabel.backgroundColor = [UIColor clearColor];
_messageLabel.font = TGSystemFontOfSize(16);
_messageLabel.hidden = true;
_messageLabel.textAlignment = NSTextAlignmentCenter;
_messageLabel.textColor = self.pallete != nil ? self.pallete.secondaryTextColor : UIColorRGB(0x8e8e93);
_messageLabel.userInteractionEnabled = false;
[_tableView addSubview:_messageLabel];
_tableViewTopInset = [self mapHeight];
_mapClipView = [[UIView alloc] initWithFrame:CGRectMake(0, -TGLocationMapClipHeight, self.view.frame.size.width, TGLocationMapClipHeight + 10.0f)];
_mapClipView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_mapClipView.clipsToBounds = true;
[_tableView addSubview:_mapClipView];
_mapViewWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, TGLocationMapClipHeight - _tableViewTopInset, self.view.frame.size.width, _tableViewTopInset + 10.0f)];
_mapViewWrapper.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[_mapClipView addSubview:_mapViewWrapper];
__weak TGLocationMapViewController *weakSelf = self;
_mapView = [[TGLocationMapView alloc] initWithFrame:CGRectMake(0, -TGLocationMapInset, self.view.frame.size.width, _tableViewTopInset + 2 * TGLocationMapInset + 10.0f)];
_mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_mapView.delegate = self;
_mapView.showsUserLocation = true;
[_mapViewWrapper addSubview:_mapView];
_optionsView = [[TGLocationOptionsView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 45.0f, 90.0f)];
if (self.pallete != nil)
_optionsView.pallete = self.pallete;
_optionsView.mapModeChanged = ^(NSInteger mapMode) {
__strong TGLocationMapViewController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_mapView setMapType:(MKMapType)mapMode];
};
_optionsView.trackModePressed = ^{
__strong TGLocationMapViewController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf userLocationButtonPressed];
};
[self.view addSubview:_optionsView];
UIImage *edgeImage = TGComponentsImageNamed(@"LocationPanelEdge");
UIImage *edgeHighlightImage = TGComponentsImageNamed(@"LocationPanelEdge_Highlighted");
if (self.pallete != nil)
{
UIGraphicsBeginImageContextWithOptions(edgeImage.size, false, 0.0f);
[edgeImage drawAtPoint:CGPointZero];
[TGTintedImage(edgeHighlightImage, self.pallete.backgroundColor) drawAtPoint:CGPointZero];
edgeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
_edgeView = [[UIImageView alloc] initWithImage:[edgeImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f)]];
_edgeView.frame = CGRectMake(0.0f, _tableViewTopInset - 10.0f, _mapViewWrapper.frame.size.width, _edgeView.frame.size.height);
[_mapViewWrapper addSubview:_edgeView];
if (self.pallete != nil)
edgeHighlightImage = TGTintedImage(edgeHighlightImage, self.pallete.selectionColor);
_edgeHighlightView = [[UIImageView alloc] initWithImage:[edgeHighlightImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f)]];
_edgeHighlightView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_edgeHighlightView.frame = _edgeView.bounds;
_edgeHighlightView.alpha = 0.0f;
_edgeHighlightView.image = [edgeHighlightImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f)];
[_edgeView addSubview:_edgeHighlightView];
self.scrollViewsForAutomaticInsetsAdjustment = @[ _tableView ];
if (![self _updateControllerInset:false])
[self controllerInsetUpdated:UIEdgeInsetsZero];
}
- (void)layoutControllerForSize:(CGSize)size duration:(NSTimeInterval)duration
{
[super layoutControllerForSize:size duration:duration];
if (!self.isViewLoaded)
return;
[self updateMapHeightAnimated:false];
_optionsView.frame = CGRectMake(self.view.bounds.size.width - 45.0f - 6.0f - self.controllerSafeAreaInset.right, 56.0f + 6.0f, 45.0f, 90.0f);
_tableView.contentOffset = CGPointMake(0.0f, -_tableViewTopInset - self.controllerInset.top);
}
- (void)setOptionsViewHidden:(bool)hidden
{
if (_optionsView.userInteractionEnabled == !hidden)
return;
_optionsView.userInteractionEnabled = !hidden;
[UIView animateWithDuration:0.15 animations:^
{
_optionsView.alpha = hidden ? 0.0f : 1.0f;
}];
}
- (void)userLocationButtonPressed
{
}
- (bool)hasUserLocation
{
return (_mapView.userLocation != nil && _mapView.userLocation.location != nil);
}
- (void)updateLocationAvailability
{
bool locationAvailable = [self hasUserLocation] || _locationServicesDisabled;
[_optionsView setLocationAvailable:locationAvailable animated:true];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == _tableView)
{
CGFloat offset = scrollView.contentInset.top + scrollView.contentOffset.y;
CGFloat mapOffset = MIN(offset, [self mapHeight]);
_mapView.frame = CGRectMake(_mapView.frame.origin.x, -TGLocationMapInset + mapOffset / 2, _mapView.frame.size.width, _mapView.frame.size.height);
[self setOptionsViewHidden:(scrollView.contentOffset.y > -180.0f)];
CGFloat additionalScrollInset = _edgeView.frame.size.height / 2.0f;
if (scrollView.contentOffset.y < -scrollView.contentInset.top)
additionalScrollInset += -scrollView.contentInset.top - scrollView.contentOffset.y;
scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(self.controllerScrollInset.top + _tableViewTopInset + additionalScrollInset, 0.0f, 0.0f, 0.0f);
}
}
- (void)_autoAdjustInsetsForScrollView:(UIScrollView *)scrollView previousInset:(UIEdgeInsets)previousInset
{
CGPoint contentOffset = scrollView.contentOffset;
UIEdgeInsets controllerInset = self.controllerInset;
controllerInset.top += _tableViewTopInset;
controllerInset.bottom += _tableViewBottomInset;
UIEdgeInsets scrollInset = self.controllerScrollInset;
scrollInset.top += _tableViewTopInset;
scrollView.contentInset = controllerInset;
scrollView.scrollIndicatorInsets = scrollInset;
if (!UIEdgeInsetsEqualToEdgeInsets(previousInset, UIEdgeInsetsZero))
{
CGFloat maxOffset = scrollView.contentSize.height - (scrollView.frame.size.height - controllerInset.bottom);
if (![self shouldAdjustScrollViewInsetsForInversedLayout])
contentOffset.y += previousInset.top - controllerInset.top;
contentOffset.y = MAX(-controllerInset.top, MIN(contentOffset.y, maxOffset));
[scrollView setContentOffset:contentOffset animated:false];
}
else if (contentOffset.y < controllerInset.top)
{
contentOffset.y = -controllerInset.top;
[scrollView setContentOffset:contentOffset animated:false];
}
_optionsView.frame = CGRectMake(self.view.bounds.size.width - 45.0f - 6.0f, 56.0f + 6.0f, 45.0f, 90.0f);
}
- (NSInteger)tableView:(UITableView *)__unused tableView numberOfRowsInSection:(NSInteger)__unused section
{
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)__unused tableView cellForRowAtIndexPath:(NSIndexPath *)__unused indexPath
{
return nil;
}
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)updateInsets
{
UIEdgeInsets previousInset = _tableView.contentInset;
CGPoint previousOffset = _tableView.contentOffset;
UIEdgeInsets controllerInset = self.controllerInset;
controllerInset.top += _tableViewTopInset;
controllerInset.bottom += _tableViewBottomInset;
_tableView.contentInset = controllerInset;
if (previousInset.bottom > FLT_EPSILON)
_tableView.contentOffset = previousOffset;
}
- (void)updateMapHeightAnimated:(bool)animated
{
void (^changeBlock)(void) = ^
{
_tableViewTopInset = [self mapHeight];
_mapViewWrapper.frame = CGRectMake(0, TGLocationMapClipHeight - _tableViewTopInset, self.view.frame.size.width, _tableViewTopInset + 10.0f);
_mapView.frame = CGRectMake(0, -TGLocationMapInset, self.view.frame.size.width, _tableViewTopInset + 2 * TGLocationMapInset + 10.0f);
_edgeView.frame = CGRectMake(0.0f, _tableViewTopInset - 10.0f, _mapViewWrapper.frame.size.width, _edgeView.frame.size.height);
[self updateInsets];
};
if (animated)
{
[UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^
{
changeBlock();
} completion:nil];
}
else
{
changeBlock();
}
}
- (CGFloat)mapHeight
{
return self.view.frame.size.height - [self visibleContentHeight] - self.controllerInset.top;
}
- (CGFloat)visibleContentHeight
{
return 0.0f;
}
- (CGFloat)safeAreaInsetBottom {
return MAX(self.context.safeAreaInset.bottom, self.controllerSafeAreaInset.bottom);
}
#pragma mark -
- (void)setMapCenterCoordinate:(CLLocationCoordinate2D)coordinate offset:(CGPoint)offset animated:(bool)animated
{
[self setMapCenterCoordinate:coordinate span:TGLocationDefaultSpan offset:offset animated:animated];
}
- (void)setMapCenterCoordinate:(CLLocationCoordinate2D)coordinate span:(MKCoordinateSpan)span offset:(CGPoint)offset animated:(bool)animated
{
@try
{
MKCoordinateRegion region = MKCoordinateRegionMake(coordinate, span);
if (!CGPointEqualToPoint(offset, CGPointZero))
{
MKMapRect mapRect = [TGLocationUtils MKMapRectForCoordinateRegion:region];
[_mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(offset.y, offset.x, 0, 0) animated:animated];
}
else
{
[_mapView setRegion:region animated:animated];
}
}
@catch (NSException *exception)
{
TGLegacyLog(@"ERROR: failed to set location picker map region with exception: %@", exception);
}
}
#pragma mark -
- (UIView *)locationMapView
{
return _mapView;
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if (overlay == _darkPolygon)
{
MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:overlay];
renderer.fillColor = [[UIColor blackColor] colorWithAlphaComponent:0.3f];
return renderer;
}
return nil;
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView *overlayView = [[MKPolygonView alloc] initWithPolygon:overlay];
overlayView.fillColor = [[UIColor blackColor] colorWithAlphaComponent:0.3f];
return overlayView;
}
return nil;
}
- (void)mapView:(MKMapView *)__unused mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
userLocation.title = @"";
_locationServicesDisabled = false;
if (userLocation.location != nil)
_userLocationPipe.sink(userLocation.location);
[self updateLocationAvailability];
}
- (void)mapView:(MKMapView *)__unused mapView didFailToLocateUserWithError:(NSError *)__unused error
{
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted)
{
_userLocationPipe.sink(nil);
_locationServicesDisabled = true;
[self updateLocationAvailability];
}
}
- (bool)locationServicesDisabled
{
return _locationServicesDisabled;
}
- (SSignal *)userLocationSignal
{
return [_userLocation signal];
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
if (_openLiveLocationMenuBlock != nil)
{
if (status == kCLAuthorizationStatusAuthorizedAlways)
_openLiveLocationMenuBlock();
_openLiveLocationMenuBlock = nil;
}
}
- (CGRect)_liveLocationMenuSourceRect
{
return CGRectZero;
}
- (void)_willStartOwnLiveLocation
{
}
- (void)dismissLiveLocationMenu:(TGMenuSheetController *)controller doNotRemove:(bool)doNotRemove
{
[self _willStartOwnLiveLocation];
if (!doNotRemove)
{
[controller dismissAnimated:true];
}
else
{
[controller setDimViewHidden:true animated:true];
[controller removeFromParentViewController];
}
}
- (void)_presentLiveLocationMenu:(CLLocationCoordinate2D)coordinate dismissOnCompletion:(bool)dismissOnCompletion
{
void (^block)(void) = ^
{
__weak TGLocationMapViewController *weakSelf = self;
CGRect (^sourceRect)(void) = ^CGRect
{
__strong TGLocationMapViewController *strongSelf = weakSelf;
if (strongSelf == nil)
return CGRectZero;
return [strongSelf _liveLocationMenuSourceRect];
};
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:false];
controller.dismissesByOutsideTap = true;
controller.hasSwipeGesture = true;
controller.narrowInLandscape = true;
controller.sourceRect = sourceRect;
NSMutableArray *itemViews = [[NSMutableArray alloc] init];
NSString *title = TGLocalized(@"Map.LiveLocationGroupDescription");
if ([self.receivingPeer isKindOfClass:[TGUser class]])
title = [NSString stringWithFormat:TGLocalized(@"Map.LiveLocationPrivateDescription"), [(TGUser *)self.receivingPeer displayFirstName]];
TGMenuSheetTitleItemView *titleItem = [[TGMenuSheetTitleItemView alloc] initWithTitle:nil subtitle:title];
[itemViews addObject:titleItem];
__weak TGMenuSheetController *weakController = controller;
TGMenuSheetButtonItemView *for15MinutesItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Map.LiveLocationFor15Minutes") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
{
__strong TGLocationMapViewController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
if (strongSelf.liveLocationStarted != nil)
strongSelf.liveLocationStarted(coordinate, 15 * 60);
[strongSelf dismissLiveLocationMenu:strongController doNotRemove:!dismissOnCompletion];
}];
[itemViews addObject:for15MinutesItem];
TGMenuSheetButtonItemView *for1HourItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Map.LiveLocationFor1Hour") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
{
__strong TGLocationMapViewController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
if (strongSelf.liveLocationStarted != nil)
strongSelf.liveLocationStarted(coordinate, 60 * 60 - 1);
[strongSelf dismissLiveLocationMenu:strongController doNotRemove:!dismissOnCompletion];
}];
[itemViews addObject:for1HourItem];
TGMenuSheetButtonItemView *for8HoursItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Map.LiveLocationFor8Hours") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
{
__strong TGLocationMapViewController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
if (strongSelf.liveLocationStarted != nil)
strongSelf.liveLocationStarted(coordinate, 8 * 60 * 60);
[strongSelf dismissLiveLocationMenu:strongController doNotRemove:!dismissOnCompletion];
}];
[itemViews addObject:for8HoursItem];
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 (^errorBlock)(void) = ^
{
[[[LegacyComponentsGlobals provider] accessChecker] checkLocationAuthorizationStatusForIntent:TGLocationAccessIntentLiveLocation alertDismissComlpetion:nil];
};
if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedAlways && [CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse)
{
errorBlock();
}
else
{
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways)
{
block();
}
else
{
if (![TGLocationUtils requestAlwaysUserLocationAuthorizationWithLocationManager:_locationManager])
errorBlock();
else
_openLiveLocationMenuBlock = [block copy];
}
}
}
@end
@implementation TGLocationTableView
- (BOOL)touchesShouldCancelInContentView:(UIView *)__unused view
{
return true;
}
static void TGLocationTableViewAdjustContentOffsetIfNecessary(__unused id self, __unused SEL _cmd)
{
}
+ (void)initialize
{
static bool initialized = false;
if (!initialized)
{
initialized = true;
FreedomDecoration instanceDecorations[] =
{
{ .name = 0x584ab24eU,
.imp = (IMP)&TGLocationTableViewAdjustContentOffsetIfNecessary,
.newIdentifier = FreedomIdentifierEmpty,
.newEncoding = FreedomIdentifierEmpty
}
};
freedomClassAutoDecorate(0x5bfec194, NULL, 0, instanceDecorations, sizeof(instanceDecorations) / sizeof(instanceDecorations[0]));
}
}
@end
@implementation TGLocationPallete
+ (instancetype)palleteWithBackgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor destructiveColor:(UIColor *)destructiveColor locationColor:(UIColor *)locationColor liveLocationColor:(UIColor *)liveLocationColor iconColor:(UIColor *)iconColor sectionHeaderBackgroundColor:(UIColor *)sectionHeaderBackgroundColor sectionHeaderTextColor:(UIColor *)sectionHeaderTextColor searchBarPallete:(TGSearchBarPallete *)searchBarPallete avatarPlaceholder:(UIImage *)avatarPlaceholder
{
TGLocationPallete *pallete = [[TGLocationPallete alloc] init];
pallete->_backgroundColor = backgroundColor;
pallete->_selectionColor = selectionColor;
pallete->_separatorColor = separatorColor;
pallete->_textColor = textColor;
pallete->_secondaryTextColor = secondaryTextColor;
pallete->_accentColor = accentColor;
pallete->_destructiveColor = destructiveColor;
pallete->_locationColor = locationColor;
pallete->_liveLocationColor = liveLocationColor;
pallete->_iconColor = iconColor;
pallete->_sectionHeaderBackgroundColor = sectionHeaderBackgroundColor;
pallete->_sectionHeaderTextColor = sectionHeaderTextColor;
pallete->_searchBarPallete = searchBarPallete;
pallete->_avatarPlaceholder = avatarPlaceholder;
return pallete;
}
@end

View File

@ -1,15 +0,0 @@
#import "TGLocationTrackingButton.h"
@class TGLocationPallete;
@interface TGLocationOptionsView : UIView
@property (nonatomic, strong) TGLocationPallete *pallete;
@property (nonatomic, copy) void (^mapModeChanged)(NSInteger);
@property (nonatomic, copy) void (^trackModePressed)(void);
- (void)setTrackingMode:(TGLocationTrackingMode)trackingMode animated:(bool)animated;
- (void)setLocationAvailable:(bool)available animated:(bool)animated;
- (void)setMapModeControlHidden:(bool)hidden animated:(bool)animated;
@end

View File

@ -1,213 +0,0 @@
#import "TGLocationOptionsView.h"
#import "TGFont.h"
#import "TGModernButton.h"
#import "TGLocationMapModeControl.h"
#import "TGLocationMapViewController.h"
#import "TGSearchBar.h"
#import "LegacyComponentsInternal.h"
#import "TGImageUtils.h"
@interface TGLocationOptionsView ()
{
UIImageView *_backgroundView;
TGModernButton *_mapModeButton;
UIView *_mapModeClipView;
UIView *_mapModeBackgroundView;
TGLocationMapModeControl *_mapModeControl;
TGLocationTrackingButton *_trackButton;
UIView *_separatorView;
}
@end
@implementation TGLocationOptionsView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
_mapModeClipView = [[UIView alloc] init];
_mapModeClipView.clipsToBounds = true;
[self addSubview:_mapModeClipView];
_backgroundView = [[UIImageView alloc] init];
_backgroundView.image = [TGComponentsImageNamed(@"LocationTopPanel") resizableImageWithCapInsets:UIEdgeInsetsMake(15.0f, 15.0f, 18.0f, 15.0f)];
[self addSubview:_backgroundView];
UIView *mapModeBackgroundView = nil;
if (iosMajorVersion() >= 8)
{
_mapModeBackgroundView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]];
mapModeBackgroundView = ((UIVisualEffectView *)_mapModeBackgroundView).contentView;
}
else
{
_mapModeBackgroundView = [[UIView alloc] init];
_mapModeBackgroundView.backgroundColor = [UIColor whiteColor];
mapModeBackgroundView = _mapModeBackgroundView;
}
_mapModeBackgroundView.clipsToBounds = true;
_mapModeBackgroundView.layer.cornerRadius = 4.0f;
_mapModeBackgroundView.alpha = 0.0f;
_mapModeBackgroundView.userInteractionEnabled = false;
_mapModeBackgroundView.hidden = true;
[_mapModeClipView addSubview:_mapModeBackgroundView];
_mapModeControl = [[TGLocationMapModeControl alloc] init];
_mapModeControl.selectedSegmentIndex = 0;
[_mapModeControl addTarget:self action:@selector(modeChanged:) forControlEvents:UIControlEventValueChanged];
[mapModeBackgroundView addSubview:_mapModeControl];
_mapModeButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 45.0f, 45.0f)];
_mapModeButton.adjustsImageWhenHighlighted = false;
[_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo.png") forState:UIControlStateNormal];
[_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo_Active.png") forState:UIControlStateSelected];
[_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo_Active.png") forState:UIControlStateSelected | UIControlStateHighlighted];
[_mapModeButton addTarget:self action:@selector(mapModeButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_mapModeButton];
_trackButton = [[TGLocationTrackingButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 45.0f, 45.0f)];
[_trackButton addTarget:self action:@selector(trackPressed:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_trackButton];
_separatorView = [[UIView alloc] init];
_separatorView.backgroundColor = UIColorRGB(0xcccccc);
[self addSubview:_separatorView];
}
return self;
}
- (void)setPallete:(TGLocationPallete *)pallete
{
_pallete = pallete;
if (![_mapModeBackgroundView isKindOfClass:[UIVisualEffectView class]])
_mapModeBackgroundView.backgroundColor = pallete.backgroundColor;
else
((UIVisualEffectView *)_mapModeBackgroundView).effect = pallete.searchBarPallete.isDark ? [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark] : [UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight];
if (![pallete.backgroundColor isEqual:[UIColor whiteColor]])
{
UIGraphicsBeginImageContextWithOptions(_backgroundView.image.size, false, 0.0f);
[_backgroundView.image drawAtPoint:CGPointZero];
CGContextRef context = UIGraphicsGetCurrentContext();
[pallete.backgroundColor setFill];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(4.0f, 4.0f, 46.5f, 46.5f) cornerRadius:10.0f];
CGContextAddPath(context, path.CGPath);
CGContextFillPath(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_backgroundView.image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(15.0f, 15.0f, 18.0f, 15.0f)];
}
_separatorView.backgroundColor = pallete.separatorColor;
[_mapModeButton setImage:TGTintedImage(TGComponentsImageNamed(@"LocationInfo.png"), pallete.accentColor) forState:UIControlStateNormal];
[_mapModeButton setImage:TGTintedImage(TGComponentsImageNamed(@"LocationInfo_Active.png"), pallete.accentColor) forState:UIControlStateSelected];
[_mapModeButton setImage:TGTintedImage(TGComponentsImageNamed(@"LocationInfo_Active.png"), pallete.accentColor) forState:UIControlStateSelected | UIControlStateHighlighted];
[_trackButton setAccentColor:pallete.accentColor spinnerColor:pallete.secondaryTextColor];
if (pallete != nil && pallete.searchBarPallete.segmentedControlBackgroundImage == nil)
_mapModeButton.tintColor = pallete.accentColor;
[_mapModeControl setBackgroundImage:pallete.searchBarPallete.segmentedControlBackgroundImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[_mapModeControl setBackgroundImage:pallete.searchBarPallete.segmentedControlSelectedImage forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
[_mapModeControl setBackgroundImage:pallete.searchBarPallete.segmentedControlSelectedImage forState:UIControlStateSelected | UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[_mapModeControl setBackgroundImage:pallete.searchBarPallete.segmentedControlHighlightedImage forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[_mapModeControl setDividerImage:pallete.searchBarPallete.segmentedControlDividerImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[_mapModeControl setTitleTextAttributes:@{UITextAttributeTextColor:pallete.searchBarPallete.accentColor, UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateNormal];
[_mapModeControl setTitleTextAttributes:@{UITextAttributeTextColor:pallete.searchBarPallete.accentColor, UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateSelected];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
bool result = [super pointInside:point withEvent:event];
if (!result)
result = [_mapModeControl pointInside:[self convertPoint:point toView:_mapModeControl] withEvent:event];
return result;
}
- (void)mapModeButtonPressed
{
_mapModeButton.selected = !_mapModeButton.selected;
[self setMapModeControlHidden:!_mapModeButton.selected animated:true];
}
- (void)modeChanged:(TGLocationMapModeControl *)sender
{
if (self.mapModeChanged != nil)
self.mapModeChanged(sender.selectedSegmentIndex);
_mapModeButton.selected = false;
[self setMapModeControlHidden:true animated:true];
}
- (void)trackPressed:(TGLocationTrackingButton *)sender
{
if (self.trackModePressed != nil)
self.trackModePressed();
}
- (void)setTrackingMode:(TGLocationTrackingMode)trackingMode animated:(bool)animated
{
[_trackButton setTrackingMode:trackingMode animated:animated];
}
- (void)setLocationAvailable:(bool)available animated:(bool)animated
{
[_trackButton setLocationAvailable:available animated:animated];
}
- (void)layoutSubviews
{
_backgroundView.frame = CGRectMake(-5.0f, -5.0f, 45.0f + 10.0f, 90.0f + 5.0f + 6.0f);
_mapModeButton.frame = CGRectMake(0.0f, 0.0f, 45.0f, 45.0f);
_trackButton.frame = CGRectMake(0.0f, 45.0f, 45.0f, 45.0f);
_separatorView.frame = CGRectMake(0.0f, 45.0f, 45.0f, TGScreenPixel);
}
- (void)setMapModeControlHidden:(bool)hidden animated:(bool)animated
{
_mapModeBackgroundView.userInteractionEnabled = !hidden;
if (!hidden)
{
_mapModeClipView.frame = CGRectMake(-self.frame.origin.x + 12.0f, 8.0f, self.superview.frame.size.width - 45.0f - 12.0f, _mapModeControl.frame.size.height);
_mapModeControl.frame = CGRectMake(0.0f, 0.0f, _mapModeClipView.frame.size.width - 16.0f, _mapModeControl.frame.size.height);
if (_mapModeBackgroundView.hidden)
{
_mapModeBackgroundView.hidden = false;
_mapModeBackgroundView.frame = CGRectMake(_mapModeClipView.frame.size.width, 0.0f, _mapModeClipView.frame.size.width - 16.0f, _mapModeControl.frame.size.height);
}
}
if (animated)
{
[UIView animateWithDuration:0.3f delay:0.0f options:7 << 16 animations:^
{
_mapModeBackgroundView.frame = CGRectMake(hidden ? _mapModeClipView.frame.size.width : 0.0f, 0.0f, _mapModeClipView.frame.size.width - 16.0f, _mapModeControl.frame.size.height);
} completion:nil];
[UIView animateWithDuration:0.25f animations:^
{
_mapModeBackgroundView.alpha = hidden ? 0.0f : 1.0f;
}];
}
else
{
_mapModeBackgroundView.alpha = hidden ? 0.0f : 1.0f;
}
}
@end

View File

@ -1,23 +0,0 @@
#import <MapKit/MapKit.h>
@class TGLocationPallete;
@interface TGLocationPinAnnotationView : MKAnnotationView
- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation;
@property (nonatomic, assign, getter=isPinRaised) bool pinRaised;
- (void)setPinRaised:(bool)raised avatar:(bool)avatar animated:(bool)animated completion:(void (^)(void))completion;
- (void)setCustomPin:(bool)customPin animated:(bool)animated;
@property (nonatomic, strong) TGLocationPallete *pallete;
@end
extern NSString * const TGLocationPinAnnotationKind;
@interface TGLocationPinWrapperView : UIView
@end

View File

@ -1,679 +0,0 @@
#import "TGLocationPinAnnotationView.h"
#import "LegacyComponentsInternal.h"
#import "TGImageUtils.h"
#import "TGLocationMapViewController.h"
#import "TGUser.h"
#import "TGConversation.h"
#import "TGLocationAnnotation.h"
#import "TGLocationMediaAttachment.h"
#import "TGImageView.h"
#import "TGLetteredAvatarView.h"
#import "TGLocationPulseView.h"
NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
@interface TGLocationPinAnnotationView ()
{
TGLocationPulseView *_pulseView;
UIImageView *_smallView;
UIImageView *_shadowView;
UIImageView *_backgroundView;
TGImageView *_iconView;
UIImageView *_dotView;
TGLetteredAvatarView *_avatarView;
UIImageView *_smallArrowView;
UIImageView *_arrowView;
bool _liveLocation;
SMetaDisposable *_userDisposable;
bool _animating;
bool _observingExpiration;
}
@end
@implementation TGLocationPinAnnotationView
- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation
{
self = [super initWithAnnotation:annotation reuseIdentifier:TGLocationPinAnnotationKind];
if (self != nil)
{
_pulseView = [[TGLocationPulseView alloc] init];
[self addSubview:_pulseView];
_shadowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"LocationPinShadow")];
[self addSubview:_shadowView];
_backgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 83.0f, 83.0f)];
[_shadowView addSubview:_backgroundView];
_iconView = [[TGImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 64.0f, 64.0f)];
_iconView.contentMode = UIViewContentModeCenter;
[_backgroundView addSubview:_iconView];
static dispatch_once_t onceToken;
static UIImage *smallDotImage;
static UIImage *largeDotImage;
static UIImage *smallHeadingArrowImage;
static UIImage *largeHeadingArrowImage;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6.0f, 6.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, UIColorRGB(0x008df2).CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 6.0f, 6.0f));
smallDotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1.0f, [UIColor colorWithWhite:0.0f alpha:0.22f].CGColor);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(2, 2, 12.0, 12.0));
CGContextRestoreGState(context);
CGContextSetFillColorWithColor(context, UIColorRGB(0x008df2).CGColor);
CGContextFillEllipseInRect(context, CGRectMake(4.0f, 4.0f, 8.0f, 8.0f));
largeDotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, CGRectMake(0, 0, 18.0f, 18.0f));
CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor);
CGContextMoveToPoint(context, 9, 0);
CGContextAddLineToPoint(context, 13, 7);
CGContextAddLineToPoint(context, 5, 7);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextFillEllipseInRect(context, CGRectMake(3.0, 3.0, 12.0, 12.0));
smallHeadingArrowImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(CGSizeMake(33.0f, 33.0f), false, 0.0f);
context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, CGRectMake(0, 0, 16.0f, 16.0f));
CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor);
CGContextMoveToPoint(context, 16.5, 0);
CGContextAddLineToPoint(context, 21.5, 6);
CGContextAddLineToPoint(context, 11.5, 6);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextFillEllipseInRect(context, CGRectMake(3.0, 3.0, 27.0, 27.0));
largeHeadingArrowImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
_smallView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"LocationSmallCircle")];
_smallView.hidden = true;
[self addSubview:_smallView];
UIImage *dotImage = smallDotImage;
if ([annotation isKindOfClass:[TGLocationAnnotation class]])
{
TGLocationAnnotation *locationAnnotation = (TGLocationAnnotation *)annotation;
if (locationAnnotation.location.period > 0) {
dotImage = largeDotImage;
}
}
_dotView = [[UIImageView alloc] initWithImage:dotImage];
[self addSubview:_dotView];
_smallArrowView = [[UIImageView alloc] initWithImage:smallHeadingArrowImage];
_smallArrowView.hidden = true;
[self addSubview:_smallArrowView];
_arrowView = [[UIImageView alloc] initWithImage:largeHeadingArrowImage];
_arrowView.hidden = true;
[self addSubview:_arrowView];
[self setAnnotation:annotation];
}
return self;
}
- (void)dealloc
{
[self unsubscribeFromExpiration];
}
- (void)prepareForReuse
{
[_pulseView stop];
[_iconView reset];
_smallView.hidden = true;
_backgroundView.hidden = false;
}
- (void)subscribeForExpiration
{
if (_observingExpiration)
return;
_observingExpiration = true;
[self addObserver:self forKeyPath:@"annotation.isExpired" options:NSKeyValueObservingOptionNew context:NULL];
[self addObserver:self forKeyPath:@"annotation.heading" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)unsubscribeFromExpiration
{
if (!_observingExpiration)
return;
_observingExpiration = false;
[self removeObserver:self forKeyPath:@"annotation.isExpired"];
[self removeObserver:self forKeyPath:@"annotation.heading"];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
if (iosMajorVersion() < 7)
animated = false;
[super setSelected:selected animated:animated];
if (!_liveLocation)
return;
if (animated)
{
[self layoutSubviews];
_animating = true;
if (selected)
{
//dispatch_async(dispatch_get_main_queue(), ^
//{
UIView *avatarSnapshot = [_avatarView snapshotViewAfterScreenUpdates:false];
[_smallView addSubview:avatarSnapshot];
avatarSnapshot.transform = _avatarView.transform;
avatarSnapshot.center = CGPointMake(_smallView.frame.size.width / 2.0f, _smallView.frame.size.height / 2.0f);
_avatarView.transform = CGAffineTransformIdentity;
[_backgroundView addSubview:_avatarView];
_avatarView.center = CGPointMake(_backgroundView.frame.size.width / 2.0f, _backgroundView.frame.size.height / 2.0f - 5.0f);
_dotView.alpha = 0.0f;
_shadowView.center = CGPointMake(_shadowView.center.x, _shadowView.center.y + _shadowView.frame.size.height / 2.0f);
_shadowView.layer.anchorPoint = CGPointMake(0.5f, 1.0f);
_shadowView.hidden = false;
_shadowView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
[UIView animateWithDuration:0.35 delay:0.0 usingSpringWithDamping:0.6f initialSpringVelocity:0.5f options:kNilOptions animations:^
{
_smallView.transform = CGAffineTransformMakeScale(0.001f, 0.001f);
_shadowView.transform = CGAffineTransformIdentity;
if (_dotView.hidden)
_smallView.alpha = 0.0f;
} completion:^(BOOL finished)
{
_animating = false;
_shadowView.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
_smallView.hidden = true;
_smallView.transform = CGAffineTransformIdentity;
[avatarSnapshot removeFromSuperview];
[self addSubview:_avatarView];
}];
[UIView animateWithDuration:0.2 animations:^
{
_dotView.alpha = 1.0f;
}];
//});
}
else
{
UIView *avatarSnapshot = [_avatarView snapshotViewAfterScreenUpdates:false];
[_backgroundView addSubview:avatarSnapshot];
avatarSnapshot.transform = _avatarView.transform;
avatarSnapshot.center = CGPointMake(_backgroundView.frame.size.width / 2.0f, _backgroundView.frame.size.height / 2.0f - 5.0f);
_avatarView.transform = CGAffineTransformMakeScale(0.64f, 0.64f);
[_smallView addSubview:_avatarView];
_avatarView.center = CGPointMake(_smallView.frame.size.width / 2.0f, _smallView.frame.size.height / 2.0f);
_smallView.hidden = false;
_smallView.transform = CGAffineTransformMakeScale(0.01f, 0.01f);
_shadowView.center = CGPointMake(_shadowView.center.x, _shadowView.center.y + _shadowView.frame.size.height / 2.0f);
_shadowView.layer.anchorPoint = CGPointMake(0.5f, 1.0f);
[UIView animateWithDuration:0.35 delay:0.0 usingSpringWithDamping:0.6f initialSpringVelocity:0.5f options:kNilOptions animations:^
{
_smallView.transform = CGAffineTransformIdentity;
_shadowView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
if (_dotView.hidden)
_smallView.alpha = 1.0f;
} completion:^(BOOL finished)
{
_animating = false;
_shadowView.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
_shadowView.hidden = true;
_shadowView.transform = CGAffineTransformIdentity;
[avatarSnapshot removeFromSuperview];
[self addSubview:_avatarView];
}];
[UIView animateWithDuration:0.1 animations:^
{
_dotView.alpha = 0.0f;
} completion:nil];
}
}
else
{
_smallView.hidden = selected;
_shadowView.hidden = !selected;
_dotView.alpha = selected ? 1.0f : 0.0f;
_smallView.alpha = 1.0f;
[self layoutSubviews];
}
[self updateHeading];
}
- (void)setPallete:(TGLocationPallete *)pallete
{
if (pallete == nil || _pallete == pallete)
return;
_pallete = pallete;
UIImage *dotImage;
if ([self.annotation isKindOfClass:[TGLocationAnnotation class]])
{
TGLocationAnnotation *locationAnnotation = (TGLocationAnnotation *)self.annotation;
if (locationAnnotation.location.period > 0) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1.0f, [UIColor colorWithWhite:0.0f alpha:0.22f].CGColor);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(2, 2, 12.0, 12.0));
CGContextRestoreGState(context);
CGContextSetFillColorWithColor(context, pallete.locationColor.CGColor);
CGContextFillEllipseInRect(context, CGRectMake(4.0f, 4.0f, 8.0f, 8.0f));
dotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
}
if (dotImage == nil) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6.0f, 6.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, pallete.locationColor.CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 6.0f, 6.0f));
dotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
_dotView.image = dotImage;
[self setAnnotation:self.annotation];
}
- (void)setAnnotation:(id<MKAnnotation>)annotation
{
[super setAnnotation:annotation];
if ([annotation isKindOfClass:[TGLocationPickerAnnotation class]])
{
_avatarView.hidden = false;
_avatarView.alpha = 1.0f;
_iconView.hidden = true;
_dotView.hidden = true;
_backgroundView.image = TGComponentsImageNamed(@"LocationPinBackground");
_liveLocation = false;
[self setPeer:((TGLocationPickerAnnotation *)annotation).peer];
[self unsubscribeFromExpiration];
}
else if ([annotation isKindOfClass:[TGLocationAnnotation class]])
{
TGLocationAnnotation *locationAnnotation = ((TGLocationAnnotation *)annotation);
TGLocationMediaAttachment *location = locationAnnotation.location;
if (location.period == 0)
{
_dotView.hidden = false;
_avatarView.hidden = true;
_avatarView.alpha = 1.0f;
_iconView.hidden = false;
UIColor *color = _pallete != nil ? _pallete.locationColor : UIColorRGB(0x008df2);
UIColor *pinColor = _pallete != nil ? _pallete.iconColor : [UIColor whiteColor];
if (locationAnnotation.color != nil) {
color = locationAnnotation.color;
pinColor = [UIColor whiteColor];
}
_backgroundView.image = TGTintedImage(TGComponentsImageNamed(@"LocationPinBackground"), color);
if (location.venue.type.length > 0)
{
[_iconView loadUri:[NSString stringWithFormat:@"location-venue-icon://type=%@&width=%d&height=%d&color=%d", location.venue.type, 64, 64, TGColorHexCode(pinColor)] withOptions:nil];
}
else
{
[_iconView reset];
UIImage *image = TGComponentsImageNamed(@"LocationPinIcon");
if (_pallete != nil)
image = TGTintedImage(image, _pallete.iconColor);
_iconView.image = image;
}
_liveLocation = false;
[self unsubscribeFromExpiration];
}
else
{
_avatarView.hidden = false;
_avatarView.alpha = locationAnnotation.isExpired ? 0.5f : 1.0f;
_iconView.hidden = true;
_backgroundView.image = TGComponentsImageNamed(@"LocationPinBackground");
[self setPeer:locationAnnotation.peer];
if (!locationAnnotation.isOwn)
{
if (!locationAnnotation.isExpired)
[_pulseView start];
_dotView.hidden = false;
}
else
{
_dotView.hidden = true;
}
[self subscribeForExpiration];
_liveLocation = true;
if (!self.selected)
{
_shadowView.hidden = true;
_smallView.hidden = false;
}
}
}
}
- (void)updateHeading
{
if ([self.annotation isKindOfClass:[TGLocationAnnotation class]]) {
NSNumber *heading = ((TGLocationAnnotation *)self.annotation).heading;
if (heading != nil) {
_arrowView.hidden = self.isSelected;
_smallArrowView.hidden = !self.isSelected;
_arrowView.transform = CGAffineTransformMakeRotation(heading.floatValue);
_smallArrowView.transform = CGAffineTransformMakeRotation(heading.floatValue);
} else {
_arrowView.hidden = true;
_smallArrowView.hidden = true;
}
} else {
_arrowView.hidden = true;
_smallArrowView.hidden = true;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"annotation.isExpired"])
{
if (((TGLocationAnnotation *)self.annotation).isExpired)
{
[_pulseView stop];
_avatarView.alpha = 0.5f;
}
else
{
[_pulseView start];
_avatarView.alpha = 1.0f;
}
}
else if ([keyPath isEqual:@"annotation.heading"])
{
[self updateHeading];
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)setPeer:(id)peer
{
CGFloat diameter = 55.0f;
static UIImage *placeholder = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
//!placeholder
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
CGContextSetStrokeColorWithColor(context, UIColorRGB(0xd9d9d9).CGColor);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeEllipseInRect(context, CGRectMake(0.5f, 0.5f, diameter - 1.0f, diameter - 1.0f));
placeholder = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
if (_avatarView == nil)
{
_avatarView = [[TGLetteredAvatarView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 55.0f, 55.0f)];
[_avatarView setSingleFontSize:24.0f doubleFontSize:24.0f useBoldFont:false];
[self addSubview:_avatarView];
}
else
{
[_avatarView.superview bringSubviewToFront:_avatarView];
}
bool isUser = [peer isKindOfClass:[TGUser class]];
NSString *avatarUrl = isUser ? ((TGUser *)peer).photoFullUrlSmall : ((TGConversation *)peer).chatPhotoFullSmall;
if (avatarUrl.length != 0)
{
_avatarView.fadeTransitionDuration = 0.3;
if (![avatarUrl isEqualToString:_avatarView.currentUrl])
[_avatarView loadImage:avatarUrl filter:@"circle:55x55" placeholder:placeholder];
}
else
{
if (isUser)
{
[_avatarView loadUserPlaceholderWithSize:CGSizeMake(diameter, diameter) uid:((TGUser *)peer).uid firstName:((TGUser *)peer).firstName lastName:((TGUser *)peer).lastName placeholder:placeholder];
}
else
{
[_avatarView loadGroupPlaceholderWithSize:CGSizeMake(diameter, diameter) conversationId:((TGConversation *)peer).conversationId title:((TGConversation *)peer).chatTitle placeholder:placeholder];
}
}
}
#pragma mark - Layout
- (void)layoutSubviews
{
[super layoutSubviews];
if (_animating)
return;
_dotView.center = CGPointZero;
_smallView.center = CGPointZero;
_arrowView.center = CGPointZero;
_smallArrowView.center = CGPointZero;
_shadowView.center = CGPointMake(TGScreenPixel, -36.0f);
_backgroundView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f);
_iconView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f - 5.0f);
if (_liveLocation)
{
if (self.selected)
{
_avatarView.center = CGPointMake(TGScreenPixel, -41.0f);
_avatarView.transform = CGAffineTransformIdentity;
}
else
{
_avatarView.center = CGPointZero;
_avatarView.transform = CGAffineTransformMakeScale(0.64f, 0.64f);
}
}
else
{
_avatarView.center = CGPointMake(TGScreenPixel, -41.0f);
_avatarView.transform = CGAffineTransformIdentity;
}
}
- (void)setPinRaised:(bool)raised
{
[self setPinRaised:raised avatar:false animated:false completion:nil];
}
- (void)setPinRaised:(bool)raised avatar:(bool)avatar animated:(bool)animated completion:(void (^)(void))completion
{
_pinRaised = raised;
avatar = false;
[_shadowView.layer removeAllAnimations];
if (iosMajorVersion() < 7)
animated = false;
if (animated)
{
if (raised)
{
[UIView animateWithDuration:0.2 delay:0.0 options:7 << 16 | UIViewAnimationOptionAllowAnimatedContent animations:^
{
_shadowView.center = CGPointMake(TGScreenPixel, -66.0f);
if (avatar)
_avatarView.center = CGPointMake(TGScreenPixel, -71.0f);
} completion:^(BOOL finished) {
if (finished && completion != nil)
completion();
}];
}
else
{
[UIView animateWithDuration:0.2 delay:0.0 usingSpringWithDamping:0.6 initialSpringVelocity:0.0 options:UIViewAnimationOptionAllowAnimatedContent animations:^
{
_shadowView.center = CGPointMake(TGScreenPixel, -36.0f);
if (avatar)
_avatarView.center = CGPointMake(TGScreenPixel, -41.0f);
} completion:^(BOOL finished)
{
if (finished && completion != nil)
completion();
}];
}
}
else
{
_shadowView.center = CGPointMake(TGScreenPixel, raised ? -66.0f : -36.0f);
if (avatar)
_avatarView.center = CGPointMake(TGScreenPixel, raised ? -71.0 : -41.0f);
if (completion != nil)
completion();
}
}
- (void)setCustomPin:(bool)customPin animated:(bool)animated
{
if (animated)
{
_animating = true;
UIImage *image = TGComponentsImageNamed(@"LocationPinIcon");
if (_pallete != nil)
image = TGTintedImage(image, _pallete.iconColor);
_iconView.image = image;
[_backgroundView addSubview:_avatarView];
_avatarView.center = CGPointMake(_backgroundView.frame.size.width / 2.0f, _backgroundView.frame.size.height / 2.0f - 5.0f);
_shadowView.center = CGPointMake(TGScreenPixel, -36.0f);
_backgroundView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f);
_iconView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f - 5.0f);
TGDispatchAfter(0.01, dispatch_get_main_queue(), ^
{
[UIView transitionWithView:_backgroundView duration:0.2 options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowAnimatedContent animations:^
{
_backgroundView.image = customPin ? TGTintedImage(TGComponentsImageNamed(@"LocationPinBackground"), _pallete != nil ? _pallete.locationColor : UIColorRGB(0x008df2)) : TGComponentsImageNamed(@"LocationPinBackground");
_avatarView.hidden = customPin;
_iconView.hidden = !customPin;
} completion:^(BOOL finished)
{
if (!customPin)
[self addSubview:_avatarView];
_animating = false;
[self setNeedsLayout];
}];
});
[self setNeedsLayout];
}
else
{
}
_dotView.hidden = !customPin;
}
@end
@implementation TGLocationPinWrapperView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *view = [super hitTest:point withEvent:event];
if (view == self)
return nil;
return view;
}
@end

View File

@ -1,11 +0,0 @@
#import <UIKit/UIKit.h>
@class TGLocationPallete;
@interface TGLocationPinView : UIView
@property (nonatomic, strong) TGLocationPallete *pallete;
@property (nonatomic, assign, getter=isPinRaised) bool pinRaised;
- (void)setPinRaised:(bool)raised animated:(bool)animated completion:(void (^)(void))completion;
@end

View File

@ -1,125 +0,0 @@
#import "TGLocationPinView.h"
#import "TGImageUtils.h"
#import "LegacyComponentsInternal.h"
#import "TGLocationMapViewController.h"
const CGSize TGLocationPinSize = { 13.5f, 36 };
const CGFloat TGLocationPinDamping = 2.0f;
const CGFloat TGLocationPinPinnedOrigin = 47;
const CGFloat TGLocationPinRaisedOrigin = 7;
const CGPoint TGLocationPinShadowPinnedOrigin = { 43, 47 };
const CGPoint TGLocationPinShadowRaisedOrigin = { 87, -33 };
@interface TGLocationPinView ()
{
UIImageView *_pinView;
UIImageView *_pinPointView;
UIImageView *_shadowView;
}
@end
@implementation TGLocationPinView
- (instancetype)init
{
self = [super initWithFrame:CGRectMake(0, 0, 100, 100)];
if (self != nil)
{
self.userInteractionEnabled = false;
_shadowView = [[UIImageView alloc] initWithFrame:CGRectMake(43, 47, 32, 39)];
_shadowView.alpha = 0.9f;
_shadowView.image = TGComponentsImageNamed(@"LocationPinShadow.png");
[self addSubview:_shadowView];
_pinPointView = [[UIImageView alloc] initWithFrame:CGRectMake(CGFloor(self.frame.size.width / 2 - 2), self.frame.size.height - 18.5f, 3.5f, 1.5f)];
_pinPointView.image = TGComponentsImageNamed(@"LocationPinPoint.png");
[self addSubview:_pinPointView];
_pinView = [[UIImageView alloc] initWithFrame:CGRectMake(CGFloor(self.frame.size.width / 2 - 7), 47, 13.5f, 36)];
_pinView.image = TGComponentsImageNamed(@"LocationPin.png");
[self addSubview:_pinView];
}
return self;
}
- (void)setPallete:(TGLocationPallete *)pallete
{
_pallete = pallete;
_pinView.image = TGTintedImage(_pinView.image, pallete.locationColor);
_pinPointView.image = TGTintedImage(_pinPointView.image, pallete.locationColor);
}
- (void)setPinRaised:(bool)pinRaised
{
[self setPinRaised:pinRaised animated:false completion:nil];
}
- (void)setPinRaised:(bool)raised animated:(bool)animated completion:(void (^)(void))completion
{
_pinRaised = raised;
[_pinView.layer removeAllAnimations];
[_shadowView.layer removeAllAnimations];
if (animated)
{
if (raised)
{
[UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
_pinView.frame = CGRectMake(_pinView.frame.origin.x, TGLocationPinRaisedOrigin, TGLocationPinSize.width, TGLocationPinSize.height);
_shadowView.frame = CGRectMake(TGLocationPinShadowRaisedOrigin.x, TGLocationPinShadowRaisedOrigin.y, _shadowView.frame.size.width, _shadowView.frame.size.height);
} completion:^(BOOL finished)
{
if (finished && completion != nil)
completion();
}];
}
else
{
[UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
_pinView.frame = CGRectMake(_pinView.frame.origin.x, TGLocationPinPinnedOrigin, TGLocationPinSize.width, TGLocationPinSize.height);
_shadowView.frame = CGRectMake(TGLocationPinShadowPinnedOrigin.x, TGLocationPinShadowPinnedOrigin.y, _shadowView.frame.size.width, _shadowView.frame.size.height);
} completion:^(BOOL finished)
{
if (finished)
{
[UIView animateWithDuration:0.1f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
_pinView.frame = CGRectMake(_pinView.frame.origin.x, TGLocationPinPinnedOrigin + TGLocationPinDamping, TGLocationPinSize.width, TGLocationPinSize.height - TGLocationPinDamping);
} completion:^(BOOL finished)
{
if (finished)
{
[UIView animateWithDuration:0.1f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
_pinView.frame = CGRectMake(_pinView.frame.origin.x, TGLocationPinPinnedOrigin, TGLocationPinSize.width, TGLocationPinSize.height);
} completion:^(BOOL finished)
{
if (finished && completion != nil)
completion();
}];
}
}];
}
}];
}
}
else
{
_pinView.frame = CGRectMake(_pinView.frame.origin.x, raised ? TGLocationPinRaisedOrigin : TGLocationPinPinnedOrigin, TGLocationPinSize.width, TGLocationPinSize.height);
CGPoint shadowOrigin = raised ? TGLocationPinShadowRaisedOrigin : TGLocationPinShadowPinnedOrigin;
_shadowView.frame = CGRectMake(shadowOrigin.x, shadowOrigin.y, _shadowView.frame.size.width, _shadowView.frame.size.height);
if (completion != nil)
completion();
}
}
@end

View File

@ -1,59 +0,0 @@
#import "TGLocationPulseView.h"
#import "LegacyComponentsInternal.h"
@interface TGLocationPulseView ()
{
CAShapeLayer *_circleLayer;
}
@end
@implementation TGLocationPulseView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.userInteractionEnabled = false;
_circleLayer = [CAShapeLayer layer];
_circleLayer.hidden = true;
_circleLayer.opacity = 0.0f;
_circleLayer.path = CGPathCreateWithEllipseInRect(CGRectMake(-60.0f, -60.0f, 120.0f, 120.0f), NULL);
_circleLayer.fillColor = UIColorRGBA(0x007aff, 0.27f).CGColor;
[self.layer addSublayer:_circleLayer];
}
return self;
}
- (void)start
{
_circleLayer.hidden = false;
if (_circleLayer.animationKeys.count > 0)
return;
CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.values = @[@0.0f, @0.72f, @1.0f, @1.0f];
scaleAnimation.keyTimes = @[@0.0, @0.49f, @0.88f, @1.0f];
scaleAnimation.duration = 3.0;
scaleAnimation.repeatCount = INFINITY;
scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[_circleLayer addAnimation:scaleAnimation forKey:@"circle-scale"];
CAKeyframeAnimation *opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
opacityAnimation.values = @[@1.0f, @0.2f, @0.0, @0.0f];
opacityAnimation.keyTimes = @[@0.0, @0.4f, @0.62f, @1.0f];
opacityAnimation.duration = 3.0;
opacityAnimation.repeatCount = INFINITY;
[_circleLayer addAnimation:opacityAnimation forKey:@"circle-opacity"];
}
- (void)stop
{
_circleLayer.hidden = true;
[_circleLayer removeAllAnimations];
}
@end

View File

@ -1,23 +0,0 @@
#import <CoreLocation/CoreLocation.h>
@interface TGLocationReverseGeocodeResult : NSObject
@property (nonatomic, readonly) NSString *identifier;
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, readonly) NSString *displayAddress;
@property (nonatomic, readonly) NSString *country;
@property (nonatomic, readonly) NSString *countryAbbr;
@property (nonatomic, readonly) NSString *state;
@property (nonatomic, readonly) NSString *stateAbbr;
@property (nonatomic, readonly) NSString *city;
@property (nonatomic, readonly) NSString *district;
@property (nonatomic, readonly) NSString *street;
@property (nonatomic, readonly) NSString *fullAddress;
+ (TGLocationReverseGeocodeResult *)reverseGeocodeResultWithDictionary:(NSDictionary *)dictionary;
+ (TGLocationReverseGeocodeResult *)reverseGeocodeResultWithPlacemark:(CLPlacemark *)placemark;
@end

View File

@ -1,85 +0,0 @@
#import "TGLocationReverseGeocodeResult.h"
@implementation TGLocationReverseGeocodeResult
+ (TGLocationReverseGeocodeResult *)reverseGeocodeResultWithDictionary:(NSDictionary *)dictionary
{
TGLocationReverseGeocodeResult *result = [[TGLocationReverseGeocodeResult alloc] init];
for (NSDictionary *component in dictionary[@"address_components"])
{
NSArray *types = component[@"types"];
__unused NSString *shortName = component[@"short_name"];
NSString *longName = component[@"long_name"];
if ([types containsObject:@"country"])
{
result->_country = longName;
result->_countryAbbr = shortName;
}
else if ([types containsObject:@"administrative_area_level_1"])
{
result->_state = longName;
result->_stateAbbr = shortName;
}
else if ([types containsObject:@"locality"])
{
result->_city = longName;
}
else if ([types containsObject:@"sublocality"])
{
result->_district = longName;
}
else if ([types containsObject:@"neighborhood"])
{
if (result->_district.length == 0)
result->_district = longName;
}
else if ([types containsObject:@"route"])
{
result->_street = longName;
}
}
return result;
}
+ (TGLocationReverseGeocodeResult *)reverseGeocodeResultWithPlacemark:(CLPlacemark *)placemark
{
TGLocationReverseGeocodeResult *result = [[TGLocationReverseGeocodeResult alloc] init];
result->_country = placemark.country;
result->_countryAbbr = placemark.ISOcountryCode;
result->_city = placemark.locality;
result->_district = placemark.subLocality;
result->_street = placemark.thoroughfare;
if (placemark.name.length > 0 && result->_street.length == 0) {
result->_street = placemark.name;
}
return result;
}
- (NSString *)displayAddress
{
if (self.street.length > 0)
return self.street;
else if (self.city.length > 0)
return self.city;
else if (self.country.length > 0)
return self.country;
return nil;
}
- (NSString *)fullAddress
{
NSMutableArray *components = [[NSMutableArray alloc] init];
if (self.street.length > 0)
[components addObject:self.street];
if (self.city.length > 0)
[components addObject:self.city];
if (self.country.length > 0)
[components addObject:self.country];
return [components componentsJoinedByString:@", "];
}
@end

View File

@ -1,14 +0,0 @@
#import <UIKit/UIKit.h>
@class TGLocationPallete;
@interface TGLocationSectionHeaderCell : UITableViewCell
@property (nonatomic, strong) TGLocationPallete *pallete;
- (void)configureWithTitle:(NSString *)title;
@end
extern NSString *const TGLocationSectionHeaderKind;
extern const CGFloat TGLocationSectionHeaderHeight;

View File

@ -1,73 +0,0 @@
#import "TGLocationSectionHeaderCell.h"
#import "TGLocationMapViewController.h"
#import "LegacyComponentsInternal.h"
#import "TGFont.h"
NSString *const TGLocationSectionHeaderKind = @"TGLocationSectionHeaderKind";
const CGFloat TGLocationSectionHeaderHeight = 29.0f;
@interface TGLocationSectionHeaderCell ()
{
UILabel *_titleLabel;
}
@end
@implementation TGLocationSectionHeaderCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self != nil)
{
self.backgroundColor = UIColorRGB(0xf7f7f7);
self.selectedBackgroundView = [[UIView alloc] init];
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = self.backgroundColor;
_titleLabel.font = TGMediumSystemFontOfSize(12);
_titleLabel.textColor = UIColorRGB(0x8e8e93);
[self addSubview:_titleLabel];
}
return self;
}
- (void)setPallete:(TGLocationPallete *)pallete
{
if (pallete == nil || _pallete == pallete)
return;
_pallete = pallete;
self.backgroundColor = pallete.sectionHeaderBackgroundColor;
_titleLabel.backgroundColor = self.backgroundColor;
_titleLabel.textColor = pallete.sectionHeaderTextColor;
}
- (void)configureWithTitle:(NSString *)title
{
title = [title uppercaseString];
void (^changeBlock)(void) = ^
{
_titleLabel.text = title;
};
if ([_titleLabel.text isEqualToString:title])
return;
if (_titleLabel.text.length == 0)
changeBlock();
else
[UIView transitionWithView:_titleLabel duration:0.2 options:UIViewAnimationOptionTransitionCrossDissolve animations:changeBlock completion:nil];
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat padding = 14.0f;
_titleLabel.frame = CGRectMake(padding, 1.0f, self.frame.size.width - padding, self.frame.size.height - 2.0f);
}
@end

View File

@ -1,18 +0,0 @@
#import <SSignalKit/SSignalKit.h>
#import <CoreLocation/CoreLocation.h>
@interface TGLocationSignals : NSObject
+ (SSignal *)geocodeAddress:(NSString *)address;
+ (SSignal *)geocodeAddressDictionary:(NSDictionary *)dictionary;
+ (SSignal *)reverseGeocodeCoordinate:(CLLocationCoordinate2D)coordinate;
+ (SSignal *)cityForCoordinate:(CLLocationCoordinate2D)coordinate;
+ (SSignal *)driveEta:(CLLocationCoordinate2D)coordinate;
+ (void)storeLastKnownUserLocation:(CLLocation *)location;
+ (CLLocation *)lastKnownUserLocation;
+ (SSignal *)userLocation:(SVariable *)locationRequired;
@end

View File

@ -1,257 +0,0 @@
#import "TGLocationSignals.h"
#import "LegacyComponentsInternal.h"
#import "TGStringUtils.h"
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "TGLocationVenue.h"
#import "TGLocationReverseGeocodeResult.h"
NSString *const TGLocationGoogleGeocodeLocale = @"en";
@interface TGLocationHelper : NSObject <CLLocationManagerDelegate> {
CLLocationManager *_locationManager;
void (^_locationDetermined)(CLLocation *);
bool _startedUpdating;
}
@end
@implementation TGLocationHelper
- (instancetype)initWithLocationDetermined:(void (^)(CLLocation *))locationDetermined {
self = [super init];
if (self != nil) {
_locationDetermined = [locationDetermined copy];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
bool startUpdating = false;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
switch ([CLLocationManager authorizationStatus])
{
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusAuthorizedWhenInUse:
startUpdating = true;
default:
break;
}
}
if (startUpdating) {
[self startUpdating];
}
}
return self;
}
- (void)dealloc {
[_locationManager stopUpdatingLocation];
}
- (void)startUpdating {
if (!_startedUpdating) {
_startedUpdating = true;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
[_locationManager requestWhenInUseAuthorization];
}
[_locationManager startUpdatingLocation];
}
}
- (void)locationManager:(CLLocationManager *)__unused manager didUpdateLocations:(NSArray *)locations {
if (locations.count != 0) {
if (_locationDetermined) {
_locationDetermined([locations lastObject]);
}
}
}
@end
@implementation TGLocationSignals
+ (SSignal *)geocodeAddress:(NSString *)address
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error)
{
if (error != nil)
{
[subscriber putError:error];
return;
}
else
{
[subscriber putNext:placemarks.firstObject];
[subscriber putCompletion];
}
}];
return [[SBlockDisposable alloc] initWithBlock:^
{
[geocoder cancelGeocode];
}];
}];
}
+ (SSignal *)geocodeAddressDictionary:(NSDictionary *)dictionary
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressDictionary:dictionary completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error)
{
if (error != nil)
{
[subscriber putError:error];
return;
}
else
{
[subscriber putNext:placemarks.firstObject];
[subscriber putCompletion];
}
}];
return [[SBlockDisposable alloc] initWithBlock:^
{
[geocoder cancelGeocode];
}];
}];
}
+ (SSignal *)reverseGeocodeCoordinate:(CLLocationCoordinate2D)coordinate
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:[[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude] completionHandler:^(NSArray *placemarks, NSError *error)
{
if (error != nil)
{
[subscriber putError:error];
return;
}
else
{
[subscriber putNext:[TGLocationReverseGeocodeResult reverseGeocodeResultWithPlacemark:placemarks.firstObject]];
[subscriber putCompletion];
}
}];
return [[SBlockDisposable alloc] initWithBlock:^
{
[geocoder cancelGeocode];
}];
}];
}
+ (SSignal *)cityForCoordinate:(CLLocationCoordinate2D)coordinate
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:[[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude] completionHandler:^(NSArray *placemarks, NSError *error)
{
if (error != nil)
{
[subscriber putError:error];
return;
}
else
{
[subscriber putNext:[placemarks.firstObject locality]];
[subscriber putCompletion];
}
}];
return [[SBlockDisposable alloc] initWithBlock:^
{
[geocoder cancelGeocode];
}];
}];
}
+ (SSignal *)driveEta:(CLLocationCoordinate2D)destinationCoordinate
{
if (iosMajorVersion() < 7)
return [SSignal single:@0];
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
MKPlacemark *destinationPlacemark = [[MKPlacemark alloc] initWithCoordinate:destinationCoordinate addressDictionary:nil];
MKMapItem *destinationMapItem = [[MKMapItem alloc] initWithPlacemark:destinationPlacemark];
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
request.source = [MKMapItem mapItemForCurrentLocation];
request.destination = destinationMapItem;
request.transportType = MKDirectionsTransportTypeAutomobile;
request.requestsAlternateRoutes = false;
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateETAWithCompletionHandler:^(MKETAResponse *response, NSError *error)
{
if (error != nil)
{
[subscriber putError:error];
return;
}
[subscriber putNext:@(response.expectedTravelTime)];
[subscriber putCompletion];
}];
return [[SBlockDisposable alloc] initWithBlock:^
{
[directions cancel];
}];
}];
}
#pragma mark -
static CLLocation *lastKnownUserLocation;
+ (void)storeLastKnownUserLocation:(CLLocation *)location
{
lastKnownUserLocation = location;
}
+ (CLLocation *)lastKnownUserLocation
{
NSTimeInterval locationAge = -[lastKnownUserLocation.timestamp timeIntervalSinceNow];
if (locationAge > 600)
lastKnownUserLocation = nil;
return lastKnownUserLocation;
}
+ (SSignal *)userLocation:(SVariable *)locationRequired {
return [[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
TGLocationHelper *helper = [[TGLocationHelper alloc] initWithLocationDetermined:^(CLLocation *location) {
[subscriber putNext:location];
}];
id<SDisposable> requiredDisposable = [[[[locationRequired signal] take:1] deliverOn:[SQueue mainQueue]] startWithNext:^(__unused id next) {
[helper startUpdating];
}];
return [[SBlockDisposable alloc] initWithBlock:^{
[helper description]; // keep reference
[requiredDisposable dispose];
}];
}] startOn:[SQueue mainQueue]];
}
@end

View File

@ -1,12 +0,0 @@
#import <UIKit/UIKit.h>
@interface TGLocationTitleView : UIView
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *address;
@property (nonatomic, assign) UIInterfaceOrientation interfaceOrientation;
@property (nonatomic, assign) CGFloat backButtonWidth;
@property (nonatomic, assign) CGFloat actionsButtonWidth;
@end

View File

@ -1,135 +0,0 @@
#import "TGLocationTitleView.h"
#import "LegacyComponentsInternal.h"
#import "TGFont.h"
@interface TGLocationTitleView ()
{
UILabel *_titleLabel;
UILabel *_addressLabel;
}
@end
@implementation TGLocationTitleView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, frame.size.width, 20)];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.font = TGBoldSystemFontOfSize(17);
_titleLabel.textColor = [UIColor blackColor];
_titleLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:_titleLabel];
_addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 21, frame.size.width, 21)];
_addressLabel.backgroundColor = [UIColor clearColor];
_addressLabel.font = TGSystemFontOfSize(13.0f);
_addressLabel.textColor = UIColorRGB(0x787878);
_addressLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:_addressLabel];
}
return self;
}
- (NSString *)title
{
return _titleLabel.text;
}
- (void)setTitle:(NSString *)title
{
_titleLabel.text = title;
[self setNeedsLayout];
}
- (NSString *)address
{
return _addressLabel.text;
}
- (void)setAddress:(NSString *)address
{
_addressLabel.text = address;
[self setNeedsLayout];
}
- (UIView *)_findNavigationBar:(UIView *)view
{
if (view.superview == nil)
return nil;
else if ([view.superview isKindOfClass:[UINavigationBar class]])
return view.superview;
else
return [self _findNavigationBar:view.superview];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[_titleLabel sizeToFit];
[_addressLabel sizeToFit];
CGRect titleFrame = _titleLabel.frame;
titleFrame.size.width = CGCeil(titleFrame.size.width);
CGRect addressFrame = _addressLabel.frame;
addressFrame.size.width = CGCeil(addressFrame.size.width);
UIView *navigationBar = [self _findNavigationBar:self];
CGRect offsetRect = [self.superview convertRect:self.frame toView:navigationBar];
UIEdgeInsets edges = UIEdgeInsetsMake(0, self.backButtonWidth, 0, navigationBar.frame.size.width - self.actionsButtonWidth);
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
{
if (_addressLabel.text.length > 0)
titleFrame.origin.y = 5;
else
titleFrame.origin.y = 12;
addressFrame.origin.y = 23.5f;
titleFrame.origin.x = (navigationBar.frame.size.width - titleFrame.size.width) / 2;
if (titleFrame.origin.x < edges.left || CGRectGetMaxX(titleFrame) > edges.right)
{
titleFrame.origin.x = edges.left;
titleFrame.size.width = edges.right - edges.left;
}
CGFloat titleCenter = CGRectGetMidX(titleFrame);
addressFrame.origin.x = titleCenter - addressFrame.size.width / 2;
if (addressFrame.origin.x < edges.left || CGRectGetMaxX(addressFrame) > edges.right)
{
addressFrame.origin.x = edges.left;
addressFrame.size.width = edges.right - edges.left;
}
}
else
{
titleFrame.origin.y = 10;
addressFrame.origin.y = 13;
CGFloat jointWidth = titleFrame.size.width + addressFrame.size.width + 6;
CGFloat jointOrigin = (navigationBar.frame.size.width - jointWidth) / 2;
if (jointOrigin < edges.left || jointOrigin + jointWidth > edges.right)
{
jointOrigin = edges.left;
CGFloat newJointWidth = edges.right - edges.left;
addressFrame.size.width -= (jointWidth - newJointWidth);
jointWidth = newJointWidth;
}
titleFrame.origin.x = jointOrigin;
addressFrame.origin.x = jointOrigin + jointWidth - addressFrame.size.width;
}
_titleLabel.frame = CGRectOffset(titleFrame, -offsetRect.origin.x, 0);
_addressLabel.frame = CGRectOffset(addressFrame, -offsetRect.origin.x, 0);
}
@end

View File

@ -1,23 +0,0 @@
#import <MapKit/MKMapView.h>
#import "TGModernButton.h"
typedef enum {
TGLocationTrackingModeNone,
TGLocationTrackingModeFollow,
TGLocationTrackingModeFollowWithHeading
} TGLocationTrackingMode;
@interface TGLocationTrackingButton : TGModernButton
@property (nonatomic, assign) TGLocationTrackingMode trackingMode;
- (void)setTrackingMode:(TGLocationTrackingMode)trackingMode animated:(bool)animated;
@property (nonatomic, assign, getter=isLocationAvailable) bool locationAvailable;
- (void)setLocationAvailable:(bool)available animated:(bool)animated;
- (void)setAccentColor:(UIColor *)accentColor spinnerColor:(UIColor *)spinnerColor;
+ (TGLocationTrackingMode)locationTrackingModeWithUserTrackingMode:(MKUserTrackingMode)mode;
+ (MKUserTrackingMode)userTrackingModeWithLocationTrackingMode:(TGLocationTrackingMode)mode;
@end

View File

@ -1,151 +0,0 @@
#import "TGLocationTrackingButton.h"
#import "TGImageUtils.h"
#import "LegacyComponentsInternal.h"
@interface TGLocationTrackingButton ()
{
UIImageView *_noneModeIconView;
UIImageView *_followModeIconView;
UIImageView *_followWithHeadingModeIconView;
UIActivityIndicatorView *_activityIndicator;
}
@end
@implementation TGLocationTrackingButton
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.exclusiveTouch = true;
_noneModeIconView = [[UIImageView alloc] initWithFrame:self.bounds];
_noneModeIconView.contentMode = UIViewContentModeCenter;
_noneModeIconView.image = TGComponentsImageNamed(@"TrackingLocationOff.png");
[self addSubview:_noneModeIconView];
_followModeIconView = [[UIImageView alloc] initWithFrame:self.bounds];
_followModeIconView.contentMode = UIViewContentModeCenter;
_followModeIconView.image = TGComponentsImageNamed(@"TrackingLocation.png");
[self addSubview:_followModeIconView];
_followWithHeadingModeIconView = [[UIImageView alloc] initWithFrame:CGRectOffset(self.bounds, 1, 0.5f)];
_followWithHeadingModeIconView.contentMode = UIViewContentModeCenter;
_followWithHeadingModeIconView.image = TGComponentsImageNamed(@"TrackingHeading.png");
[self addSubview:_followWithHeadingModeIconView];
_activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
_activityIndicator.userInteractionEnabled = false;
_activityIndicator.frame = CGRectOffset(_activityIndicator.frame, 0, 0);
_activityIndicator.alpha = 0.0f;
_activityIndicator.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
[self addSubview:_activityIndicator];
_locationAvailable = true;
[self setTrackingMode:TGLocationTrackingModeNone];
}
return self;
}
- (void)setAccentColor:(UIColor *)accentColor spinnerColor:(UIColor *)spinnerColor
{
_noneModeIconView.image = TGTintedImage(TGComponentsImageNamed(@"TrackingLocationOff.png"), accentColor);
_followModeIconView.image = TGTintedImage(TGComponentsImageNamed(@"TrackingLocation.png"), accentColor);
_followWithHeadingModeIconView.image = TGTintedImage(TGComponentsImageNamed(@"TrackingHeading.png"), accentColor);
_activityIndicator.color = spinnerColor;
}
- (void)setTrackingMode:(TGLocationTrackingMode)trackingMode
{
[self setTrackingMode:trackingMode animated:false];
}
- (void)setTrackingMode:(TGLocationTrackingMode)trackingMode animated:(bool)animated
{
_trackingMode = trackingMode;
CGFloat noneModeAlpha = (trackingMode == TGLocationTrackingModeNone) ? 1.0f : 0.0f;
CGFloat followModeAlpha = (trackingMode == TGLocationTrackingModeFollow) ? 1.0f : 0.0f;
CGFloat followWithHeadingModeAlpha = (trackingMode == TGLocationTrackingModeFollowWithHeading) ? 1.0f : 0.0f;
void (^changeBlock)(void) = ^
{
_noneModeIconView.alpha = noneModeAlpha;
_followModeIconView.alpha = followModeAlpha;
_followWithHeadingModeIconView.alpha = followWithHeadingModeAlpha;
if (followWithHeadingModeAlpha < FLT_EPSILON)
{
_noneModeIconView.transform = CGAffineTransformIdentity;
_followModeIconView.transform = CGAffineTransformIdentity;
_followWithHeadingModeIconView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
}
else
{
_noneModeIconView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
_followModeIconView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
_followWithHeadingModeIconView.transform = CGAffineTransformIdentity;
}
};
if (animated)
[UIView animateWithDuration:0.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:changeBlock completion:nil];
else
changeBlock();
}
- (void)setIsLocationAvailable:(bool)available
{
[self setLocationAvailable:available animated:false];
}
- (void)setLocationAvailable:(bool)available animated:(bool)animated
{
if (available == _locationAvailable)
return;
_locationAvailable = available;
if (animated)
{
}
else
{
}
}
+ (TGLocationTrackingMode)locationTrackingModeWithUserTrackingMode:(MKUserTrackingMode)mode
{
switch (mode)
{
case MKUserTrackingModeFollow:
return TGLocationTrackingModeFollow;
case MKUserTrackingModeFollowWithHeading:
return TGLocationTrackingModeFollowWithHeading;
default:
return TGLocationTrackingModeNone;
}
}
+ (MKUserTrackingMode)userTrackingModeWithLocationTrackingMode:(TGLocationTrackingMode)mode
{
switch (mode)
{
case TGLocationTrackingModeFollow:
return MKUserTrackingModeFollow;
case TGLocationTrackingModeFollowWithHeading:
return MKUserTrackingModeFollowWithHeading;
default:
return MKUserTrackingModeNone;
}
}
@end

View File

@ -1,50 +0,0 @@
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
@interface TGLocationUtils : NSObject
+ (MKMapRect)MKMapRectForCoordinateRegion:(MKCoordinateRegion)region;
+ (bool)requestWhenInUserLocationAuthorizationWithLocationManager:(CLLocationManager *)locationManager;
+ (bool)requestAlwaysUserLocationAuthorizationWithLocationManager:(CLLocationManager *)locationManager;
+ (NSString *)stringFromDistance:(CLLocationDistance)distance;
+ (NSString *)stringFromAccuracy:(CLLocationAccuracy)accuracy;
+ (NSString *)stringForCoordinate:(CLLocationCoordinate2D)coordinate;
@end
@interface TGLocationUtils (GoogleMaps)
+ (CLLocationDegrees)adjustGMapLatitude:(CLLocationDegrees)latitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom;
+ (CLLocationDegrees)adjustGMapLongitude:(CLLocationDegrees)longitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom;
+ (CLLocationCoordinate2D)adjustGMapCoordinate:(CLLocationCoordinate2D)coordinate withPixelOffset:(CGPoint)offset zoom:(NSInteger)zoom;
@end
@interface TGLocationUtils (ThirdPartyAppLauncher)
+ (void)openMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections locationName:(NSString *)locationName;
+ (void)openGoogleMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections;
+ (bool)isGoogleMapsInstalled;
+ (void)openGoogleWithPlaceId:(NSString *)placeId;
+ (void)openFoursquareWithVenueId:(NSString *)venueId;
+ (bool)isFoursquareInstalled;
+ (void)openHereMapsWithCoordinate:(CLLocationCoordinate2D)coordinate;
+ (bool)isHereMapsInstalled;
+ (void)openYandexMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections;
+ (bool)isYandexMapsInstalled;
+ (void)openDirectionsInYandexNavigatorWithCoordinate:(CLLocationCoordinate2D)coordinate;
+ (bool)isYandexNavigatorInstalled;
+ (void)openWazeWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections;
+ (bool)isWazeInstalled;
@end

View File

@ -1,381 +0,0 @@
#import "TGLocationUtils.h"
#import "LegacyComponentsInternal.h"
#import "TGLocalization.h"
#import <MapKit/MapKit.h>
@implementation TGLocationUtils
+ (MKMapRect)MKMapRectForCoordinateRegion:(MKCoordinateRegion)region
{
MKMapPoint a = MKMapPointForCoordinate(CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta / 2,
region.center.longitude - region.span.longitudeDelta / 2));
MKMapPoint b = MKMapPointForCoordinate(CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta / 2,
region.center.longitude + region.span.longitudeDelta / 2));
return MKMapRectMake(MIN(a.x,b.x), MIN(a.y,b.y), ABS(a.x-b.x), ABS(a.y-b.y));
}
+ (bool)requestWhenInUserLocationAuthorizationWithLocationManager:(CLLocationManager *)locationManager
{
CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
if (authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted)
return false;
if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])
{
if (authorizationStatus == kCLAuthorizationStatusNotDetermined)
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"TG_askedForAlwaysAuthorization_v0"];
[locationManager requestWhenInUseAuthorization];
return true;
}
return false;
}
+ (bool)requestAlwaysUserLocationAuthorizationWithLocationManager:(CLLocationManager *)locationManager
{
CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
if (authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted)
return false;
if ([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
{
NSString *key = @"TG_askedForAlwaysAuthorization_v0";
bool askedForAlwaysAuthorization = [[[NSUserDefaults standardUserDefaults] objectForKey:key] boolValue];
if (authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse && askedForAlwaysAuthorization)
return false;
[locationManager requestAlwaysAuthorization];
[[NSUserDefaults standardUserDefaults] setObject:@true forKey:key];
return true;
}
return false;
}
+ (NSString *)stringFromDistance:(CLLocationDistance)distance
{
if (iosMajorVersion() >= 7)
{
MKDistanceFormatter *formatter = [self sharedDistanceFormatter];
NSString *systemLocale = [NSLocale currentLocale].localeIdentifier;
NSString *finalLocale = legacyEffectiveLocalization().code;
NSRange range = [systemLocale rangeOfString:@"_"];
if (range.location != NSNotFound) {
finalLocale = [finalLocale stringByAppendingString:[systemLocale substringFromIndex:range.location]];
}
formatter.locale = [NSLocale localeWithLocaleIdentifier:finalLocale];
if ([[formatter.locale objectForKey:NSLocaleUsesMetricSystem] boolValue])
formatter.unitStyle = MKDistanceFormatterUnitStyleAbbreviated;
else
formatter.unitStyle = MKDistanceFormatterUnitStyleDefault;
return [[self sharedDistanceFormatter] stringFromDistance:distance];
}
else
{
return [self _customStringFromDistance:distance];
}
}
+ (NSString *)stringFromAccuracy:(CLLocationAccuracy)accuracy
{
if (iosMajorVersion() >= 7)
{
MKDistanceFormatter *formatter = [self sharedAccuracyFormatter];
//formatter.locale = [NSLocale localeWithLocaleIdentifier:legacyEffectiveLocalization().code];
return [[self sharedAccuracyFormatter] stringFromDistance:accuracy];
}
else
{
return [self _customStringFromDistance:accuracy];
}
}
+ (NSString *)stringForCoordinate:(CLLocationCoordinate2D)coordinate
{
NSInteger latSeconds = (NSInteger)(coordinate.latitude * 3600);
NSInteger latDegrees = latSeconds / 3600;
latSeconds = labs(latSeconds % 3600);
NSInteger latMinutes = latSeconds / 60;
latSeconds %= 60;
NSInteger longSeconds = (NSInteger)(coordinate.longitude * 3600);
NSInteger longDegrees = longSeconds / 3600;
longSeconds = labs(longSeconds % 3600);
NSInteger longMinutes = longSeconds / 60;
longSeconds %= 60;
NSString *result = [NSString stringWithFormat:@"%@%02ld° %02ld' %02ld\" %@%02ld° %02ld' %02ld\"", latDegrees >= 0 ? @"N" : @"S", labs(latDegrees), (long)latMinutes, (long)latSeconds, longDegrees >= 0 ? @"E" : @"W", labs(longDegrees), (long)longMinutes, (long)longSeconds];
return result;
}
+ (NSString *)_customStringFromDistance:(CLLocationDistance)distance
{
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:legacyEffectiveLocalization().code];
bool metricUnits = [[locale objectForKey:NSLocaleUsesMetricSystem] boolValue];
NSString *distanceString = nil;
if (metricUnits)
{
if (distance >= 1000 * 1000)
distanceString = [[NSString alloc] initWithFormat:@"%.1fK km", distance / (1000.0 * 1000.0)];
else if (distance > 1000)
distanceString = [[NSString alloc] initWithFormat:@"%.1f km", distance / 1000.0];
else
distanceString = [[NSString alloc] initWithFormat:@"%d m", (int)distance];
}
else
{
double feetDistance = distance / 0.3048;
if (feetDistance >= 5280)
{
char buf[32];
snprintf(buf, 32, "%.1f", feetDistance / 5280.0);
bool dot = false;
for (int i = 0; i < 32; i++)
{
char c = buf[i];
if (c == '\0')
break;
else if (c < '0' || c > '9')
{
dot = true;
break;
}
}
distanceString = [[NSString alloc] initWithFormat:@"%s mile%s", buf, dot || feetDistance / 5280.0 > 1.0 ? "s" : ""];
}
else
{
distanceString = [[NSString alloc] initWithFormat:@"%d %s", (int)feetDistance, (int)feetDistance != 1 ? "feet" : "foot"];
}
}
return distanceString;
}
+ (MKDistanceFormatter *)sharedDistanceFormatter
{
static dispatch_once_t once;
static MKDistanceFormatter *distanceFormatter;
dispatch_once(&once, ^
{
distanceFormatter = [[MKDistanceFormatter alloc] init];
});
return distanceFormatter;
}
+ (MKDistanceFormatter *)sharedAccuracyFormatter
{
static dispatch_once_t once;
static MKDistanceFormatter *accuracyFormatter;
dispatch_once(&once, ^
{
accuracyFormatter = [[MKDistanceFormatter alloc] init];
accuracyFormatter.unitStyle = MKDistanceFormatterUnitStyleFull;
});
return accuracyFormatter;
}
@end
const NSInteger TGGoogleMapsOffset = 268435456;
const CGFloat TGGoogleMapsRadius = TGGoogleMapsOffset / (CGFloat)M_PI;
@implementation TGLocationUtils (GoogleMaps)
+ (CLLocationCoordinate2D)adjustGMapCoordinate:(CLLocationCoordinate2D)coordinate withPixelOffset:(CGPoint)offset zoom:(NSInteger)zoom
{
return CLLocationCoordinate2DMake([self adjustGMapLatitude:coordinate.latitude withPixelOffset:(NSInteger)offset.y zoom:zoom], [self adjustGMapLongitude:coordinate.longitude withPixelOffset:(NSInteger)offset.x zoom:zoom]);
}
+ (CLLocationDegrees)adjustGMapLatitude:(CLLocationDegrees)latitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom
{
return [self _yToLatitude:([self _latitudeToY:latitude] + (offset << (21 - zoom)))];
}
+ (CLLocationDegrees)adjustGMapLongitude:(CLLocationDegrees)longitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom
{
return [self _xToLongitude:([self _longitudeToX:longitude] + (offset << (21 - zoom)))];
}
+ (NSInteger)_latitudeToY:(CLLocationDegrees)latitude
{
return (NSInteger)round(TGGoogleMapsOffset - TGGoogleMapsRadius * log((1 + sin(latitude * M_PI / 180.0)) / (1 - sin(latitude * M_PI / 180.0))) / 2);
}
+ (CLLocationDegrees)_yToLatitude:(NSInteger)y
{
return (M_PI_2 - 2 * atan(exp((y - TGGoogleMapsOffset) / TGGoogleMapsRadius))) * 180.0 / M_PI;
}
+ (NSInteger)_longitudeToX:(CLLocationDegrees)longitude
{
return (NSInteger)round(TGGoogleMapsOffset + TGGoogleMapsRadius * longitude * M_PI / 180);
}
+ (CLLocationDegrees)_xToLongitude:(NSInteger)x
{
return (x - TGGoogleMapsOffset) / TGGoogleMapsRadius * 180.0 / M_PI;
}
@end
@implementation TGLocationUtils (ThirdPartyAppLauncher)
#pragma mark Apple Maps
+ (void)openMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections locationName:(NSString *)locationName
{
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate
addressDictionary:nil];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:locationName];
if (withDirections)
{
NSDictionary *options = @{ MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving };
MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation];
[MKMapItem openMapsWithItems:@[ currentLocationMapItem, mapItem ]
launchOptions:options];
}
else
{
[mapItem openInMapsWithLaunchOptions:nil];
}
}
#pragma mark Google Maps
+ (void)openGoogleMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections
{
NSURL *url = nil;
NSString *coordinatePair = [NSString stringWithFormat:@"%f,%f", coordinate.latitude, coordinate.longitude];
if (withDirections)
{
url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"comgooglemaps-x-callback://?daddr=%@&directionsmode=driving&x-success=telegram://?resume=true&&x-source=Telegram", coordinatePair]];
}
else
{
url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"comgooglemaps-x-callback://?center=%@&q=%@&x-success=telegram://?resume=true&&x-source=Telegram", coordinatePair, coordinatePair]];
}
[[LegacyComponentsGlobals provider] openURL:url];
}
+ (bool)isGoogleMapsInstalled
{
return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"comgooglemaps-x-callback://"]];
}
+ (void)openGoogleWithPlaceId:(NSString *)placeId
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://maps.google.com/maps/place?cid=%@", placeId]];
[[LegacyComponentsGlobals provider] openURL:url];
}
#pragma mark Foursquare
+ (void)openFoursquareWithVenueId:(NSString *)venueId
{
NSURL *url = nil;
if ([self isFoursquareInstalled])
url = [NSURL URLWithString:[NSString stringWithFormat:@"foursquare://venues/%@", venueId]];
else
url = [NSURL URLWithString:[NSString stringWithFormat:@"https://foursquare.com/venue/%@", venueId]];
[[LegacyComponentsGlobals provider] openURL:url];
}
+ (bool)isFoursquareInstalled
{
return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"foursquare://"]];
}
#pragma mark Here Maps
+ (void)openHereMapsWithCoordinate:(CLLocationCoordinate2D)coordinate
{
NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"here-location://%f,%f", coordinate.latitude, coordinate.longitude]];
[[LegacyComponentsGlobals provider] openURL:url];
}
+ (bool)isHereMapsInstalled
{
return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"here-location://"]];
}
#pragma mark Yandex Maps
+ (void)openYandexMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections
{
NSURL *url = nil;
if (withDirections)
{
url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexmaps://build_route_on_map?lat_to=%f&lon_to=%f", coordinate.latitude, coordinate.longitude]];
}
else
{
url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexmaps://maps.yandex.ru/?pt=%f,%f&z=16", coordinate.longitude, coordinate.latitude]];
}
[[LegacyComponentsGlobals provider] openURL:url];
}
+ (bool)isYandexMapsInstalled
{
return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"yandexmaps://"]];
}
#pragma mark Yandex Navigator
+ (void)openDirectionsInYandexNavigatorWithCoordinate:(CLLocationCoordinate2D)coordinate
{
NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexnavi://build_route_on_map?lat_to=%f&lon_to=%f", coordinate.latitude, coordinate.longitude]];
[[LegacyComponentsGlobals provider] openURL:url];
}
+ (bool)isYandexNavigatorInstalled
{
return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"yandexnavi://"]];
}
#pragma mark - Waze
+ (void)openWazeWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections
{
NSURL *url = nil;
if (withDirections)
{
url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"waze://?ll=%f,%f&navigate=yes", coordinate.latitude, coordinate.longitude]];
}
else
{
url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"waze://?ll=%f,%f", coordinate.latitude, coordinate.longitude]];
}
[[LegacyComponentsGlobals provider] openURL:url];
}
+ (bool)isWazeInstalled
{
return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"waze://"]];
}
@end

View File

@ -1,118 +0,0 @@
#import "TGLocationVenue.h"
#import <LegacyComponents/TGLocationMediaAttachment.h>
NSString *const TGLocationGooglePlacesVenueProvider = @"google";
NSString *const TGLocationFoursquareVenueProvider = @"foursquare";
@interface TGLocationVenue ()
{
NSString *_displayAddress;
}
@end
@implementation TGLocationVenue
+ (TGLocationVenue *)venueWithFoursquareDictionary:(NSDictionary *)dictionary
{
TGLocationVenue *venue = [[TGLocationVenue alloc] init];
venue->_identifier = dictionary[@"id"];
venue->_name = dictionary[@"name"];
NSDictionary *location = dictionary[@"location"];
venue->_coordinate = CLLocationCoordinate2DMake([location[@"lat"] doubleValue], [location[@"lng"] doubleValue]);
NSArray *categories = dictionary[@"categories"];
if (categories.count > 0)
{
NSDictionary *category = categories.firstObject;
NSDictionary *icon = category[@"icon"];
if (icon != nil)
venue->_categoryIconUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@64%@", icon[@"prefix"], icon[@"suffix"]]];
NSURL *url = [NSURL URLWithString:icon[@"prefix"]];
NSArray *components = url.pathComponents;
NSString *categoryName = [[NSString stringWithFormat:@"%@/%@", [components objectAtIndex:components.count - 2], components.lastObject] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"_"]];
venue->_categoryName = categoryName;
}
venue->_country = location[@"country"];
venue->_state = location[@"state"];
venue->_city = location[@"city"];
venue->_address = location[@"address"];
venue->_crossStreet = location[@"crossStreet"];
venue->_provider = TGLocationFoursquareVenueProvider;
return venue;
}
+ (TGLocationVenue *)venueWithGooglePlacesDictionary:(NSDictionary *)dictionary
{
TGLocationVenue *venue = [[TGLocationVenue alloc] init];
venue->_identifier = dictionary[@"place_id"];
venue->_name = dictionary[@"name"];
NSDictionary *location = dictionary[@"geometry"][@"location"];
venue->_coordinate = CLLocationCoordinate2DMake([location[@"lat"] doubleValue], [location[@"lng"] doubleValue]);
NSArray *types = dictionary[@"types"];
if (types.count > 0)
{
if ([types containsObject:@"political"])
return nil;
venue->_categoryName = types.firstObject;
}
venue->_displayAddress = dictionary[@"vicinity"];
venue->_provider = TGLocationGooglePlacesVenueProvider;
return venue;
}
+ (TGLocationVenue *)venueWithLocationAttachment:(TGLocationMediaAttachment *)attachment
{
TGLocationVenue *venue = [[TGLocationVenue alloc] init];
venue->_identifier = attachment.venue.venueId;
venue->_name = attachment.venue.title;
venue->_coordinate = CLLocationCoordinate2DMake(attachment.latitude, attachment.longitude);
venue->_categoryName = attachment.venue.type;
venue->_displayAddress = attachment.venue.address;
venue->_provider = attachment.venue.provider;
return venue;
}
- (NSString *)displayAddress
{
if (_displayAddress.length > 0)
return _displayAddress;
if (self.street.length > 0)
return self.street;
else if (self.city.length > 0)
return self.city;
else if (self.country.length > 0)
return self.country;
return nil;
}
- (NSString *)street
{
if (self.address.length > 0)
return self.address;
else
return self.crossStreet;
}
- (TGVenueAttachment *)venueAttachment
{
return [[TGVenueAttachment alloc] initWithTitle:self.name address:self.displayAddress provider:self.provider venueId:self.identifier type:self.categoryName];
}
@end

View File

@ -1,17 +0,0 @@
#import <UIKit/UIKit.h>
@class TGLocationVenue;
@class TGLocationPallete;
@interface TGLocationVenueCell : UITableViewCell
@property (nonatomic, strong) TGLocationPallete *pallete;
- (void)configureWithVenue:(TGLocationVenue *)venue;
+ (UIImage *)circleImage;
@end
extern NSString *const TGLocationVenueCellKind;
extern const CGFloat TGLocationVenueCellHeight;

View File

@ -1,140 +0,0 @@
#import "TGLocationVenueCell.h"
#import "TGLocationMapViewController.h"
#import "LegacyComponentsInternal.h"
#import "TGColor.h"
#import "TGFont.h"
#import "TGStringUtils.h"
#import "TGImageUtils.h"
#import "TGLocationVenue.h"
#import <LegacyComponents/TGImageView.h>
NSString *const TGLocationVenueCellKind = @"TGLocationVenueCell";
const CGFloat TGLocationVenueCellHeight = 56.0f;
@interface TGLocationVenueCell ()
{
UIImageView *_circleView;
TGImageView *_iconView;
UILabel *_titleLabel;
UILabel *_addressLabel;
UIView *_separatorView;
}
@end
@implementation TGLocationVenueCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self != nil)
{
self.selectedBackgroundView = [[UIView alloc] init];
self.selectedBackgroundView.backgroundColor = TGSelectionColor();
_circleView = [[UIImageView alloc] initWithFrame:CGRectMake(12.0f, 12.0f, 48.0f, 48.0f)];
[self setCircleColor:UIColorRGB(0xf2f2f2)];
[self.contentView addSubview:_circleView];
_iconView = [[TGImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 48.0f, 48.0f)];
_iconView.contentMode = UIViewContentModeCenter;
[_circleView addSubview:_iconView];
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.font = TGSystemFontOfSize(16.0f);
_titleLabel.textColor = [UIColor blackColor];
[self.contentView addSubview:_titleLabel];
_addressLabel = [[UILabel alloc] init];
_addressLabel.backgroundColor = [UIColor clearColor];
_addressLabel.font = TGSystemFontOfSize(13);
_addressLabel.textColor = UIColorRGB(0x8e8e93);
[self.contentView addSubview:_addressLabel];
_separatorView = [[UIView alloc] init];
_separatorView.backgroundColor = TGSeparatorColor();
[self addSubview:_separatorView];
}
return self;
}
- (void)setPallete:(TGLocationPallete *)pallete
{
if (pallete == nil || _pallete == pallete)
return;
_pallete = pallete;
self.backgroundColor = pallete.backgroundColor;
self.selectedBackgroundView.backgroundColor = pallete.selectionColor;
[self setCircleColor:pallete.sectionHeaderBackgroundColor];
_titleLabel.textColor = pallete.textColor;
_addressLabel.textColor = pallete.secondaryTextColor;
_separatorView.backgroundColor = pallete.separatorColor;
}
- (void)setCircleColor:(UIColor *)color
{
UIImage *circleImage = [TGLocationVenueCell circleImage];
_circleView.image = TGTintedImage(circleImage, color);
}
- (void)prepareForReuse
{
[super prepareForReuse];
[_iconView reset];
}
- (void)configureWithVenue:(TGLocationVenue *)venue
{
_titleLabel.text = venue.name;
_addressLabel.text = venue.displayAddress;
if (venue.categoryName.length > 0)
{
[_iconView loadUri:[NSString stringWithFormat:@"location-venue-icon://type=%@&width=%d&height=%d&color=%d", venue.categoryName, 48, 48, TGColorHexCode(_pallete != nil ? _pallete.sectionHeaderTextColor : UIColorRGB(0xa0a0a0))] withOptions:nil];
}
else
{
UIImage *pinImage = TGComponentsImageNamed(@"LocationMessagePinIcon");
if (self.pallete != nil)
pinImage = TGTintedImage(pinImage, self.pallete.sectionHeaderTextColor);
else
pinImage = TGTintedImage(pinImage, UIColorRGB(0xa0a0a0));
[_iconView loadUri:@"embedded://" withOptions:@{ TGImageViewOptionEmbeddedImage:pinImage }];
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat padding = 76.0f;
CGFloat separatorThickness = TGScreenPixel;
_circleView.frame = CGRectMake(12.0f, 4.0f, 48.0f, 48.0f);
_iconView.frame = CGRectMake(0.0f, 0.0f, 48.0f, 48.0f);
_titleLabel.frame = CGRectMake(padding, 8, self.frame.size.width - padding - 14, 20);
_addressLabel.frame = CGRectMake(padding, 29, self.frame.size.width - padding - 14, 20);
_separatorView.frame = CGRectMake(padding, self.frame.size.height - separatorThickness, self.frame.size.width - padding, separatorThickness);
}
+ (UIImage *)circleImage
{
static dispatch_once_t onceToken;
static UIImage *circleImage;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(48.0f, 48.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 48.0f, 48.0f));
circleImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
return circleImage;
}
@end

File diff suppressed because it is too large Load Diff

View File

@ -1,126 +0,0 @@
#import "TGLocationWavesView.h"
#import "LegacyComponentsInternal.h"
#import "TGImageUtils.h"
#import "TGPhotoEditorUtils.h"
@interface TGLocationWavesView ()
{
CADisplayLink *_displayLink;
NSTimeInterval _previousTime;
CGFloat _progress;
UIImage *_image;
}
@end
@implementation TGLocationWavesView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.backgroundColor = [UIColor clearColor];
_image = TGComponentsImageNamed(@"LocationWave");
self.userInteractionEnabled = false;
_color = [UIColor whiteColor];
}
return self;
}
- (void)invalidate
{
[_displayLink invalidate];
_displayLink = nil;
}
- (CADisplayLink *)displayLink {
if (_displayLink == nil) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkUpdate)];
_displayLink.paused = true;
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
return _displayLink;
}
- (void)setColor:(UIColor *)color
{
_color = color;
_image = TGTintedImage(TGComponentsImageNamed(@"LocationWave"), color);
}
- (void)start
{
[self displayLink].paused = false;
}
- (void)stop
{
_displayLink.paused = true;
}
- (void)drawRect:(CGRect)rect
{
CGPoint center = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f);
CGFloat length = 9.0f;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, _color.CGColor);
void (^draw)(CGFloat, bool) = ^(CGFloat pos, bool right)
{
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, center.x, center.y, length * pos + 7.0f, right ? TGDegreesToRadians(-26) : TGDegreesToRadians(154), right ? TGDegreesToRadians(26) : TGDegreesToRadians(206), false);
CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(path, NULL, 1.65f, kCGLineCapRound, kCGLineJoinMiter, 10);
CGContextAddPath(context, strokedArc);
CGPathRelease(strokedArc);
CGPathRelease(path);
CGContextFillPath(context);
};
CGFloat position = _progress;
CGFloat alpha = position / 0.5f;
if (alpha > 1.0f)
alpha = 2.0f - alpha;
CGContextSetAlpha(context, alpha * 0.7f);
draw(position, false);
draw(position, true);
CGFloat progress = _progress + 0.5f;
if (progress > 1.0f)
progress = progress - 1.0f;
CGFloat largerPos = progress;
CGFloat largerAlpha = largerPos / 0.5f;
if (largerAlpha > 1.0f)
largerAlpha = 2.0f - largerAlpha;
CGContextSetAlpha(context, largerAlpha * 0.7f);
draw(largerPos, false);
draw(largerPos, true);
}
- (void)displayLinkUpdate
{
NSTimeInterval previousTime = _previousTime;
NSTimeInterval currentTime = CACurrentMediaTime();
_previousTime = currentTime;
NSTimeInterval delta = previousTime > DBL_EPSILON ? currentTime - previousTime : 0.0;
if (delta < DBL_EPSILON)
return;
_progress += delta * 0.52;
if (_progress > 1.0f)
_progress = 1.0f - _progress;
[self setNeedsDisplay];
}
@end

View File

@ -1,288 +0,0 @@
import Foundation
import UIKit
import Display
import SwiftSignalKit
import LegacyComponents
import TelegramCore
import SyncCore
import Postbox
import TelegramPresentationData
import AccountContext
import ShareController
import LegacyUI
import OpenInExternalAppUI
import AppBundle
import LocationResources
import DeviceLocationManager
private func generateClearIcon(color: UIColor) -> UIImage? {
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color)
}
func makeLegacyPeer(_ peer: Peer) -> AnyObject? {
if let user = peer as? TelegramUser {
let legacyUser = TGUser()
legacyUser.uid = user.id.id
legacyUser.firstName = user.firstName
legacyUser.lastName = user.lastName
if let representation = smallestImageRepresentation(user.photo) {
legacyUser.photoUrlSmall = legacyImageLocationUri(resource: representation.resource)
}
return legacyUser
} else if let channel = peer as? TelegramChannel {
let legacyConversation = TGConversation()
legacyConversation.conversationId = Int64(channel.id.id)
legacyConversation.chatTitle = channel.title
if let representation = smallestImageRepresentation(channel.photo) {
legacyConversation.chatPhotoSmall = legacyImageLocationUri(resource: representation.resource)
}
return legacyConversation
} else {
return nil
}
}
private func makeLegacyMessage(_ message: Message) -> TGMessage {
let result = TGMessage()
result.mid = message.id.id
result.date = Double(message.timestamp)
if message.flags.contains(.Failed) {
result.deliveryState = TGMessageDeliveryStateFailed
} else if message.flags.contains(.Sending) {
result.deliveryState = TGMessageDeliveryStatePending
} else {
result.deliveryState = TGMessageDeliveryStateDelivered
}
for attribute in message.attributes {
if let attribute = attribute as? EditedMessageAttribute {
result.editDate = Double(attribute.date)
}
}
var media: [Any] = []
for m in message.media {
if let mapMedia = m as? TelegramMediaMap {
let legacyLocation = TGLocationMediaAttachment()
legacyLocation.latitude = mapMedia.latitude
legacyLocation.longitude = mapMedia.longitude
if let venue = mapMedia.venue {
legacyLocation.venue = TGVenueAttachment(title: venue.title, address: venue.address, provider: venue.provider, venueId: venue.id, type: venue.type)
}
if let liveBroadcastingTimeout = mapMedia.liveBroadcastingTimeout {
legacyLocation.period = liveBroadcastingTimeout
}
media.append(legacyLocation)
}
}
if !media.isEmpty {
result.mediaAttachments = media
}
return result
}
private func legacyRemainingTime(message: TGMessage) -> SSignal {
var liveBroadcastingTimeoutValue: Int32?
if let mediaAttachments = message.mediaAttachments {
for media in mediaAttachments {
if let m = media as? TGLocationMediaAttachment, m.period != 0 {
liveBroadcastingTimeoutValue = m.period
}
}
}
guard let liveBroadcastingTimeout = liveBroadcastingTimeoutValue else {
return SSignal.fail(nil)
}
if message.deliveryState != TGMessageDeliveryStateDelivered {
return SSignal.single(liveBroadcastingTimeout as NSNumber)
}
let remainingTime = SSignal.`defer`({
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let remainingTime = max(0, Int32(message.date) + liveBroadcastingTimeout - currentTime)
var signal = SSignal.single(remainingTime as NSNumber)
if remainingTime == 0 {
signal = signal?.then(SSignal.fail(nil))
}
return signal
})!
return (remainingTime.then(SSignal.complete().delay(5.0, on: SQueue.main()))).restart().`catch`({ _ in
return SSignal.complete()
})
}
private func telegramMap(for location: TGLocationMediaAttachment) -> TelegramMediaMap {
let mapVenue: MapVenue?
if let venue = location.venue {
mapVenue = MapVenue(title: venue.title ?? "", address: venue.address ?? "", provider: venue.provider ?? "", id: venue.venueId ?? "", type: venue.type ?? "")
} else {
mapVenue = nil
}
return TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: mapVenue, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)
}
func legacyLocationPalette(from theme: PresentationTheme) -> TGLocationPallete {
let listTheme = theme.list
let searchTheme = theme.rootController.navigationSearchBar
return TGLocationPallete(backgroundColor: listTheme.plainBackgroundColor, selectionColor: listTheme.itemHighlightedBackgroundColor, separatorColor: listTheme.itemPlainSeparatorColor, textColor: listTheme.itemPrimaryTextColor, secondaryTextColor: listTheme.itemSecondaryTextColor, accentColor: listTheme.itemAccentColor, destructiveColor: listTheme.itemDestructiveColor, locationColor: UIColor(rgb: 0x008df2), liveLocationColor: UIColor(rgb: 0xff6464), iconColor: searchTheme.backgroundColor, sectionHeaderBackgroundColor: theme.chatList.sectionHeaderFillColor, sectionHeaderTextColor: theme.chatList.sectionHeaderTextColor, searchBarPallete: TGSearchBarPallete(dark: theme.overallDarkAppearance, backgroundColor: searchTheme.backgroundColor, highContrastBackgroundColor: searchTheme.backgroundColor, textColor: searchTheme.inputTextColor, placeholderColor: searchTheme.inputPlaceholderTextColor, clearIcon: generateClearIcon(color: theme.rootController.navigationSearchBar.inputClearButtonColor), barBackgroundColor: searchTheme.backgroundColor, barSeparatorColor: searchTheme.separatorColor, plainBackgroundColor: searchTheme.backgroundColor, accentColor: searchTheme.accentColor, accentContrastColor: searchTheme.backgroundColor, menuBackgroundColor: searchTheme.backgroundColor, segmentedControlBackgroundImage: nil, segmentedControlSelectedImage: nil, segmentedControlHighlightedImage: nil, segmentedControlDividerImage: nil), avatarPlaceholder: nil)
}
public func legacyLocationController(message: Message?, mapMedia: TelegramMediaMap, context: AccountContext, openPeer: @escaping (Peer) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, stopLiveLocation: @escaping () -> Void, openUrl: @escaping (String) -> Void) -> ViewController {
let legacyLocation = TGLocationMediaAttachment()
legacyLocation.latitude = mapMedia.latitude
legacyLocation.longitude = mapMedia.longitude
if let venue = mapMedia.venue {
legacyLocation.venue = TGVenueAttachment(title: venue.title, address: venue.address, provider: venue.provider, venueId: venue.id, type: venue.type)
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let legacyController = LegacyController(presentation: .navigation, theme: presentationData.theme, strings: presentationData.strings)
legacyController.navigationPresentation = .modal
let controller: TGLocationViewController
let venueColor = mapMedia.venue?.type.flatMap { venueIconColor(type: $0) }
if let message = message {
let legacyMessage = makeLegacyMessage(message)
let legacyAuthor: AnyObject? = message.author.flatMap(makeLegacyPeer)
let updatedLocations = SSignal(generator: { subscriber in
let disposable = topPeerActiveLiveLocationMessages(viewTracker: context.account.viewTracker, accountPeerId: context.account.peerId, peerId: message.id.peerId).start(next: { (_, messages) in
var result: [Any] = []
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
loop: for message in messages {
var liveBroadcastingTimeout: Int32 = 0
mediaLoop: for media in message.media {
if let map = media as? TelegramMediaMap, let timeout = map.liveBroadcastingTimeout {
liveBroadcastingTimeout = timeout
break mediaLoop
}
}
let legacyMessage = makeLegacyMessage(message)
guard let legacyAuthor = message.author.flatMap(makeLegacyPeer) else {
continue loop
}
let remainingTime = max(0, message.timestamp + liveBroadcastingTimeout - currentTime)
if legacyMessage.locationAttachment?.period != 0 {
let hasOwnSession = message.localTags.contains(.OutgoingLiveLocation)
var isOwn = false
if !message.flags.contains(.Incoming) {
isOwn = true
} else if let peer = message.peers[message.id.peerId] as? TelegramChannel {
isOwn = peer.hasPermission(.sendMessages)
}
let liveLocation = TGLiveLocation(message: legacyMessage, peer: legacyAuthor, hasOwnSession: hasOwnSession, isOwnLocation: isOwn, isExpired: remainingTime == 0)!
result.append(liveLocation)
}
}
subscriber?.putNext(result)
})
return SBlockDisposable(block: {
disposable.dispose()
})
})!
if let liveBroadcastingTimeout = mapMedia.liveBroadcastingTimeout {
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let remainingTime = max(0, message.timestamp + liveBroadcastingTimeout - currentTime)
let messageLiveLocation = TGLiveLocation(message: legacyMessage, peer: legacyAuthor, hasOwnSession: false, isOwnLocation: false, isExpired: remainingTime == 0)!
controller = TGLocationViewController(context: legacyController.context, liveLocation: messageLiveLocation)
if remainingTime == 0 {
let freezeLocations: [Any] = [messageLiveLocation]
controller.setLiveLocationsSignal(.single(freezeLocations))
} else {
controller.setLiveLocationsSignal(updatedLocations)
if message.flags.contains(.Incoming) {
context.account.viewTracker.updateSeenLiveLocationForMessageIds(messageIds: Set([message.id]))
}
}
} else {
controller = TGLocationViewController(context: legacyController.context, message: legacyMessage, peer: legacyAuthor, color: venueColor)!
controller.receivingPeer = message.peers[message.id.peerId].flatMap(makeLegacyPeer)
controller.setLiveLocationsSignal(updatedLocations)
}
let namespacesWithEnabledLiveLocation: Set<PeerId.Namespace> = Set([
Namespaces.Peer.CloudChannel,
Namespaces.Peer.CloudGroup,
Namespaces.Peer.CloudUser
])
if namespacesWithEnabledLiveLocation.contains(message.id.peerId.namespace) {
controller.allowLiveLocationSharing = true
}
} else {
let attachment = TGLocationMediaAttachment()
attachment.latitude = mapMedia.latitude
attachment.longitude = mapMedia.longitude
controller = TGLocationViewController(context: legacyController.context, locationAttachment: attachment, peer: nil, color: venueColor)
}
controller.remainingTimeForMessage = { message in
return legacyRemainingTime(message: message!)
}
controller.liveLocationStarted = { [weak legacyController] coordinate, period in
sendLiveLocation(coordinate, period)
legacyController?.dismiss()
}
controller.liveLocationStopped = { [weak legacyController] in
stopLiveLocation()
legacyController?.dismiss()
}
let theme = (context.sharedContext.currentPresentationData.with { $0 }).theme
controller.pallete = legacyLocationPalette(from: theme)
controller.modalMode = true
controller.presentActionsMenu = { [weak legacyController] legacyLocation, directions in
if let strongLegacyController = legacyController, let location = legacyLocation {
let map = telegramMap(for: location)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let shareAction = OpenInControllerAction(title: presentationData.strings.Conversation_ContextMenuShare, action: {
strongLegacyController.present(ShareController(context: context, subject: .mapMedia(map), externalShare: true), in: .window(.root), with: nil)
})
strongLegacyController.present(OpenInActionSheetController(context: context, item: .location(location: map, withDirections: directions), additionalAction: !directions ? shareAction : nil, openUrl: openUrl), in: .window(.root), with: nil)
}
}
controller.onViewDidAppear = { [weak controller] in
if let strongController = controller {
strongController.view.disablesInteractiveModalDismiss = true
strongController.view.disablesInteractiveTransitionGestureRecognizer = true
strongController.locationMapView.interactiveTransitionGestureRecognizerTest = { point -> Bool in
return point.x > 30.0
}
}
}
legacyController.navigationItem.title = controller.navigationItem.title
controller.updateRightBarItem = { item, action, animated in
if action {
legacyController.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(theme), style: .plain, target: controller, action: #selector(controller.actionsButtonPressed))
} else {
legacyController.navigationItem.setRightBarButton(item, animated: animated)
}
}
legacyController.bind(controller: controller)
let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in
if let controller = controller {
controller.pallete = legacyLocationPalette(from: presentationData.theme)
}
})
legacyController.disposables.add(presentationDisposable)
return legacyController
}

View File

@ -1,113 +0,0 @@
import Foundation
import UIKit
import Display
import LegacyComponents
import TelegramCore
import SyncCore
import Postbox
import SwiftSignalKit
import TelegramPresentationData
import AccountContext
import LegacyUI
import AppBundle
private func generateClearIcon(color: UIColor) -> UIImage? {
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color)
}
public func legacyLocationPickerController(context: AccountContext, selfPeer: Peer, peer: Peer, sendLocation: @escaping (CLLocationCoordinate2D, MapVenue?, String?) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, theme: PresentationTheme, customLocationPicker: Bool = false, hasLiveLocation: Bool = true, presentationCompleted: @escaping () -> Void = {}) -> ViewController {
let legacyController = LegacyController(presentation: .navigation, theme: theme)
legacyController.navigationPresentation = .modal
let controller = TGLocationPickerController(context: legacyController.context, intent: customLocationPicker ? TGLocationPickerControllerCustomLocationIntent : TGLocationPickerControllerDefaultIntent)!
legacyController.presentationCompleted = { [weak controller] in
presentationCompleted()
controller?.view.disablesInteractiveModalDismiss = true
controller?.view.disablesInteractiveTransitionGestureRecognizer = true
}
controller.peer = makeLegacyPeer(selfPeer)
controller.receivingPeer = makeLegacyPeer(peer)
controller.pallete = legacyLocationPalette(from: theme)
let namespacesWithEnabledLiveLocation: Set<PeerId.Namespace> = Set([
Namespaces.Peer.CloudChannel,
Namespaces.Peer.CloudGroup,
Namespaces.Peer.CloudUser
])
if namespacesWithEnabledLiveLocation.contains(peer.id.namespace) && !customLocationPicker && hasLiveLocation {
controller.allowLiveLocationSharing = true
}
let navigationController = TGNavigationController(controllers: [controller])!
controller.navigation_setDismiss({ [weak legacyController] in
legacyController?.dismiss()
}, rootController: nil)
legacyController.bind(controller: navigationController)
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
controller.locationPicked = { [weak legacyController] coordinate, venue, address in
sendLocation(coordinate, venue.flatMap { venue in
return MapVenue(title: venue.title, address: venue.address, provider: venue.provider, id: venue.venueId, type: venue.type)
}, address)
legacyController?.dismiss()
}
controller.liveLocationStarted = { [weak legacyController] coordinate, period in
sendLiveLocation(coordinate, period)
legacyController?.dismiss()
}
controller.nearbyPlacesSignal = { query, location in
return SSignal(generator: { subscriber in
let nearbyPlacesSignal: Signal<[TGLocationVenue], NoError> = resolvePeerByName(account: context.account, name: "foursquare")
|> take(1)
|> mapToSignal { peerId -> Signal<ChatContextResultCollection?, NoError> in
guard let peerId = peerId else {
return .single(nil)
}
return requestChatContextResults(account: context.account, botId: peerId, peerId: selfPeer.id, query: query ?? "", location: .single((location?.coordinate.latitude ?? 0.0, location?.coordinate.longitude ?? 0.0)), offset: "")
|> map { results -> ChatContextResultCollection? in
return results?.results
}
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
return .single(nil)
}
}
|> mapToSignal { contextResult -> Signal<[TGLocationVenue], NoError> in
guard let contextResult = contextResult else {
return .single([])
}
var list: [TGLocationVenue] = []
for result in contextResult.results {
switch result.message {
case let .mapLocation(mapMedia, _):
let legacyLocation = TGLocationMediaAttachment()
legacyLocation.latitude = mapMedia.latitude
legacyLocation.longitude = mapMedia.longitude
if let venue = mapMedia.venue {
legacyLocation.venue = TGVenueAttachment(title: venue.title, address: venue.address, provider: venue.provider, venueId: venue.id, type: venue.type)
}
list.append(TGLocationVenue(locationAttachment: legacyLocation))
default:
break
}
}
return .single(list)
}
let disposable = nearbyPlacesSignal.start(next: { next in
subscriber?.putNext(next as NSArray)
}, completed: {
subscriber?.putCompletion()
})
return SBlockDisposable(block: {
disposable.dispose()
})
})
}
let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in
if let controller = controller {
controller.pallete = legacyLocationPalette(from: presentationData.theme)
}
})
legacyController.disposables.add(presentationDisposable)
return legacyController
}

View File

@ -17,6 +17,18 @@ import ContactsPeerItem
import ChatListSearchItemHeader
import ItemListUI
enum ParticipantRevealActionType {
case promote
case restrict
case remove
}
struct ParticipantRevealAction: Equatable {
let type: ItemListPeerItemRevealOptionType
let title: String
let action: ParticipantRevealActionType
}
public enum ChannelMembersSearchMode {
case searchMembers
case searchAdmins

File diff suppressed because it is too large Load Diff

View File

@ -3648,19 +3648,27 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
let context = self.context
let presentationData = self.presentationData
let mapMedia = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), address: location.address, provider: nil, id: nil, type: nil), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)
let locationController = legacyLocationController(message: nil, mapMedia: mapMedia, context: context, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: {}, openUrl: { url in
let map = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), address: location.address, provider: nil, id: nil, type: nil), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)
let controllerParams = LocationViewParams(sendLiveLocation: { _ in
}, stopLiveLocation: { _ in
}, openUrl: { url in
context.sharedContext.applicationBindings.openUrl(url)
})
self.controller?.push(locationController)
}, openPeer: { _ in
}, showAll: false)
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let controller = LocationViewController(context: context, subject: message, params: controllerParams)
self.controller?.push(controller)
}
private func editingOpenSetupLocation() {
guard let data = self.data, let peer = data.peer else {
return
}
let presentationData = self.presentationData
let locationController = legacyLocationPickerController(context: self.context, selfPeer: peer, peer: peer, sendLocation: { [weak self] coordinate, _, address in
let controller = LocationPickerController(context: self.context, mode: .pick, completion: { [weak self] location, address in
guard let strongSelf = self else {
return
}
@ -3668,12 +3676,12 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if let address = address {
addressSignal = .single(address)
} else {
addressSignal = reverseGeocodeLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
addressSignal = reverseGeocodeLocation(latitude: location.latitude, longitude: location.longitude)
|> map { placemark in
if let placemark = placemark {
return placemark.fullAddress
} else {
return "\(coordinate.latitude), \(coordinate.longitude)"
return "\(location.latitude), \(location.longitude)"
}
}
}
@ -3681,12 +3689,11 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
let context = strongSelf.context
let _ = (addressSignal
|> mapToSignal { address -> Signal<Bool, NoError> in
return updateChannelGeoLocation(postbox: context.account.postbox, network: context.account.network, channelId: peer.id, coordinate: (coordinate.latitude, coordinate.longitude), address: address)
return updateChannelGeoLocation(postbox: context.account.postbox, network: context.account.network, channelId: peer.id, coordinate: (location.latitude, location.longitude), address: address)
}
|> deliverOnMainQueue).start()
}, sendLiveLocation: { _, _ in }, theme: presentationData.theme, customLocationPicker: true, presentationCompleted: {
})
self.controller?.push(locationController)
self.controller?.push(controller)
}
private func openPeerInfo(peer: Peer, isMember: Bool) {