2022-03-30 02:21:33 +00:00
//
// I s s u e R e p o r t e r V i e w C o n t r o l l e r . s w i f t
// T u s k e r
//
// C r e a t e d b y S h a d o w f a c t s o n 6 / 2 0 / 2 0 .
// C o p y r i g h t © 2 0 2 0 S h a d o w f a c t s . A l l r i g h t s r e s e r v e d .
//
import UIKit
import MessageUI
2022-10-09 18:26:44 +00:00
import OSLog
2022-03-30 02:21:33 +00:00
class IssueReporterViewController : UIViewController {
static func create ( _ self : IssueReporterViewController ) -> UINavigationController {
let nav = UINavigationController ( rootViewController : self )
nav . navigationBar . prefersLargeTitles = true
return nav
}
2022-03-30 13:58:50 +00:00
static func create ( reportText : String , reportFilename : String ? = nil , dismiss : @ escaping ( ) -> Void ) -> UINavigationController {
2022-03-30 02:21:33 +00:00
let filename = reportFilename ? ? " Tusker-error- \( ISO8601DateFormatter ( ) . string ( from : Date ( ) ) ) .txt "
2022-03-30 13:58:50 +00:00
return create ( IssueReporterViewController ( reportText : reportText , reportFilename : filename , dismiss : dismiss ) )
2022-03-30 02:21:33 +00:00
}
let reportText : String
let reportFilename : String
2022-10-29 04:23:18 +00:00
private let doDismiss : ( ) -> Void
2022-03-30 02:21:33 +00:00
var preamble : String {
" Tusker has encountered an error. You can email a report to the developer. You may review the report below before sending. \n \n If you choose to send the report, please include any additional details about what you were doing prior that may be pertinent. "
}
var subject : String {
" Tusker Error Report "
}
2022-10-09 18:26:44 +00:00
private let logDataTask : Task < Data ? , Never >
2022-03-30 02:21:33 +00:00
@IBOutlet weak var crashReportTextView : UITextView !
@IBOutlet weak var sendReportButton : UIButton !
2022-03-30 13:58:50 +00:00
init ( reportText : String , reportFilename : String , dismiss : @ escaping ( ) -> Void ) {
2022-03-30 02:21:33 +00:00
self . reportText = reportText
self . reportFilename = reportFilename
2022-10-29 04:23:18 +00:00
self . doDismiss = dismiss
2022-10-09 18:26:44 +00:00
self . logDataTask = Task ( priority : . userInitiated ) {
return await withCheckedContinuation ( { continuation in
DispatchQueue . global ( ) . async {
2022-10-10 18:21:12 +00:00
continuation . resume ( returning : Logging . getLogData ( ) )
2022-10-09 18:26:44 +00:00
}
} )
}
2022-03-30 02:21:33 +00:00
super . init ( nibName : " IssueReporterViewController " , bundle : . main )
}
required init ? ( coder : NSCoder ) {
fatalError ( " init(coder:) has not been implemented " )
}
override func viewDidLoad ( ) {
super . viewDidLoad ( )
navigationItem . title = " Report an Error "
navigationItem . largeTitleDisplayMode = . always
crashReportTextView . font = . monospacedSystemFont ( ofSize : 14 , weight : . regular )
let attributed = NSMutableAttributedString ( )
attributed . append ( NSAttributedString ( string : preamble , attributes : [
NSAttributedString . Key . font : UIFont . systemFont ( ofSize : 17 ) ,
NSAttributedString . Key . foregroundColor : UIColor . label
] ) )
attributed . append ( NSAttributedString ( string : " \n \n " ) )
attributed . append ( NSAttributedString ( string : reportText , attributes : [
NSAttributedString . Key . font : UIFont . monospacedSystemFont ( ofSize : 14 , weight : . regular ) ,
NSAttributedString . Key . foregroundColor : UIColor . label
] ) )
crashReportTextView . attributedText = attributed
sendReportButton . layer . cornerRadius = 12.5
sendReportButton . layer . masksToBounds = true
sendReportButton . addGestureRecognizer ( UILongPressGestureRecognizer ( target : self , action : #selector ( sendReportButtonLongPressed ) ) )
}
private func updateSendReportButtonColor ( lightened : Bool , animate : Bool ) {
let color : UIColor
if lightened {
var hue : CGFloat = 0 , saturation : CGFloat = 0 , brightness : CGFloat = 0 , alpha : CGFloat = 0
UIColor . systemBlue . getHue ( & hue , saturation : & saturation , brightness : & brightness , alpha : & alpha )
color = UIColor ( hue : hue , saturation : 0.85 * saturation , brightness : brightness , alpha : alpha )
} else {
color = . systemBlue
}
if animate {
UIView . animate ( withDuration : 0.25 ) {
self . sendReportButton . backgroundColor = color
}
} else {
sendReportButton . backgroundColor = color
}
}
@IBAction func sendReportTouchDown ( _ sender : Any ) {
updateSendReportButtonColor ( lightened : true , animate : false )
}
@IBAction func sendReportButtonTouchDragExit ( _ sender : Any ) {
updateSendReportButtonColor ( lightened : false , animate : true )
}
@IBAction func sendReportButtonTouchDragEnter ( _ sender : Any ) {
updateSendReportButtonColor ( lightened : true , animate : true )
}
@IBAction func sendReportTouchUpInside ( _ sender : Any ) {
updateSendReportButtonColor ( lightened : false , animate : true )
2022-10-29 04:23:18 +00:00
sendReportButton . isEnabled = false
2022-03-30 02:21:33 +00:00
2022-10-09 18:26:44 +00:00
Task {
let composeVC = MFMailComposeViewController ( )
composeVC . mailComposeDelegate = self
composeVC . setToRecipients ( [ " me@shadowfacts.net " ] )
composeVC . setSubject ( subject )
let data = reportText . data ( using : . utf8 ) !
composeVC . addAttachmentData ( data , mimeType : " text/plain " , fileName : reportFilename )
2022-10-29 04:23:18 +00:00
if let ( logData , name ) = await getLogData ( ) {
composeVC . addAttachmentData ( logData , mimeType : " text/plain " , fileName : name )
2022-10-09 18:26:44 +00:00
}
self . present ( composeVC , animated : true )
2022-10-29 04:23:18 +00:00
sendReportButton . isEnabled = true
2022-10-09 18:26:44 +00:00
}
2022-03-30 02:21:33 +00:00
}
@objc func sendReportButtonLongPressed ( ) {
let dir = FileManager . default . temporaryDirectory
let url = dir . appendingPathComponent ( reportFilename )
try ! reportText . data ( using : . utf8 ) ! . write ( to : url )
let activityController = UIActivityViewController ( activityItems : [ url ] , applicationActivities : nil )
2022-10-28 03:11:21 +00:00
activityController . popoverPresentationController ? . sourceView = sendReportButton
2022-03-30 02:21:33 +00:00
present ( activityController , animated : true )
}
@IBAction func cancelPressed ( _ sender : Any ) {
2022-10-29 04:23:18 +00:00
self . finishedReport ( )
doDismiss ( )
}
func getLogData ( ) async -> ( Data , String ) ? {
guard let data = await logDataTask . value else {
return nil
}
let timestamp = ISO8601DateFormatter ( ) . string ( from : Date ( ) )
return ( data , " Tusker- \( timestamp ) .log " )
}
func finishedReport ( ) {
2022-03-30 02:21:33 +00:00
}
}
extension IssueReporterViewController : MFMailComposeViewControllerDelegate {
func mailComposeController ( _ controller : MFMailComposeViewController , didFinishWith result : MFMailComposeResult , error : Error ? ) {
controller . dismiss ( animated : true ) {
2022-10-28 03:10:00 +00:00
if result = = . cancelled {
// d o n ' t d i s m i s s o u r s e l f , t o a l l o w e t h e u s e r t o s e n d t h e r e p o r t a d i f f e r e n t w a y
} else {
2022-10-29 04:23:18 +00:00
self . finishedReport ( )
self . doDismiss ( )
2022-10-28 03:10:00 +00:00
}
2022-03-30 02:21:33 +00:00
}
}
}