Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CTNotificationContent.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
525715EE1E67B8C0000E455B /* CTNotificationContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 525715EC1E67B8C0000E455B /* CTNotificationContent.h */; settings = {ATTRIBUTES = (Public, ); }; };
525715F71E67B990000E455B /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 525715F51E67B990000E455B /* UserNotifications.framework */; };
525715F81E67B990000E455B /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 525715F61E67B990000E455B /* UserNotificationsUI.framework */; };
58614E962F98B4BA000D1C8A /* CTTimerBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58614E942F98B4BA000D1C8A /* CTTimerBoxView.swift */; };
A1B2C3D4E5F60001A1B2C3D4 /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = A1B2C3D4E5F60002A1B2C3D4 /* SDWebImage */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -91,6 +92,7 @@
525715EC1E67B8C0000E455B /* CTNotificationContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTNotificationContent.h; sourceTree = "<group>"; };
525715F51E67B990000E455B /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
525715F61E67B990000E455B /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
58614E942F98B4BA000D1C8A /* CTTimerBoxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CTTimerBoxView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -222,6 +224,7 @@
32DFD82F28BCCCBB00E72588 /* Timer */ = {
isa = PBXGroup;
children = (
58614E952F98B4BA000D1C8A /* View */,
32DFD83028BCCCBB00E72588 /* Controller */,
32DFD83228BCCCBB00E72588 /* Model */,
);
Expand Down Expand Up @@ -426,6 +429,14 @@
name = Frameworks;
sourceTree = "<group>";
};
58614E952F98B4BA000D1C8A /* View */ = {
isa = PBXGroup;
children = (
58614E942F98B4BA000D1C8A /* CTTimerBoxView.swift */,
);
path = View;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -548,6 +559,7 @@
3270BFDA28E4D80E003528ED /* CTProductDisplayVerticalViewController.swift in Sources */,
32DFD84628BCCCBB00E72588 /* CTZeroBezelController.swift in Sources */,
488F31C12817012500AE3AC8 /* CTNotificationViewController.m in Sources */,
58614E962F98B4BA000D1C8A /* CTTimerBoxView.swift in Sources */,
32DFD84B28BCCCBB00E72588 /* CTCarouselController.swift in Sources */,
3270BFF128E4D89C003528ED /* RatingProperties.swift in Sources */,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import UserNotificationsUI
import SDWebImage

@objc public class CTTimerTemplateController: BaseCTNotificationContentViewController {

var contentView: UIView = UIView(frame: .zero)
@objc public var data: String = ""
@objc public var templateCaption: String = ""
Expand Down Expand Up @@ -54,16 +55,51 @@ import SDWebImage
subcaptionLabel.translatesAutoresizingMaskIntoConstraints = false
return subcaptionLabel
}()
private var timerLabel: UILabel = {
let timerLabel = UILabel()
timerLabel.textAlignment = .center
timerLabel.adjustsFontSizeToFitWidth = false
timerLabel.font = UIFont.boldSystemFont(ofSize: 18.0)
timerLabel.textColor = UIColor.black
timerLabel.translatesAutoresizingMaskIntoConstraints = false
return timerLabel
}()

private var timerBoxView: CTTimerBoxView?
private var timerLabel: UILabel?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need timerLabel if we are using timerBoxView?

private var captionTrailingConstraint: NSLayoutConstraint?
private var subcaptionTrailingConstraint: NSLayoutConstraint?

private enum CTTimerStyleCapability {
static var supportsRichTimerBox: Bool {
if #available(iOS 13.0, *) { return true }
return false
}
}

private func setTimerText(_ text: String) {
timerBoxView?.timerLabel.text = text
timerLabel?.text = text
}

private func hideTimerDisplay() {
timerBoxView?.isHidden = true
timerLabel?.isHidden = true
}

private func showTimerForExpandedState() {
timerBoxView?.isHidden = false
timerLabel?.isHidden = false
captionTrailingConstraint?.constant = -Constraints.kTimerLabelWidth
subcaptionTrailingConstraint?.constant = -Constraints.kTimerLabelWidth
}

private func isDarkMode() -> Bool {
if #available(iOS 12.0, *) {
return traitCollection.userInterfaceStyle == .dark
}
return false
}

