Skip to content

Commit 4c56262

Browse files
feat: impl widget action url handler
1 parent fa8bb85 commit 4c56262

File tree

4 files changed

+119
-103
lines changed

4 files changed

+119
-103
lines changed

CountlyCommon.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ - (NSURLSession *)URLSession
330330
}
331331

332332
- (CGSize)getWindowSize {
333+
#if (TARGET_OS_IOS)
333334
UIWindow *window = nil;
334335

335336
if (@available(iOS 13.0, *)) {
@@ -365,6 +366,8 @@ - (CGSize)getWindowSize {
365366
}
366367

367368
return size;
369+
#endif
370+
return CGSizeZero;
368371
}
369372

370373
@end

CountlyFeedbackWidget.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66

77
#import <Foundation/Foundation.h>
88
#import "CountlyConfig.h"
9-
#if (TARGET_OS_IOS)
10-
#import <WebKit/WebKit.h>
11-
#endif
129

1310
NS_ASSUME_NONNULL_BEGIN
1411

@@ -21,11 +18,7 @@ extern NSString* const kCountlyReservedEventSurvey;
2118
extern NSString* const kCountlyReservedEventNPS;
2219
extern NSString* const kCountlyReservedEventRating;
2320

24-
#if TARGET_OS_IOS
25-
@interface CountlyFeedbackWidget : NSObject <WKNavigationDelegate>
26-
#else
2721
@interface CountlyFeedbackWidget : NSObject
28-
#endif
2922
#if (TARGET_OS_IOS)
3023

3124
@property (nonatomic, readonly) CLYFeedbackWidgetType type;

CountlyFeedbackWidget.m

Lines changed: 70 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Please visit www.count.ly for more information.
66

77
#import "CountlyCommon.h"
8+
#import "CountlyWebViewManager.h"
89
#if (TARGET_OS_IOS)
910
#import <WebKit/WebKit.h>
1011
#endif
@@ -58,108 +59,105 @@ - (void)present
5859
- (void)presentWithAppearBlock:(void(^ __nullable)(void))appearBlock andDismissBlock:(void(^ __nullable)(void))dismissBlock
5960
{
6061
CLY_LOG_I(@"%s %@ %@", __FUNCTION__, appearBlock, dismissBlock);
61-
[self presentWithCallback:^(WidgetState widgetState) {
62+
id widgetCallback = ^(WidgetState widgetState) {
6263
if(appearBlock && widgetState == WIDGET_APPEARED) {
6364
appearBlock();
6465
}
6566

6667
if(dismissBlock && widgetState == WIDGET_CLOSED) {
6768
dismissBlock();
6869
}
69-
}];
70+
};
71+
72+
if (self.widgetVersion) {
73+
[self presentWidget_new:widgetCallback];
74+
} else {
75+
[self presentWithCallback:widgetCallback];
76+
}
77+
7078
}
7179

72-
- (void)presentWithCallback:(WidgetCallback) widgetCallback;
80+
- (void)presentWidget_new:(WidgetCallback) widgetCallback;
7381
{
7482
CLY_LOG_I(@"%s %@", __FUNCTION__, widgetCallback);
7583
if (!CountlyConsentManager.sharedInstance.consentForFeedback)
7684
return;
7785

78-
BOOL isWidgetOld = ![self.widgetVersion length];
86+
CGSize size = [CountlyCommon.sharedInstance getWindowSize];
7987

80-
_webVC = CLYInternalViewController.new;
81-
_widgetCallback = widgetCallback;
88+
dispatch_async(dispatch_get_main_queue(), ^ {
89+
CGRect frame = CGRectMake(0.0, 0.0, size.width, size.height);
90+
91+
// Log the URL and the frame
92+
CLY_LOG_I(@"%s, Placement frame: %@", __FUNCTION__, NSStringFromCGRect(frame));
93+
94+
CountlyWebViewManager* webViewManager = CountlyWebViewManager.new;
95+
[webViewManager createWebViewWithURL:[self generateWidgetURL] frame:frame appearBlock:^
96+
{
97+
CLY_LOG_I(@"%s, Webview appeared", __FUNCTION__);
98+
if(widgetCallback)
99+
widgetCallback(WIDGET_APPEARED);
100+
} dismissBlock:^
101+
{
102+
CLY_LOG_I(@"%s, Webview dismissed", __FUNCTION__);
103+
if (widgetCallback)
104+
widgetCallback(WIDGET_CLOSED);
105+
[self recordReservedEventForDismissing];
106+
}];
107+
});
108+
}
109+
110+
- (void)presentWithCallback:(WidgetCallback) widgetCallback;
111+
{
112+
CLY_LOG_I(@"%s %@", __FUNCTION__, widgetCallback);
113+
if (!CountlyConsentManager.sharedInstance.consentForFeedback)
114+
return;
82115

83-
if (isWidgetOld) {
84-
_webVC.view.backgroundColor = [UIColor.blackColor colorWithAlphaComponent:0.4];
85-
_webVC.modalPresentationStyle = UIModalPresentationCustom;
86-
} else {
87-
_webVC.modalPresentationStyle = UIModalPresentationOverFullScreen;
88-
_webVC.view.bounds = UIScreen.mainScreen.bounds;
89-
_webVC.view.backgroundColor = [UIColor clearColor];
116+
if (self.widgetVersion) {
117+
[self presentWidget_new:widgetCallback];
118+
return;
90119
}
91120

121+
__block CLYInternalViewController* webVC = CLYInternalViewController.new;
122+
webVC.view.backgroundColor = [UIColor.blackColor colorWithAlphaComponent:0.4];
123+
webVC.modalPresentationStyle = UIModalPresentationCustom;
124+
92125
// Configure WKWebView with non-persistent data store
93126
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
94127
configuration.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
95-
WKWebView* webView = [[WKWebView alloc] initWithFrame:_webVC.view.bounds configuration:configuration];
96-
webView.navigationDelegate = self;
97-
98-
if (isWidgetOld) {
99-
webView.layer.shadowColor = UIColor.blackColor.CGColor;
100-
webView.layer.shadowOpacity = 0.5;
101-
webView.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
102-
webView.layer.masksToBounds = NO;
103-
}
128+
WKWebView* webView = [[WKWebView alloc] initWithFrame:webVC.view.bounds configuration:configuration];
129+
webView.layer.shadowColor = UIColor.blackColor.CGColor;
130+
webView.layer.shadowOpacity = 0.5;
131+
webView.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
132+
webView.layer.masksToBounds = NO;
104133

105134
webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
106-
[_webVC.view addSubview:webView];
107-
_webVC.webView = webView;
108-
NSURLRequest* request = [self displayRequest];
135+
[webVC.view addSubview:webView];
136+
webVC.webView = webView;
137+
NSURLRequest* request = [NSURLRequest requestWithURL:[self generateWidgetURL]];
109138
[webView loadRequest:request];
110139

111-
112-
if (isWidgetOld) {
113-
CLYButton* dismissButton = [CLYButton dismissAlertButton];
114-
dismissButton.onClick = ^(id sender)
140+
CLYButton* dismissButton = [CLYButton dismissAlertButton];
141+
dismissButton.onClick = ^(id sender)
142+
{
143+
[webVC dismissViewControllerAnimated:YES completion:^
115144
{
116-
[self closeFeedbackWidget];
117-
};
118-
[webView addSubview:dismissButton];
119-
[dismissButton positionToTopRight];
120-
}
121-
122-
[CountlyCommon.sharedInstance tryPresentingViewController:self.webVC withCompletion:^{
145+
CLY_LOG_D(@"Feedback widget dismissed. Widget ID: %@, Name: %@", self.ID, self.name);
146+
if (widgetCallback)
147+
widgetCallback(WIDGET_CLOSED);
148+
webVC = nil;
149+
}];
150+
[self recordReservedEventForDismissing];
151+
};
152+
[webView addSubview:dismissButton];
153+
[dismissButton positionToTopRight];
154+
[CountlyCommon.sharedInstance tryPresentingViewController:webVC withCompletion:^{
123155
CLY_LOG_D(@"Feedback widget presented. Widget ID: %@, Name: %@", self.ID, self.name);
124156
if(widgetCallback)
125157
widgetCallback(WIDGET_APPEARED);
126158
}];
127159
}
128160

129-
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
130-
NSString *url = navigationAction.request.URL.absoluteString;
131-
132-
if ([url hasPrefix:@"https://countly_action_event"] && [url containsString:@"cly_widget_command=1"]) {
133-
if ([url containsString:@"close=1"]) {
134-
[self closeFeedbackWidget];
135-
}
136-
137-
decisionHandler(WKNavigationActionPolicyCancel);
138-
} else {
139-
decisionHandler(WKNavigationActionPolicyAllow);
140-
}
141-
}
142-
143-
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
144-
CLY_LOG_I(@"%s Web view has started loading", __FUNCTION__);
145-
}
146-
147-
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
148-
CLY_LOG_I(@"%s Web view has finished loading", __FUNCTION__);
149-
}
150-
151-
- (void)closeFeedbackWidget{
152-
[self.webVC dismissViewControllerAnimated:YES completion:^
153-
{
154-
CLY_LOG_D(@"Feedback widget dismissed. Widget ID: %@, Name: %@", self.ID, self.name);
155-
if (self.widgetCallback)
156-
self.widgetCallback(WIDGET_CLOSED);
157-
self.webVC = nil;
158-
self.widgetCallback = nil;
159-
}];
160-
[self recordReservedEventForDismissing];
161-
}
162-
163161
- (void)getWidgetData:(void (^)(NSDictionary * __nullable widgetData, NSError * __nullable error))completionHandler
164162
{
165163
CLY_LOG_I(@"%s %@", __FUNCTION__, completionHandler);
@@ -248,7 +246,7 @@ - (NSURLRequest *)dataRequest
248246
}
249247
}
250248

