Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
weihan-plugin
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Wei Han
weihan-plugin
Commits
1323f31a
Commit
1323f31a
authored
Jan 28, 2026
by
Wei Han
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fixed drawer logic
parent
7735252a
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
410 additions
and
69 deletions
+410
-69
AnnounceViewController.mm
...QmsPluginFramework/Announcement/AnnounceViewController.mm
+1
-1
AppointmentDetailsViewController.h
...nFramework/Appointment/AppointmentDetailsViewController.h
+0
-2
DashboardViewController.mm
...k/QmsPluginFramework/Dashboard/DashboardViewController.mm
+3
-2
DrawerController.mm
...inFramework/QmsPluginFramework/Drawer/DrawerController.mm
+12
-3
DrawerViewController.h
...ramework/QmsPluginFramework/Drawer/DrawerViewController.h
+3
-0
DrawerViewController.mm
...amework/QmsPluginFramework/Drawer/DrawerViewController.mm
+117
-34
HelpViewController.mm
...Framework/QmsPluginFramework/Drawer/HelpViewController.mm
+1
-1
NotificationViewController.h
...rk/QmsPluginFramework/Drawer/NotificationViewController.h
+3
-5
NotificationViewController.mm
...k/QmsPluginFramework/Drawer/NotificationViewController.mm
+167
-20
ProfileViewController.mm
...mework/QmsPluginFramework/Drawer/ProfileViewController.mm
+0
-0
SyncScreenViewController.mm
...ework/QmsPluginFramework/Sync/SyncScreenViewController.mm
+1
-1
APIClient.h
...ginFramework/QmsPluginFramework/Utilities/API/APIClient.h
+6
-0
APIClient.mm
...inFramework/QmsPluginFramework/Utilities/API/APIClient.mm
+96
-0
No files found.
framework/QmsPluginFramework/QmsPluginFramework/Announcement/AnnounceViewController.mm
View file @
1323f31a
...
...
@@ -330,7 +330,7 @@
#pragma mark - helpers
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil
];
[self.navigationController popViewControllerAnimated:YES
];
}
@end
...
...
framework/QmsPluginFramework/QmsPluginFramework/Appointment/AppointmentDetailsViewController.h
View file @
1323f31a
...
...
@@ -4,8 +4,6 @@
@interface
AppointmentDetailsViewController
:
UIViewController
@property
(
nonatomic
,
strong
)
NSString
*
appointmentID
;
@property
(
nonatomic
,
strong
)
NSDictionary
*
appointmentData
;
@end
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardViewController.mm
View file @
1323f31a
...
...
@@ -72,6 +72,8 @@
NSLog(@"dashboard.drawerController = %@", self.drawerController);
//[[OfflineSyncManager shared] clearOfflineQueue];
[[NSNotificationCenter defaultCenter] postNotificationName:@"ShowHiddenButtonNotification" object:@(YES)];
}
- (void)viewWillAppear:(BOOL)animated {
...
...
@@ -102,7 +104,7 @@
}];
dispatch_group_enter(group);
[APIClient requestAnnouncement:^(BOOL success, NSDictionary *data, NSError *error) {
[APIClient request
Client
Announcement:^(BOOL success, NSDictionary *data, NSError *error) {
if (!error) announcementData = data;
dispatch_group_leave(group);
}];
...
...
@@ -1431,7 +1433,6 @@
[self presentViewController:vc animated:YES completion:nil];
}
- (void)handleIssueTap:(UITapGestureRecognizer *)gesture {
UIView *tappedCard = gesture.view;
NSDictionary *issueData = objc_getAssociatedObject(tappedCard, "issueData");
...
...
framework/QmsPluginFramework/QmsPluginFramework/Drawer/DrawerController.mm
View file @
1323f31a
// DrawerController.m
#import "DrawerController.h"
#import "ProjectViewController.h"
#import "DrawerViewController.h"
NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotification";
@interface DrawerController () <UIGestureRecognizerDelegate>
@property (nonatomic, strong) ProjectViewController *mainVC;
@property (nonatomic, strong) UIViewController *mainVC;
@property (nonatomic, strong) UINavigationController *mainNav;
@property (nonatomic, strong) UIViewController *drawerVC;
@property (nonatomic, strong) UIView *overlayView;
@property (nonatomic, assign) BOOL drawerOpen;
...
...
@@ -27,12 +29,14 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica
if (!self) return nil;
// 1️⃣ Initialize DashboardViewController with props
_main
VC = [[ProjectViewController alloc] initWithClientID:clientID
ProjectViewController *root
VC = [[ProjectViewController alloc] initWithClientID:clientID
clientCode:clientCode
userToken:user_token];
// 2️⃣ Set back-reference to drawerController
_mainVC.drawerController = self;
rootVC.drawerController = self;
_mainNav = [[UINavigationController alloc] initWithRootViewController:rootVC];
_mainVC = _mainNav;
// 3️⃣ Save props locally
_ClientID = [clientID copy];
...
...
@@ -41,6 +45,11 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica
// 4️⃣ Set drawer VC and drawer width
_drawerVC = drawer;
if ([_drawerVC isKindOfClass:[DrawerViewController class]]) {
[(DrawerViewController *)_drawerVC attachDrawerController:self];
}
_drawerWidth = MIN([UIScreen mainScreen].bounds.size.width * 0.78, 360);
return self;
...
...
framework/QmsPluginFramework/QmsPluginFramework/Drawer/DrawerViewController.h
View file @
1323f31a
// DrawerViewController.h
#import <UIKit/UIKit.h>
#import "DrawerController.h"
NS_ASSUME_NONNULL_BEGIN
...
...
@@ -10,7 +11,9 @@ NS_ASSUME_NONNULL_BEGIN
@property
(
nonatomic
,
strong
)
NSString
*
profileEmail
;
@property
(
nonatomic
,
strong
)
NSURL
*
profileImageURL
;
@property
(
nonatomic
,
strong
)
NSArray
<
NSDictionary
*>
*
menuItems
;
// array of { @"title":..., @"action":... }
@property
(
nonatomic
,
weak
,
readonly
)
DrawerController
*
drawerController
;
-
(
void
)
attachDrawerController
:(
DrawerController
*
)
drawerController
;
@end
NS_ASSUME_NONNULL_END
framework/QmsPluginFramework/QmsPluginFramework/Drawer/DrawerViewController.mm
View file @
1323f31a
This diff is collapsed.
Click to expand it.
framework/QmsPluginFramework/QmsPluginFramework/Drawer/HelpViewController.mm
View file @
1323f31a
...
...
@@ -190,7 +190,7 @@
#pragma mark - Back
- (void)onBack {
[self
dismissViewControllerAnimated:YES completion:nil
];
[self
.navigationController popViewControllerAnimated:YES
];
}
@end
framework/QmsPluginFramework/QmsPluginFramework/Drawer/NotificationViewController.h
View file @
1323f31a
// NotificationViewController.h
#import "DrawerController.h"
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface
NotificationViewController
:
UIViewController
@property
(
nonatomic
,
weak
)
DrawerController
*
drawerController
;
@end
NS_ASSUME_NONNULL_END
framework/QmsPluginFramework/QmsPluginFramework/Drawer/NotificationViewController.mm
View file @
1323f31a
...
...
@@ -3,6 +3,12 @@
#import "NotificationViewController.h"
#import "APIClient.h"
#import "ImageCacheHelper.h"
#import <objc/runtime.h>
#import "APIConfig.h"
#import "AppointmentDetailsViewController.h"
#import "DashboardViewController.h"
static char kNotificationKey;
@interface NotificationViewController ()
// Header (existing)
...
...
@@ -15,6 +21,7 @@
@property (nonatomic, strong) UIStackView *stackView;
@property (nonatomic, strong) NSArray *notifications;
@property (nonatomic, strong) NSArray *projectList;
@property (nonatomic, strong) NSString *oldUnitId;
@end
@implementation NotificationViewController
...
...
@@ -22,6 +29,9 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
if (self.oldUnitId) {
[APIConfig setDrawingPlanId:self.oldUnitId];
}
[self requestProjectList];
[self setupHeader];
...
...
@@ -157,7 +167,6 @@
[card addGestureRecognizer:tap];
card.userInteractionEnabled = YES;
UIImageView *icon = [[UIImageView alloc] init];
icon.translatesAutoresizingMaskIntoConstraints = NO;
icon.contentMode = UIViewContentModeScaleAspectFit;
...
...
@@ -172,15 +181,12 @@
icon.image = image;
UIImage *img = image;
icon.image = img;
// Remove old aspect ratio if exists
for (NSLayoutConstraint *c in icon.constraints) {
if (c.firstAttribute == NSLayoutAttributeHeight &&
c.secondAttribute == NSLayoutAttributeWidth) {
[icon removeConstraint:c];
}
}
CGFloat ratio = img.size.height / img.size.width;
NSLayoutConstraint *aspect =
...
...
@@ -265,6 +271,11 @@
BOOL unread = [item[@"read_status"] isEqualToString:@"0"];
unreadDot.hidden = !unread;
objc_setAssociatedObject(card,
&kNotificationKey,
item,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return card;
}
...
...
@@ -291,7 +302,7 @@
#pragma mark - helpers
- (void)onBack {
[self
dismissViewControllerAnimated:YES completion:nil
];
[self
.navigationController popViewControllerAnimated:YES
];
}
- (void)requestNotification {
...
...
@@ -399,22 +410,158 @@
}
- (void)onNotificationTap:(UITapGestureRecognizer *)tap {
NSLog(@"notification tapped");
UIView *card = tap.view;
// NSDictionary *item =
// objc_getAssociatedObject(card, @"notification");
//
// if (!item) return;
//
// NSString *type = item[@"type"];
//
// if ([type isEqualToString:@"appointment_reminder"]) {
// [self openAppointment:item];
// } else if ([type isEqualToString:@"rectification"]) {
// [self openRectification:item];
// } else {
// [self openGenericNotification:item];
// }
NSDictionary *item =
objc_getAssociatedObject(card, &kNotificationKey);
if (!item) return;
NSString *notificationType = item[@"type"];
NSDictionary *strData = item;
self.oldUnitId = [APIConfig drawingPlanId];
NSString *unitID = @"";
if (strData[@"draw_plan_id"]) {
unitID = strData[@"draw_plan_id"];
[APIConfig setDrawingPlanId:unitID];
}
// ---- PAYMENT REMINDER ----
if ([notificationType isEqualToString:@"payment_reminder"]) {
NSLog(@"➡️ payment_reminder tapped");
return;
}
// ---- APPOINTMENT ----
if ([notificationType isEqualToString:@"appointment_reminder"] ||
[notificationType isEqualToString:@"unattend_appt_reminder"]) {
NSString *appointmentID = @"";
if (strData[@"appointment_id"]) {
appointmentID = strData[@"appointment_id"];
}
[self openAppointmentWithUnit:unitID appointmentID:appointmentID];
return;
}
// ---- CLEARANCE LETTER ----
if ([notificationType isEqualToString:@"clearance_letter"]) {
NSString *projectID = @"";
if (strData[@"project_id"]) {
projectID = strData[@"project_id"];
}
[self openClearanceWithProject:projectID unitID:unitID];
return;
}
// ---- DEFAULT: ISSUE FLOW ----
NSString *issueID = @"";
if (strData[@"issue_id"]) {
issueID = strData[@"issue_id"];
}
[self openIssueWithUnit:unitID issueID:issueID];
}
#pragma mark - Navigation (ported from RN)
- (void)openAppointmentWithUnit:(NSString *)unitID appointmentID:(NSString *)appointmentID {
NSLog(@"➡️ Open Appointment | unit=%@ appt=%@", unitID, appointmentID);
[APIClient requestDashboardInfo:^(BOOL success, NSDictionary * _Nullable data, NSError * _Nonnull error) {
if (!success || !data) {
return;
}
id appointment = data[@"Data"][@"appointment"];
if (!appointment ||
appointment == [NSNull null] ||
![appointment isKindOfClass:[NSArray class]] ||
appointmentID.length == 0) {
return;
}
NSDictionary *matchedAppointment = nil;
for (NSDictionary *appt in appointment) {
if (![appt isKindOfClass:[NSDictionary class]]) {
continue;
}
NSString *apptID = appt[@"id"];
if ([apptID isKindOfClass:[NSString class]] &&
[apptID isEqualToString:appointmentID]) {
matchedAppointment = appt;
break;
}
}
if (!matchedAppointment) {
// No match found
return;
}
// 👉 Navigate to Appointment Details
dispatch_async(dispatch_get_main_queue(), ^{
AppointmentDetailsViewController *vc = [[AppointmentDetailsViewController alloc] init];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
vc.appointmentData = matchedAppointment;
[self presentViewController:vc animated:YES completion:nil];
});
}];
}
- (void)openClearanceWithProject:(NSString *)projectID unitID:(NSString *)unitID {
NSLog(@"➡️ Open Clearance | project=%@ unit=%@", projectID, unitID);
NSArray *projects = self.projectList;
if (!projects || projects.count == 0) {
NSLog(@"❌ Project list empty");
return;
}
for (NSDictionary *project in projects) {
if (![project isKindOfClass:[NSDictionary class]]) continue;
NSString *projID = [NSString stringWithFormat:@"%@", project[@"id"]];
if (![projID isEqualToString:projectID]) continue;
NSArray *units = project[@"unit"];
if (![units isKindOfClass:[NSArray class]]) break;
for (NSDictionary *unit in units) {
if (![unit isKindOfClass:[NSDictionary class]]) continue;
NSString *uID = [NSString stringWithFormat:@"%@", unit[@"id"]];
if (![uID isEqualToString:unitID]) continue;
NSLog(@"✅ Found project %@ and unit %@", projectID, unitID);
dispatch_async(dispatch_get_main_queue(), ^{
DashboardViewController *vc = [[DashboardViewController alloc] init];
vc.projectCode = unit[@"name"] ?: @"";
vc.projectName = project[@"project_name"] ?: @"";
vc.projectId = projectID ?: @"";
vc.drawingPlanId = unitID ?: @"";
vc.projectLogo = project[@"logo"] ?: @"";
vc.lotId = unit[@"lot_id"] ?: @"";
vc.drawerController = self.drawerController;
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self.navigationController pushViewController:vc animated:YES];
});
return;
}
}
NSLog(@"❌ No matching project/unit found");
}
- (void)openIssueWithUnit:(NSString *)unitID issueID:(NSString *)issueID {
NSLog(@"➡️ Open Issue | unit=%@ issue=%@", unitID, issueID);
// TODO: push IssueDetailViewController
}
@end
framework/QmsPluginFramework/QmsPluginFramework/Drawer/ProfileViewController.mm
View file @
1323f31a
This diff is collapsed.
Click to expand it.
framework/QmsPluginFramework/QmsPluginFramework/Sync/SyncScreenViewController.mm
View file @
1323f31a
...
...
@@ -447,7 +447,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
#pragma mark - Helpers
- (void)onBack {
[self
dismissViewControllerAnimated:YES completion:nil
];
[self
.navigationController popViewControllerAnimated:YES
];
}
- (void)addTapToCard:(UIView *)card action:(SEL)selector {
...
...
framework/QmsPluginFramework/QmsPluginFramework/Utilities/API/APIClient.h
View file @
1323f31a
...
...
@@ -100,6 +100,12 @@ NS_ASSUME_NONNULL_BEGIN
+
(
void
)
requestAnnouncement
:(
void
(
^
)(
BOOL
success
,
NSDictionary
*
_Nullable
response
,
NSError
*
_Nullable
error
))
completion
;
+
(
void
)
uploadProfileImage
:(
NSData
*
)
imageData
name
:(
NSString
*
)
name
completion
:(
void
(
^
)(
BOOL
success
,
NSDictionary
*
_Nullable
response
,
NSError
*
_Nullable
error
))
completion
;
@end
NS_ASSUME_NONNULL_END
framework/QmsPluginFramework/QmsPluginFramework/Utilities/API/APIClient.mm
View file @
1323f31a
...
...
@@ -2320,6 +2320,101 @@
[task resume];
}
+ (void)uploadProfileImage:(NSData *)imageData
name:(NSString *)name
completion:(void(^)(BOOL success,
NSDictionary * _Nullable response,
NSError * _Nullable error))completion {
NSURL *url = [APIConfig urlWithPath:@"/framework/owner/auth/editUser"];
NSString *boundary = @"----ProfileImageBoundary";
NSMutableData *body = [NSMutableData data];
void (^appendString)(NSString *) = ^(NSString *string) {
[body appendData:[string dataUsingEncoding:NSUTF8StringEncoding]];
};
// name
appendString([NSString stringWithFormat:@"--%@\r\n", boundary]);
appendString(@"Content-Disposition: form-data; name=\"data[name]\"\r\n\r\n");
appendString(name ?: @"");
appendString(@"\r\n");
// language
appendString([NSString stringWithFormat:@"--%@\r\n", boundary]);
appendString(@"Content-Disposition: form-data; name=\"data[language]\"\r\n\r\n");
appendString(@"2");
appendString(@"\r\n");
// os
appendString([NSString stringWithFormat:@"--%@\r\n", boundary]);
appendString(@"Content-Disposition: form-data; name=\"data[os]\"\r\n\r\n");
appendString([APIConfig os]);
appendString(@"\r\n");
// information
appendString([NSString stringWithFormat:@"--%@\r\n", boundary]);
appendString(@"Content-Disposition: form-data; name=\"data[information]\"\r\n\r\n");
NSDictionary *info = [APIConfig deviceInfo];
NSData *jsonData =
[NSJSONSerialization dataWithJSONObject:info options:0 error:nil];
NSString *infoString =
[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
appendString(infoString ?: @"");
appendString(@"\r\n");
// avatar
if (imageData) {
appendString([NSString stringWithFormat:@"--%@\r\n", boundary]);
appendString(@"Content-Disposition: form-data; name=\"data[avatar]\"; filename=\"profile.jpg\"\r\n");
appendString(@"Content-Type: image/jpeg\r\n\r\n");
[body appendData:imageData];
appendString(@"\r\n");
}
appendString([NSString stringWithFormat:@"--%@--\r\n", boundary]);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]
forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
request.HTTPBody = body;
NSLog(@"🌍 upload profile image: %@", url.absoluteString);
NSURLSessionDataTask *task =
[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(NO, nil, error);
});
return;
}
NSError *parseError;
NSDictionary *json =
[NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
dispatch_async(dispatch_get_main_queue(), ^{
if (parseError) {
completion(NO, nil, parseError);
} else {
completion(YES, json, nil);
}
});
}];
[task resume];
}
+ (void)requestNotification:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
if ([APIConfig handleOfflineForAPI:@"Notification" completion:completion]) {
...
...
@@ -2395,6 +2490,7 @@
}
// NSURL *url = [APIConfig urlWithPath:@"/announcement"];
//place holder, need to change back eventually
NSURL *url = [NSURL URLWithString:@"https://kitadev.commudesk.com/api/announcement?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment