Commit cf0ca12f authored by Wei Han's avatar Wei Han

code update

parent 82cef0dc
......@@ -8,23 +8,23 @@
/* Begin PBXBuildFile section */
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
305DBAC1A0BEA3CAED44A62D /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20C2E11D8F9CB707F74FE9BB /* libPods-example.a */; };
44006126336D83DB220E7214 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
A5EF1323781E74E1C85341EA /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28928BBADA155E55F2B1BEB6 /* libPods-example.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
059DA6A507896EEEC4EAC550 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
20C2E11D8F9CB707F74FE9BB /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
28928BBADA155E55F2B1BEB6 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
2D377E449E2382FA80A2E644 /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = "<group>"; };
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = example/AppDelegate.swift; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = "<group>"; };
A73ECAAA402D2F3FA2A038B4 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F58A99A1418BFFCA17EA3A73 /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
305DBAC1A0BEA3CAED44A62D /* libPods-example.a in Frameworks */,
A5EF1323781E74E1C85341EA /* libPods-example.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -55,7 +55,7 @@
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
20C2E11D8F9CB707F74FE9BB /* libPods-example.a */,
28928BBADA155E55F2B1BEB6 /* libPods-example.a */,
);
name = Frameworks;
sourceTree = "<group>";
......@@ -92,8 +92,8 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup;
children = (
F58A99A1418BFFCA17EA3A73 /* Pods-example.debug.xcconfig */,
A73ECAAA402D2F3FA2A038B4 /* Pods-example.release.xcconfig */,
2D377E449E2382FA80A2E644 /* Pods-example.debug.xcconfig */,
059DA6A507896EEEC4EAC550 /* Pods-example.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
......@@ -105,13 +105,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */;
buildPhases = (
448EFC1B200E72FABB8D5D7E /* [CP] Check Pods Manifest.lock */,
931514101C7193957CD78569 /* [CP] Check Pods Manifest.lock */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
18D01E9BF38825650F353B4D /* [CP] Embed Pods Frameworks */,
CCF6FD120FD8E8FD6C5FCB1F /* [CP] Copy Pods Resources */,
D389FC8348270872B1EA86E2 /* [CP] Embed Pods Frameworks */,
C5721EFBABE8191F26C12AF3 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
......@@ -183,24 +183,7 @@
shellPath = /bin/sh;
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
};
18D01E9BF38825650F353B4D /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
448EFC1B200E72FABB8D5D7E /* [CP] Check Pods Manifest.lock */ = {
931514101C7193957CD78569 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
......@@ -222,7 +205,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
CCF6FD120FD8E8FD6C5FCB1F /* [CP] Copy Pods Resources */ = {
C5721EFBABE8191F26C12AF3 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
......@@ -239,6 +222,23 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
D389FC8348270872B1EA86E2 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
......@@ -255,7 +255,7 @@
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F58A99A1418BFFCA17EA3A73 /* Pods-example.debug.xcconfig */;
baseConfigurationReference = 2D377E449E2382FA80A2E644 /* Pods-example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
......@@ -283,7 +283,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A73ECAAA402D2F3FA2A038B4 /* Pods-example.release.xcconfig */;
baseConfigurationReference = 059DA6A507896EEEC4EAC550 /* Pods-example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
......
// AppointmentTutorialViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface AppointmentTutorialViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
// AppointmentTutorialViewController.mm
#import "AppointmentTutorialViewController.h"
@interface AppointmentTutorialViewController ()
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *skipButton;
@property (nonatomic, assign) NSInteger currentPage;
@end
@implementation AppointmentTutorialViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
[self setupHeader];
}
#pragma mark - Header Setup
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerView];
self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"]
forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(onBack)
forControlEvents:UIControlEventTouchUpInside];
self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.backButton];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Add Appointment Tutorial";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont boldSystemFontOfSize:18];
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel];
self.skipButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.skipButton setTitle:@"Skip" forState:UIControlStateNormal];
self.skipButton.tintColor = UIColor.whiteColor;
self.skipButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
[self.skipButton addTarget:self
action:@selector(onSkip)
forControlEvents:UIControlEventTouchUpInside];
self.skipButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.skipButton];
// Initially visible on first screen
self.skipButton.hidden = NO;
[NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerView.heightAnchor constraintEqualToConstant:48],
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:8],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:32],
[self.backButton.heightAnchor constraintEqualToConstant:32],
[self.skipButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-16],
[self.skipButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
]];
}
#pragma mark - Helper
- (void)onBack {
[self.navigationController popViewControllerAnimated:YES];
}
-(void)onSkip {
[self completeTutorial];
}
-(void)completeTutorial {
[self onBack];
}
-(void)updateHeaderButtons {
self.skipButton.hidden = (self.currentPage == 2);
}
- (void)nextButtonTapped {
if (self.currentPage < 2) {
self.currentPage++;
// [self showPage:self.currentPage];
[self updateHeaderButtons];
} else {
[self completeTutorial];
}
}
- (void)backButtonTapped {
if (self.currentPage > 0) {
self.currentPage--;
// [self showPage:self.currentPage];
[self updateHeaderButtons];
}
}
@end
......@@ -68,10 +68,7 @@
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
_sectionViews = [NSMutableDictionary dictionary];
[APIConfig setProjectId:self.projectId];
[APIConfig setDrawingPlanId:self.drawingPlanId];
[self setupHeader];
[self setupScrollContent];
[self setupFooter];
......
......@@ -203,7 +203,8 @@
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *project = self.projects[indexPath.item];
[APIConfig setProjectId:[project[@"id"] description]];
UnitViewController *vc = [[UnitViewController alloc] init];
vc.projectId = [project[@"id"] description];
vc.projectName = project[@"project_name"];
......@@ -266,7 +267,6 @@
[self.collectionView reloadData];
if (self.projects.count == 1) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
[self collectionView:self.collectionView didSelectItemAtIndexPath:indexPath];
}
});
......
// UnitViewController.mm
#import "UnitViewController.h"
#import "DashboardViewController.h"
#import "APIConfig.h"
@interface UnitViewController () <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
......@@ -259,6 +260,7 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"🏠 Auto-opening unit: %@", unit[@"unit"]);
NSString *projectId = [unit[@"project_id"] description];
NSString *drawingPlanId = [unit[@"id"] description];
[APIConfig setDrawingPlanId:drawingPlanId];
NSLog(@"self.projectLogo: %@", self.projectLogo);
DashboardViewController *vc = [[DashboardViewController alloc] init];
......
......@@ -3,6 +3,7 @@
#import "DetailsViewController.h"
#import "APIClient.h"
#import <AVFoundation/AVFoundation.h>
#import "IssueTutorialViewController.h"
@interface PlanViewController ()
......@@ -357,16 +358,14 @@
[self.headerView addSubview:self.backButton];
// ❓ Help button
// self.helpButton = [UIButton buttonWithType:UIButtonTypeSystem];
// self.helpButton.translatesAutoresizingMaskIntoConstraints = NO;
// UIImage *helpImage = [[UIImage systemImageNamed:@"questionmark.circle"]
// imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
// [self.helpButton setImage:helpImage forState:UIControlStateNormal];
// self.helpButton.tintColor = UIColor.whiteColor;
// [self.helpButton addTarget:self
// action:@selector(showTutorial)
// forControlEvents:UIControlEventTouchUpInside];
// [self.headerView addSubview:self.helpButton];
self.helpButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.helpButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.helpButton setImage:[UIImage systemImageNamed:@"questionmark.circle"] forState:UIControlStateNormal];
self.helpButton.tintColor = UIColor.whiteColor;
[self.helpButton addTarget:self
action:@selector(showTutorial)
forControlEvents:UIControlEventTouchUpInside];
[self.headerView addSubview:self.helpButton];
// 🏷 Title label (project name)
self.titleLabel = [[UILabel alloc] init];
......@@ -805,7 +804,9 @@
message:@"This would show the tutorial (e.g. step1/step2/step3)."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
IssuesTutorialViewController *vc = [[IssuesTutorialViewController alloc] init];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
}
#pragma mark - UIScrollViewDelegate
......
// IssueTutorialViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface IssuesTutorialViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
// IssueTutorialViewController.mm
#import "IssueTutorialViewController.h"
@interface IssuesTutorialViewController ()
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *skipButton;
@property (nonatomic, assign) NSInteger currentPage;
@end
@implementation IssuesTutorialViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
[self setupHeader];
}
#pragma mark - Header Setup
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerView];
self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"]
forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(onBack)
forControlEvents:UIControlEventTouchUpInside];
self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.backButton];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Add Issue Tutorial";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont boldSystemFontOfSize:18];
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel];
self.skipButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.skipButton setTitle:@"Skip" forState:UIControlStateNormal];
self.skipButton.tintColor = UIColor.whiteColor;
self.skipButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
[self.skipButton addTarget:self
action:@selector(onSkip)
forControlEvents:UIControlEventTouchUpInside];
self.skipButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.skipButton];
// Initially visible on first screen
self.skipButton.hidden = NO;
[NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerView.heightAnchor constraintEqualToConstant:48],
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:8],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:32],
[self.backButton.heightAnchor constraintEqualToConstant:32],
[self.skipButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-16],
[self.skipButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
]];
}
#pragma mark - Helper
- (void)onBack {
[self.navigationController popViewControllerAnimated:YES];
}
-(void)onSkip {
[self completeTutorial];
}
-(void)completeTutorial {
[self onBack];
}
-(void)updateHeaderButtons {
self.skipButton.hidden = (self.currentPage == 2);
}
- (void)nextButtonTapped {
if (self.currentPage < 2) {
self.currentPage++;
// [self showPage:self.currentPage];
[self updateHeaderButtons];
} else {
[self completeTutorial];
}
}
- (void)backButtonTapped {
if (self.currentPage > 0) {
self.currentPage--;
// [self showPage:self.currentPage];
[self updateHeaderButtons];
}
}
@end
......@@ -4,7 +4,8 @@
@property (nonatomic, strong) UILabel *progressLabel;
@property (nonatomic, strong) UIProgressView *progressBar;
@property (nonatomic, strong) UILabel *messageLabel;
- (void)updateStep:(NSInteger)step total:(NSInteger)total title:(NSString *)title;
- (void)updateStep:(NSInteger)step total:(NSInteger)total message:(NSString *)message;
@end
......@@ -23,16 +23,26 @@
[card.widthAnchor constraintEqualToConstant:260]
]];
// Progress label (numeric 1/7)
self.progressLabel = [[UILabel alloc] init];
self.progressLabel.textAlignment = NSTextAlignmentCenter;
self.progressLabel.font = [UIFont systemFontOfSize:14];
self.progressLabel.font = [UIFont boldSystemFontOfSize:16];
self.progressLabel.translatesAutoresizingMaskIntoConstraints = NO;
// Message label (descriptive text)
self.messageLabel = [[UILabel alloc] init];
self.messageLabel.textAlignment = NSTextAlignmentCenter;
self.messageLabel.font = [UIFont systemFontOfSize:14];
self.messageLabel.numberOfLines = 0;
self.messageLabel.translatesAutoresizingMaskIntoConstraints = NO;
// Progress bar
self.progressBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
self.progressBar.progress = 0;
self.progressBar.translatesAutoresizingMaskIntoConstraints = NO;
UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[self.progressLabel, self.progressBar]];
// Stack view
UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[self.progressLabel, self.messageLabel, self.progressBar]];
stack.axis = UILayoutConstraintAxisVertical;
stack.spacing = 12;
stack.translatesAutoresizingMaskIntoConstraints = NO;
......@@ -48,8 +58,10 @@
return self;
}
- (void)updateStep:(NSInteger)step total:(NSInteger)total title:(NSString *)title {
self.progressLabel.text = title ?: [NSString stringWithFormat:@"%ld / %ld", step, total];
// Updated method
- (void)updateStep:(NSInteger)step total:(NSInteger)total message:(NSString *)message {
self.progressLabel.text = [NSString stringWithFormat:@"%ld / %ld", (long)step, (long)total];
self.messageLabel.text = message ?: @"";
self.progressBar.progress = total > 0 ? (float)step / (float)total : 0;
}
......
......@@ -555,6 +555,8 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
}
NSLog(@"🔁 Starting sync again");
NSString *message = [NSString stringWithFormat:
@"Currently reuploading pending items.\nPlease do not close the app."];
[self showProgressOverlay];
[[OfflineSyncManager shared]
......@@ -565,7 +567,7 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
NSLog(@"🔄 Retry %ld / %ld – %@", (long)step, (long)total, title);
[self.progressOverlay updateStep:step
total:total
title:@"Retrying History Items"];
message:message];
} completion:^(BOOL finished) {
......
......@@ -218,6 +218,7 @@ UICollectionViewDataSource
self.offlineTableView.translatesAutoresizingMaskIntoConstraints = NO;
self.offlineTableView.delegate = self;
self.offlineTableView.dataSource = self;
self.offlineTableView.allowsSelection = NO;
[self.offlineTableView registerClass:UITableViewCell.class
forCellReuseIdentifier:@"OfflineUnitCell"];
......@@ -379,8 +380,41 @@ UICollectionViewDataSource
UITableViewCell *cell;
if (tableView == self.offlineTableView) {
cell = [tableView dequeueReusableCellWithIdentifier:@"OfflineUnitCell"
forIndexPath:indexPath];
SyncUnit *unit = self.selectedUnits[indexPath.row];
cell.textLabel.text = unit.displayName;
cell.accessoryType = UITableViewCellAccessoryNone;
// Remove old button (important for reuse)
UIButton *oldButton = [cell.contentView viewWithTag:999];
[oldButton removeFromSuperview];
UIButton *deleteButton = [UIButton buttonWithType:UIButtonTypeSystem];
deleteButton.tag = 999;
deleteButton.translatesAutoresizingMaskIntoConstraints = NO;
deleteButton.tintColor = UIColor.systemRedColor;
[deleteButton setImage:[UIImage systemImageNamed:@"trash"]
forState:UIControlStateNormal];
deleteButton.tag = indexPath.row;
[deleteButton addTarget:self
action:@selector(onDeleteUnit:)
forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:deleteButton];
[NSLayoutConstraint activateConstraints:@[
[deleteButton.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-16],
[deleteButton.centerYAnchor constraintEqualToAnchor:cell.contentView.centerYAnchor],
[deleteButton.widthAnchor constraintEqualToConstant:32],
[deleteButton.heightAnchor constraintEqualToConstant:32],
]];
return cell;
} else {
cell = [tableView dequeueReusableCellWithIdentifier:@"UnitCell"
forIndexPath:indexPath];
......@@ -406,17 +440,18 @@ UICollectionViewDataSource
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SyncUnit *unit;
if (tableView == self.offlineTableView) {
unit = self.selectedUnits[indexPath.row];
} else {
unit = self.currentProject.units[indexPath.row];
}
[self toggleUnit:unit];
[tableView reloadData];
SyncUnit *unit;
if (tableView == self.offlineTableView) {
return;
} else {
unit = self.currentProject.units[indexPath.row];
}
[self toggleUnit:unit];
[tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
#pragma mark - UICollectionViewDataSource
......@@ -784,4 +819,22 @@ didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
- (void)onDeleteUnit:(UIButton *)sender {
NSInteger index = sender.tag;
if (index < 0 || index >= self.selectedUnits.count) return;
SyncUnit *unit = self.selectedUnits[index];
unit.selected = NO;
[self.selectedUnits removeObjectAtIndex:index];
self.selectedUnitCount = self.selectedUnits.count;
self.hasChanges = YES;
[self saveSelectedUnits];
[self.offlineTableView reloadData];
}
@end
......@@ -8,6 +8,7 @@
#import "LocalStorage.h"
#import "OfflineSyncManager.h"
#import "SyncProgressOverlay.h"
#import "APIConfig.h"
typedef NS_ENUM(NSInteger, SyncCardType) {
SyncCardTypeOfflineUnits = 1,
......@@ -15,6 +16,7 @@ typedef NS_ENUM(NSInteger, SyncCardType) {
SyncCardTypeHistory
};
static NSString * const kSyncSelectedUnitsKey = @"SYNC_SELECTEDUNITS";
static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
@interface SyncScreenViewController ()
......@@ -533,13 +535,16 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
// 1️⃣ Manual pending sync
if (self.shouldSubmitPending) {
NSLog(@"Syncing Pending Items");
NSString *message = [NSString stringWithFormat:
@"Currently uploading pending items.\nPlease do not close the app."];
[[OfflineSyncManager shared]
processNextItemWithProgress:^(NSInteger step, NSInteger total, NSString *title) {
NSLog(@"📊 progress %ld/%ld %@", (long)step, (long)total, title);
[self.progressOverlay updateStep:step
total:total
title:title];
message:message];
} completion:^(BOOL finished) {
NSLog(@"🎉 Offline sync COMPLETED finished=%d", finished);
......@@ -556,19 +561,91 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
// 2️⃣ Unit + image download sync
if (self.shouldDownloadUnits) {
NSLog(@"Downloading Units");
[[OfflineSyncManager shared]
startDownloadUnitImagesWithProgress:^(NSInteger completed, NSInteger total) {
[self.progressOverlay updateStep:completed
total:total
title:@"Downloading Units"];
} completion:^(BOOL success) {
[weakSelf finishSyncWithSuccess:success];
}];
__weak typeof(self) weakSelf = self;
NSArray *selectedUnits = [LocalStorage loadArrayForKey:kSyncSelectedUnitsKey] ?: @[];
if (selectedUnits.count == 0) {
NSLog(@"⚠️ No units found in local storage for %@", kSyncSelectedUnitsKey);
[self finishSyncWithSuccess:YES];
return;
}
NSString *originalProjectId = [APIConfig projectId];
NSString *originalDrawingPlanId = [APIConfig drawingPlanId];
NSLog(@"📦 [LocalStorage] %@ = %@", kSyncSelectedUnitsKey, selectedUnits);
// 1️⃣ Calculate total tasks (sum of image counts in all units)
__block NSInteger totalTasks = 0;
for (NSDictionary *unit in selectedUnits) {
NSInteger imageCount = [unit[@"image_count"] integerValue] ?: 1; // fallback to 1 if missing
totalTasks += imageCount;
}
__block NSInteger completedTasks = 0;
__block NSInteger currentUnitIndex = 0;
__block void (^runNextUnit)(void);
runNextUnit = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
if (currentUnitIndex >= selectedUnits.count) {
// Restore original APIConfig
[APIConfig setProjectId:originalProjectId];
[APIConfig setDrawingPlanId:originalDrawingPlanId];
NSLog(@"✅ All units downloaded");
[strongSelf finishSyncWithSuccess:YES];
return;
}
NSDictionary *unit = selectedUnits[currentUnitIndex];
currentUnitIndex++;
NSString *projectId = unit[@"project"][@"id"];
NSString *planId = unit[@"plan_id"];
NSString *sortName = unit[@"sort_name"] ?: @"(unknown)";
if (!projectId || !planId) {
NSLog(@"⚠️ Missing project_id or plan_id at index %ld: %@", (long)currentUnitIndex, unit);
runNextUnit(); // skip invalid
return;
}
[APIConfig setProjectId:projectId];
[APIConfig setDrawingPlanId:planId];
NSLog(@"➡️ Downloading unit %ld / %lu: projectId=%@, drawingPlanId=%@",
(long)currentUnitIndex, (unsigned long)selectedUnits.count,
[APIConfig projectId], [APIConfig drawingPlanId]);
NSString *message = [NSString stringWithFormat:
@"Currently downloading unit \"%@\".\nPlease do not close the app.", sortName];
[[OfflineSyncManager shared] startDownloadUnitImagesWithProgress:^(NSInteger completed, NSInteger total) {
__strong typeof(weakSelf) strongSelf2 = weakSelf;
if (!strongSelf2) return;
// Update overlay with progress number and message
[strongSelf2.progressOverlay updateStep:completed
total:total
message:message];
} completion:^(BOOL success) {
__strong typeof(weakSelf) strongSelf2 = weakSelf;
if (!strongSelf2) return;
if (!success) {
NSLog(@"⚠️ Unit download failed: projectId=%@, drawingPlanId=%@", projectId, planId);
}
// Continue to next unit
runNextUnit();
}];
};
// Start first unit
runNextUnit();
}
}
......
......@@ -542,8 +542,6 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
return;
}
NSString *projectId = @"8";
NSString *drawingPlanId = @"135";
NSArray *selectedUnits = [LocalStorage loadArrayForKey:kSelectedOfflineUnitsKey] ?: @[];
// ------------------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment