#1575 (include a subscribe to site page on the share sheet)

- Now uses NewsBlur icons.
- Now posts a user notification indicating success or failure.
This commit is contained in:
David Sinclair 2022-03-10 16:46:34 -07:00
parent 0c54797839
commit 06af724619
6 changed files with 173 additions and 58 deletions

View file

@ -178,6 +178,10 @@
17F39EBA26478538004B46D1 /* content_preview_small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17F39EB726478538004B46D1 /* content_preview_small@2x.png */; }; 17F39EBA26478538004B46D1 /* content_preview_small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17F39EB726478538004B46D1 /* content_preview_small@2x.png */; };
17F39EBB26478538004B46D1 /* content_preview_large@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17F39EB826478538004B46D1 /* content_preview_large@2x.png */; }; 17F39EBB26478538004B46D1 /* content_preview_large@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17F39EB826478538004B46D1 /* content_preview_large@2x.png */; };
17FB51D723AC81C500F5D5BF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 17FB51D923AC81C500F5D5BF /* InfoPlist.strings */; }; 17FB51D723AC81C500F5D5BF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 17FB51D923AC81C500F5D5BF /* InfoPlist.strings */; };
17FF4F7D27DA9645000526E6 /* ShareAddSiteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17FF4F7C27DA9645000526E6 /* ShareAddSiteCell.swift */; };
17FF4F7E27DAAA6E000526E6 /* ak-icon-allstories.png in Resources */ = {isa = PBXBuildFile; fileRef = FF85BF7216D6A972002D334D /* ak-icon-allstories.png */; };
17FF4F7F27DAAA78000526E6 /* g_icn_folder.png in Resources */ = {isa = PBXBuildFile; fileRef = FFDD847216E887D3000AA0A2 /* g_icn_folder.png */; };
17FF4F8027DAAA78000526E6 /* g_icn_folder@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFDD847316E887D3000AA0A2 /* g_icn_folder@2x.png */; };
1D3623260D0F684500981E51 /* NewsBlurAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* NewsBlurAppDelegate.m */; }; 1D3623260D0F684500981E51 /* NewsBlurAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* NewsBlurAppDelegate.m */; };
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
@ -902,6 +906,7 @@
17F39EB726478538004B46D1 /* content_preview_small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "content_preview_small@2x.png"; sourceTree = "<group>"; }; 17F39EB726478538004B46D1 /* content_preview_small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "content_preview_small@2x.png"; sourceTree = "<group>"; };
17F39EB826478538004B46D1 /* content_preview_large@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "content_preview_large@2x.png"; sourceTree = "<group>"; }; 17F39EB826478538004B46D1 /* content_preview_large@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "content_preview_large@2x.png"; sourceTree = "<group>"; };
17FB51D823AC81C500F5D5BF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 17FB51D823AC81C500F5D5BF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
17FF4F7C27DA9645000526E6 /* ShareAddSiteCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAddSiteCell.swift; sourceTree = "<group>"; };
1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
1D3623240D0F684500981E51 /* NewsBlurAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = NewsBlurAppDelegate.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 1D3623240D0F684500981E51 /* NewsBlurAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = NewsBlurAppDelegate.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
1D3623250D0F684500981E51 /* NewsBlurAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = NewsBlurAppDelegate.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 1D3623250D0F684500981E51 /* NewsBlurAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = NewsBlurAppDelegate.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
@ -1694,6 +1699,7 @@
17DC7CCD26A5212000B0E747 /* ShareSaveTagCell.swift */, 17DC7CCD26A5212000B0E747 /* ShareSaveTagCell.swift */,
17DC7CCF26A5218000B0E747 /* ShareSaveNewCell.swift */, 17DC7CCF26A5218000B0E747 /* ShareSaveNewCell.swift */,
17DC7CD126A521A300B0E747 /* ShareCommentCell.swift */, 17DC7CD126A521A300B0E747 /* ShareCommentCell.swift */,
17FF4F7C27DA9645000526E6 /* ShareAddSiteCell.swift */,
174939151C251BFE003D98AA /* MainInterface.storyboard */, 174939151C251BFE003D98AA /* MainInterface.storyboard */,
174939211C251E43003D98AA /* Share Extension.entitlements */, 174939211C251E43003D98AA /* Share Extension.entitlements */,
174939181C251BFE003D98AA /* Info.plist */, 174939181C251BFE003D98AA /* Info.plist */,
@ -3080,7 +3086,10 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
17FF4F7F27DAAA78000526E6 /* g_icn_folder.png in Resources */,
17FF4F7E27DAAA6E000526E6 /* ak-icon-allstories.png in Resources */,
174939171C251BFE003D98AA /* MainInterface.storyboard in Resources */, 174939171C251BFE003D98AA /* MainInterface.storyboard in Resources */,
17FF4F8027DAAA78000526E6 /* g_icn_folder@2x.png in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -3563,6 +3572,7 @@
files = ( files = (
17DC7CD026A5218000B0E747 /* ShareSaveNewCell.swift in Sources */, 17DC7CD026A5218000B0E747 /* ShareSaveNewCell.swift in Sources */,
17DC7CCA26A51BB600B0E747 /* ShareViewController.swift in Sources */, 17DC7CCA26A51BB600B0E747 /* ShareViewController.swift in Sources */,
17FF4F7D27DA9645000526E6 /* ShareAddSiteCell.swift in Sources */,
17DC7CCE26A5212000B0E747 /* ShareSaveTagCell.swift in Sources */, 17DC7CCE26A5212000B0E747 /* ShareSaveTagCell.swift in Sources */,
17DC7CCC26A51E6300B0E747 /* ShareViewDelegate.swift in Sources */, 17DC7CCC26A51E6300B0E747 /* ShareViewDelegate.swift in Sources */,
17DC7CD226A521A300B0E747 /* ShareCommentCell.swift in Sources */, 17DC7CD226A521A300B0E747 /* ShareCommentCell.swift in Sources */,