private func hasRichTimerStyling(_ props: TimerTemplateProperties) -> Bool {
return props.pt_chrono_bg_clr != nil
|| props.pt_chrono_grad_clr1 != nil || props.pt_chrono_grad_clr2 != nil
|| props.pt_chrono_grad_dir != nil
|| props.pt_chrono_style != nil
|| props.pt_chrono_border_clr != nil
|| props.pt_chrono_border_width != nil || props.pt_chrono_border_radius != nil
}

@objc public override func viewDidLoad() {
super.viewDidLoad()

Expand Down Expand Up @@ -95,7 +131,23 @@ import SDWebImage
contentView.addSubview(imageView)
contentView.addSubview(captionLabel)
contentView.addSubview(subcaptionLabel)
contentView.addSubview(timerLabel)

if CTTimerStyleCapability.supportsRichTimerBox,
let props = jsonContent, hasRichTimerStyling(props) {
let box = CTTimerBoxView()
timerBoxView = box
contentView.addSubview(box)
} else {
let label = UILabel()
label.textAlignment = .center
label.adjustsFontSizeToFitWidth = false
label.font = UIFont.boldSystemFont(ofSize: 18.0)
label.textColor = UIColor.black
label.translatesAutoresizingMaskIntoConstraints = false
timerLabel = label
contentView.addSubview(label)
}
hideTimerDisplay()

captionLabel.setHTMLText(templateCaption)
subcaptionLabel.setHTMLText(templateSubcaption)
Expand Down Expand Up @@ -160,7 +212,11 @@ import SDWebImage
}

updateInterfaceColors()


if let box = timerBoxView {
box.applyStyle(properties: jsonContent, isDarkMode: isDarkMode())
}

// Handle image loading
// Load image only if timer is not ended.
if thresholdSeconds > 0 {
Expand Down Expand Up @@ -206,27 +262,36 @@ import SDWebImage
imageView.backgroundColor = UIColor(hex: isDarkMode ? bgColorDark : bgColor)
captionLabel.textColor = UIColor(hex: isDarkMode ? captionColorDark : captionColor)
subcaptionLabel.textColor = UIColor(hex: isDarkMode ? subcaptionColorDark : subcaptionColor)
timerLabel.textColor = UIColor(hex: isDarkMode ? timerColorDark : timerColor)
timerLabel?.textColor = UIColor(hex: isDarkMode ? timerColorDark : timerColor)

if let box = timerBoxView, let props = jsonContent {
box.applyStyle(properties: props, isDarkMode: isDarkMode)
}
}

func setupConstraints() {
let activeTimerView: UIView = timerBoxView ?? timerLabel!
let captionTrailing = captionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding)
let subcaptionTrailing = subcaptionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding)
captionTrailingConstraint = captionTrailing
subcaptionTrailingConstraint = subcaptionTrailing

NSLayoutConstraint.activate([
captionLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -(CTUtiltiy.getCaptionHeight() - Constraints.kCaptionTopPadding)),
captionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constraints.kCaptionLeftPadding),
captionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kTimerLabelWidth),
captionTrailing,
captionLabel.heightAnchor.constraint(equalToConstant: Constraints.kCaptionHeight),

subcaptionLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -(Constraints.kSubCaptionHeight + Constraints.kSubCaptionTopPadding)),
subcaptionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constraints.kCaptionLeftPadding),
subcaptionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kTimerLabelWidth),
subcaptionTrailing,
subcaptionLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding),
subcaptionLabel.heightAnchor.constraint(equalToConstant: Constraints.kSubCaptionHeight),

timerLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -CTUtiltiy.getCaptionHeight()),
timerLabel.leadingAnchor.constraint(equalTo: captionLabel.trailingAnchor, constant: Constraints.kCaptionLeftPadding),
timerLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding),
timerLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding),
timerLabel.heightAnchor.constraint(equalToConstant: CTUtiltiy.getCaptionHeight())

activeTimerView.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -(CTUtiltiy.getCaptionHeight() - Constraints.kCaptionTopPadding)),
activeTimerView.leadingAnchor.constraint(equalTo: captionLabel.trailingAnchor, constant: Constraints.kCaptionLeftPadding),
activeTimerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding),
activeTimerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding)
])
}

