Fix: Add new cap without internet access
This commit is contained in:
parent
c13dd8b080
commit
02e8e9a585
@ -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
|
Cap.all[self.id] = self
|
||||||
guard success else { return }
|
Cap.tiles[self.id] = self
|
||||||
Cap.all[self.id] = self
|
Cap.shouldCreateFolderForCap(self.id)
|
||||||
Cap.tiles[self.id] = self
|
Cap.save()
|
||||||
Cap.save()
|
Cap.delegate?.capHasUpdates(self)
|
||||||
Cap.updateMosaicWithNewCap(id: self.id, image)
|
//Cap.updateMosaicWithNewCap(id: self.id, image)
|
||||||
Cap.delegate?.capHasUpdates(self)
|
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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user