View file

@ -14,7 +14,7 @@
<objects> <objects>
<viewController storyboardIdentifier="DetailViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="djW-7k-haK" customClass="DetailViewController" customModule="NewsBlur" customModuleProvider="target" sceneMemberID="viewController"> <viewController storyboardIdentifier="DetailViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="djW-7k-haK" customClass="DetailViewController" customModule="NewsBlur" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jTZ-4O-xyT"> <view key="view" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jTZ-4O-xyT">
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/> <rect key="frame" x="0.0" y="0.0" width="818.5" height="834"/>
<subviews> <subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bPa-u1-Aml"> <containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bPa-u1-Aml">
<rect key="frame" x="0.0" y="74" width="1194" height="580"/> <rect key="frame" x="0.0" y="74" width="1194" height="580"/>

View file

@ -154,6 +154,41 @@
<outlet property="commentTextView" destination="mfP-7f-Dq0" id="AoJ-DT-f8q"/> <outlet property="commentTextView" destination="mfP-7f-Dq0" id="AoJ-DT-f8q"/>
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="ShareAddSiteCell" id="Sdh-Hi-VKN" customClass="ShareAddSiteCell" customModule="Share_Extension" customModuleProvider="target">
<rect key="frame" x="0.0" y="383.5" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Sdh-Hi-VKN" id="gap-sb-A4d">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="87l-vo-Aqu">
<rect key="frame" x="20" y="9.5" width="25" height="25"/>
<constraints>
<constraint firstAttribute="width" constant="25" id="PdC-PQ-ClF"/>
<constraint firstAttribute="height" constant="25" id="VdN-R3-r0z"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="enG-2W-uUc">
<rect key="frame" x="53" y="11.5" width="41.5" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="enG-2W-uUc" secondAttribute="trailing" constant="20" symbolic="YES" id="6iv-Qq-MBb"/>
<constraint firstItem="87l-vo-Aqu" firstAttribute="centerY" secondItem="gap-sb-A4d" secondAttribute="centerY" id="PxG-wV-eFZ"/>
<constraint firstItem="enG-2W-uUc" firstAttribute="leading" secondItem="87l-vo-Aqu" secondAttribute="trailing" constant="8" symbolic="YES" id="ZId-aw-V6W"/>
<constraint firstItem="87l-vo-Aqu" firstAttribute="leading" secondItem="gap-sb-A4d" secondAttribute="leading" constant="20" symbolic="YES" id="emx-Ou-ppO"/>
<constraint firstItem="enG-2W-uUc" firstAttribute="centerY" secondItem="gap-sb-A4d" secondAttribute="centerY" id="vq0-II-4Lp"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="folderImageLeadingConstraint" destination="emx-Ou-ppO" id="1cu-W4-kwz"/>
<outlet property="folderImageView" destination="87l-vo-Aqu" id="fg6-9H-E5C"/>
<outlet property="folderLabel" destination="enG-2W-uUc" id="O4Y-ka-c1D"/>
</connections>
</tableViewCell>
</prototypes> </prototypes>
<connections> <connections>
<outlet property="dataSource" destination="96H-cS-CLZ" id="puZ-kH-xg9"/> <outlet property="dataSource" destination="96H-cS-CLZ" id="puZ-kH-xg9"/>

