From e4162a6f0b0d7e88ff49cf46d7579b0b74ce7803 Mon Sep 17 00:00:00 2001
From: yandevelop <82288425+yandevelop@users.noreply.github.com>
Date: Tue, 6 Jun 2023 13:05:34 +0200
Subject: [PATCH] 1.2
- Add "BeFake" functionality: upload custom BeReals from camera roll, spoof location and the time the photo was taken
- Stability improvements (random app crashes aren't caused by Bea. BeReal itself is aware of those crashes)
---
.gitignore | 5 +-
Makefile | 13 +-
README.md | 24 +-
Tweak/Tweak.h | 6 +-
Tweak/Tweak.x | 142 +++--
Utilities/BeaUtilities.m | 11 +-
.../BeaInfoViewController.h | 7 +
.../BeaInfoViewController.m | 85 +++
.../BeaLocationViewController.h | 19 +
.../BeaLocationViewController.m | 120 ++++
Utilities/UploadTask/BeaUploadTask.h | 15 +
Utilities/UploadTask/BeaUploadTask.m | 243 ++++++++
.../BeaUploadViewController.h | 35 ++
.../BeaUploadViewController.m | 537 ++++++++++++++++++
.../StatusView/BeaStatusView.h | 6 +
.../StatusView/BeaStatusView.m | 56 ++
control | 2 +-
.../Application Support/Bea.bundle/BeFake.png | Bin 0 -> 7696 bytes
18 files changed, 1270 insertions(+), 56 deletions(-)
create mode 100644 Utilities/InfoViewController/BeaInfoViewController.h
create mode 100644 Utilities/InfoViewController/BeaInfoViewController.m
create mode 100644 Utilities/LocationViewController/BeaLocationViewController.h
create mode 100644 Utilities/LocationViewController/BeaLocationViewController.m
create mode 100644 Utilities/UploadTask/BeaUploadTask.h
create mode 100644 Utilities/UploadTask/BeaUploadTask.m
create mode 100644 Utilities/UploadViewController/BeaUploadViewController.h
create mode 100644 Utilities/UploadViewController/BeaUploadViewController.m
create mode 100644 Utilities/UploadViewController/StatusView/BeaStatusView.h
create mode 100644 Utilities/UploadViewController/StatusView/BeaStatusView.m
create mode 100755 layout/Library/Application Support/Bea.bundle/BeFake.png
diff --git a/.gitignore b/.gitignore
index 30f486c..a6b5be2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
.theos/
packages/
.DS_Store
-Versions/
\ No newline at end of file
+Versions/
+Utilities/errorCodes
+notes
+realmoji
\ No newline at end of file
diff --git a/Makefile b/Makefile
index c2b3f97..cf7f030 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ TARGET := iphone:clang:latest:14.0
INSTALL_TARGET_PROCESSES = BeReal
ARCHS = arm64 arm64e
FINALPACKAGE = 1
-PACKAGE_VERSION = 1.1.2
+PACKAGE_VERSION = 1.2
export SYSROOT = $(THEOS)/sdks/iPhoneOS15.5.sdk
@@ -12,6 +12,15 @@ TWEAK_NAME = Bea
Bea_FILES = Tweak/Tweak.x
Bea_CFLAGS = -fobjc-arc
-Bea_FRAMEWORKS = UIKit
+Bea_LDFLAGS += -ObjC
+Bea_FRAMEWORKS = UIKit MapKit
+
+ifeq ($(JAILED), 1)
+Bea_CFLAGS += -D JAILED=1
+endif
+
+ifeq ($(LEGACY_SUPPORT), 1)
+Bea_CFLAGS += -D LEGACY_SUPPORT=1
+endif
include $(THEOS_MAKE_PATH)/tweak.mk
diff --git a/README.md b/README.md
index f6223b9..9c6b51d 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,30 @@
# Bea
- Bea adds small enhancements to the BeReal. app.
-
-## Installation
- 1. Add https://havoc.app to your package manager.
- 2. Install Bea
+ Bea is an iOS tweak that adds enhancements to the BeReal. app.
## Compatibility
-All iOS devices running iOS 14 or later.
+Compatible with all iOS devices running iOS 14 or later.
+
+## Features
+ - View BeReals without posting your own
+ - Upload BeReals from the camera roll:
+ - Post on time, even if you're posting late
+ - Set a custom location
+ - Set a custom retake count
+ - Download BeReals
+ - Bypass screenshot detection
## Build
+
+ To build Bea for the purpose of injecting it into an IPA and installing it through sideloading, set the JAILED flag to true. |
+
- Clone this repository using `git clone https://github.com/yandevelop/Bea`
- `cd Bea`
- Run `make package`
+## Installation
+ 1. Add https://havoc.app to your package manager.
+ 2. Install Bea
+
## License
You may not copy, modify, sublicense or distribute the source code or any packages from it.
diff --git a/Tweak/Tweak.h b/Tweak/Tweak.h
index 77638ff..910b663 100644
--- a/Tweak/Tweak.h
+++ b/Tweak/Tweak.h
@@ -1,5 +1,9 @@
#import
#import
#import "../Utilities/BeaUtilities.m"
+#import "../Utilities/UploadViewController/BeaUploadViewController.m"
+#import
-BOOL isUnblurred = NO;
\ No newline at end of file
+BOOL isUnblurred = NO;
+
+NSString *authorizationKey = nil;
\ No newline at end of file
diff --git a/Tweak/Tweak.x b/Tweak/Tweak.x
index 05f4544..0f5c26e 100644
--- a/Tweak/Tweak.x
+++ b/Tweak/Tweak.x
@@ -1,12 +1,17 @@
#import "Tweak.h"
+
%hook DoublePhotoView
- (void)layoutSubviews {
%orig;
UIView *doublePhotoView = (UIView *)self;
+
if ([doublePhotoView.subviews.lastObject isKindOfClass:[BeaButton class]] || doublePhotoView.frame.size.width < 180) return;
+ // make the view accept touches (dragging photos etc)
+ doublePhotoView.superview.userInteractionEnabled = YES;
+
UIResponder *responder = self;
while (responder && ![responder isKindOfClass:[UIViewController class]]) {
responder = [responder nextResponder];
@@ -27,26 +32,32 @@
%hook UIAlertController
- (void)viewDidLoad {
- UIAlertController *alertController = (UIAlertController *)self;
%orig;
+ if (isUnblurred) return;
+
+ UIAlertController *alertController = (UIAlertController *)self;
- if (![alertController.presentingViewController isKindOfClass:objc_getClass("BeReal.NavigationController")]) return %orig;
- UIAlertAction *thirdAction = alertController.actions[2];
- id block = [thirdAction valueForKey:@"_handler"];
- if (block) {
- void (^handler)(UIAlertAction *) = block;
- handler(thirdAction);
- isUnblurred = YES;
+ if ([alertController.actions[2].title isEqual:@"👀 Unblur"]) {
+ UIAlertAction *thirdAction = alertController.actions[2];
+ id block = [thirdAction valueForKey:@"_handler"];
+ if (block) {
+ void (^handler)(UIAlertAction *) = block;
+ handler(thirdAction);
+ }
}
}
- (void)viewWillAppear:(id)arg1 {
%orig;
- if ([self.presentingViewController isKindOfClass:objc_getClass("BeReal.NavigationController")] || [self.presentingViewController isKindOfClass:[UINavigationController class]]) {
+ if (isUnblurred) return;
+ UIAlertController *alertController = (UIAlertController *)self;
+ if ([alertController.actions[2].title isEqual:@"👀 Unblur"]) {
// Set the whole view to hidden
self.view.superview.hidden = YES;
+
+ isUnblurred = YES;
- // Dismiss the UIAlertController automatically
+ // Dismiss the UIAlertController
[self dismissViewControllerAnimated:NO completion:nil];
}
}
@@ -55,40 +66,63 @@
%hook HomeViewController
- (void)viewDidLoad {
%orig;
- UIViewController *homeViewController = (UIViewController *)self;
- if (!isUnblurred && [homeViewController respondsToSelector:@selector(openDebugMenu)] && [homeViewController.childViewControllers.lastObject isKindOfClass:objc_getClass("BeReal.SUIFeedViewController")]) {
- [homeViewController performSelector:@selector(openDebugMenu)];
+ UIViewController *homeViewController = (UIViewController *)self;
+
+ if (!isUnblurred && [homeViewController respondsToSelector:@selector(openDebugMenu)] && [homeViewController.childViewControllers.lastObject isKindOfClass:objc_getClass("BeReal.SUIFeedViewController")]) {
+ [homeViewController performSelector:@selector(openDebugMenu)];
+ #ifndef LEGACY_SUPPORT
+ NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+ NSComparisonResult result = [version compare:@"1.1.2" options:NSNumericSearch];
+ if (result == NSOrderedAscending) {
+ BeaAlertView *alertView = [[BeaAlertView alloc] init];
+ [homeViewController.view addSubview:alertView];
+ }
+ #endif
+ }
- NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
- NSComparisonResult result = [version compare:@"1.1.2" options:NSNumericSearch];
- if (result == NSOrderedAscending) {
- BeaAlertView *alertView = [[BeaAlertView alloc] init];
- [homeViewController.view addSubview:alertView];
- }
- }
+ UIImageView *beRealLogoView = [self valueForKey:@"ibNavBarLogoImageView"];
+ beRealLogoView.userInteractionEnabled = YES;
+
+ #ifdef JAILED
+ NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Bea" ofType:@"bundle"];
+ NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
+ UIImage *beFakeLogo = [UIImage imageWithContentsOfFile:[bundle pathForResource:@"BeFake" ofType:@"png"]];
+ #else
+ NSBundle *bundle = [NSBundle bundleWithPath:ROOT_PATH_NS(@"/Library/Application Support/Bea.bundle")];
+ UIImage *beFakeLogo = [UIImage imageNamed:@"BeFake.png" inBundle:bundle compatibleWithTraitCollection:nil];
+ #endif
+
+ CGSize targetSize = beRealLogoView.image.size;
+
+ UIGraphicsBeginImageContextWithOptions(targetSize, NO, 0);
+ [beFakeLogo drawInRect:CGRectMake(0, 0, targetSize.width, targetSize.height)];
+ UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ beRealLogoView.image = resizedImage;
+
+ UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
+ [beRealLogoView addGestureRecognizer:tapGestureRecognizer];
}
-%end
-%hook SwiftView
-- (void)layoutSubviews {
- %orig;
- UIView *s = (UIView *)self;
- // removes the eye view and the post late button from the blurred view
- for (UIView *v in s.subviews) {
- if (v.frame.size.width <= 48 && v.frame.size.width > 32) {
- v.hidden = YES;
- }
- if (([v isKindOfClass:objc_getClass("SwiftUI._UIGraphicsView")] || [v isKindOfClass:[UIView class]]) && v.frame.size.width > 350 && v.subviews.count == 0) {
- v.hidden = YES;
- }
- }
+%new
+- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer {
+
+ UIViewController *vc = (UIViewController *)self;
+ // display the error view here
+ if (!authorizationKey) return;
+ BeaUploadViewController *beaUploadViewController = [[BeaUploadViewController alloc] initWithAuthorization:authorizationKey];
+ beaUploadViewController.modalPresentationStyle = UIModalPresentationFullScreen;
+ [vc presentViewController:beaUploadViewController animated:YES completion:nil];
}
%end
+
%hook CALayer
- (void)setFilters:(NSArray *)filter {
- return;
+ if (!isUnblurred) return;
+ %orig;
}
%end
@@ -100,7 +134,7 @@
UITableView *labelView = vc.view.subviews.firstObject;
UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, labelView.frame.size.width, 50)];
- headerLabel.text = @"Bea 1.1.2\nmade with ❤️ by yan";
+ headerLabel.text = @"Bea 1.2\nmade with ❤️ by yan";
headerLabel.numberOfLines = 0;
headerLabel.font = [UIFont fontWithName:@"Inter" size:10];
headerLabel.textAlignment = NSTextAlignmentCenter;
@@ -135,14 +169,36 @@
}
%end
+%hook NSMutableURLRequest
+-(void)setAllHTTPHeaderFields:(NSDictionary *)arg1 {
+ %orig;
+ if ([[arg1 allKeys] containsObject:@"Authorization"] && !authorizationKey) {
+ authorizationKey = arg1[@"Authorization"];
+ }
+}
+%end
+
+%hook UIHostingView
+- (void)layoutSubviews {
+ %orig;
+ UIView *s = (UIView *)self;
+ for (UIView *v in s.superview.subviews) {
+ if ((v.frame.size.width <= 48 && v.frame.size.width > 32) || (([v isKindOfClass:objc_getClass("SwiftUI._UIGraphicsView")] || [v isKindOfClass:[UIView class]]) && v.frame.size.width > 350 && v.subviews.count == 0)) {
+ v.hidden = YES;
+ }
+ }
+}
+%end
%ctor {
- %init(HomeViewController = objc_getClass("_TtC6BeReal18HomeViewController"),
- DoublePhotoView = objc_getClass("RealComponents.DoublePhotoView"),
- SettingsViewController = objc_getClass("_TtC6BeReal22SettingsViewController"),
- SwiftView = objc_getClass("_TtCC7SwiftUI17HostingScrollView22PlatformGroupContainer"));
+ #ifdef LEGACY_SUPPORT
+ Class photoView = objc_getClass("BeReal.DoublePhotoView");
+ #else
+ Class photoView = objc_getClass("RealComponents.DoublePhotoView");
+ #endif
- // Enable dualCamera?
- [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"debug_dualCameraPreviewEnabled"];
- [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"debug_developerModeEnabled"];
+ %init(HomeViewController = objc_getClass("_TtC6BeReal18HomeViewController"),
+ DoublePhotoView = photoView,
+ SettingsViewController = objc_getClass("_TtC6BeReal22SettingsViewController"),
+ UIHostingView = objc_getClass("_TtC7SwiftUIP33_A34643117F00277B93DEBAB70EC0697116_UIInheritedView"));
}
\ No newline at end of file
diff --git a/Utilities/BeaUtilities.m b/Utilities/BeaUtilities.m
index 7833618..e135a00 100644
--- a/Utilities/BeaUtilities.m
+++ b/Utilities/BeaUtilities.m
@@ -5,8 +5,15 @@ + (void)downloadImage:(id)sender {
UIButton *button = (UIButton *)sender;
UIView *tableContentView = button.superview.superview;
UIImageView *imageView = nil;
+
+ #ifdef LEGACY_SUPPORT
+ NSString *viewClass = @"BeReal.DoublePhotoView";
+ #else
+ NSString *viewClass = @"RealComponents.DoublePhotoView";
+ #endif
+
for (UIView *view in tableContentView.subviews) {
- if ([NSStringFromClass([view class]) isEqualToString:@"RealComponents.DoublePhotoView"]) {
+ if ([NSStringFromClass([view class]) isEqualToString:viewClass]) {
imageView = view.subviews.firstObject;
break;
}
@@ -125,7 +132,7 @@ - (void)setupAlertView {
}
- (void)updateButtonTapped {
- NSString *appStoreLink = @"https://apps.apple.com/de/app/id1459645446";
+ NSString *appStoreLink = @"https://apps.apple.com/app/id1459645446";
NSURL *appStoreURL = [NSURL URLWithString:appStoreLink];
if ([[UIApplication sharedApplication] canOpenURL:appStoreURL]) {
diff --git a/Utilities/InfoViewController/BeaInfoViewController.h b/Utilities/InfoViewController/BeaInfoViewController.h
new file mode 100644
index 0000000..41b1a46
--- /dev/null
+++ b/Utilities/InfoViewController/BeaInfoViewController.h
@@ -0,0 +1,7 @@
+@interface BeaInfoViewController : UIViewController
+@property (nonatomic, strong) UIImageView *profileImageView;
+@property (nonatomic, strong) UILabel *twitterLabel;
+@property (nonatomic, strong) UILabel *smallLabel;
+@property (nonatomic, strong) UIView *wrapperView;
+@property (nonatomic, strong) UILabel *versionLabel;
+@end
\ No newline at end of file
diff --git a/Utilities/InfoViewController/BeaInfoViewController.m b/Utilities/InfoViewController/BeaInfoViewController.m
new file mode 100644
index 0000000..a66ae45
--- /dev/null
+++ b/Utilities/InfoViewController/BeaInfoViewController.m
@@ -0,0 +1,85 @@
+#import "BeaInfoViewController.h"
+
+@implementation BeaInfoViewController
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.view.backgroundColor = [UIColor clearColor];
+
+ self.wrapperView = [[UIView alloc] init];
+ self.wrapperView.translatesAutoresizingMaskIntoConstraints = NO;
+ self.wrapperView.userInteractionEnabled = YES;
+ UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwitterTap)];
+ [self.wrapperView addGestureRecognizer:tapGestureRecognizer];
+
+ self.profileImageView = [[UIImageView alloc] init];
+ self.profileImageView.contentMode = UIViewContentModeScaleAspectFit;
+ self.profileImageView.layer.cornerRadius = 25;
+ self.profileImageView.layer.masksToBounds = YES;
+ self.profileImageView.translatesAutoresizingMaskIntoConstraints = NO;
+
+ NSData *profileImageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://avatars.githubusercontent.com/u/82288425?v=4"]];
+ UIImage *profileImage = [UIImage imageWithData:profileImageData];
+ self.profileImageView.image = profileImage;
+ [self.wrapperView addSubview:self.profileImageView];
+
+ self.smallLabel = [[UILabel alloc] init];
+ self.smallLabel.textAlignment = NSTextAlignmentCenter;
+ self.smallLabel.textColor = [UIColor whiteColor];
+ self.smallLabel.text = @"developed by";
+ self.smallLabel.font = [UIFont fontWithName:@"Inter" size:10];
+ self.smallLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.wrapperView addSubview:self.smallLabel];
+
+ self.twitterLabel = [[UILabel alloc] init];
+ self.twitterLabel.textAlignment = NSTextAlignmentCenter;
+ self.twitterLabel.textColor = [UIColor whiteColor];
+ self.twitterLabel.font = [UIFont fontWithName:@"Inter" size:18];
+ self.twitterLabel.text = @"yandevelop";
+ self.twitterLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.wrapperView addSubview:self.twitterLabel];
+
+ self.versionLabel = [[UILabel alloc] init];
+ self.versionLabel.textAlignment = NSTextAlignmentCenter;
+ self.versionLabel.textColor = [UIColor whiteColor];
+ self.versionLabel.font = [UIFont fontWithName:@"Inter" size:9];
+ self.versionLabel.text = @"Bea\nVersion 1.2";
+ self.versionLabel.numberOfLines = 0;
+ self.versionLabel.lineBreakMode = NSLineBreakByWordWrapping;
+ self.versionLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:self.versionLabel];
+
+ UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular]];
+ blurEffectView.frame = self.view.bounds;
+ blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [self.view addSubview:blurEffectView];
+ [self.view sendSubviewToBack:blurEffectView];
+
+ [self.view addSubview:self.wrapperView];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [self.wrapperView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
+ [self.wrapperView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
+ [self.wrapperView.widthAnchor constraintEqualToConstant:125],
+ [self.wrapperView.heightAnchor constraintEqualToConstant:100],
+
+ [self.profileImageView.centerXAnchor constraintEqualToAnchor:self.wrapperView.centerXAnchor],
+ [self.profileImageView.widthAnchor constraintEqualToConstant:50],
+ [self.profileImageView.heightAnchor constraintEqualToConstant:50],
+
+ [self.smallLabel.centerXAnchor constraintEqualToAnchor:self.wrapperView.centerXAnchor],
+ [self.smallLabel.topAnchor constraintEqualToAnchor:self.profileImageView.bottomAnchor constant:10],
+
+ [self.twitterLabel.centerXAnchor constraintEqualToAnchor:self.wrapperView.centerXAnchor],
+ [self.twitterLabel.topAnchor constraintEqualToAnchor:self.smallLabel.bottomAnchor],
+
+ [self.versionLabel.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
+ [self.versionLabel.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor]
+ ]];
+}
+
+- (void)handleTwitterTap {
+ NSURL *twitterWebURL = [NSURL URLWithString:@"https://twitter.com/yandevelop"];
+ [[UIApplication sharedApplication] openURL:twitterWebURL options:@{} completionHandler:nil];
+}
+@end
\ No newline at end of file
diff --git a/Utilities/LocationViewController/BeaLocationViewController.h b/Utilities/LocationViewController/BeaLocationViewController.h
new file mode 100644
index 0000000..a3caf5c
--- /dev/null
+++ b/Utilities/LocationViewController/BeaLocationViewController.h
@@ -0,0 +1,19 @@
+#import
+#import
+
+@protocol BeaLocationViewControllerDelegate;
+
+@interface BeaLocationViewController : UIViewController
+@property (nonatomic, strong) MKMapView *mapView;
+@property (nonatomic, strong) UIButton *doneButton;
+@property (nonatomic, weak) id delegate;
+@property (nonatomic, strong) UIButton *userLocationButton;
+@property (nonatomic, assign) BOOL userLocationEnabled;
+@property (nonatomic, assign) double latitude;
+@property (nonatomic, assign) double longitude;
+@property (nonatomic, strong) CLLocationManager *locationManager;
+@end
+
+@protocol BeaLocationViewControllerDelegate
+- (void)locationViewController:(BeaLocationViewController *)viewController didSelectLocationWithLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude;
+@end
\ No newline at end of file
diff --git a/Utilities/LocationViewController/BeaLocationViewController.m b/Utilities/LocationViewController/BeaLocationViewController.m
new file mode 100644
index 0000000..6ee5b1a
--- /dev/null
+++ b/Utilities/LocationViewController/BeaLocationViewController.m
@@ -0,0 +1,120 @@
+#import "BeaLocationViewController.h"
+
+@implementation BeaLocationViewController
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
+ self.mapView.delegate = self;
+ [self.view addSubview:self.mapView];
+
+ self.locationManager = [CLLocationManager performSelector:@selector(sharedManager)];
+ //self.locationManager = [[CLLocationManager alloc] init];
+ self.locationManager.delegate = self;
+
+ self.doneButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ [self.doneButton setTitle:@"Done" forState:UIControlStateNormal];
+ [self.doneButton addTarget:self action:@selector(doneButtonTapped) forControlEvents:UIControlEventTouchUpInside];
+ [self.doneButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
+ self.doneButton.backgroundColor = [UIColor whiteColor];
+ self.doneButton.layer.cornerRadius = 8.0;
+ self.doneButton.titleLabel.font = [UIFont fontWithName:@"Inter" size:17];
+ self.doneButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:self.doneButton];
+
+ self.userLocationButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ [self.userLocationButton setImage:[UIImage systemImageNamed:@"location"] forState:UIControlStateNormal];
+ self.userLocationButton.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.6];
+ self.userLocationButton.layer.cornerRadius = 8.0;
+ self.userLocationButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.userLocationButton addTarget:self action:@selector(locationButtonTapped) forControlEvents:UIControlEventTouchUpInside];
+ [self.view addSubview:self.userLocationButton];
+
+ UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
+ [self.mapView addGestureRecognizer:gestureRecognizer];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [self.doneButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
+ [self.doneButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0],
+ [self.doneButton.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-10.0],
+ [self.doneButton.heightAnchor constraintEqualToConstant:44.0],
+
+ [self.userLocationButton.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:20.0],
+ [self.userLocationButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
+ [self.userLocationButton.widthAnchor constraintEqualToConstant:44.0],
+ [self.userLocationButton.heightAnchor constraintEqualToConstant:44.0],
+ ]];
+}
+
+- (void)setUserLocationEnabledState:(BOOL)arg1 {
+ self.userLocationEnabled = arg1;
+
+ // if the user dropped a pin, remove it
+ if (self.mapView.annotations.firstObject) {
+ [self.mapView removeAnnotations:self.mapView.annotations];
+ }
+
+ if (self.userLocationEnabled) {
+ [UIView animateWithDuration:0.3 animations:^{
+ [self.userLocationButton setImage:[UIImage systemImageNamed:@"location.fill"] forState:UIControlStateNormal];
+ }];
+ [self.locationManager requestWhenInUseAuthorization];
+ [self.locationManager startUpdatingLocation];
+ self.mapView.showsUserLocation = YES;
+ } else {
+ [UIView animateWithDuration:0.3 animations:^{
+ [self.userLocationButton setImage:[UIImage systemImageNamed:@"location"] forState:UIControlStateNormal];
+ }];
+ [self.locationManager stopUpdatingLocation];
+ self.mapView.showsUserLocation = NO;
+
+ self.longitude = 0.0;
+ self.latitude = 0.0;
+ }
+}
+
+- (void)locationButtonTapped {
+ [self setUserLocationEnabledState:!self.userLocationEnabled];
+}
+
+- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer {
+ if (self.userLocationEnabled) [self setUserLocationEnabledState:NO];
+
+ if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
+ CGPoint touchPoint = [gestureRecognizer locationInView:self.mapView];
+ CLLocationCoordinate2D coordinate = [self.mapView convertPoint:touchPoint toCoordinateFromView:self.mapView];
+
+ MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
+ annotation.coordinate = coordinate;
+ [self.mapView removeAnnotations:self.mapView.annotations];
+ [self.mapView addAnnotation:annotation];
+
+ self.latitude = coordinate.latitude;
+ self.longitude = coordinate.longitude;
+ }
+}
+
+- (void)doneButtonTapped {
+ if ([self.delegate respondsToSelector:@selector(locationViewController:didSelectLocationWithLatitude:longitude:)]) {
+ [self.delegate locationViewController:self didSelectLocationWithLatitude:self.latitude longitude:self.longitude];
+ }
+ [self.locationManager stopUpdatingLocation];
+ [self dismissViewControllerAnimated:YES completion:nil];
+}
+
+#pragma mark - CLLocationManagerDelegate
+
+- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
+ CLLocation *location = locations.lastObject;
+ self.longitude = location.coordinate.longitude;
+ self.latitude = location.coordinate.latitude;
+}
+
+#pragma mark - MKMapViewDelegate
+
+// update the maps view when user location updated
+- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
+ MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 1000, 1000);
+ [mapView setRegion:region animated:YES];
+}
+@end
\ No newline at end of file
diff --git a/Utilities/UploadTask/BeaUploadTask.h b/Utilities/UploadTask/BeaUploadTask.h
new file mode 100644
index 0000000..44119d2
--- /dev/null
+++ b/Utilities/UploadTask/BeaUploadTask.h
@@ -0,0 +1,15 @@
+@interface BeaUploadTask : NSObject
+- (instancetype)initWithData:(NSDictionary *)data frontImage:(UIImage *)frontImage backImage:(UIImage *)backImage;
+@property (nonatomic, strong) NSString *authorizationKey;
+@property (nonatomic, retain) NSData *frontImageData;
+@property (nonatomic, retain) NSData *backImageData;
+@property (nonatomic, strong) NSDictionary *userDictionary;
+@property (nonatomic, strong) NSString *takenAt;
+@property (nonatomic, strong) NSString *lastMoment;
+- (void)uploadBeRealWithCompletion:(void (^)(BOOL success, NSError *error))completion;
+- (void)makePUTRequestWithData:(NSDictionary *)data completion:(void (^)(BOOL success, NSError *error))completion;
+- (void)putPhotoWithURL:(NSURL *)url headers:(NSDictionary *)headers imageData:(NSData *)imageData completion:(void (^)(BOOL success))completion;
+- (void)postBeRealWithFrontPath:(NSString *)frontPath backPath:(NSString *)backPath frontBucket:(NSString *)frontBucket backBucket:(NSString *)backBucket completion:(void (^)(BOOL success, NSError *error))completion;
+- (void)getLastMoment;
+- (void)handleErrorWithTitle:(NSString *)title message:(NSString *)message completion:(void (^)(BOOL success, NSError *error))completion;
+@end
\ No newline at end of file
diff --git a/Utilities/UploadTask/BeaUploadTask.m b/Utilities/UploadTask/BeaUploadTask.m
new file mode 100644
index 0000000..46af81e
--- /dev/null
+++ b/Utilities/UploadTask/BeaUploadTask.m
@@ -0,0 +1,243 @@
+#import "BeaUploadTask.h"
+
+@implementation BeaUploadTask
+NSData* compressImage(UIImage *image, NSUInteger targetDataSize) {
+ CGFloat compressionFactor = 1.0;
+ NSData *imageData = UIImageJPEGRepresentation(image, compressionFactor);
+
+ // if the current data length is below the target's size return the image
+ if (imageData.length < targetDataSize) {
+ return imageData;
+ }
+
+ while (imageData.length > targetDataSize && compressionFactor > 0.0) {
+ compressionFactor -= 0.1;
+ imageData = UIImageJPEGRepresentation(image, compressionFactor);
+ }
+
+ return imageData;
+}
+
+- (UIImage *)resizeImage:(UIImage *)image toSize:(CGSize)size {
+ UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
+ [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
+ UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return resizedImage;
+}
+
+- (instancetype)initWithData:(NSDictionary *)data frontImage:(UIImage *)frontImage backImage:(UIImage *)backImage {
+ self = [super init];
+ if (self) {
+ self.userDictionary = data;
+ self.authorizationKey = data[@"authorization"];
+
+ UIImage *resizedFrontImage = [self resizeImage:frontImage toSize:CGSizeMake(1500, 2000)];
+ UIImage *resizedBackImage = [self resizeImage:backImage toSize:CGSizeMake(1500, 2000)];
+
+ self.frontImageData = compressImage(resizedFrontImage, 1048576);
+ self.backImageData = compressImage(resizedBackImage, 1048576);
+ }
+ return self;
+}
+
+- (void)handleErrorWithTitle:(NSString *)title message:(NSString *)message completion:(void (^)(BOOL success, NSError *error))completion {
+ NSError *error = [NSError errorWithDomain:@"com.yan.bea" code:0 userInfo:@{ @"title":title, @"description":message }];
+ completion(NO, error);
+}
+
+- (void)uploadBeRealWithCompletion:(void (^)(BOOL success, NSError *error))completion {
+
+ [self getLastMoment];
+
+ // create the first request
+ NSURL *uploadRequestURL = [NSURL URLWithString:@"https://mobile.bereal.com/api/content/posts/upload-url?mimeType=image/webp"];
+ NSMutableURLRequest *uploadRequest = [NSMutableURLRequest requestWithURL:uploadRequestURL];
+ [uploadRequest setHTTPMethod:@"GET"];
+ [uploadRequest setValue:self.authorizationKey forHTTPHeaderField:@"Authorization"];
+ [uploadRequest setValue:@"*/*" forHTTPHeaderField:@"Accept"];
+ [uploadRequest setValue:@"iOS" forHTTPHeaderField:@"bereal-platform"];
+ [uploadRequest setValue:@"14.7.1" forHTTPHeaderField:@"bereal-os-version"];
+ [uploadRequest setValue:@"en-US;q=1.0" forHTTPHeaderField:@"Accept-Language"];
+ [uploadRequest setValue:@"BeReal/0.28.2 (AlexisBarreyat.BeReal; build:8425; iOS 14.7.1) 1.0.0/BRApiKit" forHTTPHeaderField:@"User-Agent"];
+ [uploadRequest setValue:@"en-US" forHTTPHeaderField:@"bereal-app-language"];
+ [uploadRequest setValue:@"en" forHTTPHeaderField:@"bereal-device-language"];
+
+ NSURLSession *session = [NSURLSession sharedSession];
+ NSURLSessionDataTask *uploadRequestTask = [session dataTaskWithRequest:uploadRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *getError) {
+ NSDictionary *uploadRequestResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ if (uploadRequestResponse[@"error"] || getError) {
+ [self handleErrorWithTitle:@"Something went wrong..." message:@"0 - Bea could not initiate the upload process" completion:completion];
+ } else {
+ [self makePUTRequestWithData:uploadRequestResponse completion:completion];
+ }
+ }];
+
+ [uploadRequestTask resume];
+
+}
+
+- (void)makePUTRequestWithData:(NSDictionary *)response completion:(void (^)(BOOL success, NSError *error))completion {
+ if (!response) return;
+
+ NSString *frontCameraURLString = response[@"data"][0][@"url"];
+ NSString *backCameraURLString = response[@"data"][1][@"url"];
+
+ NSURL *frontCameraURL = [NSURL URLWithString:frontCameraURLString];
+ NSURL *backCameraURL = [NSURL URLWithString:backCameraURLString];
+
+ // those headers have to be included in the next put request
+ NSDictionary *frontHeaders = response[@"data"][0][@"headers"];
+ NSDictionary *backHeaders = response[@"data"][1][@"headers"];
+
+ NSString *frontImageUploadPath = response[@"data"][0][@"path"];
+ NSString *backImageUploadPath = response[@"data"][1][@"path"];
+
+ NSString *frontImageBucket = response[@"data"][0][@"bucket"];
+ NSString *backImageBucket = response[@"data"][1][@"bucket"];
+
+ // otherwise the postbereal function would get called even if one of the put requests didnt succeed
+ dispatch_group_t group = dispatch_group_create();
+ dispatch_group_enter(group);
+ [self putPhotoWithURL:frontCameraURL headers:frontHeaders imageData:self.frontImageData completion:^(BOOL success) {
+ if (!success) {
+ return;
+ }
+ dispatch_group_leave(group);
+ }];
+
+ dispatch_group_enter(group);
+ [self putPhotoWithURL:backCameraURL headers:backHeaders imageData:self.backImageData completion:^(BOOL success) {
+ if (!success) {
+ return;
+ }
+ dispatch_group_leave(group);
+ }];
+
+
+ dispatch_group_notify(group, dispatch_get_main_queue(), ^{
+ [self postBeRealWithFrontPath:frontImageUploadPath backPath:backImageUploadPath frontBucket:frontImageBucket backBucket:backImageBucket completion:completion];
+ });
+}
+
+- (void)putPhotoWithURL:(NSURL *)url headers:(NSDictionary *)headers imageData:(NSData *)imageData completion:(void (^)(BOOL success))completion {
+
+ NSMutableURLRequest *putRequest = [NSMutableURLRequest requestWithURL:url];
+ [putRequest setHTTPMethod:@"PUT"];
+ [putRequest setAllHTTPHeaderFields:headers];
+
+ NSURLSessionTask *task = [[NSURLSession sharedSession] uploadTaskWithRequest:putRequest fromData:imageData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+ if (error || httpResponse.statusCode > 299) {
+ completion(NO);
+ return;
+ }
+
+ if (data) {
+ completion(YES);
+ }
+ }];
+
+ [task resume];
+}
+
+- (void)postBeRealWithFrontPath:(NSString *)frontPath backPath:(NSString *)backPath frontBucket:(NSString *)frontBucket backBucket:(NSString *)backBucket completion:(void (^)(BOOL success, NSError *error))completion {
+ NSDate *currentDate = [NSDate date];
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"];
+ [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
+
+ if ([self.userDictionary[@"isLate"] boolValue]) {
+ self.takenAt = [dateFormatter stringFromDate:currentDate];
+ } else {
+ // randomize the taken at to be between the startDate and endDate because its
+ // logically impossible to "post" on the start time
+ NSDate *moment = [dateFormatter dateFromString:self.lastMoment];
+ NSInteger randomSeconds = arc4random_uniform(105 - 60) + 60;
+ NSDate *dateInRange = [moment dateByAddingTimeInterval:randomSeconds];
+ NSString *dateString = [dateFormatter stringFromDate:dateInRange];
+ self.takenAt = dateString;
+ }
+
+ NSMutableDictionary *payload = [NSMutableDictionary dictionaryWithDictionary:@{
+ @"visibility": @[@"friends"],
+ @"isLate": @([self.userDictionary[@"isLate"] boolValue]),
+ @"retakeCounter": self.userDictionary[@"retakeCounter"] ?: @0,
+ @"takenAt": self.takenAt,
+ @"backCamera": @{
+ @"bucket": backBucket,
+ @"height": @1500,
+ @"width": @2000,
+ @"path": backPath
+ },
+ @"frontCamera": @{
+ @"bucket": frontBucket,
+ @"height": @1500,
+ @"width": @2000,
+ @"path": frontPath
+ }
+ }];
+
+ if (self.userDictionary[@"longitude"] && self.userDictionary[@"latitude"]) {
+ NSDictionary *locationDict = @{
+ @"latitude": self.userDictionary[@"latitude"],
+ @"longitude": self.userDictionary[@"longitude"]
+ };
+ [payload setObject:locationDict forKey:@"location"];
+ }
+
+ if (self.userDictionary[@"caption"]) {
+ [payload setObject:self.userDictionary[@"caption"] forKey:@"caption"];
+ }
+
+ NSData *payloadJSON = [NSJSONSerialization dataWithJSONObject:payload options:NSJSONWritingWithoutEscapingSlashes error:nil];
+
+ NSURL *postBeRealURL = [NSURL URLWithString:@"https://mobile.bereal.com/api/content/posts"];
+ NSMutableURLRequest *postBeRealRequest = [NSMutableURLRequest requestWithURL:postBeRealURL];
+
+ [postBeRealRequest setHTTPMethod:@"POST"];
+ [postBeRealRequest setValue:@"application/json" forHTTPHeaderField:@"content-type"];
+ [postBeRealRequest setValue:@"*/*" forHTTPHeaderField:@"Accept"];
+ [postBeRealRequest setValue:self.authorizationKey forHTTPHeaderField:@"Authorization"];
+ [postBeRealRequest setValue:@"en-US" forHTTPHeaderField:@"bereal-app-language"];
+
+
+ NSURLSessionUploadTask *uploadTask = [[NSURLSession sharedSession] uploadTaskWithRequest:postBeRealRequest fromData:payloadJSON completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+ if (error || httpResponse.statusCode > 299) {
+ NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ NSString *message = [NSString stringWithFormat:@"1 - Uploading failed: %@: %@", responseDictionary[@"statusCode"], responseDictionary[@"errorKey"]];
+ [self handleErrorWithTitle:@"API Error" message:message completion:completion];
+ return;
+ }
+
+ if (data) {
+ // the upload succeded
+ completion(YES, nil);
+ }
+ }];
+
+ [uploadTask resume];
+}
+
+- (void)getLastMoment {
+ NSURL *lastMomentURL = [NSURL URLWithString:@"https://mobile.bereal.com/api/bereal/moments/last/"];
+
+ NSMutableURLRequest *lastMomentRequest = [NSMutableURLRequest requestWithURL:lastMomentURL];
+ [lastMomentRequest setHTTPMethod:@"GET"];
+ [lastMomentRequest setValue:self.authorizationKey forHTTPHeaderField:@"Authorization"];
+ [lastMomentRequest setValue:@"*/*" forHTTPHeaderField:@"Accept"];
+
+ NSURLSession *session = [NSURLSession sharedSession];
+ NSURLSessionDataTask *lastMomentRequestTask = [session dataTaskWithRequest:lastMomentRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+ if (error || httpResponse.statusCode != 200) {
+ return;
+ } else {
+ NSDictionary *lastMomentResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ self.lastMoment = lastMomentResponse[@"startDate"];
+ }
+ }];
+ [lastMomentRequestTask resume];
+}
+@end
\ No newline at end of file
diff --git a/Utilities/UploadViewController/BeaUploadViewController.h b/Utilities/UploadViewController/BeaUploadViewController.h
new file mode 100644
index 0000000..d58fa2b
--- /dev/null
+++ b/Utilities/UploadViewController/BeaUploadViewController.h
@@ -0,0 +1,35 @@
+#import
+#import
+#import "StatusView/BeaStatusView.m"
+#import "../InfoViewController/BeaInfoViewController.m"
+#import "../UploadTask/BeaUploadTask.m"
+#import "../LocationViewController/BeaLocationViewController.m"
+
+@interface BeaUploadViewController : UIViewController
+@property (nonatomic, strong) BeaLocationViewController *locationVC;
+@property (nonatomic, strong) NSString *authorizationKey;
+@property (nonatomic, strong) UIImageView *frontImageView;
+@property (nonatomic, strong) UIImageView *backImageView;
+@property (nonatomic, strong) UILabel *frontTextLabel;
+@property (nonatomic, strong) UILabel *backTextLabel;
+@property (nonatomic, strong) UIImage *frontImage;
+@property (nonatomic, strong) UIImage *backImage;
+@property (nonatomic, strong) UITextField *captionTextField;
+@property (nonatomic, strong) NSString *caption;
+@property (nonatomic, strong) UITextField *retakeTextField;
+@property (nonatomic, strong) NSNumber *retakeCount;
+@property (nonatomic, strong) UIButton *actionButton;
+@property (nonatomic, strong) BeaStatusView *statusView;
+@property (nonatomic, strong) UIButton *locationButton;
+@property (nonatomic, strong) UILabel *locationLabel;
+@property (nonatomic, assign) double latitude;
+@property (nonatomic, assign) double longitude;
+@property (nonatomic, strong) UIImageView *titleImageView;
+@property (nonatomic, strong) UIButton *backButton;
+@property (nonatomic, strong) UIImageView *backButtonImageView;
+@property (nonatomic, strong) UISwitch *isLateSwitch;
+@property (nonatomic, strong) UILabel *isLateLabel;
+@property (nonatomic, assign) BOOL isLate;
+@property (nonatomic, strong) UIButton *infoButton;
+@property (nonatomic, strong) UIImageView *infoButtonImageView;
+@end
\ No newline at end of file
diff --git a/Utilities/UploadViewController/BeaUploadViewController.m b/Utilities/UploadViewController/BeaUploadViewController.m
new file mode 100644
index 0000000..c67dadc
--- /dev/null
+++ b/Utilities/UploadViewController/BeaUploadViewController.m
@@ -0,0 +1,537 @@
+#import "BeaUploadViewController.h"
+
+@implementation BeaUploadViewController
+
+- (instancetype)initWithAuthorization:(NSString *)authKey {
+ self = [super init];
+ if (self) {
+ if (!authKey) return nil;
+ self.authorizationKey = authKey;
+ self.locationVC = [[BeaLocationViewController alloc] init];
+ self.locationVC.delegate = self;
+ }
+ return self;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ #ifdef JAILED
+ NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Bea" ofType:@"bundle"];
+ NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
+ UIImage *beFakeLogo = [UIImage imageWithContentsOfFile:[bundle pathForResource:@"BeFake" ofType:@"png"]];
+ #else
+ NSBundle *bundle = [NSBundle bundleWithPath:ROOT_PATH_NS(@"/Library/Application Support/Bea.bundle")];
+ UIImage *beFakeLogo = [UIImage imageNamed:@"BeFake.png" inBundle:bundle compatibleWithTraitCollection:nil];
+ #endif
+
+ self.view.backgroundColor = [UIColor blackColor];
+
+ self.titleImageView = [[UIImageView alloc] initWithImage:beFakeLogo];
+ self.titleImageView.contentMode = UIViewContentModeScaleAspectFit;
+ self.titleImageView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:self.titleImageView];
+
+ self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
+ self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.backButton addTarget:self action:@selector(dismissViewController) forControlEvents:UIControlEventTouchUpInside];
+ [self.view addSubview:self.backButton];
+
+ UIImage *backButtonImage = [[UIImage systemImageNamed:@"chevron.down"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ self.backButtonImageView = [[UIImageView alloc] initWithImage:backButtonImage];
+ self.backButtonImageView.tintColor = [UIColor whiteColor];
+ self.backButtonImageView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.backButton addSubview:self.backButtonImageView];
+
+ self.infoButton = [UIButton buttonWithType:UIButtonTypeCustom];
+ self.infoButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.infoButton addTarget:self action:@selector(infoButtonTapped) forControlEvents:UIControlEventTouchUpInside];
+ [self.view addSubview:self.infoButton];
+
+ UIImage *infoButtonImage = [[UIImage systemImageNamed:@"info.circle.fill"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ self.infoButtonImageView = [[UIImageView alloc] initWithImage:infoButtonImage];
+ self.infoButtonImageView.tintColor = [UIColor whiteColor];
+ self.infoButtonImageView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.infoButton addSubview:self.infoButtonImageView];
+
+ self.statusView = [[BeaStatusView alloc] initWithFrame:CGRectZero];
+ [self.view addSubview:self.statusView];
+ self.statusView.translatesAutoresizingMaskIntoConstraints = NO;
+
+ self.frontImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
+ self.frontImageView.backgroundColor = [UIColor blackColor];
+ self.frontImageView.layer.borderWidth = 1.8;
+ self.frontImageView.layer.cornerRadius = 8.0;
+ self.frontImageView.layer.masksToBounds = YES;
+ self.frontImageView.layer.borderColor = [UIColor whiteColor].CGColor;
+ self.frontImageView.userInteractionEnabled = YES;
+ self.frontImageView.contentMode = UIViewContentModeScaleAspectFill;
+ self.frontImageView.translatesAutoresizingMaskIntoConstraints = NO;
+
+ UITapGestureRecognizer *frontTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageViewTapped:)];
+ [self.frontImageView addGestureRecognizer:frontTapRecognizer];
+
+ [self.view addSubview:self.frontImageView];
+
+ self.frontTextLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ self.frontTextLabel.font = [UIFont fontWithName:@"Inter" size:14];
+ self.frontTextLabel.text = @"Front image";
+ self.frontTextLabel.textAlignment = NSTextAlignmentCenter;
+ self.frontTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.frontImageView addSubview:self.frontTextLabel];
+
+ self.backImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
+ self.backImageView.backgroundColor = [UIColor blackColor];
+ self.backImageView.layer.borderWidth = 1.8;
+ self.backImageView.layer.cornerRadius = 8.0;
+ self.backImageView.layer.masksToBounds = YES;
+ self.backImageView.layer.borderColor = [UIColor whiteColor].CGColor;
+ self.backImageView.userInteractionEnabled = YES;
+ self.backImageView.contentMode = UIViewContentModeScaleAspectFill;
+ self.backImageView.translatesAutoresizingMaskIntoConstraints = NO;
+
+ UITapGestureRecognizer *backTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageViewTapped:)];
+ [self.backImageView addGestureRecognizer:backTapRecognizer];
+ [self.view addSubview:self.backImageView];
+
+ self.backTextLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ self.backTextLabel.font = [UIFont fontWithName:@"Inter" size:14];
+ self.backTextLabel.text = @"Back image";
+ self.backTextLabel.textAlignment = NSTextAlignmentCenter;
+ self.backTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.backImageView addSubview:self.backTextLabel];
+
+ self.captionTextField = [[UITextField alloc] initWithFrame:CGRectZero];
+ self.captionTextField.placeholder = @"Caption";
+ self.captionTextField.font = [UIFont fontWithName:@"Inter" size:13];
+ self.captionTextField.backgroundColor = [UIColor blackColor];
+ self.captionTextField.layer.cornerRadius = 8.0;
+ self.captionTextField.layer.borderWidth = 1.2;
+ self.captionTextField.layer.borderColor = [UIColor whiteColor].CGColor;
+ self.captionTextField.translatesAutoresizingMaskIntoConstraints = NO;
+ self.captionTextField.delegate = self;
+
+ self.captionTextField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 12, 40)];
+ self.captionTextField.leftViewMode = UITextFieldViewModeAlways;
+
+ [self.captionTextField addTarget:self action:@selector(captionTextFieldDidChange:) forControlEvents:UIControlEventEditingDidEnd];
+ [self.view addSubview: self.captionTextField];
+
+ self.retakeTextField = [[UITextField alloc] initWithFrame:CGRectZero];
+ self.retakeTextField.keyboardType = UIKeyboardTypeNumberPad;
+ self.retakeTextField.placeholder = @"Retakes";
+ self.retakeTextField.font = [UIFont fontWithName:@"Inter" size:13];
+ self.retakeTextField.backgroundColor = [UIColor blackColor];
+ self.retakeTextField.layer.cornerRadius = 8.0;
+ self.retakeTextField.layer.borderWidth = 1.2;
+ self.retakeTextField.layer.borderColor = [UIColor whiteColor].CGColor;
+ self.retakeTextField.translatesAutoresizingMaskIntoConstraints = NO;
+ self.retakeTextField.delegate = self;
+
+ self.retakeTextField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 12, 40)];
+ self.retakeTextField.leftViewMode = UITextFieldViewModeAlways;
+
+ [self.retakeTextField addTarget:self action:@selector(retakeTextFieldDidChange:) forControlEvents:UIControlEventEditingDidEnd];
+ [self.view addSubview: self.retakeTextField];
+
+ self.actionButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ [self.actionButton setTitle:@"Send" forState:UIControlStateNormal];
+ [self.actionButton addTarget:self action:@selector(sendBeReal) forControlEvents:UIControlEventTouchUpInside];
+ [self.actionButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
+ self.actionButton.backgroundColor = [UIColor whiteColor];
+ self.actionButton.titleLabel.font = [UIFont fontWithName:@"Inter" size:17];
+ self.actionButton.layer.cornerRadius = 8.0;
+ self.actionButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:self.actionButton];
+
+ self.locationLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ self.locationLabel.font = [UIFont fontWithName:@"Inter" size:22];
+ self.locationLabel.text = @"Location";
+ self.locationLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:self.locationLabel];
+
+ self.locationButton = [UIButton buttonWithType:UIButtonTypeCustom];
+ self.locationButton.frame = CGRectMake(0, 0, 32, 32);
+ self.locationButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.locationButton addTarget:self action:@selector(locationButtonTapped) forControlEvents:UIControlEventTouchUpInside];
+ [self.view addSubview:self.locationButton];
+
+ UIImage *image = [[UIImage systemImageNamed:@"mappin.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
+ imageView.tintColor = [UIColor whiteColor];
+ imageView.frame = self.locationButton.bounds;
+ [self.locationButton addSubview:imageView];
+
+ self.isLateSwitch = [[UISwitch alloc] init];
+ self.isLateSwitch.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.isLateSwitch addTarget:self action:@selector(isLateStateChanged:) forControlEvents:UIControlEventValueChanged];
+ [self.isLateSwitch setOn:NO animated:NO];
+ [self.view addSubview:self.isLateSwitch];
+
+ self.isLateLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ self.isLateLabel.font = [UIFont fontWithName:@"Inter" size:22];
+ self.isLateLabel.text = @"Post late";
+ self.isLateLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:self.isLateLabel];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [self.frontImageView.centerXAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:2 + self.view.frame.size.width / 4],
+ [self.frontImageView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:120],
+ [self.frontImageView.widthAnchor constraintEqualToConstant:150],
+ [self.frontImageView.heightAnchor constraintEqualToConstant:200],
+ [self.frontTextLabel.centerXAnchor constraintEqualToAnchor:self.frontImageView.centerXAnchor],
+ [self.frontTextLabel.centerYAnchor constraintEqualToAnchor:self.frontImageView.centerYAnchor],
+
+ [self.backImageView.centerXAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-2 - self.view.frame.size.width / 4],
+ [self.backImageView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:120],
+ [self.backImageView.widthAnchor constraintEqualToConstant:150],
+ [self.backImageView.heightAnchor constraintEqualToConstant:200],
+ [self.backTextLabel.centerXAnchor constraintEqualToAnchor:self.backImageView.centerXAnchor],
+ [self.backTextLabel.centerYAnchor constraintEqualToAnchor:self.backImageView.centerYAnchor],
+
+ [self.captionTextField.topAnchor constraintEqualToAnchor:self.frontImageView.bottomAnchor constant:20],
+ [self.captionTextField.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:22],
+ [self.captionTextField.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-22],
+ [self.captionTextField.heightAnchor constraintEqualToConstant:40],
+
+ [self.retakeTextField.topAnchor constraintEqualToAnchor:self.captionTextField.bottomAnchor constant:20],
+ [self.retakeTextField.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:22],
+ [self.retakeTextField.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-22],
+ [self.retakeTextField.heightAnchor constraintEqualToConstant:40],
+
+ [self.isLateSwitch.topAnchor constraintEqualToAnchor:self.retakeTextField.bottomAnchor constant:30],
+ [self.isLateSwitch.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-22],
+
+ [self.isLateLabel.centerYAnchor constraintEqualToAnchor:self.isLateSwitch.centerYAnchor],
+ [self.isLateLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:22],
+
+ [self.locationButton.topAnchor constraintEqualToAnchor:self.isLateSwitch.bottomAnchor constant:20],
+ [self.locationButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-22],
+ [self.locationButton.widthAnchor constraintEqualToConstant:32],
+ [self.locationButton.heightAnchor constraintEqualToAnchor:self.locationButton.widthAnchor],
+
+ [self.locationLabel.centerYAnchor constraintEqualToAnchor:self.locationButton.centerYAnchor],
+ [self.locationLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:22],
+ [self.locationLabel.trailingAnchor constraintEqualToAnchor:self.locationButton.leadingAnchor],
+
+ [self.actionButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
+ [self.actionButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
+ [self.actionButton.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-20],
+ [self.actionButton.heightAnchor constraintEqualToConstant:44],
+
+ [self.statusView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
+ [self.statusView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
+ [self.statusView.bottomAnchor constraintEqualToAnchor:self.actionButton.topAnchor constant: -20],
+
+ [self.titleImageView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:12],
+ [self.titleImageView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
+ [self.titleImageView.heightAnchor constraintEqualToConstant:18],
+ [self.titleImageView.widthAnchor constraintEqualToConstant:84],
+
+ [self.backButton.centerYAnchor constraintEqualToAnchor:self.titleImageView.centerYAnchor],
+ [self.backButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
+ [self.backButton.widthAnchor constraintEqualToConstant:40],
+ [self.backButton.heightAnchor constraintEqualToConstant:40],
+
+ [self.backButtonImageView.centerYAnchor constraintEqualToAnchor:self.backButton.centerYAnchor],
+ [self.backButtonImageView.widthAnchor constraintEqualToConstant:20],
+ [self.backButtonImageView.heightAnchor constraintEqualToConstant:20],
+
+ [self.infoButton.centerYAnchor constraintEqualToAnchor:self.titleImageView.centerYAnchor],
+ [self.infoButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
+ [self.infoButton.widthAnchor constraintEqualToConstant:40],
+ [self.infoButton.heightAnchor constraintEqualToConstant:40],
+
+ [self.infoButtonImageView.centerYAnchor constraintEqualToAnchor:self.infoButton.centerYAnchor],
+ [self.infoButtonImageView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
+ [self.infoButtonImageView.widthAnchor constraintEqualToConstant:20],
+ [self.infoButtonImageView.heightAnchor constraintEqualToConstant:20]
+ ]];
+}
+
+- (void)infoButtonTapped {
+ BeaInfoViewController *infoViewController = [[BeaInfoViewController alloc] init];
+ [self presentViewController:infoViewController animated:YES completion:nil];
+}
+
+- (void)isLateStateChanged:(UISwitch *)sender {
+ if (sender.isOn) {
+ self.isLate = YES;
+ } else {
+ self.isLate = NO;
+ }
+}
+
+- (void)dismissViewController {
+ [self dismissViewControllerAnimated:YES completion:nil];
+}
+
+- (void)locationButtonTapped {
+ [self presentViewController:self.locationVC animated:YES completion:nil];
+}
+
+- (void)locationViewController:(BeaLocationViewController *)viewController didSelectLocationWithLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude {
+ self.latitude = latitude;
+ self.longitude = longitude;
+
+ if (latitude == 0.0 && longitude == 0.0) {
+ self.locationLabel.text = @"Location";
+ return;
+ }
+
+ self.locationLabel.text = @"Loading...";
+
+ CLLocation *location = [[CLLocation alloc] initWithLatitude:self.latitude longitude:self.longitude];
+ CLGeocoder *geocoder = [[CLGeocoder alloc] init];
+ [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray * _Nullable placemarks, NSError * _Nullable error) {
+ if (error) {
+ self.locationLabel.text = @"An error occured";
+ return;
+ }
+
+ if (placemarks.count > 0) {
+ CLPlacemark *placemark = placemarks.firstObject;
+ if (placemark.locality && placemark.country) {
+ self.locationLabel.text = [NSString stringWithFormat:@"%@, %@", placemark.locality, placemark.ISOcountryCode];
+ return;
+ }
+ }
+ self.locationLabel.text = @"City not found";
+ }];
+}
+
+- (void)showErrorWithTitle:(NSString *)title message:(NSString *)message {
+ self.statusView.titleLabel.text = title;
+ self.statusView.messageLabel.text = message;
+ self.statusView.backgroundColor = [UIColor colorWithRed: 0.95 green: 0.15 blue: 0.07 alpha: 1.00];
+ self.statusView.imageView.image = self.statusView.image;
+
+ [UIView animateWithDuration:0.3 animations:^{
+ self.statusView.alpha = 1.0;
+ }];
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [UIView animateWithDuration:0.3 animations:^{
+ self.statusView.alpha = 0.0;
+ }];
+ });
+}
+
+- (void)imageViewTapped:(UITapGestureRecognizer *)gestureRecognizer {
+ if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
+ UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
+ imagePicker.delegate = self;
+ UIImageView *tapped = (UIImageView *)gestureRecognizer.view;
+ if (tapped == self.frontImageView) {
+ imagePicker.view.tag = 1;
+ } else if (tapped == self.backImageView) {
+ imagePicker.view.tag = 2;
+ }
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Choose an Image Source" message:@"Select the desired image source for your content." preferredStyle:UIAlertControllerStyleActionSheet];
+
+ UIAlertAction *cameraAction = [UIAlertAction actionWithTitle:@"Open Camera" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
+ imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
+ if (tapped == self.frontImageView) {
+ imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
+ } else {
+ imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
+ }
+ [self presentViewController:imagePicker animated:YES completion:nil];
+ }];
+
+ UIImage *cameraImage = [UIImage systemImageNamed:@"camera" withConfiguration:[UIImageSymbolConfiguration configurationWithPointSize:19]];
+ [cameraAction setValue:cameraImage forKey:@"image"];
+
+ UIAlertAction *photoAction = [UIAlertAction actionWithTitle:@"Choose from Library" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
+ imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
+ [self presentViewController:imagePicker animated:YES completion:nil];
+ }];
+
+ UIImage *photoImage = [UIImage systemImageNamed:@"photo" withConfiguration:[UIImageSymbolConfiguration configurationWithPointSize:19]];
+ [photoAction setValue:photoImage forKey:@"image"];
+
+ UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
+
+ [alertController addAction:cameraAction];
+ [alertController addAction:photoAction];
+ [alertController addAction:cancelAction];
+
+ [self presentViewController:alertController animated:YES completion:nil];
+ }
+}
+
+- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
+ UIImage *pickedImage = info[UIImagePickerControllerOriginalImage];
+
+ // check if the source type is camera or photo library and then check
+ // if the picked photo is for the front image or back image view and assign it to it
+ if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
+ if (picker.view.tag == 1) {
+ UIImage *mirrored = [UIImage imageWithCGImage:pickedImage.CGImage scale:pickedImage.scale orientation:UIImageOrientationLeftMirrored];
+ self.frontImage = mirrored;
+ self.frontImageView.image = self.frontImage;
+ } else if (picker.view.tag == 2) {
+ self.backImage = pickedImage;
+ self.backImageView.image = self.backImage;
+ }
+ } else if (picker.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
+ if (picker.view.tag == 1) {
+ self.frontImage = pickedImage;
+ self.frontImageView.image = self.frontImage;
+ } else if (picker.view.tag == 2) {
+ self.backImage = pickedImage;
+ self.backImageView.image = self.backImage;
+ }
+ }
+
+ [picker dismissViewControllerAnimated:YES completion:nil];
+}
+
+// methods for the text field
+- (BOOL)textFieldShouldReturn:(UITextField *)textField {
+ [textField resignFirstResponder];
+ return YES;
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+ [self.view endEditing:YES];
+}
+
+// method for the caption text field
+- (void)captionTextFieldDidChange:(UITextField *)textField {
+ self.caption = textField.text;
+}
+
+// method for the retake text field
+- (void)retakeTextFieldDidChange:(UITextField *)textField {
+ NSCharacterSet *nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
+ BOOL containsNonDigits = [textField.text rangeOfCharacterFromSet:nonDigits].location != NSNotFound;
+
+ if (!containsNonDigits) {
+ NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+ NSNumber *number = [numberFormatter numberFromString:textField.text];
+ self.retakeCount = number;
+ textField.layer.borderColor = [UIColor whiteColor].CGColor;
+ } else {
+ textField.layer.borderColor = [UIColor redColor].CGColor;
+ CALayer *layer = textField.layer;
+
+ CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
+ animation.duration = 0.3;
+ animation.repeatCount = 1;
+ animation.values = @[
+ [NSValue valueWithCGPoint:CGPointMake(layer.position.x - 10, layer.position.y)],
+ [NSValue valueWithCGPoint:CGPointMake(layer.position.x + 10, layer.position.y)],
+ [NSValue valueWithCGPoint:CGPointMake(layer.position.x - 10, layer.position.y)],
+ [NSValue valueWithCGPoint:CGPointMake(layer.position.x + 10, layer.position.y)],
+ [NSValue valueWithCGPoint:CGPointMake(layer.position.x, layer.position.y)]
+ ];
+
+ [layer addAnimation:animation forKey:@"shake"];
+ }
+}
+
+- (void)sendBeReal {
+ if (!self.frontImage || !self.backImage) {
+ [self showErrorWithTitle:@"Missing images" message:@"Select all required images."];
+ return;
+ }
+ self.actionButton.enabled = NO;
+ [self.actionButton setTitle:@"" forState:UIControlStateNormal];
+
+ UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
+ spinner.center = self.actionButton.center;
+ [self.view addSubview:spinner];
+ [spinner startAnimating];
+
+ [UIView animateWithDuration:0.3 animations:^{
+ self.actionButton.alpha = 0.4;
+ }];
+
+ NSDictionary *userData = [self createDataDictionary];
+
+
+ // because of processing the images the spinner lags a bit
+ BeaUploadTask *task = [[BeaUploadTask alloc] initWithData:userData frontImage:self.frontImage backImage:self.backImage];
+ [task uploadBeRealWithCompletion:^(BOOL success, NSError *error) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [spinner stopAnimating];
+ [spinner removeFromSuperview];
+ self.actionButton.enabled = YES;
+ [self.actionButton setTitle:@"Send" forState:UIControlStateNormal];
+ [UIView animateWithDuration:0.3 animations:^{
+ self.actionButton.alpha = 1.0;
+ }];
+
+ if (success) {
+ [self uploadDidSucceed];
+ } else {
+ [self showErrorWithTitle:error.userInfo[@"title"] message:error.userInfo[@"description"]];
+ }
+ });
+ }];
+}
+
+- (void)uploadDidSucceed {
+ self.statusView.backgroundColor = [UIColor colorWithRed:76.0/255.0 green:178.0/255.0 blue:80.0/255.0 alpha:1.0];
+ self.statusView.titleLabel.text = @"Success 🎉";
+ self.statusView.messageLabel.text = @"Your BeReal was uploaded successfully!";
+
+ UIImage *checkmarkImage = [UIImage systemImageNamed:@"checkmark.circle"];
+
+ self.statusView.imageView.image = checkmarkImage;
+
+ [UIView animateWithDuration:0.3 animations:^{
+ self.statusView.alpha = 1.0;
+ self.frontTextLabel.alpha = 1.0;
+ self.backTextLabel.alpha = 1.0;
+ }];
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 7 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [UIView animateWithDuration:0.3 animations:^{
+ self.statusView.alpha = 0.0;
+ }];
+ });
+
+ // reset our view and properties to initial state
+ self.frontImageView.image = nil;
+ self.backImageView.image = nil;
+ self.frontImage = nil;
+ self.backImage = nil;
+ self.caption = nil;
+ self.captionTextField.text = nil;
+ self.retakeCount = nil;
+ self.retakeTextField.text = nil;
+ self.longitude = 0.0;
+ self.latitude = 0.0;
+}
+
+- (NSDictionary *)createDataDictionary {
+ NSMutableDictionary *data = [NSMutableDictionary dictionary];
+
+ if (!self.authorizationKey) {
+ [self showErrorWithTitle:@"Something went wrong" message:@"2 - Please restart the app and try again."];
+ return nil;
+ }
+
+ [data setObject:self.authorizationKey forKey:@"authorization"];
+ [data setValue:@(self.isLate) forKey:@"isLate"];
+
+ if (self.caption) {
+ [data setObject:self.caption forKey:@"caption"];
+ }
+ if (self.retakeCount) {
+ [data setObject:self.retakeCount forKey:@"retakeCounter"];
+ }
+ if ((self.latitude != 0.0) && (self.longitude != 0.0)) {
+ NSNumber *longitudeNumber = @(self.longitude);
+ NSNumber *latitudeNumber = @(self.latitude);
+
+ [data setObject:longitudeNumber forKey:@"longitude"];
+ [data setObject:latitudeNumber forKey:@"latitude"];
+ }
+
+
+ return [data copy];
+}
+@end
\ No newline at end of file
diff --git a/Utilities/UploadViewController/StatusView/BeaStatusView.h b/Utilities/UploadViewController/StatusView/BeaStatusView.h
new file mode 100644
index 0000000..62bbb4d
--- /dev/null
+++ b/Utilities/UploadViewController/StatusView/BeaStatusView.h
@@ -0,0 +1,6 @@
+@interface BeaStatusView : UIView
+@property (nonatomic, strong) UILabel *titleLabel;
+@property (nonatomic, strong) UILabel *messageLabel;
+@property (nonatomic, strong) UIImage *image;
+@property (nonatomic, strong) UIImageView *imageView;
+@end
diff --git a/Utilities/UploadViewController/StatusView/BeaStatusView.m b/Utilities/UploadViewController/StatusView/BeaStatusView.m
new file mode 100644
index 0000000..53341a0
--- /dev/null
+++ b/Utilities/UploadViewController/StatusView/BeaStatusView.m
@@ -0,0 +1,56 @@
+#import "BeaStatusView.h"
+
+@implementation BeaStatusView
+- (instancetype)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ self.alpha = 0.0;
+
+ self.layer.cornerRadius = 8.0;
+ self.layer.masksToBounds = YES;
+
+ self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ self.titleLabel.font = [UIFont fontWithName:@"Inter" size:20.0];
+ self.titleLabel.textColor = [UIColor whiteColor];
+
+ self.messageLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ self.messageLabel.font = [UIFont fontWithName:@"Inter" size:11.0];
+ self.messageLabel.numberOfLines = 2;
+ self.messageLabel.lineBreakMode = NSLineBreakByWordWrapping;
+ self.messageLabel.textColor = [UIColor whiteColor];
+
+ [self addSubview:self.titleLabel];
+ [self addSubview:self.messageLabel];
+
+ self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ self.messageLabel.translatesAutoresizingMaskIntoConstraints = NO;
+
+ self.image = [UIImage systemImageNamed:@"exclamationmark.triangle"];
+ self.imageView = [[UIImageView alloc] initWithImage:self.image];
+ self.imageView.contentMode = UIViewContentModeScaleAspectFit;
+ self.imageView.tintColor = [UIColor whiteColor];
+ self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self addSubview:self.imageView];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [self.heightAnchor constraintEqualToConstant:64.0],
+
+ [self.titleLabel.topAnchor constraintEqualToAnchor:self.topAnchor constant:8],
+ [self.titleLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:66],
+ [self.titleLabel.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-8],
+ [self.titleLabel.heightAnchor constraintEqualToConstant:24],
+
+ [self.messageLabel.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:-2],
+ [self.messageLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:66],
+ [self.messageLabel.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-8],
+ [self.messageLabel.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-8],
+
+ [self.imageView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:12.0],
+ [self.imageView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor constant:-2],
+ [self.imageView.widthAnchor constraintEqualToConstant:38.0],
+ [self.imageView.heightAnchor constraintEqualToConstant:38.0]
+ ]];
+ }
+ return self;
+}
+@end
\ No newline at end of file
diff --git a/control b/control
index 0c91688..d401012 100644
--- a/control
+++ b/control
@@ -1,6 +1,6 @@
Package: com.yan.bea
Name: Bea
-Version: 1.1.2
+Version: 1.2
Architecture: iphoneos-arm
Description: Lightweight BeReal. enhancement tweak.
Maintainer: yan
diff --git a/layout/Library/Application Support/Bea.bundle/BeFake.png b/layout/Library/Application Support/Bea.bundle/BeFake.png
new file mode 100755
index 0000000000000000000000000000000000000000..e44ae28d75810a658fbe5c0d678f04fe9633c58a
GIT binary patch
literal 7696
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
w4mTM@oO!tN%d*V59$Y0(O