mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Cleanup legacy location viewer & picker
This commit is contained in:
parent
5547abb639
commit
c392c94d9d
@ -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
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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
|
@ -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
|
@ -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;
|
@ -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
|
@ -1,8 +0,0 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TGLocationPulseView : UIView
|
||||
|
||||
- (void)start;
|
||||
- (void)stop;
|
||||
|
||||
@end
|
@ -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;
|
@ -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
|
@ -1,11 +0,0 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TGLocationWavesView : UIView
|
||||
|
||||
@property (nonatomic, strong) UIColor *color;
|
||||
|
||||
- (void)invalidate;
|
||||
- (void)start;
|
||||
- (void)stop;
|
||||
|
||||
@end
|
@ -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
|
@ -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
|
@ -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;
|
@ -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
|
@ -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;
|
||||
|
@ -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
|
@ -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;
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,5 +0,0 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TGLocationMapModeControl : UISegmentedControl
|
||||
|
||||
@end
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
@ -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
@ -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
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user