View file

@ -0,0 +1,20 @@
//
// ShareAddSiteCell.swift
// Share Extension
//
// Created by David Sinclair on 2022-03-10.
// Copyright © 2022 NewsBlur. All rights reserved.
//
import UIKit
class ShareAddSiteCell: UITableViewCell {
@IBOutlet weak var folderImageView: UIImageView!
@IBOutlet weak var folderLabel: UILabel!
@IBOutlet weak var folderImageLeadingConstraint: NSLayoutConstraint!
/// The reuse identifier for this table view cell.
static let reuseIdentifier = "ShareAddSiteCell"
}

View file

@ -8,6 +8,7 @@
import UIKit import UIKit
import MobileCoreServices import MobileCoreServices
import UserNotifications
class ShareViewController: UIViewController { class ShareViewController: UIViewController {
@IBOutlet var delegate: ShareViewDelegate! @IBOutlet var delegate: ShareViewDelegate!
@ -98,7 +99,7 @@ class ShareViewController: UIViewController {
if let foldersArray = prefs.object(forKey: "share:folders") as? [String] { if let foldersArray = prefs.object(forKey: "share:folders") as? [String] {
folders = foldersArray folders = foldersArray
folders.removeAll { ["river_global", "river_blurblogs", "infrequent", "read_stories", "saved_searches", "saved_stories"].contains($0) } folders.removeAll { ["river_global", "river_blurblogs", "infrequent", "widget_stories", "read_stories", "saved_searches", "saved_stories"].contains($0) }
} }
updateSaveButtonState() updateSaveButtonState()
@ -351,10 +352,46 @@ private extension ShareViewController {
extension ShareViewController: URLSessionTaskDelegate { extension ShareViewController: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
let content = UNMutableNotificationContent()
content.title = "NewsBlur"
if let error = error { if let error = error {
print("task completed with error: \(error)") print("task completed with error: \(error)")
switch mode {
case .save:
content.body = "Unable to save this story"
case .share:
content.body = "Unable to share this story"
case .add:
content.body = "Unable to add this site"
}
} else { } else {
print("task completed successfully") print("task completed successfully: \(String(describing: task.response))")
NSLog("⚾️ share: \(String(describing: task.response))")
switch mode {
case .save:
content.body = "Saved this story"
case .share:
content.body = "Shared this story"
case .add:
content.body = "Added this site"
}
}
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let uuidString = UUID().uuidString
let request = UNNotificationRequest(identifier: uuidString,
content: content, trigger: trigger)
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request) { (error) in
if let error = error {
print("notification error: \(error)")
}
} }
} }
} }

View file