251-
- (NSURLRequest *)displayRequest {
249+
- (NSURL *)generateWidgetURL {
252250
// Create the base URL with endpoint and feedback type
253251
NSMutableString *URL = [NSMutableString stringWithFormat:@"%@%@/%@",
254252
CountlyConnectionManager.sharedInstance.host,
@@ -296,8 +294,7 @@ - (NSURLRequest *)displayRequest {
296294
[URL appendFormat:@"&custom=%@", customString.cly_URLEscaped];
297295
}
298296

299-
// Create and return the NSURLRequest
300-
return [NSURLRequest requestWithURL:[NSURL URLWithString:URL]];
297+
return [NSURL URLWithString:URL];
301298
}
302299

303300

CountlyWebViewManager.m

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ - (void)createWebViewWithURL:(NSURL *)url
5555
}
5656

5757
- (void)applyTopMargin{
58-
UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
59-
60-
CGRect backgroundFrame = rootViewController.view.bounds;
6158
UIWindow *window = nil;
6259
if (@available(iOS 13.0, *)) {
6360
for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
@@ -72,7 +69,6 @@ - (void)applyTopMargin{
7269

7370
if (@available(iOS 11.0, *)) {
7471
UIEdgeInsets safeArea = window.safeAreaInsets;
75-
CGFloat screenScale = [UIScreen mainScreen].scale;
7672
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
7773
if(!UIInterfaceOrientationIsLandscape(orientation) && !self.topMarginApplied){
7874
self.topMarginApplied = YES;
@@ -123,26 +119,27 @@ - (void)configureDismissButton:(CLYButton *)dismissButton forWebView:(WKWebView
123119
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
124120
NSString *url = navigationAction.request.URL.absoluteString;
125121

126-
if ([url hasPrefix:@"https://countly_action_event"] && [url containsString:@"cly_x_action_event=1"]) {
127-
NSDictionary *queryParameters = [self parseQueryString:url];
128-
NSString *action = queryParameters[@"action"];
129-
if(action) {
130-
if ([action isEqualToString:@"event"]) {
131-
NSString *eventsJson = queryParameters[@"event"];
132-
if(eventsJson) {
133-
[self recordEventsWithJSONString:eventsJson];
134-
}
135-
} else if ([action isEqualToString:@"link"]) {
136-
NSString *link = queryParameters[@"link"];
137-
if(link) {
138-
[self openExternalLink:link];
139-
}
140-
} else if ([action isEqualToString:@"resize_me"]) {
141-
NSString *resize = queryParameters[@"resize_me"];
142-
if(resize) {
143-
[self resizeWebViewWithJSONString:resize];
144-
}
122+
if ([url containsString:@"cly_x_int=1"]) {
123+
CLY_LOG_I(@"%s Opening url [%@] in external browser", __FUNCTION__, url);
124+
[[UIApplication sharedApplication] openURL:navigationAction.request.URL options:@{} completionHandler:^(BOOL success) {
125+
if (success) {
126+
CLY_LOG_I(@"%s url [%@] opened in external browser", __FUNCTION__, url);
127+
}
128+
else {
129+
CLY_LOG_I(@"%s unable to open url [%@] in external browser", __FUNCTION__, url);
145130
}
131+
}];
132+
decisionHandler(WKNavigationActionPolicyCancel);
133+
return;
134+
}
135+
136+
if ([url hasPrefix:@"https://countly_action_event"]) {
137+
NSDictionary *queryParameters = [self parseQueryString:url];
138+
139+
if([url containsString:@"cly_x_action_event=1"]){
140+
[self contentURLAction:queryParameters];
141+
} else if([url containsString:@"cly_widget_command=1"]){
142+
[self widgetURLAction:queryParameters];
146143
}
147144

148145
if ([queryParameters[@"close"] boolValue]) {
@@ -198,6 +195,32 @@ - (void)animateView:(UIView *)view withAnimationType:(AnimationType)animationTyp
198195
}];
199196
}
200197

198+
- (void)contentURLAction:(NSDictionary *)queryParameters {
199+
NSString *action = queryParameters[@"action"];
200+
if(action) {
201+
if ([action isEqualToString:@"event"]) {
202+
NSString *eventsJson = queryParameters[@"event"];
203+
if(eventsJson) {
204+
[self recordEventsWithJSONString:eventsJson];
205+
}
206+
} else if ([action isEqualToString:@"link"]) {
207+
NSString *link = queryParameters[@"link"];
208+
if(link) {
209+
[self openExternalLink:link];
210+
}
211+
} else if ([action isEqualToString:@"resize_me"]) {
212+
NSString *resize = queryParameters[@"resize_me"];
213+
if(resize) {
214+
[self resizeWebViewWithJSONString:resize];
215+
}
216+
}
217+
}
218+
}
219+
220+
- (void)widgetURLAction:(NSDictionary *)queryParameters {
221+
// none action yet
222+
}
223+
201224
- (NSDictionary *)parseQueryString:(NSString *)url {
202225
NSMutableDictionary *queryDict = [NSMutableDictionary dictionary];
203226
NSArray *urlComponents = [url componentsSeparatedByString:@"?"];

0 commit comments

Comments
 (0)