diff --git a/Client.xcodeproj/project.pbxproj b/Client.xcodeproj/project.pbxproj index 2924066ae65f..c94fdbc4b5b0 100644 --- a/Client.xcodeproj/project.pbxproj +++ b/Client.xcodeproj/project.pbxproj @@ -9,6 +9,18 @@ /* Begin PBXBuildFile section */ 03CCC9181AF05E7300DBF30D /* RelativeDatesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CCC9171AF05E7300DBF30D /* RelativeDatesTests.swift */; }; 0430A545203B372D00FDF76D /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0430A544203B372D00FDF76D /* IntegrationTests.swift */; }; + 048B4C7924A53E7B001B56E8 /* TodayModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C7824A53E7B001B56E8 /* TodayModel.swift */; }; + 048B4C7A24A53E7B001B56E8 /* TodayModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C7824A53E7B001B56E8 /* TodayModel.swift */; }; + 048B4C7D24A53EA8001B56E8 /* TodayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C7C24A53EA8001B56E8 /* TodayViewModel.swift */; }; + 048B4C7E24A53EA8001B56E8 /* TodayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C7C24A53EA8001B56E8 /* TodayViewModel.swift */; }; + 048B4C8024A53EEB001B56E8 /* ImageButtonWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C7F24A53EEB001B56E8 /* ImageButtonWithLabel.swift */; }; + 048B4C8124A53EEB001B56E8 /* ImageButtonWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C7F24A53EEB001B56E8 /* ImageButtonWithLabel.swift */; }; + 048B4C8324A53F26001B56E8 /* ButtonWithSublabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C8224A53F26001B56E8 /* ButtonWithSublabel.swift */; }; + 048B4C8424A53F26001B56E8 /* ButtonWithSublabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C8224A53F26001B56E8 /* ButtonWithSublabel.swift */; }; + 048B4C8624A53F3E001B56E8 /* TodayUX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C8524A53F3E001B56E8 /* TodayUX.swift */; }; + 048B4C8724A53F3E001B56E8 /* TodayUX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C8524A53F3E001B56E8 /* TodayUX.swift */; }; + 048B4C8924A53F81001B56E8 /* UIButtonExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C8824A53F81001B56E8 /* UIButtonExtensions.swift */; }; + 048B4C8A24A53F81001B56E8 /* UIButtonExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B4C8824A53F81001B56E8 /* UIButtonExtensions.swift */; }; 0B21E8061E26CCB7000C8779 /* EarlGrey.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0B21E8051E26CCB7000C8779 /* EarlGrey.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 0B305E1B1E3A98A900BE0767 /* BookmarkingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B305E1A1E3A98A900BE0767 /* BookmarkingTests.swift */; }; 0B3D670E1E09B90B00C1EFC7 /* AuthenticationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */; }; @@ -1165,6 +1177,12 @@ /* Begin PBXFileReference section */ 03CCC9171AF05E7300DBF30D /* RelativeDatesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativeDatesTests.swift; sourceTree = ""; }; 0430A544203B372D00FDF76D /* IntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = ""; }; + 048B4C7824A53E7B001B56E8 /* TodayModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayModel.swift; sourceTree = ""; }; + 048B4C7C24A53EA8001B56E8 /* TodayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewModel.swift; sourceTree = ""; }; + 048B4C7F24A53EEB001B56E8 /* ImageButtonWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageButtonWithLabel.swift; sourceTree = ""; }; + 048B4C8224A53F26001B56E8 /* ButtonWithSublabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonWithSublabel.swift; sourceTree = ""; }; + 048B4C8524A53F3E001B56E8 /* TodayUX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayUX.swift; sourceTree = ""; }; + 048B4C8824A53F81001B56E8 /* UIButtonExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButtonExtensions.swift; sourceTree = ""; }; 0B21E8051E26CCB7000C8779 /* EarlGrey.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EarlGrey.framework; path = Carthage/Build/iOS/EarlGrey.framework; sourceTree = ""; }; 0B305E1A1E3A98A900BE0767 /* BookmarkingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkingTests.swift; sourceTree = ""; }; 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTest.swift; sourceTree = ""; }; @@ -2458,6 +2476,12 @@ 391AEFD11C8F11ED00691F84 /* Images.xcassets */, 390527531C874D35007E0BB7 /* Info.plist */, 3905274E1C874D35007E0BB7 /* TodayViewController.swift */, + 048B4C7824A53E7B001B56E8 /* TodayModel.swift */, + 048B4C7C24A53EA8001B56E8 /* TodayViewModel.swift */, + 048B4C7F24A53EEB001B56E8 /* ImageButtonWithLabel.swift */, + 048B4C8224A53F26001B56E8 /* ButtonWithSublabel.swift */, + 048B4C8524A53F3E001B56E8 /* TodayUX.swift */, + 048B4C8824A53F81001B56E8 /* UIButtonExtensions.swift */, ); path = Today; sourceTree = ""; @@ -4897,8 +4921,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 048B4C7E24A53EA8001B56E8 /* TodayViewModel.swift in Sources */, + 048B4C8724A53F3E001B56E8 /* TodayUX.swift in Sources */, + 048B4C8124A53EEB001B56E8 /* ImageButtonWithLabel.swift in Sources */, 315D05561E58DD60001F349B /* UIPasteboardExtensions.swift in Sources */, 3905274F1C874D35007E0BB7 /* TodayViewController.swift in Sources */, + 048B4C8A24A53F81001B56E8 /* UIButtonExtensions.swift in Sources */, + 048B4C7A24A53E7B001B56E8 /* TodayModel.swift in Sources */, + 048B4C8424A53F26001B56E8 /* ButtonWithSublabel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -5081,6 +5111,7 @@ 5F130D2E2483508E00B0F7D0 /* FxAWebViewModel.swift in Sources */, D0FCF7F51FE45842004A7995 /* UserScriptManager.swift in Sources */, E4A960061ABB9C450069AD6F /* ReaderModeUtils.swift in Sources */, + 048B4C8324A53F26001B56E8 /* ButtonWithSublabel.swift in Sources */, 435D660323D793DF0046EFA2 /* UpdateModel.swift in Sources */, EBF47E701F7979DF00899189 /* UnifiedTelemetry.swift in Sources */, E68F36981EA694000048CF44 /* PanelDataObservers.swift in Sources */, @@ -5099,6 +5130,7 @@ D3C3696E1CC6B78800348A61 /* LocalRequestHelper.swift in Sources */, E4B423DD1ABA0318007E66C8 /* ReaderModeHandlers.swift in Sources */, D308E4E41A5306F500842685 /* SearchEngines.swift in Sources */, + 048B4C8924A53F81001B56E8 /* UIButtonExtensions.swift in Sources */, 3BCE6D3C1CEB9E4D0080928C /* ThirdPartySearchAlerts.swift in Sources */, 745DAB301CDAAFAA00D44181 /* RecentlyClosedTabsPanel.swift in Sources */, D0B9483D22A18B78002F4AA1 /* TextFieldTableViewCell.swift in Sources */, @@ -5111,6 +5143,7 @@ 39F819C61FD70F5D009E31E4 /* TabEventHandlers.swift in Sources */, E65607611C08B4E200534B02 /* SearchInputView.swift in Sources */, FA6B2AC21D41F02D00429414 /* Punycode.swift in Sources */, + 048B4C7D24A53EA8001B56E8 /* TodayViewModel.swift in Sources */, 43446CEA2412066500F5C643 /* UIViewControllerExtension.swift in Sources */, D301AAEE1A3A55B70078DD1D /* TabTrayControllerV1.swift in Sources */, EB9A179B20E69A7F00B12184 /* ThemeManager.swift in Sources */, @@ -5119,6 +5152,7 @@ EBB89509219398E500EB91A0 /* TabContentBlocker+ContentScript.swift in Sources */, D3BE7B461B054F8600641031 /* TestAppDelegate.swift in Sources */, 3BB50E111D6274CD004B33DF /* FirefoxHomeTopSitesCell.swift in Sources */, + 048B4C8624A53F3E001B56E8 /* TodayUX.swift in Sources */, 0B62EFD21AD63CD100ACB9CD /* Clearables.swift in Sources */, 7482205C1DBAB56300EEEA72 /* MailProviders.swift in Sources */, C40046FA1CF8E0B200B08303 /* BackForwardListAnimator.swift in Sources */, @@ -5130,6 +5164,7 @@ 274A36CC239EB99400A21587 /* LibraryPanelContextMenu.swift in Sources */, D314E7F71A37B98700426A76 /* TabToolbar.swift in Sources */, CEFA977E1FAA6B490016F365 /* SyncContentSettingsViewController.swift in Sources */, + 048B4C7924A53E7B001B56E8 /* TodayModel.swift in Sources */, E60D03181D511398002FE3F6 /* SyncStatusResolver.swift in Sources */, 435D660723D7962C0046EFA2 /* UpdateCoverSheetTableViewCell.swift in Sources */, C4E3983D1D21F1E7004E89BA /* TopTabsViews.swift in Sources */, @@ -5265,6 +5300,7 @@ A93067E81D0FE18E00C49C6E /* NightModeHelper.swift in Sources */, 3B39EDCB1E16E1AA00EF029F /* CustomSearchViewController.swift in Sources */, E65075571E37F714006961AC /* FaviconFetcher.swift in Sources */, + 048B4C8024A53EEB001B56E8 /* ImageButtonWithLabel.swift in Sources */, D863C8F21F68BFC20058D95F /* GradientProgressBar.swift in Sources */, EB9A178E20E525DF00B12184 /* ThemeSettingsController.swift in Sources */, D3C744CD1A687D6C004CE85D /* URIFixup.swift in Sources */, diff --git a/Extensions/Today/ButtonWithSublabel.swift b/Extensions/Today/ButtonWithSublabel.swift new file mode 100644 index 000000000000..a3d559d46873 --- /dev/null +++ b/Extensions/Today/ButtonWithSublabel.swift @@ -0,0 +1,56 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import UIKit + +class ButtonWithSublabel: UIButton { + lazy var subtitleLabel = UILabel() + lazy var label = UILabel() + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + convenience init() { + self.init(frame: .zero) + } + + override init(frame: CGRect) { + super.init(frame: frame) + performLayout() + } + + fileprivate func performLayout() { + let buttonImage = self.imageView! + self.titleLabel?.removeFromSuperview() + addSubview(self.label) + addSubview(self.subtitleLabel) + + buttonImage.snp.makeConstraints { make in + make.centerY.left.equalTo(10) + make.width.equalTo(TodayUX.copyLinkImageWidth) + } + + self.label.snp.makeConstraints { make in + make.left.equalTo(buttonImage.snp.right).offset(10) + make.trailing.top.equalTo(self) + make.height.greaterThanOrEqualTo(15) + } + self.label.numberOfLines = 1 + self.label.lineBreakMode = .byWordWrapping + + self.subtitleLabel.lineBreakMode = .byTruncatingTail + self.subtitleLabel.snp.makeConstraints { make in + make.bottom.equalTo(self).inset(10) + make.top.equalTo(self.label.snp.bottom).offset(3) + make.leading.trailing.equalTo(self.label) + make.height.greaterThanOrEqualTo(10) + } + } + + override func setTitle(_ text: String?, for state: UIControl.State) { + self.label.text = text + super.setTitle(text, for: state) + } +} diff --git a/Extensions/Today/ImageButtonWithLabel.swift b/Extensions/Today/ImageButtonWithLabel.swift new file mode 100644 index 000000000000..5cfe20fd93fe --- /dev/null +++ b/Extensions/Today/ImageButtonWithLabel.swift @@ -0,0 +1,47 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import UIKit + +class ImageButtonWithLabel: UIView { + lazy var button = UIButton() + lazy var label = UILabel() + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override init(frame: CGRect) { + super.init(frame: frame) + performLayout() + } + + fileprivate func performLayout() { + addSubview(button) + addSubview(label) + button.imageView?.contentMode = .scaleAspectFit + + button.snp.makeConstraints { make in + make.centerX.equalTo(self) + make.top.equalTo(self.safeAreaLayoutGuide).offset(5) + make.right.greaterThanOrEqualTo(self.safeAreaLayoutGuide).offset(40) + make.left.greaterThanOrEqualTo(self.safeAreaLayoutGuide).inset(40) + make.height.greaterThanOrEqualTo(60) + } + + label.snp.makeConstraints { make in + make.top.equalTo(button.snp.bottom).offset(10) + make.leading.trailing.bottom.equalTo(self) + make.height.greaterThanOrEqualTo(10) + } + + label.numberOfLines = 1 + label.lineBreakMode = .byWordWrapping + label.textAlignment = .center + } + + func addTarget(_ target: AnyObject?, action: Selector, forControlEvents events: UIControl.Event) { + button.addTarget(target, action: action, for: events) + } +} diff --git a/Extensions/Today/TodayModel.swift b/Extensions/Today/TodayModel.swift new file mode 100644 index 000000000000..46fc04c89986 --- /dev/null +++ b/Extensions/Today/TodayModel.swift @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import Foundation + +struct TodayModel { + static var copiedURL: URL? + + var scheme: String { + guard let string = Bundle.main.object(forInfoDictionaryKey: "MozInternalURLScheme") as? String else { + // Something went wrong/weird, but we should fallback to the public one. + return "firefox" + } + return string + } +} diff --git a/Extensions/Today/TodayUX.swift b/Extensions/Today/TodayUX.swift new file mode 100644 index 000000000000..43bfe67a9d86 --- /dev/null +++ b/Extensions/Today/TodayUX.swift @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import UIKit + +struct TodayUX { + static let backgroundHightlightColor = UIColor(white: 216.0/255.0, alpha: 44.0/255.0) + static let linkTextSize: CGFloat = 9.0 + static let labelTextSize: CGFloat = 12.0 + static let imageButtonTextSize: CGFloat = 13.0 + static let copyLinkImageWidth: CGFloat = 20 + static let margin: CGFloat = 8 + static let buttonsHorizontalMarginPercentage: CGFloat = 0.1 + static let buttonStackViewSpacing: CGFloat = 20.0 + static var labelColor: UIColor { + if #available(iOS 13, *) { + return UIColor(named: "widgetLabelColors") ?? UIColor(rgb: 0x242327) + } else { + return UIColor(rgb: 0x242327) + } + } + static var subtitleLabelColor: UIColor { + if #available(iOS 13, *) { + return UIColor(named: "subtitleLableColor") ?? UIColor(rgb: 0x38383C) + } else { + return UIColor(rgb: 0x38383C) + } + } +} diff --git a/Extensions/Today/TodayViewController.swift b/Extensions/Today/TodayViewController.swift index 02d4d3ce5809..25792b194cfc 100644 --- a/Extensions/Today/TodayViewController.swift +++ b/Extensions/Today/TodayViewController.swift @@ -10,51 +10,24 @@ import XCGLogger private let log = Logger.browserLogger -struct TodayStrings { - static let NewPrivateTabButtonLabel = NSLocalizedString("TodayWidget.NewPrivateTabButtonLabel", tableName: "Today", value: "Private Search", comment: "New Private Tab button label") - static let NewTabButtonLabel = NSLocalizedString("TodayWidget.NewTabButtonLabel", tableName: "Today", value: "New Search", comment: "New Tab button label") - static let GoToCopiedLinkLabel = NSLocalizedString("TodayWidget.GoToCopiedLinkLabel", tableName: "Today", value: "Go to copied link", comment: "Go to link on clipboard") -} - -private struct TodayUX { - static let backgroundHightlightColor = UIColor(white: 216.0/255.0, alpha: 44.0/255.0) - static let linkTextSize: CGFloat = 9.0 - static let labelTextSize: CGFloat = 12.0 - static let imageButtonTextSize: CGFloat = 13.0 - static let copyLinkImageWidth: CGFloat = 20 - static let margin: CGFloat = 8 - static let buttonsHorizontalMarginPercentage: CGFloat = 0.1 - static let buttonStackViewSpacing: CGFloat = 30.0 - static var labelColor: UIColor { - if #available(iOS 13, *) { - return UIColor(named: "widgetLabelColors") ?? UIColor(rgb: 0x242327) - } else { - return UIColor(rgb: 0x242327) - } - } - static var subtitleLabelColor: UIColor { - if #available(iOS 13, *) { - return UIColor(named: "subtitleLableColor") ?? UIColor(rgb: 0x38383C) - } else { - return UIColor(rgb: 0x38383C) - } - } -} - @objc (TodayViewController) -class TodayViewController: UIViewController, NCWidgetProviding { - var copiedURL: URL? +class TodayViewController: UIViewController, NCWidgetProviding, TodayWidgetAppearanceDelegate { + + let viewModel = TodayWidgetViewModel() + let model = TodayModel() fileprivate lazy var newTabButton: ImageButtonWithLabel = { let imageButton = ImageButtonWithLabel() imageButton.addTarget(self, action: #selector(onPressNewTab), forControlEvents: .touchUpInside) - imageButton.label.text = TodayStrings.NewTabButtonLabel + imageButton.label.text = String.NewTabButtonLabel let button = imageButton.button button.setImage(UIImage(named: "search-button")?.withRenderingMode(.alwaysOriginal), for: .normal) + button.accessibilityLabel = String.NewTabButtonLabel + button.accessibilityTraits = .button let label = imageButton.label label.textColor = TodayUX.labelColor label.tintColor = TodayUX.labelColor - label.font = UIFont.systemFont(ofSize: TodayUX.imageButtonTextSize) + label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: UIFont.preferredFont(forTextStyle: .body).withSize(TodayUX.imageButtonTextSize)) imageButton.sizeToFit() return imageButton }() @@ -62,31 +35,38 @@ class TodayViewController: UIViewController, NCWidgetProviding { fileprivate lazy var newPrivateTabButton: ImageButtonWithLabel = { let imageButton = ImageButtonWithLabel() imageButton.addTarget(self, action: #selector(onPressNewPrivateTab), forControlEvents: .touchUpInside) - imageButton.label.text = TodayStrings.NewPrivateTabButtonLabel + imageButton.label.text = String.NewPrivateTabButtonLabel let button = imageButton.button button.setImage(UIImage(named: "private-search")?.withRenderingMode(.alwaysOriginal), for: .normal) + button.accessibilityLabel = String.NewPrivateTabButtonLabel + button.accessibilityTraits = .button let label = imageButton.label label.textColor = TodayUX.labelColor label.tintColor = TodayUX.labelColor - label.font = UIFont.systemFont(ofSize: TodayUX.imageButtonTextSize) + label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: UIFont.preferredFont(forTextStyle: .body).withSize(TodayUX.imageButtonTextSize)) imageButton.sizeToFit() return imageButton }() fileprivate lazy var openCopiedLinkButton: ButtonWithSublabel = { let button = ButtonWithSublabel() - button.setTitle(TodayStrings.GoToCopiedLinkLabel, for: .normal) + button.setTitle(String.GoToCopiedLinkLabel, for: .normal) button.addTarget(self, action: #selector(onPressOpenClibpoard), for: .touchUpInside) // We need to set the background image/color for .Normal, so the whole button is tappable. button.setBackgroundColor(UIColor.clear, forState: .normal) button.setBackgroundColor(TodayUX.backgroundHightlightColor, forState: .highlighted) button.setImage(UIImage(named: "copy_link_icon")?.withRenderingMode(.alwaysOriginal), for: .normal) - button.label.font = UIFont.systemFont(ofSize: TodayUX.labelTextSize) - button.subtitleLabel.font = UIFont.systemFont(ofSize: TodayUX.linkTextSize) + button.label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: UIFont.preferredFont(forTextStyle: .body).withSize(TodayUX.labelTextSize)) + button.accessibilityLabel = String.GoToCopiedLinkLabel + button.accessibilityTraits = .button button.label.textColor = TodayUX.labelColor button.label.tintColor = TodayUX.labelColor + button.label.sizeToFit() button.subtitleLabel.textColor = TodayUX.subtitleLabelColor button.subtitleLabel.tintColor = TodayUX.subtitleLabelColor + button.subtitleLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: UIFont.preferredFont(forTextStyle: .body).withSize(TodayUX.linkTextSize)) + button.label.accessibilityLabel = String.CopiedLinkLabelFromPasteBoard + button.accessibilityTraits = .none return button }() @@ -108,16 +88,9 @@ class TodayViewController: UIViewController, NCWidgetProviding { return stackView }() - fileprivate var scheme: String { - guard let string = Bundle.main.object(forInfoDictionaryKey: "MozInternalURLScheme") as? String else { - // Something went wrong/weird, but we should fallback to the public one. - return "firefox" - } - return string - } - override func viewDidLoad() { super.viewDidLoad() + viewModel.setViewDelegate(todayViewDelegate: self) let widgetView: UIView! self.extensionContext?.widgetLargestAvailableDisplayMode = .compact @@ -148,7 +121,7 @@ class TodayViewController: UIViewController, NCWidgetProviding { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - updateCopiedLink() + viewModel.updateCopiedLink() } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -160,19 +133,15 @@ class TodayViewController: UIViewController, NCWidgetProviding { return .zero } - func updateCopiedLink() { - UIPasteboard.general.asyncURL().uponQueue(.main) { res in - if let copiedURL: URL? = res.successValue, - let url = copiedURL { - self.openCopiedLinkButton.isHidden = false - self.openCopiedLinkButton.subtitleLabel.isHidden = SystemUtils.isDeviceLocked() - self.openCopiedLinkButton.subtitleLabel.text = url.absoluteDisplayString - self.copiedURL = url - } else { - self.openCopiedLinkButton.isHidden = true - self.copiedURL = nil - } + func updateCopiedLinkInView(clipboardURL: URL?) { + guard let url = clipboardURL else { + self.openCopiedLinkButton.isHidden = true + self.openCopiedLinkButton.subtitleLabel.isHidden = SystemUtils.isDeviceLocked() + return } + self.openCopiedLinkButton.isHidden = false + self.openCopiedLinkButton.subtitleLabel.isHidden = SystemUtils.isDeviceLocked() + self.openCopiedLinkButton.subtitleLabel.text = url.absoluteDisplayString } // MARK: Button behaviour @@ -183,129 +152,18 @@ class TodayViewController: UIViewController, NCWidgetProviding { @objc func onPressNewPrivateTab(_ view: UIView) { openContainingApp("?private=true") } - + //TODO: Move it to Viewmodel fileprivate func openContainingApp(_ urlSuffix: String = "") { - let urlString = "\(scheme)://open-url\(urlSuffix)" + let urlString = "\(model.scheme)://open-url\(urlSuffix)" self.extensionContext?.open(URL(string: urlString)!) { success in log.info("Extension opened containing app: \(success)") } } @objc func onPressOpenClibpoard(_ view: UIView) { - if let url = copiedURL, + if let url = TodayModel.copiedURL, let encodedString = url.absoluteString.escape() { openContainingApp("?url=\(encodedString)") } } } - -extension UIButton { - func setBackgroundColor(_ color: UIColor, forState state: UIControl.State) { - let colorView = UIView(frame: CGRect(width: 1, height: 1)) - colorView.backgroundColor = color - - UIGraphicsBeginImageContext(colorView.bounds.size) - if let context = UIGraphicsGetCurrentContext() { - colorView.layer.render(in: context) - } - let colorImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - self.setBackgroundImage(colorImage, for: state) - } -} - -class ImageButtonWithLabel: UIView { - - lazy var button = UIButton() - lazy var label = UILabel() - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override init(frame: CGRect) { - super.init(frame: frame) - performLayout() - } - - func performLayout() { - addSubview(button) - addSubview(label) - button.imageView?.contentMode = .scaleAspectFit - - button.snp.makeConstraints { make in - make.centerX.equalTo(self) - make.top.equalTo(self.safeAreaLayoutGuide).offset(5) - make.right.greaterThanOrEqualTo(self.safeAreaLayoutGuide).offset(40) - make.left.greaterThanOrEqualTo(self.safeAreaLayoutGuide).inset(40) - make.height.greaterThanOrEqualTo(60) - } - - label.snp.makeConstraints { make in - make.top.equalTo(button.snp.bottom).offset(10) - make.leading.trailing.bottom.equalTo(self) - make.height.equalTo(10) - } - - label.numberOfLines = 1 - label.lineBreakMode = .byWordWrapping - label.textAlignment = .center - } - - func addTarget(_ target: AnyObject?, action: Selector, forControlEvents events: UIControl.Event) { - button.addTarget(target, action: action, for: events) - } -} - -class ButtonWithSublabel: UIButton { - lazy var subtitleLabel = UILabel() - lazy var label = UILabel() - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - convenience init() { - self.init(frame: .zero) - } - - override init(frame: CGRect) { - super.init(frame: frame) - performLayout() - } - - fileprivate func performLayout() { - let titleLabel = self.label - self.titleLabel?.removeFromSuperview() - addSubview(titleLabel) - - let imageView = self.imageView! - let subtitleLabel = self.subtitleLabel - self.addSubview(subtitleLabel) - - imageView.snp.makeConstraints { make in - make.centerY.left.equalTo(10) - make.width.equalTo(TodayUX.copyLinkImageWidth) - } - - titleLabel.snp.makeConstraints { make in - make.left.equalTo(imageView.snp.right).offset(10) - make.trailing.top.equalTo(self) - make.height.greaterThanOrEqualTo(12) - } - - subtitleLabel.lineBreakMode = .byTruncatingTail - subtitleLabel.snp.makeConstraints { make in - make.bottom.equalTo(self).inset(10) - make.top.equalTo(titleLabel.snp.bottom) - make.leading.trailing.equalTo(titleLabel) - make.height.greaterThanOrEqualTo(10) - } - } - - override func setTitle(_ text: String?, for state: UIControl.State) { - self.label.text = text - super.setTitle(text, for: state) - } -} diff --git a/Extensions/Today/TodayViewModel.swift b/Extensions/Today/TodayViewModel.swift new file mode 100644 index 000000000000..02a121548c82 --- /dev/null +++ b/Extensions/Today/TodayViewModel.swift @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import Foundation +import NotificationCenter +protocol TodayWidgetAppearanceDelegate { + func updateCopiedLinkInView(clipboardURL: URL?) +} + +class TodayWidgetViewModel { + var AppearanceDelegate: TodayWidgetAppearanceDelegate? + + func setViewDelegate(todayViewDelegate: TodayWidgetAppearanceDelegate?) { + self.AppearanceDelegate = todayViewDelegate + } + + func updateCopiedLink() { + UIPasteboard.general.asyncURL().uponQueue(.main) { res in + guard let url: URL? = res.successValue else { + TodayModel.copiedURL = nil + self.AppearanceDelegate?.updateCopiedLinkInView(clipboardURL: nil) + return + } + TodayModel.copiedURL = url + self.AppearanceDelegate?.updateCopiedLinkInView(clipboardURL: url) + } + } +} diff --git a/Extensions/Today/UIButtonExtensions.swift b/Extensions/Today/UIButtonExtensions.swift new file mode 100644 index 000000000000..a008b3a059ea --- /dev/null +++ b/Extensions/Today/UIButtonExtensions.swift @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import UIKit + +extension UIButton { + func setBackgroundColor(_ color: UIColor, forState state: UIControl.State) { + let colorView = UIView(frame: CGRect(width: 1, height: 1)) + colorView.backgroundColor = color + + UIGraphicsBeginImageContext(colorView.bounds.size) + if let context = UIGraphicsGetCurrentContext() { + colorView.layer.render(in: context) + } + let colorImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + self.setBackgroundImage(colorImage, for: state) + } +}