Fix port, begin upload UI
This commit is contained in:
parent
75ebdf59ae
commit
746b69defc
Binary file not shown.
@ -60,7 +60,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
let serverUrl = URL(string: "https://christophhagen.de")!
|
||||
let serverUrl = URL(string: "https://christophhagen.de:6000")!
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
app = self
|
||||
|
@ -1,9 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="qlf-I7-aOI">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="qlf-I7-aOI">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
|
@ -128,13 +128,23 @@ final class Database {
|
||||
|
||||
var pendingCapUploads: [Cap] {
|
||||
do {
|
||||
return try db.prepare(Cap.table.filter(Cap.columnUploaded == false)).map(Cap.init)
|
||||
return try db.prepare(Cap.table.filter(Cap.columnUploaded == false).order(Cap.columnId.asc)).map(Cap.init)
|
||||
} catch {
|
||||
log("Failed to get pending cap uploads")
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
var hasPendingCapUploads: Bool {
|
||||
do {
|
||||
let query = Cap.table.filter(Cap.columnUploaded == false).count
|
||||
return try db.scalar(query) > 0
|
||||
} catch {
|
||||
log("Failed to get pending cap upload count")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var classifierVersion: Int {
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: Classifier.userDefaultsKey)
|
||||
@ -409,22 +419,15 @@ final class Database {
|
||||
// MARK: Downloads
|
||||
|
||||
@discardableResult
|
||||
func downloadMainImage(for cap: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool {
|
||||
return download.mainImage(for: cap) { success in
|
||||
guard success else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
func downloadImage(for cap: Int, version: Int = 0, completion: @escaping (_ image: UIImage?) -> Void) -> Bool {
|
||||
return download.image(for: cap, version: version) { image in
|
||||
if version == 0 && image != nil {
|
||||
DispatchQueue.main.async {
|
||||
self.delegate?.database(didLoadImageForCap: cap)
|
||||
}
|
||||
completion(true)
|
||||
}
|
||||
completion(image)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func downloadImage(for cap: Int, version: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool {
|
||||
return download.image(for: cap, version: version, completion: completion)
|
||||
}
|
||||
|
||||
func downloadCapNames(completion: @escaping (_ success: Bool) -> Void) {
|
||||
@ -496,7 +499,7 @@ final class Database {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
for part in caps.split(intoPartsOf: split) {
|
||||
for id in part {
|
||||
let downloading = self.downloadMainImage(for: id) { _ in
|
||||
let downloading = self.downloadImage(for: id) { _ in
|
||||
group.leave()
|
||||
}
|
||||
if downloading {
|
||||
@ -659,6 +662,24 @@ final class Database {
|
||||
}
|
||||
}
|
||||
|
||||
private func uploadNextItem() {
|
||||
let capUploads = self.pendingCapUploads
|
||||
if let id = capUploads.first {
|
||||
|
||||
return
|
||||
}
|
||||
let imageUploads = pendingImageUploads
|
||||
guard imageUploads.count > 0 else {
|
||||
log("No pending image uploads")
|
||||
return
|
||||
}
|
||||
uploadRemainingImages()
|
||||
}
|
||||
|
||||
private func upload(cap: Int) {
|
||||
|
||||
}
|
||||
|
||||
func uploadRemainingData() {
|
||||
guard !isInOfflineMode else {
|
||||
log("Not uploading pending data due to offline mode")
|
||||
@ -673,23 +694,33 @@ final class Database {
|
||||
log("\(uploads.count) cap uploads pending")
|
||||
|
||||
var remaining = uploads.count
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
let group = DispatchGroup()
|
||||
for cap in uploads {
|
||||
upload.upload(name: cap.name, for: cap.id) { success in
|
||||
group.enter()
|
||||
self.upload.upload(name: cap.name, for: cap.id) { success in
|
||||
group.leave()
|
||||
if success {
|
||||
self.log("Uploaded cap \(cap.id)")
|
||||
self.update(uploaded: true, for: cap.id)
|
||||
} else {
|
||||
self.log("Failed to upload cap \(cap.id)")
|
||||
return
|
||||
}
|
||||
|
||||
remaining -= 1
|
||||
if remaining == 0 {
|
||||
|
||||
}
|
||||
guard group.wait(timeout: .now() + .seconds(60)) == .success else {
|
||||
self.log("Timed out uploading cap \(cap.id)")
|
||||
return
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.uploadRemainingImages()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func uploadRemainingImages() {
|
||||
@ -700,18 +731,37 @@ final class Database {
|
||||
}
|
||||
log("\(uploads.count) image uploads pending")
|
||||
|
||||
for (cap, version) in uploads {
|
||||
upload.uploadImage(for: cap, version: version) { count in
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
let group = DispatchGroup()
|
||||
for (id, version) in uploads {
|
||||
guard let cap = self.cap(for: id) else {
|
||||
self.log("No cap \(id) to upload image \(version)")
|
||||
self.removePendingUpload(of: id, version: version)
|
||||
continue
|
||||
}
|
||||
guard cap.uploaded else {
|
||||
self.log("Cap \(id) not uploaded, skipping image upload")
|
||||
continue
|
||||
}
|
||||
group.enter()
|
||||
self.upload.uploadImage(for: id, version: version) { count in
|
||||
group.leave()
|
||||
guard let _ = count else {
|
||||
self.log("Failed to upload version \(version) of cap \(cap)")
|
||||
self.log("Failed to upload version \(version) of cap \(id)")
|
||||
return
|
||||
}
|
||||
self.log("Uploaded version \(version) of cap \(cap)")
|
||||
self.removePendingUpload(of: cap, version: version)
|
||||
self.log("Uploaded version \(version) of cap \(id)")
|
||||
self.removePendingUpload(of: id, version: version)
|
||||
}
|
||||
guard group.wait(timeout: .now() + .seconds(60)) == .success else {
|
||||
self.log("Timed out uploading version \(version) of cap \(id)")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func removePendingUpload(of cap: Int, version: Int) -> Bool {
|
||||
do {
|
||||
|
@ -116,59 +116,35 @@ final class Download {
|
||||
- Parameter cap: The id of the cap.
|
||||
- Parameter version: The image version to download.
|
||||
- Parameter completion: A closure with the resulting image
|
||||
- Note: The closure will be called from the main queue.
|
||||
- Returns: `true`, of the file download was started, `false`, if the image is already downloading.
|
||||
*/
|
||||
@discardableResult
|
||||
func mainImage(for cap: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool {
|
||||
let url = serverImageUrl(for: cap)
|
||||
let query = "Main image of cap \(cap)"
|
||||
func image(for cap: Int, version: Int = 0, completion: @escaping (_ image: UIImage?) -> Void) -> Bool {
|
||||
// Check if main image, and already being downloaded
|
||||
if version == 0 {
|
||||
guard !downloadingMainImages.contains(cap) else {
|
||||
return false
|
||||
}
|
||||
downloadingMainImages.insert(cap)
|
||||
|
||||
let task = session.downloadTask(with: url) { fileUrl, response, error in
|
||||
DispatchQueue.main.async {
|
||||
self.downloadingMainImages.remove(cap)
|
||||
}
|
||||
guard let fileUrl = self.convertResponse(to: query, fileUrl, response, error) else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
guard app.storage.saveImage(at: fileUrl, for: cap) else {
|
||||
self.log("Request '\(query)' could not move downloaded file")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
completion(true)
|
||||
}
|
||||
task.resume()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
Download an image for a cap.
|
||||
- Parameter cap: The id of the cap.
|
||||
- Parameter version: The image version to download.
|
||||
- Parameter completion: A closure with the resulting image
|
||||
- Returns: `true`, of the file download was started, `false`, if the image is already downloading.
|
||||
*/
|
||||
@discardableResult
|
||||
func image(for cap: Int, version: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool {
|
||||
let url = serverImageUrl(for: cap, version: version)
|
||||
let query = "Image of cap \(cap) version \(version)"
|
||||
let task = session.downloadTask(with: url) { fileUrl, response, error in
|
||||
if version == 0 {
|
||||
DispatchQueue.main.async {
|
||||
self.downloadingMainImages.remove(cap)
|
||||
}
|
||||
}
|
||||
guard let fileUrl = self.convertResponse(to: query, fileUrl, response, error) else {
|
||||
completion(false)
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
guard app.storage.saveImage(at: fileUrl, for: cap, version: version) else {
|
||||
guard let image = app.storage.saveImage(at: fileUrl, for: cap, version: version) else {
|
||||
self.log("Request '\(query)' could not move downloaded file")
|
||||
completion(false)
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
completion(true)
|
||||
completion(image)
|
||||
}
|
||||
task.resume()
|
||||
return true
|
||||
|
@ -56,17 +56,17 @@ final class Storage {
|
||||
- parameter version: The version of the image to get
|
||||
- returns: True, if the image was saved
|
||||
*/
|
||||
func saveImage(at url: URL, for cap: Int, version: Int = 0) -> Bool {
|
||||
func saveImage(at url: URL, for cap: Int, version: Int = 0) -> UIImage? {
|
||||
let targetUrl = localImageUrl(for: cap, version: version)
|
||||
do {
|
||||
if fm.fileExists(atPath: targetUrl.path) {
|
||||
try fm.removeItem(at: targetUrl)
|
||||
}
|
||||
try fm.moveItem(at: url, to: targetUrl)
|
||||
return true
|
||||
return UIImage(contentsOfFile: targetUrl.path)
|
||||
} catch {
|
||||
log("Failed to delete or move image \(version) for cap \(cap)")
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,17 +18,6 @@
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>db-n81tx2g638wuffl</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
|
@ -230,8 +230,8 @@ class GridViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func downloadImage(cap id: Int, tile: Int) {
|
||||
app.database.downloadMainImage(for: id) { success in
|
||||
guard success else {
|
||||
app.database.downloadImage(for: id) { img in
|
||||
guard img != nil else {
|
||||
return
|
||||
}
|
||||
guard let view = self.installedTiles[tile] else {
|
||||
|
@ -96,33 +96,28 @@ class ImageSelector: UIViewController {
|
||||
private func downloadImages() {
|
||||
images = [UIImage?](repeating: nil, count: cap.count)
|
||||
log("\(cap.count) images for cap \(cap.id)")
|
||||
if let image = app.storage.image(for: cap.id) {
|
||||
self.images[0] = image
|
||||
self.collection.reloadItems(at: [IndexPath(row: 0, section: 0)])
|
||||
for version in 0..<cap.count {
|
||||
if let image = app.storage.image(for: cap.id, version: version) {
|
||||
log("Image \(version) already downloaded")
|
||||
set(image, for: version)
|
||||
} else {
|
||||
app.database.downloadMainImage(for: cap.id) { success in
|
||||
guard success, let image = app.storage.image(for: self.cap.id) else {
|
||||
return
|
||||
log("Downloading image \(version)")
|
||||
app.database.downloadImage(for: cap.id, version: version) { image in
|
||||
self.set(image, for: version)
|
||||
if image != nil {
|
||||
self.log("Downloaded version \(version)")
|
||||
} else {
|
||||
self.log("Failed to download image \(version)")
|
||||
}
|
||||
self.images[0] = image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func set(_ image: UIImage?, for version: Int) {
|
||||
self.images[version] = image
|
||||
DispatchQueue.main.async {
|
||||
self.collection.reloadItems(at: [IndexPath(row: 0, section: 0)])
|
||||
}
|
||||
}
|
||||
}
|
||||
guard cap.count > 0 else {
|
||||
return
|
||||
}
|
||||
for number in 1..<cap.count {
|
||||
app.database.downloadImage(for: cap.id, version: number) { success in
|
||||
guard success, let image = app.storage.image(for: self.cap.id, version: number) else {
|
||||
return
|
||||
}
|
||||
self.images[number] = image
|
||||
DispatchQueue.main.async {
|
||||
self.collection.reloadItems(at: [IndexPath(row: number, section: 0)])
|
||||
}
|
||||
}
|
||||
self.collection.reloadItems(at: [IndexPath(row: version, section: 0)])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,8 @@ class TableView: UITableViewController {
|
||||
|
||||
private var processingScreenHud: JGProgressHUD?
|
||||
|
||||
private var titleViewUpdateTimer: Timer?
|
||||
|
||||
// MARK: Computed properties
|
||||
|
||||
private var titleText: String {
|
||||
@ -855,7 +857,7 @@ extension TableView {
|
||||
cell.set(image: image)
|
||||
} else {
|
||||
cell.set(image: nil)
|
||||
app.database.downloadMainImage(for: cap.id) { _ in
|
||||
app.database.downloadImage(for: cap.id) { _ in
|
||||
// Delegate call will update image
|
||||
}
|
||||
}
|
||||
@ -1072,4 +1074,13 @@ extension TableView: CapAccessoryDelegate {
|
||||
func capAccessoryCameraButtonPressed() {
|
||||
showCameraView()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension TableView {
|
||||
|
||||
|
||||
private func switchBetweenNavigationTitleViewsIfNeeded() {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable class CropView: UIView {
|
||||
//@IBDesignable
|
||||
class CropView: UIView {
|
||||
|
||||
@IBInspectable var lineColor: UIColor = UIColor.black
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user