Skip to content

Commit 80fba53

Browse files
committed
Dodo codes: add comments
1 parent 154e105 commit 80fba53

File tree

12 files changed

+393
-6
lines changed

12 files changed

+393
-6
lines changed

ACHNBrowserUI/ACHNBrowserUI.xcodeproj/project.pbxproj

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@
179179
69CFCAF9245ADDA80059C067 /* photos in Resources */ = {isa = PBXBuildFile; fileRef = 69CFCA9B245AAF520059C067 /* photos */; };
180180
69CFCAFB245ADDA80059C067 /* fish in Resources */ = {isa = PBXBuildFile; fileRef = 69CFCA9D245AAF520059C067 /* fish */; };
181181
69CFCAFC245ADDA80059C067 /* rugs in Resources */ = {isa = PBXBuildFile; fileRef = 69CFCA9E245AAF520059C067 /* rugs */; };
182+
69D44C1D2497462A00378FDD /* DodoCodeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D44C1C2497462A00378FDD /* DodoCodeDetailView.swift */; };
183+
69D44C1F249746F100378FDD /* DodoCodeDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D44C1E249746F100378FDD /* DodoCodeDetailViewModel.swift */; };
182184
69E4BBB2245AE06B001035E2 /* UI in Frameworks */ = {isa = PBXBuildFile; productRef = 69E4BBB1245AE06B001035E2 /* UI */; };
183185
69E4BBB4245AE472001035E2 /* ItemRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E4BBB3245AE472001035E2 /* ItemRow.swift */; };
184186
69E4BBB6245AE5C9001035E2 /* ItemDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E4BBB5245AE5C9001035E2 /* ItemDetailView.swift */; };
@@ -387,6 +389,8 @@
387389
69CFCAD2245ADC7A0059C067 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
388390
69CFCAD4245ADC7A0059C067 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
389391
69CFCADA245ADC9C0059C067 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
392+
69D44C1C2497462A00378FDD /* DodoCodeDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DodoCodeDetailView.swift; sourceTree = "<group>"; };
393+
69D44C1E249746F100378FDD /* DodoCodeDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DodoCodeDetailViewModel.swift; sourceTree = "<group>"; };
390394
69E4BBAE245ADF26001035E2 /* UI */ = {isa = PBXFileReference; lastKnownFileType = folder; name = UI; path = ACHNBrowserUI/packages/UI; sourceTree = "<group>"; };
391395
69E4BBB3245AE472001035E2 /* ItemRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemRow.swift; sourceTree = "<group>"; };
392396
69E4BBB5245AE5C9001035E2 /* ItemDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailView.swift; sourceTree = "<group>"; };
@@ -771,6 +775,7 @@
771775
AE2562F324841B48008B0779 /* DesignListViewModel.swift */,
772776
694C8B4624861D4E000B9B5F /* LikeButtonViewModel.swift */,
773777
69855148248A281300F7BDBA /* TabbarViewModel.swift */,
778+
69D44C1E249746F100378FDD /* DodoCodeDetailViewModel.swift */,
774779
);
775780
path = viewModels;
776781
sourceTree = "<group>";
@@ -956,6 +961,7 @@
956961
69E67EF12493833200ED7AC8 /* DodoCodeFormView.swift */,
957962
69E67EED24935DD200ED7AC8 /* DodoCodeListView.swift */,
958963
69E67EEF249376DF00ED7AC8 /* DodoCodeRow.swift */,
964+
69D44C1C2497462A00378FDD /* DodoCodeDetailView.swift */,
959965
);
960966
path = dodocode;
961967
sourceTree = "<group>";
@@ -1186,6 +1192,7 @@
11861192
69157B7F2471A5A1005B9002 /* TodayNookazonSection.swift in Sources */,
11871193
69157B802471A5A1005B9002 /* UserListFormView.swift in Sources */,
11881194
69157B812471A5A1005B9002 /* RowLoadingView.swift in Sources */,
1195+
69D44C1F249746F100378FDD /* DodoCodeDetailViewModel.swift in Sources */,
11891196
157C03E7247AE56600568917 /* TodaySectionView.swift in Sources */,
11901197
69157B822471A5A1005B9002 /* ItemDetailViewModel.swift in Sources */,
11911198
6985514C248A297400F7BDBA /* PlayerView.swift in Sources */,
@@ -1213,6 +1220,7 @@
12131220
69157B922471A5A1005B9002 /* TurnipsChart.swift in Sources */,
12141221
69157B932471A5A1005B9002 /* TabbarView.swift in Sources */,
12151222
69157B942471A5A1005B9002 /* ActivityControllerView.swift in Sources */,
1223+
69D44C1D2497462A00378FDD /* DodoCodeDetailView.swift in Sources */,
12161224
3D7A1786247B21D200DB873D /* CustomTasksListView.swift in Sources */,
12171225
69157B952471A5A1005B9002 /* ItemsCrosslineSectionView.swift in Sources */,
12181226
69867DF1247C136900C6A759 /* CollectionProgressRowViewModel.swift in Sources */,
@@ -1382,7 +1390,7 @@
13821390
CODE_SIGN_ENTITLEMENTS = ACHNBrowserUI/ACHNBrowserUI.entitlements;
13831391
CODE_SIGN_IDENTITY = "Apple Development: Thomas Ricouard (7MB55D6BJ5)";
13841392
CODE_SIGN_STYLE = Manual;
1385-
CURRENT_PROJECT_VERSION = 061412020;
1393+
CURRENT_PROJECT_VERSION = 061512020;
13861394
DEVELOPMENT_ASSET_PATHS = "\"ACHNBrowserUI/Preview Content\"";
13871395
DEVELOPMENT_TEAM = Z6P74P6T99;
13881396
ENABLE_PREVIEWS = YES;
@@ -1392,7 +1400,7 @@
13921400
"$(inherited)",
13931401
"@executable_path/Frameworks",
13941402
);
1395-
MARKETING_VERSION = 1.7.4;
1403+
MARKETING_VERSION = 1.7.5;
13961404
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.ACNH;
13971405
PRODUCT_NAME = "AC Helper";
13981406
PROVISIONING_PROFILE_SPECIFIER = "AC Dev";
@@ -1413,7 +1421,7 @@
14131421
CODE_SIGN_IDENTITY = "iPhone Developer";
14141422
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
14151423
CODE_SIGN_STYLE = Manual;
1416-
CURRENT_PROJECT_VERSION = 061412020;
1424+
CURRENT_PROJECT_VERSION = 061512020;
14171425
DEVELOPMENT_ASSET_PATHS = "\"ACHNBrowserUI/Preview Content\"";
14181426
DEVELOPMENT_TEAM = Z6P74P6T99;
14191427
ENABLE_PREVIEWS = YES;
@@ -1423,7 +1431,7 @@
14231431
"$(inherited)",
14241432
"@executable_path/Frameworks",
14251433
);
1426-
MARKETING_VERSION = 1.7.4;
1434+
MARKETING_VERSION = 1.7.5;
14271435
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.ACNH;
14281436
PRODUCT_NAME = "AC Helper";
14291437
PROVISIONING_PROFILE_SPECIFIER = "AC Dev";