Expand All @@ -236,15 +301,15 @@ import SDWebImage
let sec = thresholdSeconds % 60
if thresholdSeconds > 0 {
if hr < 1 {
self.timerLabel.text = String(format: "%02i:%02i", min, sec)
setTimerText(String(format: "%02i:%02i", min, sec))
}
else {
self.timerLabel.text = String(format: "%02i:%02i:%02i", hr, min, sec)
setTimerText(String(format: "%02i:%02i:%02i", hr, min, sec))
}
thresholdSeconds -= 1
} else {
timer.invalidate()
self.timerLabel.isHidden = true
hideTimerDisplay()
updateViewForExpiredTime()
}
}
Expand Down Expand Up @@ -298,6 +363,10 @@ import SDWebImage
view.frame = frame
contentView.frame = frame
preferredContentSize = CGSize(width: viewWidth, height: viewHeight)

if thresholdSeconds > 0 {
showTimerForExpandedState()
}
}

func activateImageViewContraints() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,31 @@ struct TimerTemplateProperties: Decodable {
let pt_big_img_alt_alt_text: String?
let pt_gif: String?
let pt_gif_alt: String?


// Timer box background
let pt_chrono_bg_clr: String?
let pt_chrono_bg_clr_dark: String?
let pt_chrono_grad_clr1: String?
let pt_chrono_grad_clr2: String?
let pt_chrono_grad_dir: String?

// Timer box style ("solid" | "gradient_linear" | "gradient_radial", default "solid")
let pt_chrono_style: String?

// Timer box border
let pt_chrono_border_clr: String?
let pt_chrono_border_clr_dark: String?
let pt_chrono_border_width: FlexibleDouble?
let pt_chrono_border_radius: FlexibleDouble?

enum CodingKeys: String, CodingKey {
case pt_title, pt_title_alt, pt_msg, pt_msg_alt, pt_msg_summary, pt_dl1, pt_big_img, pt_big_img_alt, pt_bg, pt_bg_dark, pt_chrono_title_clr, pt_chrono_title_clr_dark, pt_timer_threshold, pt_timer_end, pt_title_clr, pt_title_clr_dark, pt_msg_clr, pt_msg_clr_dark, pt_big_img_alt_text, pt_big_img_alt_alt_text, pt_gif, pt_gif_alt
case pt_chrono_bg_clr, pt_chrono_bg_clr_dark
case pt_chrono_grad_clr1, pt_chrono_grad_clr2
case pt_chrono_grad_dir
case pt_chrono_style
case pt_chrono_border_clr, pt_chrono_border_clr_dark
case pt_chrono_border_width, pt_chrono_border_radius
}

init(from decoder: Decoder) throws {
Expand All @@ -58,7 +80,18 @@ struct TimerTemplateProperties: Decodable {
pt_big_img_alt_alt_text = try container.decodeIfPresent(String.self, forKey: .pt_big_img_alt_alt_text)
pt_gif = try container.decodeIfPresent(String.self, forKey: .pt_gif)
pt_gif_alt = try container.decodeIfPresent(String.self, forKey: .pt_gif_alt)


pt_chrono_bg_clr = try container.decodeIfPresent(String.self, forKey: .pt_chrono_bg_clr)
pt_chrono_bg_clr_dark = try container.decodeIfPresent(String.self, forKey: .pt_chrono_bg_clr_dark)
pt_chrono_grad_clr1 = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_clr1)
pt_chrono_grad_clr2 = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_clr2)
pt_chrono_grad_dir = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_dir)
pt_chrono_style = try container.decodeIfPresent(String.self, forKey: .pt_chrono_style)
pt_chrono_border_clr = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_clr)
pt_chrono_border_clr_dark = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_clr_dark)
pt_chrono_border_width = try container.decodeIfPresent(FlexibleDouble.self, forKey: .pt_chrono_border_width)
pt_chrono_border_radius = try container.decodeIfPresent(FlexibleDouble.self, forKey: .pt_chrono_border_radius)

// Value for pt_timer_threshold and pt_timer_end key can be Int or String if received from JSON data or individual keys respectively, so checked for both case if present or else nil.
var thresholdValue: Int? = nil
do {
Expand Down
Loading