@ -62,72 +62,85 @@ extension ShareViewDelegate: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if viewController.mode == .save { if viewController.mode == .save {
if indexPath.item < viewController.tags.count { if indexPath.item < viewController.tags.count {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareSaveTagCell.reuseIdentifier, for: indexPath) as? ShareSaveTagCell else { return makeSaveTagCell(for: tableView, indexPath: indexPath)
preconditionFailure("Expected to dequeue a ShareSaveTagCell")
}
let tag = viewController.tags[indexPath.item]
cell.tagLabel.text = tag.name
cell.countLabel.text = "\(tag.count)"
return cell
} else { } else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareSaveNewCell.reuseIdentifier, for: indexPath) as? ShareSaveNewCell else { return makeSaveNewCell(for: tableView, indexPath: indexPath, name: "", placeholder: "new tag")
preconditionFailure("Expected to dequeue a ShareSaveNewCell")
}
cell.tagField.text = ""
cell.tagField.placeholder = "new tag"
return cell
} }
} else if viewController.mode == .add { } else if viewController.mode == .add {
if indexPath.section == 0 { if indexPath.section == 0 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareSaveTagCell.reuseIdentifier, for: indexPath) as? ShareSaveTagCell else { return makeAddSiteCell(for: tableView, indexPath: indexPath)
preconditionFailure("Expected to dequeue a ShareSaveTagCell")
}
let components = viewController.folders[indexPath.item].components(separatedBy: "")
cell.countLabel.text = ""
if components.first == "everything" {
cell.tagLabel.text = "🗃 Top Level"
} else {
cell.tagLabel.text = "\(String(repeating: " ", count: components.count))📁 \(components.last ?? "?")"
}
cell.accessoryType = indexPath == viewController.selectedFolderIndexPath ? .checkmark : .none
return cell
} else { } else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareSaveNewCell.reuseIdentifier, for: indexPath) as? ShareSaveNewCell else { return makeSaveNewCell(for: tableView, indexPath: indexPath, name: viewController.newFolder, placeholder: "new tag")
preconditionFailure("Expected to dequeue a ShareSaveNewCell")
}
cell.tagField.text = viewController.newFolder
cell.tagField.placeholder = "new folder title"
return cell
} }
} else { } else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareCommentCell.reuseIdentifier, for: indexPath) as? ShareCommentCell else { return makeShareCommentCell(for: tableView, indexPath: indexPath)
preconditionFailure("Expected to dequeue a ShareCommentCell")
}
cell.commentTextView.text = ""
cell.commentTextView.delegate = self
DispatchQueue.main.async {
cell.commentTextView.becomeFirstResponder()
}
return cell
} }
} }
} }
private extension ShareViewDelegate {
func makeSaveTagCell(for tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareSaveTagCell.reuseIdentifier, for: indexPath) as? ShareSaveTagCell else {
preconditionFailure("Expected to dequeue a ShareSaveTagCell")
}
let tag = viewController.tags[indexPath.item]
cell.tagLabel.text = tag.name
cell.countLabel.text = "\(tag.count)"
return cell
}
func makeSaveNewCell(for tableView: UITableView, indexPath: IndexPath, name: String, placeholder: String) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareSaveNewCell.reuseIdentifier, for: indexPath) as? ShareSaveNewCell else {
preconditionFailure("Expected to dequeue a ShareSaveNewCell")
}
cell.tagField.text = name
cell.tagField.placeholder = placeholder
return cell
}
func makeAddSiteCell(for tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareAddSiteCell.reuseIdentifier, for: indexPath) as? ShareAddSiteCell else {
preconditionFailure("Expected to dequeue a ShareAddSiteCell")
}
let components = viewController.folders[indexPath.item].components(separatedBy: "")
if components.first == "everything" {
cell.folderImageView.image = UIImage(named: "ak-icon-allstories.png")
cell.folderLabel.text = "Top Level"
cell.folderImageLeadingConstraint.constant = 20
} else {
cell.folderImageView.image = UIImage(named: "g_icn_folder.png")
cell.folderLabel.text = components.last ?? "?"
cell.folderImageLeadingConstraint.constant = 20 + CGFloat(components.count * 35)
}
cell.accessoryType = indexPath == viewController.selectedFolderIndexPath ? .checkmark : .none
return cell
}
func makeShareCommentCell(for tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ShareCommentCell.reuseIdentifier, for: indexPath) as? ShareCommentCell else {
preconditionFailure("Expected to dequeue a ShareCommentCell")
}
cell.commentTextView.text = ""
cell.commentTextView.delegate = self
DispatchQueue.main.async {
cell.commentTextView.becomeFirstResponder()
}
return cell
}
}
extension ShareViewDelegate: UITextViewDelegate { extension ShareViewDelegate: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) { func textViewDidChange(_ textView: UITextView) {
viewController.comments = textView.text viewController.comments = textView.text