ACHNBrowserUI/ACHNBrowserUI/SceneDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
2828
.environmentObject(MusicPlayerManager.shared)
2929
.environmentObject(AppUserDefaults.shared)
3030
.environmentObject(DodoCodeService.shared)
31+
.environmentObject(CommentService.shared)
3132

3233
if let windowScene = scene as? UIWindowScene {
3334

ACHNBrowserUI/ACHNBrowserUI/fr.lproj/Localizable.strings

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,3 +865,13 @@ Merci d'être respecteux. Tout code signalé sera supprimé";
865865
"To help us support the application and get notifications when a new Dodo code is posted, you can try out AC Helper+" = "Pour nous aider à supporter l'application et recevoir des notifications quand un nouveau Dodo codes est posté, vous pouvez souscrire à AC Helper+";
866866
"ACDodoCode.notification.title" = "Un nouveau Dodo code est disponible";
867867
"ACDodoCode.notification.subtitle" = "Quelqu'un a ouvert ses portes et a posté un Dodo code dans l'application!";
868+
869+
// dodo code comments
870+
"Comments" = "Commentaires";
871+
"Be the first to leave a comment!" = "Soyez le premier à laisser un commentaire !";
872+
"%@ from %@" = "%@ de %@";
873+
"In order to add a comment you must add an island name and a username. Tap here to add them in settings" = "Pour ajouter un nouveau commentaire vous devez ajouter votre nom d'île et un nom d'utilisateur dans les réglages. Tapez ici pour les ajouter";
874+
"Add your comment" = "Ajouter votre commentaire";
875+
"Send" = "Envoyer";
876+
"In game name / username" = "Nom du joueur / nom d'utilisateur";
877+
"You need to have an iCloud account in your device settings in order to add comments" = "Vous devez avoir un compte iCloud dans les réglages de votre appareil pour ajouter des commentaires"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Thomas Ricouard on 15/06/2020.
6+
//
7+
8+
import Foundation
9+
import CloudKit
10+
11+
public struct Comment: Equatable, Identifiable {
12+
public let id: String
13+
public let text: String
14+
public let name: String
15+
public let islandName: String
16+
public let isMine: Bool
17+
18+
public var record: CKRecord?
19+
public var owner: CKRecord.Reference?
20+
21+
public var creationDate: Date {
22+
record?.creationDate ?? Date()
23+
}
24+
25+
enum RecordKeys: String {
26+
case id, text, name, islandName, owner
27+
}
28+
29+
public init(text: String, name: String, islandName: String) {
30+
self.id = UUID().uuidString
31+
self.text = text
32+
self.name = name
33+
self.islandName = islandName
34+
self.isMine = true
35+
}
36+
37+
init(withRecord record: CKRecord) {
38+
self.id = record[RecordKeys.id.rawValue] as? String ?? ""
39+
self.name = record[RecordKeys.name.rawValue] as? String ?? ""
40+
self.islandName = record[RecordKeys.islandName.rawValue] as? String ?? ""
41+
self.text = record[RecordKeys.text.rawValue] as? String ?? ""
42+
self.owner = record[RecordKeys.owner.rawValue] as? CKRecord.Reference
43+
self.isMine = record.creatorUserRecordID?.recordName.contains("defaultOwner") == true
44+
self.record = record
45+
}
46+
47+
func toRecord(owner: CKRecord) -> CKRecord {
48+
let record = self.record ?? CKRecord(recordType: CommentService.recordType)
49+
record[RecordKeys.id.rawValue] = id
50+
record[RecordKeys.text.rawValue] = text
51+
record[RecordKeys.name.rawValue] = name
52+
record[RecordKeys.islandName.rawValue] = islandName
53+
record[RecordKeys.owner.rawValue] = CKRecord.Reference(record: owner, action: .deleteSelf)
54+
return record
55+
}
56+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Thomas Ricouard on 15/06/2020.
6+
//
7+
8+
import Foundation
9+
import CloudKit
10+
11+
public class CommentService: ObservableObject {
12+
13+
// MARK: - Vars
14+
public static let shared = CommentService()
15+
public static let recordType = "ACComment"
16+
17+
@Published public var comments: [CKRecord.ID: [Comment]] = [:]
18+
@Published public var mostRecentError: Error?
19+
20+
private var cloudKitDatabase = CKContainer.default().publicCloudDatabase
21+
22+
public func addComment(comment: Comment, owner: CKRecord) {
23+
let record = comment.toRecord(owner: owner)
24+
var comment = comment
25+
let operation = CKModifyRecordsOperation(recordsToSave: [record], recordIDsToDelete: nil)
26+
operation.modifyRecordsCompletionBlock = { saved, _, error in
27+
if let realRecord = saved?.first {
28+
comment.record = realRecord
29+
DispatchQueue.main.async {
30+
if var comments = self.comments[owner.recordID] {
31+
comments.insert(comment, at: 0)
32+
self.comments[owner.recordID] = comments
33+
} else {
34+
self.comments[owner.recordID] = [comment]
35+
}
36+
}
37+
}
38+
DispatchQueue.main.async {
39+
self.setError(error: error)
40+
}
41+
}
42+
cloudKitDatabase.add(operation)
43+
}
44+
45+
public func fetchComments(record: CKRecord) {
46+
let reference = CKRecord.Reference(record: record, action: .deleteSelf)
47+
let predicate = NSPredicate(format: "owner == %@", reference)
48+
let sort = NSSortDescriptor(key: "creationDate", ascending: false)
49+
let query = CKQuery(recordType: Self.recordType, predicate: predicate)
50+
query.sortDescriptors = [sort]
51+
cloudKitDatabase.perform(query, inZoneWith: nil) { (records, error) in
52+
self.setError(error: error)
53+
if let records = records {
54+
let comments = records.map{ Comment(withRecord: $0) }
55+
DispatchQueue.main.async {
56+
self.comments[record.recordID] = comments
57+
}
58+
}
59+
}
60+
}
61+
62+
public func deleteComment(comment: Comment, owner: CKRecord) {
63+
if let record = comment.record {
64+
let operation = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: [record.recordID])
65+
operation.modifyRecordsCompletionBlock = { _, _, _ in
66+
self.fetchComments(record: owner)
67+
}
68+
cloudKitDatabase.add(operation)
69+
}
70+
}
71+
72+
private func setError(error: Error?) {
73+
DispatchQueue.main.async {
74+
self.mostRecentError = error
75+
}
76+
}
77+
}

ACHNBrowserUI/ACHNBrowserUI/packages/Backend/Sources/Backend/services/DodoCodeService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public class DodoCodeService: ObservableObject {
7171
}
7272

7373
public func upvote(code: DodoCode) {
74-
if var record = code.record {
74+
if let record = code.record {
7575
record[DodoCode.RecordKeys.upvotes.rawValue] = code.upvotes + 1
7676
let operation = CKModifyRecordsOperation(recordsToSave: [record],
7777
recordIDsToDelete: nil)

ACHNBrowserUI/ACHNBrowserUI/packages/Backend/Sources/Backend/utils/AppUserDefault.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ public class AppUserDefaults: ObservableObject {
1919
}
2020
}
2121

22+
@UserDefault("game_name", defaultValue: "")
23+
public var inGameName: String {
24+
willSet {
25+
objectWillChange.send()
26+
}
27+
}
28+
2229
@UserDefault("friend_code", defaultValue: "")
2330
public var friendCode: String {
2431
willSet {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// DodoCodeDetailViewModel.swift
3+
// ACHNBrowserUI
4+
//
5+
// Created by Thomas Ricouard on 15/06/2020.
6+
// Copyright © 2020 Thomas Ricouard. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import SwiftUI
11+
import Combine
12+
import Backend
13+
14+
class DodoCodeDetailViewModel: ObservableObject {
15+
let code: DodoCode
16+
17+
@Published var comments: [Comment] = []
18+
@Published var isLoading = false
19+
@Published var isPosting = false
20+
@Published var inDeletion: String?
21+
22+
private let commentService: CommentService
23+
private var commentsCancellable: AnyCancellable?
24+
25+
init(code: DodoCode, commentService: CommentService = .shared) {
26+
self.code = code
27+
self.commentService = commentService
28+
29+
if let record = code.record {
30+
self.commentsCancellable = commentService.$comments.sink { comments in
31+
DispatchQueue.main.async {
32+
self.comments = comments[record.recordID] ?? []
33+
self.isLoading = false
34+
self.isPosting = false
35+
self.inDeletion = nil
36+
}
37+
}
38+
}
39+
}
40+
41+
func fetchComments() {
42+
isLoading = true
43+
if let record = code.record {
44+
commentService.fetchComments(record: record)
45+
}
46+
}
47+
48+
func addComment(comment: Comment) {
49+
isPosting = true
50+
if let record = code.record {
51+
commentService.addComment(comment: comment,
52+
owner: record)
53+
}
54+
}
55+
56+
func deleteComment(comment: Comment) {
57+
if let record = code.record {
58+
isLoading = true
59+
inDeletion = comment.id
60+
commentService.deleteComment(comment: comment,
61+
owner: record)
62+
}
63+
}
64+
65+
}

0 commit comments

Comments
 (0)