Skip to content

Commit 68b1d35

Browse files
Merge pull request #185 from ngageoint/develop (iOS Mage 4.4.0 Release)
iOS Mage 4.4.0 Release
2 parents 9b42a5a + 0e66b8a commit 68b1d35

File tree

247 files changed

+6739
-3177
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

247 files changed

+6739
-3177
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @ngageoint/mage

MAGE.xcodeproj/project.pbxproj

Lines changed: 170 additions & 161 deletions
Large diffs are not rendered by default.

MAGE.xctestplan

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,21 @@
3838
},
3939
"testTargets" : [
4040
{
41-
"parallelizable" : true,
41+
"parallelizable" : false,
4242
"skippedTests" : [
43+
"AttachmentFieldViewTests\/testSetOneAttachmentThatIsSyncedAndOneThatIsNot()",
44+
"AttachmentFieldViewTests\/testSetOneAttachmentThatIsSyncedAndOneThatIsNotDifferentOrder()",
45+
"AttachmentFieldViewTests\/testSetOneAttachmentWithObservationAndTwoLater()",
46+
"AttachmentFieldViewTests\/testSetOneAttachmentWithObservationAndTwoLaterThenRemoveFirst()",
47+
"AttachmentFieldViewTests\/testSetOneAttachmentWithObservationAndTwoLaterThenRemoveSecond()",
48+
"AttachmentFieldViewTests\/testShouldCallTheAttachmentSelectionDelegateOnTap()",
4349
"AttachmentPushServiceTests",
50+
"AuthenticationCoordinatorTests",
4451
"CanCreateObservationTests\/testInitializeTheCanCreateObservationAndLongPressTheMap()",
52+
"EventChooserControllerTests",
4553
"EventChooserCoordinatorTests",
4654
"FeedItemsViewControllerNoTimestampTests",
47-
"FeedItemsViewControllerNoTimestampTests\/testOneFeedItemWithPrimaryAndSecondaryValueAndIcon()",
48-
"FeedItemsViewControllerNoTimestampTests\/testOneFeedItemWithPrimaryValue()",
55+
"FeedItemsViewControllerNoTimestampTests\/testOneFeedItemNoContent()",
4956
"FeedItemsViewControllerWithTimestampTests",
5057
"FeedItemsViewControllerWithTimestampTests\/testOneFeedItemWithPrimaryAndSecondaryValueAndIconWithoutTimestamp()",
5158
"MageServerTestsSwift\/testShouldFailWhenAnImageIsrReturned()",
@@ -68,6 +75,7 @@
6875
"parallelizable" : false,
6976
"skippedTests" : [
7077
"GeoPackageImporterTests\/testImportGeoPackageFileIntoLayer()",
78+
"GeoPackageImporterUITests\/testShouldHandleGeoPackageImportTwiceOverwrite()",
7179
"GeoPackageLayerMapTests\/testInitializeTheStaticLayerMapWithANotLoadedLayerThenLoadItButDontAddItToTheMap()",
7280
"GeoPackageTests\/testAddGeoPackageTileLayerThenReAdd()",
7381
"GeoPackageTests\/testGetFeatureKeys()"

Mage/AskToDownloadViewController.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ import Kingfisher
101101

102102
self.downloadBlock.isHidden = true;
103103
if (!DataConnectionUtilities.shouldFetchAttachments()) {
104-
if (self.attachment.contentType?.hasPrefix("image") == true) {
104+
if (self.attachment.isImage) {
105105
self.descriptionLabel.text = "Your attachment fetch settings do not allow auto downloading full size images. Would you like to view the image?";
106-
} else if (self.attachment.contentType?.hasPrefix("video") == true) {
106+
} else if (self.attachment.isVideo) {
107107
self.descriptionLabel.text = String.init(format: "Your attachment fetch settings do not allow auto downloading of videos. This video is %.2FMB. Would you like to view the video?", (self.attachment.size!.doubleValue / (1024.0 * 1024.0)));
108108
}
109109
}

Mage/AttachmentCell.swift

Lines changed: 210 additions & 123 deletions
Large diffs are not rendered by default.

Mage/AttachmentCreationCoordinator.swift

Lines changed: 56 additions & 91 deletions
Large diffs are not rendered by default.

Mage/AttachmentFieldView.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import UIKit
1717
@objc func addGalleryAttachment();
1818
}
1919

20-
class AttachmentFieldView : BaseFieldView {
20+
class AttachmentFieldView : BaseFieldView, UICollectionViewDelegate {
2121
private var attachments: [AttachmentModel]?;
2222
private weak var attachmentSelectionDelegate: AttachmentSelectionDelegate?;
2323
var attachmentCreationCoordinator: AttachmentCreationCoordinator?;
@@ -42,10 +42,8 @@ class AttachmentFieldView : BaseFieldView {
4242
var cv: UICollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout);
4343
cv.configureForAutoLayout();
4444
cv.register(AttachmentCell.self, forCellWithReuseIdentifier: "AttachmentCell");
45-
cv.delegate = attachmentCollectionDataStore;
45+
cv.delegate = self;
4646
cv.dataSource = attachmentCollectionDataStore;
47-
cv.accessibilityLabel = "Attachment Collection";
48-
cv.accessibilityIdentifier = "Attachment Collection";
4947
attachmentCollectionDataStore.attachmentCollection = cv;
5048
return cv;
5149
}();

Mage/AttachmentFieldViewSwiftUI.swift

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,64 +8,84 @@
88

99
import Foundation
1010
import SwiftUI
11-
import Kingfisher
1211
import Combine
1312

1413
class AttachmentFieldViewModel: ObservableObject {
15-
@Injected(\.attachmentRepository)
16-
var repository: AttachmentRepository
14+
@Injected(\.attachmentRepository) var repository: AttachmentRepository
1715

18-
@Published
19-
var attachments: [AttachmentModel]?
20-
21-
@Published
22-
var fieldTitle: String
16+
@Published var attachments: [AttachmentModel] = []
17+
@Published var fieldTitle: String
2318

2419
var cancellable = Set<AnyCancellable>()
2520

2621
init(observationUri: URL?, observationFormId: String, fieldName: String, fieldTitle: String) {
2722
self.fieldTitle = fieldTitle
28-
self.repository.observeAttachments(
23+
24+
repository.observeAttachments(
2925
observationUri: observationUri,
3026
observationFormId: observationFormId,
3127
fieldName: fieldName
32-
)?
28+
)
3329
.receive(on: DispatchQueue.main)
34-
.sink { changes in
35-
var attachments: [AttachmentModel] = []
36-
for change in changes {
37-
switch (change) {
38-
case .insert(offset: _, element: let element, associatedWith: _):
39-
attachments.append(element)
40-
case .remove(offset: _, element: let element, associatedWith: _):
41-
attachments.removeAll { model in
42-
model.attachmentUri == element.attachmentUri
43-
}
30+
.scan([AttachmentModel]()) { current, diff in
31+
Self.apply(diff: diff, to: current)
32+
}
33+
.sink { [weak self] snapshot in
34+
self?.attachments = snapshot
35+
}
36+
.store(in: &cancellable)
37+
}
38+
39+
// Apply a CollectionDifference to an Array without using .applying(_:)
40+
private static func apply(
41+
diff: CollectionDifference<AttachmentModel>,
42+
to base: [AttachmentModel]
43+
) -> [AttachmentModel] {
44+
var result = base
45+
46+
for change in diff {
47+
switch change {
48+
case let .insert(offset, element, _):
49+
// If offset is within bounds, respect it; otherwise append.
50+
if offset <= result.count {
51+
result.insert(element, at: offset)
52+
} else {
53+
result.append(element)
54+
}
55+
56+
case let .remove(_, element, _):
57+
// Prefer identity by attachmentUri; fall back to equality if needed.
58+
if let idx = result.firstIndex(where: { $0.attachmentUri == element.attachmentUri }) {
59+
result.remove(at: idx)
60+
} else if let idx = result.firstIndex(of: element) { // if Equatable
61+
result.remove(at: idx)
4462
}
4563
}
46-
self.attachments = attachments
4764
}
48-
.store(in: &cancellable)
65+
66+
return result
4967
}
68+
5069

5170
func appendAttachmentViewRoute(router: MageRouter, attachment: AttachmentModel) {
5271
repository.appendAttachmentViewRoute(router: router, attachment: attachment)
5372
}
5473

55-
var orderedAttachments: [AttachmentModel]? {
56-
return attachments?.sorted(by: { first, second in
74+
var orderedAttachments: [AttachmentModel] {
75+
attachments.sorted { first, second in
5776
let firstOrder = first.order.intValue
5877
let secondOrder = second.order.intValue
59-
return (firstOrder != secondOrder) ? (firstOrder < secondOrder) : (first.lastModified ?? Date()) < (second.lastModified ?? Date())
60-
})
78+
79+
if firstOrder != secondOrder { return firstOrder < secondOrder }
80+
81+
return (first.lastModified ?? Date()) < (second.lastModified ?? Date())
82+
}
6183
}
6284
}
6385

6486
struct AttachmentFieldViewSwiftUI: View {
6587
@StateObject var viewModel: AttachmentFieldViewModel
66-
67-
@EnvironmentObject
68-
var router: MageRouter
88+
@EnvironmentObject var router: MageRouter
6989

7090
var selectedUnsentAttachment: (_ localPath: String, _ contentType: String) -> Void
7191

@@ -75,12 +95,12 @@ struct AttachmentFieldViewSwiftUI: View {
7595
]
7696

7797
var body: some View {
78-
if !(viewModel.attachments ?? []).isEmpty {
98+
if !viewModel.attachments.isEmpty {
7999
VStack(alignment: .leading, spacing: 2) {
80100
Text(viewModel.fieldTitle)
81101
.secondaryText()
82102
LazyVGrid(columns:layout) {
83-
ForEach(viewModel.orderedAttachments ?? []) { attachment in
103+
ForEach(viewModel.orderedAttachments) { attachment in
84104
AttachmentPreviewView(attachment: attachment) {
85105
viewModel.appendAttachmentViewRoute(router: router, attachment: attachment)
86106
}

Mage/AttachmentSlideshow.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,10 @@ class AttachmentSlideShow: UIView {
211211
guard let imageView = imageView else {
212212
return;
213213
}
214-
if (attachment.contentType?.hasPrefix("image") ?? false) {
214+
if attachment.isImage {
215215
imageView.setAttachment(attachment: attachment);
216216
showThumbnail(imageView: imageView);
217-
} else if (attachment.contentType?.hasPrefix("video") ?? false) {
217+
} else if attachment.isVideo {
218218
let url = self.getAttachmentUrl(attachment: attachment);
219219
imageView.setAttachment(attachment: attachment);
220220
let localPath: String? = (attachment.localPath != nil && FileManager.default.fileExists(atPath: attachment.localPath!)) ? attachment.localPath : nil
@@ -245,7 +245,7 @@ class AttachmentSlideShow: UIView {
245245
}
246246
});
247247
}
248-
} else if (attachment.contentType?.hasPrefix("audio") ?? false) {
248+
} else if attachment.isAudio {
249249
imageView.image = UIImage(systemName: "speaker.wave.2.fill");
250250
imageView.contentMode = .scaleAspectFit;
251251
imageView.tintColor = scheme?.colorScheme.onSurfaceColor.withAlphaComponent(0.4)

0 commit comments

Comments
 (0)