diff --git a/GitSpace/Sources/Models/Chat.swift b/GitSpace/Sources/Models/Chat.swift index 3d220c4b..70248318 100644 --- a/GitSpace/Sources/Models/Chat.swift +++ b/GitSpace/Sources/Models/Chat.swift @@ -16,7 +16,7 @@ struct Chat: Identifiable, Codable { let joinedMemberIDs: [String] // 채팅방에 참여한 유저 ID 리스트 var lastContent: String // 마지막 메세지 내용 var lastContentDate: Date // 마지막 메세지 날짜 - let knockContent: String // 노크 메세지 내용 + var knockContent: String // 노크 메세지 내용 let knockContentDate: Date // 노크 메세지 날짜 var unreadMessageCount: [String : Int] // 읽지 않은 메시지 갯수 (userID : 읽지 않은 메시지 갯수) @@ -75,4 +75,18 @@ struct Chat: Identifiable, Codable { unreadMessageCount: [:] ) } + + static func encodedChat(with chat: Chat) -> Chat { + var encodedChat: Chat = chat + encodedChat.knockContent = chat.knockContent.asBase64 ?? "" + encodedChat.lastContent = chat.lastContent.asBase64 ?? "" + return encodedChat + } + + static func decodedChat(with chat: Chat) -> Chat { + var decodedChat: Chat = chat + decodedChat.knockContent = chat.knockContent.decodedBase64String ?? "" + decodedChat.lastContent = chat.lastContent.decodedBase64String ?? "" + return decodedChat + } } diff --git a/GitSpace/Sources/Models/Message.swift b/GitSpace/Sources/Models/Message.swift index b4c1e3ff..dbb261fd 100644 --- a/GitSpace/Sources/Models/Message.swift +++ b/GitSpace/Sources/Models/Message.swift @@ -1,5 +1,5 @@ // -// ChatCellModel.swift +// MessageCellModel.swift // GitSpace // // Created by 원태영 on 2023/01/27. @@ -11,7 +11,7 @@ import Foundation struct Message : Identifiable, Codable { let id: String // 메세지 ID let senderID: String // 메세지 작성자 유저 ID - let textContent: String // 메세지 내용 + var textContent: String // 메세지 내용 let imageContent: String? // 메세지에 첨부한 이미지 let sentDate: Date // 메세지 작성 날짜 let isRead: Bool // 수신 유저의 메세지 확인 여부 @@ -24,4 +24,16 @@ struct Message : Identifiable, Codable { return dateFormatter.string(from: sentDate) } + + static func encodedMessage(with message: Message) -> Message { + var encodedMessage: Message = message + encodedMessage.textContent = message.textContent.asBase64 ?? "" + return encodedMessage + } + + static func decodedMessage(with message: Message) -> Message { + var decodedMessage: Message = message + decodedMessage.textContent = message.textContent.decodedBase64String ?? "" + return decodedMessage + } } diff --git a/GitSpace/Sources/ViewModels/ChatViewModel.swift b/GitSpace/Sources/ViewModels/ChatViewModel.swift index 2a3932f2..ca4119de 100644 --- a/GitSpace/Sources/ViewModels/ChatViewModel.swift +++ b/GitSpace/Sources/ViewModels/ChatViewModel.swift @@ -75,7 +75,8 @@ extension ChatStore { private func decodeNewChat(change: QueryDocumentSnapshot) -> Chat? { do { let newChat = try change.data(as: Chat.self) - return newChat + let decodedNewChat: Chat = .decodedChat(with: newChat) + return decodedNewChat } catch { print("Error-\(#file)-\(#function) : \(error.localizedDescription)") } @@ -122,7 +123,8 @@ extension ChatStore { func addListener() { listener = db .collection(const.COLLECTION_CHAT) - .whereField(const.FIELD_JOINED_MEMBER_IDS, arrayContains: Utility.loginUserID) + .whereField(const.FIELD_JOINED_MEMBER_IDS, + arrayContains: Utility.loginUserID) .addSnapshotListener { snapshot, error in guard let snapshot else { return } @@ -194,7 +196,8 @@ extension ChatStore { do { let chat: Chat = try document.data(as: Chat.self) if let targetUserInfo = await UserStore.requestAndReturnUser(userID: chat.targetUserID) { - newChats.append(chat) + let decodedChat: Chat = .decodedChat(with: chat) + newChats.append(decodedChat) targetUserInfoDict[chat.id] = targetUserInfo } } catch { @@ -212,10 +215,11 @@ extension ChatStore { do { let pushedChat = try await doc.getDocument(as: Chat.self) // FIXME: Chat의 targetUserName을 사용하지 않기 위해 UserStore의 UserInfo 요청 메서드 구현. 해당 메서드로 로직 대체 By. 태영 - + let decodedPushedChat: Chat = .decodedChat(with: pushedChat) + if let targetUserInfo = await UserStore.requestAndReturnUser(userID: pushedChat.targetUserID) { targetUserInfoDict[pushedChat.id] = targetUserInfo - return pushedChat + return decodedPushedChat } return nil } catch { @@ -226,22 +230,24 @@ extension ChatStore { // MARK: -Chat CRUD func addChat(_ chat: Chat) async { + let encodedChat: Chat = .encodedChat(with: chat) do { try db.collection(const.COLLECTION_CHAT) .document(chat.id) - .setData(from: chat.self) + .setData(from: encodedChat.self) } catch { print("Error-\(#file)-\(#function) : \(error.localizedDescription)") } } func updateChat(_ chat: Chat) async { + let encodedChat: Chat = .encodedChat(with: chat) do { try await db.collection(const.COLLECTION_CHAT) .document(chat.id) - .updateData([const.FIELD_LAST_CONTENT_DATE : chat.lastContentDate, - const.FIELD_LAST_CONTENT : chat.lastContent, - const.FIELD_UNREAD_MESSAGE_COUNT : chat.unreadMessageCount]) + .updateData([const.FIELD_LAST_CONTENT_DATE : encodedChat.lastContentDate, + const.FIELD_LAST_CONTENT : encodedChat.lastContent, + const.FIELD_UNREAD_MESSAGE_COUNT : encodedChat.unreadMessageCount]) } catch { print("Error-\(#file)-\(#function) : \(error.localizedDescription)") } diff --git a/GitSpace/Sources/ViewModels/KnockHistory/KnockViewManager.swift b/GitSpace/Sources/ViewModels/KnockHistory/KnockViewManager.swift index e1cee18c..f330d9ef 100644 --- a/GitSpace/Sources/ViewModels/KnockHistory/KnockViewManager.swift +++ b/GitSpace/Sources/ViewModels/KnockHistory/KnockViewManager.swift @@ -192,11 +192,21 @@ extension KnockViewManager { searchText: String, userSelectedTab: String ) -> Bool { - if isSearching, userSelectedTab == Constant.KNOCK_RECEIVED { // 검색중 + 내 수신함 - // 현재 내가 선택한 필터 옵션과 같아야 하고, 내 수신함에는 보낸 사람의 정보를 가져야 한다. - return knock.knockStatus == equalsToKnockStatus && knock.sentUserName.contains(searchText, isCaseInsensitive: true) + if isSearching, + userSelectedTab == Constant.KNOCK_RECEIVED { + // 검색중 + 내 수신함 + // 현재 내가 선택한 필터 옵션과 같아야 하고, 내 수신함에는 보낸 사람의 정보를 가져야 한다. + return knock.knockStatus == equalsToKnockStatus && + knock.sentUserName.contains( + searchText, + isCaseInsensitive: true + ) } else if isSearching, userSelectedTab == Constant.KNOCK_SENT { // 검색중 + 내 발신함 - return knock.knockStatus == equalsToKnockStatus && knock.receivedUserName.contains(searchText, isCaseInsensitive: true) + return knock.knockStatus == equalsToKnockStatus && + knock.receivedUserName.contains( + searchText, + isCaseInsensitive: true + ) } else if userSelectedTab == Constant.KNOCK_RECEIVED { return knock.knockStatus == equalsToKnockStatus } else if userSelectedTab == Constant.KNOCK_SENT { @@ -205,7 +215,10 @@ extension KnockViewManager { return false } - public func sortedByDateValue(lhs: Knock, rhs: Knock) -> Bool { + public func sortedByDateValue( + lhs: Knock, + rhs: Knock + ) -> Bool { lhs.knockedDate.dateValue() > rhs.knockedDate.dateValue() } } @@ -225,16 +238,34 @@ extension KnockViewManager { snapshot.documentChanges.forEach { docDiff in switch docDiff.type { case .added: - if let newKnock = self.decodeKnockElementForListener(with: docDiff, currentUser: currentUser) { - self.appendKnockElementInTempList(newKnock: newKnock, currentUser: currentUser) + if let newKnock = self.decodeKnockElementForListener( + with: docDiff, + currentUser: currentUser + ) { + self.appendKnockElementInTempList( + newKnock: newKnock, + currentUser: currentUser + ) } case .modified: - if let diffKnock = self.decodeKnockElementForListener(with: docDiff, currentUser: currentUser) { - self.updateTempKnockList(diffKnock: diffKnock, currentUser: currentUser) + if let diffKnock = self.decodeKnockElementForListener( + with: docDiff, + currentUser: currentUser + ) { + self.updateTempKnockList( + diffKnock: diffKnock, + currentUser: currentUser + ) } case .removed: - if let removedKnock = self.decodeKnockElementForListener(with: docDiff, currentUser: currentUser) { - self.removeKnocksInTempKnockList(removedKnock: removedKnock, currentUser: currentUser) + if let removedKnock = self.decodeKnockElementForListener( + with: docDiff, + currentUser: currentUser + ) { + self.removeKnocksInTempKnockList( + removedKnock: removedKnock, + currentUser: currentUser + ) } } } @@ -268,6 +299,7 @@ extension KnockViewManager { /** 1. 만들어진 노크를 DB에 등록합니다. + - Important: User Created Contents는 모두 Base String으로 콘솔에 저장됩니다. */ public func createKnockOnFirestore(knock: Knock) async -> Void { let document = firebaseDatabase.document("\(knock.id)") @@ -276,10 +308,10 @@ extension KnockViewManager { try await document.setData([ "id": knock.id, "knockedDate": knock.knockedDate, - "declineMessage": knock.declineMessage ?? "", + "declineMessage": knock.declineMessage?.asBase64 ?? "", "knockCategory": knock.knockCategory, "knockStatus": Constant.KNOCK_WAITING, - "knockMessage": knock.knockMessage, + "knockMessage": knock.knockMessage.asBase64 ?? "암호화", "receivedUserName": knock.receivedUserName, "sentUserName": knock.sentUserName, "sentUserID": knock.sentUserID, @@ -312,7 +344,8 @@ extension KnockViewManager { let isKnockMessageEdited, isKnockMessageEdited { try await document.updateData([ - "knockMessage": knock.knockMessage + // !!!: base String으로 콘솔 저장 + "knockMessage": knock.knockMessage.asBase64 ?? "" ]) } @@ -326,7 +359,8 @@ extension KnockViewManager { case Constant.KNOCK_DECLINED: try await document.setData([ "declinedDate": Timestamp(date: .now), - "declineMessage": declineMessage ?? "\(knock.receivedUserName) decided not to give you a decline message." + // !!!: base String으로 콘솔 저장 + "declineMessage": declineMessage?.asBase64 ?? "\(knock.receivedUserName) decided not to give you a decline message." ], merge: true) default: diff --git a/GitSpace/Sources/ViewModels/MessageViewModel.swift b/GitSpace/Sources/ViewModels/MessageViewModel.swift index 3b6b376f..4f35bc35 100644 --- a/GitSpace/Sources/ViewModels/MessageViewModel.swift +++ b/GitSpace/Sources/ViewModels/MessageViewModel.swift @@ -84,7 +84,8 @@ extension MessageStore { for document in snapshot.documents { do { let message: Message = try document.data(as: Message.self) - newMessages.append(message) + let decodedMessage: Message = .decodedMessage(with: message) + newMessages.append(decodedMessage) } catch { print("Error-\(#file)-\(#function) : \(error.localizedDescription)") } @@ -95,19 +96,21 @@ extension MessageStore { // MARK: - Message CRUD func addMessage(_ message: Message, chatID: String) { + let encodedMessage: Message = .encodedMessage(with: message) do { try db .collection(const.COLLECTION_CHAT) .document(chatID) .collection(const.COLLECTION_MESSAGE) .document(message.id) - .setData(from: message.self) + .setData(from: encodedMessage.self) } catch { print("Error-\(#file)-\(#function) : \(error.localizedDescription)") } } func updateMessage(_ message: Message, chatID: String) async { + let encodedMessage: Message = .encodedMessage(with: message) do { try await db .collection(const.COLLECTION_CHAT) @@ -115,8 +118,8 @@ extension MessageStore { .collection(const.COLLECTION_MESSAGE) .document(message.id) .updateData( - [const.FIELD_TEXT_CONTENT : message.textContent, - const.FIELD_SENT_DATE : message.sentDate]) + [const.FIELD_TEXT_CONTENT : encodedMessage.textContent, + const.FIELD_SENT_DATE : encodedMessage.sentDate]) } catch { print("Error-\(#file)-\(#function) : \(error.localizedDescription)") } @@ -143,7 +146,8 @@ extension MessageStore { func fetchNewMessage(change : QueryDocumentSnapshot) -> Message? { do { let newMessage = try change.data(as: Message.self) - return newMessage + let decodedNewMessage: Message = .decodedMessage(with: newMessage) + return decodedNewMessage } catch { print("Error-\(#file)-\(#function) : \(error.localizedDescription)") } diff --git a/GitSpace/Sources/ViewModels/TagViewModel.swift b/GitSpace/Sources/ViewModels/TagViewModel.swift index 57881ca6..404c28d5 100644 --- a/GitSpace/Sources/ViewModels/TagViewModel.swift +++ b/GitSpace/Sources/ViewModels/TagViewModel.swift @@ -31,7 +31,12 @@ final class TagViewModel: ObservableObject { let id = document[const.FIELD_ID] as? String ?? "" let tagName = document[const.FIELD_TAGNAME] as? String ?? "" let repositories = document[const.FIELD_REPOSITORIES] as? [String] ?? [] - self.tags.append( Tag(id: id, tagName: tagName, repositories: repositories) ) + /// 이미 사용 중인 사용자들의 Tag 데이터는 약식 암호화가 적용되지 않아서 임시로 분기 처리함. + if let decodedTagName = tagName.decodedBase64String { + self.tags.append(Tag(id: id, tagName: decodedTagName, repositories: repositories)) + } else { + self.tags.append(Tag(id: id, tagName: tagName, repositories: repositories)) + } } } catch { print("Error-\(#file)-\(#function): \(error.localizedDescription)") @@ -43,14 +48,15 @@ final class TagViewModel: ObservableObject { func registerTag(tagName: String) async -> Tag? { do { let tid = UUID().uuidString + guard let encodeTagName = tagName.asBase64 else { return nil } try await database.collection(const.COLLECTION_USER_INFO) .document(Auth.auth().currentUser?.uid ?? "") .collection(const.COLLECTION_TAG) .document(tid) .setData([ const.FIELD_ID: tid, - const.FIELD_TAGNAME: tagName, - const.FIELD_REPOSITORIES: [] + const.FIELD_TAGNAME: encodeTagName, + const.FIELD_REPOSITORIES: Array() ]) return Tag(id: tid, tagName: tagName, repositories: []) } catch { @@ -73,7 +79,6 @@ final class TagViewModel: ObservableObject { } } - // FIXME: 수정 시나리오 완성 후 메서드 구현 /* // MARK: Update Custom Tag /// 특정 사용자 태그를 수정합니다. @@ -96,9 +101,14 @@ final class TagViewModel: ObservableObject { .getDocuments() for document in snapshot.documents { let id = document.data()[const.FIELD_ID] as? String ?? "" - let name = document.data()[const.FIELD_TAGNAME] as? String ?? "" + let tagName = document.data()[const.FIELD_TAGNAME] as? String ?? "" let repositories = document.data()[const.FIELD_REPOSITORIES] as? [String] ?? [] - tagNameList.append(Tag(id: id, tagName: name, repositories: repositories)) + /// 이미 사용 중인 사용자들의 Tag 데이터는 약식 암호화가 적용되지 않아서 임시로 분기 처리함. + if let decodedTagName = tagName.decodedBase64String { + tagNameList.append(Tag(id: id, tagName: decodedTagName, repositories: repositories)) + } else { + tagNameList.append(Tag(id: id, tagName: tagName, repositories: repositories)) + } } return tagNameList } catch { @@ -125,7 +135,6 @@ final class TagViewModel: ObservableObject { } } - // FIXME: 정말 필요한 함수인지 확인하기 /* // MARK: Update Repository Tag /// 선택된 레포지토리의 태그를 수정합니다. diff --git a/GitSpace/Sources/Views/Chat/ChatRoom/ChatRoomKnockSection.swift b/GitSpace/Sources/Views/Chat/ChatRoom/ChatRoomKnockSection.swift index 067f25d4..f63bb1dc 100644 --- a/GitSpace/Sources/Views/Chat/ChatRoom/ChatRoomKnockSection.swift +++ b/GitSpace/Sources/Views/Chat/ChatRoom/ChatRoomKnockSection.swift @@ -14,7 +14,8 @@ struct ChatDetailKnockSection: View { var body: some View { VStack(spacing: 10) { - GSText.CustomTextView(style: .sectionTitle, string: chat.knockContentDateAsString) + GSText.CustomTextView(style: .sectionTitle, + string: chat.knockContentDateAsString) .padding(.bottom, 10) HStack { @@ -36,16 +37,12 @@ struct ChatDetailKnockSection: View { GSCanvas.CustomCanvasView(style: .primary) { HStack { - Spacer() - GSText.CustomTextView(style: .body1, string: chat.knockContent) - Spacer() + GSText.CustomTextView(style: .body1, + string: chat.knockContent) + .frame(maxWidth: .infinity) } } .padding(.horizontal, 20) - -// Text(chat.knockContent) -// .modifier(KnockMessageModifier()) - } } } diff --git a/GitSpace/Sources/Views/Home/AddTagSheetView.swift b/GitSpace/Sources/Views/Home/AddTagSheetView.swift index fcc1c767..a7a7466b 100644 --- a/GitSpace/Sources/Views/Home/AddTagSheetView.swift +++ b/GitSpace/Sources/Views/Home/AddTagSheetView.swift @@ -179,7 +179,6 @@ struct AddTagSheetView: View { items: Array( zip(tagViewModel.tags.indices.reversed(), tagViewModel.tags.reversed())) ) { index, tag in GSButton.CustomButtonView( style: .tag( -// isAppliedInView: selectedTags.contains(tag), isSelectedInAddTagSheet: selectedTags.contains{ $0.id == tag.id } ) ) { diff --git a/GitSpace/Sources/Views/Home/StarredView.swift b/GitSpace/Sources/Views/Home/StarredView.swift index 8e7c53b3..f29cef86 100644 --- a/GitSpace/Sources/Views/Home/StarredView.swift +++ b/GitSpace/Sources/Views/Home/StarredView.swift @@ -79,7 +79,6 @@ struct StarredView: View { GSButton.CustomButtonView( style: .tag( isAppliedInView: true -// isSelectedInAddTagSheet: false ) ) { withAnimation { diff --git a/GitSpace/Sources/Views/Knock/MainKnockBox/EachKnockCell.swift b/GitSpace/Sources/Views/Knock/MainKnockBox/EachKnockCell.swift index 16b8336a..9e8da97a 100644 --- a/GitSpace/Sources/Views/Knock/MainKnockBox/EachKnockCell.swift +++ b/GitSpace/Sources/Views/Knock/MainKnockBox/EachKnockCell.swift @@ -90,7 +90,8 @@ struct EachKnockCell: View { .id(eachKnock.id) HStack { - Text(eachKnock.knockMessage) + // !!!: base String을 decode + Text(eachKnock.knockMessage.decodedBase64String ?? "복호화") .lineLimit(1) Spacer() diff --git a/GitSpace/Sources/Views/Knock/MainKnockBox/KnockHistoryView.swift b/GitSpace/Sources/Views/Knock/MainKnockBox/KnockHistoryView.swift index 249b7bd7..c5c34c18 100644 --- a/GitSpace/Sources/Views/Knock/MainKnockBox/KnockHistoryView.swift +++ b/GitSpace/Sources/Views/Knock/MainKnockBox/KnockHistoryView.swift @@ -74,7 +74,6 @@ struct KnockHistoryView: View { TextEditor(text: $editedKnockMessage) .autocorrectionDisabled() .textInputAutocapitalization(.never) - .foregroundColor(.black) .textEditorBackgroundClear() .frame( maxWidth: UIScreen.main.bounds.width / 1.2, @@ -93,7 +92,7 @@ struct KnockHistoryView: View { withAnimation { endTextEditing() isEditingKnockMessage.toggle() - editedKnockMessage = eachKnock.knockMessage + editedKnockMessage = eachKnock.knockMessage.decodedBase64String ?? "복호화" } } label: { Text("Cancel") @@ -145,7 +144,7 @@ struct KnockHistoryView: View { } else { VStack(alignment: .leading) { HStack { - Text("\(eachKnock.knockMessage)") + Text("\(eachKnock.knockMessage.decodedBase64String ?? "복호화")") .font(.callout) .foregroundColor(Color.black) } @@ -343,7 +342,7 @@ struct KnockHistoryView: View { } private func assignKnockMessageIntoEditState() { - editedKnockMessage = eachKnock.knockMessage + editedKnockMessage = eachKnock.knockMessage.decodedBase64String ?? "복호화" } private func assignChatWithChatID() async { diff --git a/GitSpace/Sources/Views/Knock/MainKnockBox/ReceivedKnockDetailView.swift b/GitSpace/Sources/Views/Knock/MainKnockBox/ReceivedKnockDetailView.swift index 405116aa..69b69b76 100644 --- a/GitSpace/Sources/Views/Knock/MainKnockBox/ReceivedKnockDetailView.swift +++ b/GitSpace/Sources/Views/Knock/MainKnockBox/ReceivedKnockDetailView.swift @@ -97,6 +97,7 @@ struct ReceivedKnockDetailView: View { /// 3. 메세지 내용 VStack(alignment: .leading) { HStack { + // !!!: base String decode Text("\(knock.knockMessage)") .font(.callout) .foregroundColor(Color.black) @@ -269,7 +270,8 @@ struct ReceivedKnockDetailView: View { ], lastContent: "", lastContentDate: .now, - knockContent: knock.knockMessage, + // !!!: Decoded + knockContent: knock.knockMessage.decodedBase64String ?? "", knockContentDate: knock.knockedDate.dateValue(), unreadMessageCount: [ userStore.currentUser?.id ?? "": 0, @@ -278,15 +280,18 @@ struct ReceivedKnockDetailView: View { ) } - private func makeNewChat() -> Chat { - return Chat.init(id: UUID().uuidString, - createdDate: .now, - joinedMemberIDs: [knock.sentUserID, knock.receivedUserID], - lastContent: "", - lastContentDate: .now, - knockContent: knock.knockMessage, - knockContentDate: knock.knockedDate.dateValue(), - unreadMessageCount: [knock.sentUserID : 0, knock.receivedUserID : 0]) + private func makeNewChat() -> Chat { + return Chat.init( + id: UUID().uuidString, + createdDate: .now, + joinedMemberIDs: [knock.sentUserID, knock.receivedUserID], + lastContent: "", + lastContentDate: .now, + // !!!: Decoded + knockContent: knock.knockMessage.decodedBase64String ?? "", + knockContentDate: knock.knockedDate.dateValue(), + unreadMessageCount: [knock.sentUserID : 0, knock.receivedUserID : 0] + ) } } // diff --git a/GitSpace/Sources/Views/Knock/SendKnock/AfterSendKnockSection.swift b/GitSpace/Sources/Views/Knock/SendKnock/AfterSendKnockSection.swift index ac65d7ac..e5fba6f5 100644 --- a/GitSpace/Sources/Views/Knock/SendKnock/AfterSendKnockSection.swift +++ b/GitSpace/Sources/Views/Knock/SendKnock/AfterSendKnockSection.swift @@ -44,19 +44,22 @@ struct AfterSendKnockSection: View { } .padding(.leading, 20) - GSCanvas.CustomCanvasView.init( + GSCanvas.CustomCanvasView( style: .primary, content: { HStack { Spacer() GSText.CustomTextView( style: .captionPrimary1, - string: "\(knockViewManager.newKnock?.knockMessage ?? "")") + string: "\(knockViewManager.newKnock?.knockMessage.decodedBase64String ?? "")") Spacer() } }) .padding(.horizontal, 20) .padding(.vertical, 5) + .onAppear { + print("DECODED?", knockViewManager.newKnock?.knockMessage.decodedBase64String) + } } } } diff --git a/GitSpace/Sources/Views/Knock/SendKnock/SendKnockTextEditSection.swift b/GitSpace/Sources/Views/Knock/SendKnock/SendKnockTextEditSection.swift index 8211fcf9..f61d802d 100644 --- a/GitSpace/Sources/Views/Knock/SendKnock/SendKnockTextEditSection.swift +++ b/GitSpace/Sources/Views/Knock/SendKnock/SendKnockTextEditSection.swift @@ -78,7 +78,6 @@ struct SendKnockTextEditSection: View { // Assign New Knock On Model knockViewManager.assignNewKnock(newKnock: newKnock) - // TODO: 알람 보내기 // 새로운 노크가 생성될 때의 Push Notification 전달 await pushNotificationManager.sendNotification( with: .knock( diff --git a/GitSpace/Sources/Views/Knock/SendKnock/SendKnockView.swift b/GitSpace/Sources/Views/Knock/SendKnock/SendKnockView.swift index 75af0263..fc8b28dd 100644 --- a/GitSpace/Sources/Views/Knock/SendKnock/SendKnockView.swift +++ b/GitSpace/Sources/Views/Knock/SendKnock/SendKnockView.swift @@ -39,7 +39,7 @@ struct SendKnockView: View { if let targetUserInfo { ScrollViewReader { proxy in ScrollView { - HStack {}.id(topID) + HStack { }.id(topID) VStack(alignment: .center, spacing: 10) { TopperProfileView( targetUserInfo: targetUserInfo @@ -101,8 +101,7 @@ struct SendKnockView: View { } - HStack { - } + HStack { } .id(bottomID) .frame(height: isKnockSent ? 5 : 280) } diff --git a/GitSpace/Utilities/Extensions/String+.swift b/GitSpace/Utilities/Extensions/String+.swift index 940916ae..f92bd2e5 100644 --- a/GitSpace/Utilities/Extensions/String+.swift +++ b/GitSpace/Utilities/Extensions/String+.swift @@ -27,7 +27,20 @@ extension String { return date } + /// Base64 인코딩 + public var asBase64: String? { + let utf8Data = self.data(using: .utf8) + return utf8Data?.base64EncodedString() + } + + /// Base64 디코딩 + public var decodedBase64String: String? { + let data: Data? = Data(base64Encoded: self) + guard let data else { return nil } + return String(data: data, encoding: .utf8) + public func isValidPattern(pattern: String) -> Bool { return self.range(of: pattern, options: .regularExpression) != nil + } }