Fix: Add new cap without internet access

This commit is contained in:
Christoph Hagen 2019-04-12 13:46:18 +02:00
parent c13dd8b080
commit 02e8e9a585
2 changed files with 54 additions and 163 deletions

View File

@ -154,18 +154,21 @@ final class Cap {
self.id = Cap.nextUnusedId self.id = Cap.nextUnusedId
self.tile = id - 1 self.tile = id - 1
self.name = name self.name = name
self.count = 1 self.count = 0
self.cleanName = name.clean self.cleanName = name.clean
guard save(mainImage: image) else { guard save(mainImage: image) else {
return nil return nil
} }
upload(mainImage: image) { success in
guard success else { return }
Cap.all[self.id] = self Cap.all[self.id] = self
Cap.tiles[self.id] = self Cap.tiles[self.id] = self
Cap.shouldCreateFolderForCap(self.id)
Cap.save() Cap.save()
Cap.updateMosaicWithNewCap(id: self.id, image)
Cap.delegate?.capHasUpdates(self) Cap.delegate?.capHasUpdates(self)
//Cap.updateMosaicWithNewCap(id: self.id, image)
DispatchQueue.global(qos: .userInitiated).async {
self.add(image: image) { _ in
}
} }
} }
@ -299,7 +302,7 @@ final class Cap {
} }
func add(image: UIImage, completion: @escaping (Bool) -> Void) { func add(image: UIImage, completion: @escaping (Bool) -> Void) {
upload(image: image) { saved in self.upload(image: image) { saved in
guard saved else { guard saved else {
completion(false) completion(false)
return return
@ -312,13 +315,6 @@ final class Cap {
// MARK: - Image upload // MARK: - Image upload
private func upload(mainImage: UIImage, completion: @escaping (_ success: Bool) -> Void) {
self.createFolder { created in
guard created else { return }
self.upload(image: mainImage, number: 0, savedCallback: completion)
}
}
private func folderExists(completion: @escaping (_ exists: Bool?) -> Void) { private func folderExists(completion: @escaping (_ exists: Bool?) -> Void) {
let path = "/Images" let path = "/Images"
DropboxController.client.files.listFolder(path: path).response { response, error in DropboxController.client.files.listFolder(path: path).response { response, error in
@ -338,15 +334,20 @@ final class Cap {
} }
} }
private func createFolder(completion: @escaping (_ success: Bool) -> Void) { private static func createFolder(forCap cap: Int, completion: @escaping (_ success: Bool) -> Void) {
guard shouldCreateFolder(forCap: cap) else {
completion(true)
return
}
// Create folder for cap // Create folder for cap
let path = "/Images/\(id)" let path = "/Images/\(cap)"
DropboxController.client.files.createFolderV2(path: path).response { _, error in DropboxController.client.files.createFolderV2(path: path).response { _, error in
if let err = error { if let err = error {
self.event("Could not create folder for new cap \(self.id): \(err)") self.event("Could not create folder for cap \(cap): \(err)")
completion(false) completion(false)
} else { } else {
self.event("Created folder for new cap \(self.id)") self.event("Created folder for cap \(cap)")
didCreateFolder(forCap: cap)
completion(true) completion(true)
} }
} }
@ -372,13 +373,19 @@ final class Cap {
event("Saved image \(number) for cap \(id) for upload") event("Saved image \(number) for cap \(id) for upload")
savedCallback(true) savedCallback(true)
Cap.upload(url: url) { success in Cap.uploadCapImage(at: url, forCap: id) { success in
} }
} }
private static func upload(url: URL, completion: @escaping (Bool) -> Void) { private static func uploadCapImage(at url: URL, forCap cap: Int, completion: @escaping (Bool) -> Void) {
let cap = Int(url.lastPathComponent.components(separatedBy: "-").first!)! createFolder(forCap: cap) { created in
guard created else { return }
uploadCapImage(at: url, forCapWithExistingFolder: cap, completion: completion)
}
}
private static func uploadCapImage(at url: URL, forCapWithExistingFolder cap: Int, completion: @escaping (Bool) -> Void) {
let path = "/Images/\(cap)/" + url.lastPathComponent let path = "/Images/\(cap)/" + url.lastPathComponent
let data: Data let data: Data
@ -417,7 +424,8 @@ final class Cap {
event("\(list.count) image uploads pending") event("\(list.count) image uploads pending")
for url in list { for url in list {
upload(url: url) { didUpload in let cap = Int(url.lastPathComponent.components(separatedBy: "-").first!)!
uploadCapImage(at: url, forCap: cap) { didUpload in
// Delete image from disk if uploaded // Delete image from disk if uploaded
guard didUpload else { guard didUpload else {
self.error("Could not upload image at url \(url)") self.error("Could not upload image at url \(url)")
@ -505,9 +513,6 @@ final class Cap {
return return
} }
self.readNames(from: lines) self.readNames(from: lines)
DispatchQueue.main.async {
createAndSaveMosaic()
}
} }
} }
@ -551,150 +556,27 @@ final class Cap {
return capList(sortedBy: .id, ascending: true).reduce("") { $0 + $1.description } return capList(sortedBy: .id, ascending: true).reduce("") { $0 + $1.description }
} }
// MARK: - GridView // MARK: - Folders to upload
private static func size(for tiles: Int) -> CGSize { static func shouldCreateFolderForCap(_ cap: Int) {
let columns = CGFloat(mosaicColumns) let oldCaps = Persistence.folderNotCreated
// Add half of a cell due to row shift guard !oldCaps.contains(cap) else {
let width = (columns + 0.5) * mosaicCellSize
let rows = (CGFloat(tiles) / columns).rounded(.up)
// Add margin because the last row does not overlap
let height = rows * mosaicRowHeight + mosaicMargin
return CGSize(width: width, height: height)
}
static func origin(for tile: Int) -> CGPoint {
let row = tile / mosaicColumns
let column = tile - row * mosaicColumns
let x = ( CGFloat(column) + (row.isEven ? 0 : 0.5) ) * mosaicCellSize
let y = CGFloat(row) * mosaicRowHeight
return CGPoint(x: x, y: y)
}
private static func makeTile(_ tile: Int, image: UIImage) -> RoundedImageView {
let point = origin(for: tile)
let frame = CGRect(origin: point, size: CGSize(width: mosaicCellSize, height: mosaicCellSize))
let view = RoundedImageView(frame: frame)
view.image = image
return view
}
private static func makeMosaicCanvas() -> UIView {
let view = UIView(frame: CGRect(origin: .zero, size: size(for: Cap.totalCapCount)))
view.backgroundColor = UIColor(red: 36/255, green: 36/255, blue: 36/255, alpha: 1)
return view
}
private static func makeMosaic() -> UIImage? {
let canvas = makeMosaicCanvas()
for cap in Cap.all.values {
if let img = cap.image {
let view = makeTile(cap.id - 1, image: img)
canvas.addSubview(view)
} else {
error("No image for cap \(cap.id)")
}
}
return render(view: canvas)
}
private static func render(view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0)
defer { UIGraphicsEndImageContext() }
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
return UIGraphicsGetImageFromCurrentImageContext()
}
private static func createAndSaveMosaic() {
guard !DiskManager.mosaicExists else {
event("Mosaic already created")
return return
} }
updateMosaic() let newCaps = oldCaps + [cap]
Persistence.folderNotCreated = newCaps
} }
static var mosaic: UIImage? { static func shouldCreateFolder(forCap cap: Int) -> Bool {
guard let data = DiskManager.mosaicData else { return Persistence.folderNotCreated.contains(cap)
error("No mosaic data on disk")
return nil
}
guard let image = UIImage(data: data, scale: UIScreen.main.scale) else {
error("Failed to create image from mosaic data")
return nil
}
return image
} }
private static func updateMosaicWithNewCap(id: Int, _ image: UIImage) { static func didCreateFolder(forCap cap: Int) {
guard let old = mosaic else { return } let oldCaps = Persistence.folderNotCreated
guard oldCaps.contains(cap) else {
let view = UIImageView(image: old)
let canvas = makeMosaicCanvas()
let tile = makeTile(id - 1, image: image)
canvas.addSubview(view)
canvas.addSubview(tile)
guard let img = render(view: canvas) else {
error("Failed to update mosaic for cap \(id)")
return return
} }
saveMosaic(img) Persistence.folderNotCreated = oldCaps.filter { $0 != cap }
}
static func tile(for point: CGPoint) -> Int? {
let s = point.y.truncatingRemainder(dividingBy: mosaicRowHeight)
let row = Int(point.y / mosaicRowHeight)
guard s > mosaicMargin else {
return nil
}
let column: CGFloat
if row.isEven {
column = point.x / mosaicCellSize
} else {
column = (point.x - mosaicCellSize / 2) / mosaicCellSize
}
return row * mosaicColumns + Int(column)
}
static func switchTilesInMosaic(_ mosaic: UIImageView, tile1: Int, tile2: Int) {
let tileView1 = makeTile(tile1, image: Cap.tileImage(tile: tile1)!)
let tileView2 = makeTile(tile2, image: Cap.tileImage(tile: tile2)!)
mosaic.addSubview(tileView1)
mosaic.addSubview(tileView2)
defer {
tileView1.removeFromSuperview()
tileView2.removeFromSuperview()
}
guard let img = render(view: mosaic) else {
error("Failed to switch \(tile1) and \(tile2) in mosaic")
return
}
mosaic.image = img
saveMosaic(img)
}
static func updateMosaic() {
guard let image = makeMosaic() else {
error("No mosaik image created")
return
}
saveMosaic(image)
}
static func saveMosaic(_ image: UIImage) {
guard let data = image.pngData() else {
error("Failed to convert mosaic to data")
return
}
guard DiskManager.saveMosaicData(data) else {
error("Failed to write mosaic to disk")
return
}
event("Mosaic saved")
} }
} }

View File

@ -75,4 +75,13 @@ final class Persistence {
UserDefaults.standard.set(newValue, forKey: "xcode") UserDefaults.standard.set(newValue, forKey: "xcode")
} }
} }
static var folderNotCreated: [Int] {
get {
return UserDefaults.standard.array(forKey: "folders") as? [Int] ?? []
}
set {
UserDefaults.standard.set(newValue, forKey: "folders")
}
}
} }