From b32911dfb224d9e167505b5df3c4f71c4f5205cd Mon Sep 17 00:00:00 2001 From: diyaa Date: Mon, 27 Jul 2026 12:09:51 +0200 Subject: [PATCH] Sorry Change name to Fchaty --- FchatiApp/Package.swift | 18 ---- .../FchatyApp.entitlements | 2 + {FchatiApp => FchatyApp}/Info.plist | 10 +-- FchatyApp/Package.swift | 18 ++++ .../Resources/AppIcon.png | Bin .../Resources/Fchaty.icns | Bin .../Resources/MenuBarIcon.png | Bin .../Sources/FchatyApp/FchatyApp.swift | 8 +- .../FchatyApp}/Features/Chat/ChatView.swift | 43 +++++++--- .../Features/Chat/ChatViewModel.swift | 16 +++- .../Features/Chat/MessageBubble.swift | 0 .../Features/Pairing/PairingView.swift | 6 -- .../Features/Pairing/PairingViewModel.swift | 3 +- .../Features/Settings/SettingsView.swift | 2 +- .../FchatyApp}/Networking/RelayAPI.swift | 0 .../FchatyApp}/Networking/WSClient.swift | 0 .../Notifications/NotificationManager.swift | 0 .../FchatyApp}/Session/AppSession.swift | 2 +- .../FchatyApp}/Storage/KeychainStore.swift | 2 +- .../FchatyApp}/Storage/MessageStore.swift | 2 +- .../Tests/FchatyAppTests}/AppTabTests.swift | 2 +- .../ChatMessageBubbleTests.swift | 2 +- .../MessageStoreModelTests.swift | 2 +- .../PairingViewModelTests.swift | 2 +- .../RelayAPIResponseTests.swift | 2 +- .../FchatyAppTests}/WSMessageTests.swift | 2 +- IMPLEMENTATION_STATUS.md | 80 +++++++++--------- TASKS.md | 40 ++++----- relay-server/docker-compose.yml | 24 +++--- relay-server/package.json | 2 +- relay-server/src/index.js | 2 +- scripts/run-macos-app.sh | 22 ++--- 32 files changed, 172 insertions(+), 142 deletions(-) delete mode 100644 FchatiApp/Package.swift rename FchatiApp/FchatiApp.entitlements => FchatyApp/FchatyApp.entitlements (85%) rename {FchatiApp => FchatyApp}/Info.plist (80%) create mode 100644 FchatyApp/Package.swift rename {FchatiApp => FchatyApp}/Resources/AppIcon.png (100%) rename FchatiApp/Resources/Fchati.icns => FchatyApp/Resources/Fchaty.icns (100%) rename {FchatiApp => FchatyApp}/Resources/MenuBarIcon.png (100%) rename FchatiApp/Sources/FchatiApp/FchatiApp.swift => FchatyApp/Sources/FchatyApp/FchatyApp.swift (94%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Features/Chat/ChatView.swift (73%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Features/Chat/ChatViewModel.swift (88%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Features/Chat/MessageBubble.swift (100%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Features/Pairing/PairingView.swift (94%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Features/Pairing/PairingViewModel.swift (96%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Features/Settings/SettingsView.swift (97%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Networking/RelayAPI.swift (100%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Networking/WSClient.swift (100%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Notifications/NotificationManager.swift (100%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Session/AppSession.swift (99%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Storage/KeychainStore.swift (98%) rename {FchatiApp/Sources/FchatiApp => FchatyApp/Sources/FchatyApp}/Storage/MessageStore.swift (98%) rename {FchatiApp/Tests/FchatiAppTests => FchatyApp/Tests/FchatyAppTests}/AppTabTests.swift (92%) rename {FchatiApp/Tests/FchatiAppTests => FchatyApp/Tests/FchatyAppTests}/ChatMessageBubbleTests.swift (94%) rename {FchatiApp/Tests/FchatiAppTests => FchatyApp/Tests/FchatyAppTests}/MessageStoreModelTests.swift (97%) rename {FchatiApp/Tests/FchatiAppTests => FchatyApp/Tests/FchatyAppTests}/PairingViewModelTests.swift (93%) rename {FchatiApp/Tests/FchatiAppTests => FchatyApp/Tests/FchatyAppTests}/RelayAPIResponseTests.swift (97%) rename {FchatiApp/Tests/FchatiAppTests => FchatyApp/Tests/FchatyAppTests}/WSMessageTests.swift (95%) diff --git a/FchatiApp/Package.swift b/FchatiApp/Package.swift deleted file mode 100644 index 7bdc10d..0000000 --- a/FchatiApp/Package.swift +++ /dev/null @@ -1,18 +0,0 @@ -// swift-tools-version: 5.9 -import PackageDescription - -let package = Package( - name: "FchatiApp", - platforms: [.macOS(.v14)], - targets: [ - .executableTarget( - name: "FchatiApp", - path: "Sources/FchatiApp" - ), - .testTarget( - name: "FchatiAppTests", - dependencies: ["FchatiApp"], - path: "Tests/FchatiAppTests" - ) - ] -) diff --git a/FchatiApp/FchatiApp.entitlements b/FchatyApp/FchatyApp.entitlements similarity index 85% rename from FchatiApp/FchatiApp.entitlements rename to FchatyApp/FchatyApp.entitlements index 9548c69..122ea52 100644 --- a/FchatiApp/FchatiApp.entitlements +++ b/FchatyApp/FchatyApp.entitlements @@ -10,5 +10,7 @@ com.apple.security.device.audio-input + com.apple.security.files.user-selected.read-only + diff --git a/FchatiApp/Info.plist b/FchatyApp/Info.plist similarity index 80% rename from FchatiApp/Info.plist rename to FchatyApp/Info.plist index a23436b..05476b0 100644 --- a/FchatiApp/Info.plist +++ b/FchatyApp/Info.plist @@ -5,15 +5,15 @@ CFBundleDevelopmentRegion en CFBundleExecutable - FchatiApp + FchatyApp CFBundleIdentifier - de.diyaa.fchati + de.diyaa.fchaty CFBundleIconFile - Fchati.icns + Fchaty.icns CFBundleInfoDictionaryVersion 6.0 CFBundleName - Fchati + Fchaty CFBundlePackageType APPL CFBundleShortVersionString @@ -25,6 +25,6 @@ NSHighResolutionCapable NSMicrophoneUsageDescription - Fchati uses the microphone to record voice messages. + Fchaty uses the microphone to record voice messages. diff --git a/FchatyApp/Package.swift b/FchatyApp/Package.swift new file mode 100644 index 0000000..95cad57 --- /dev/null +++ b/FchatyApp/Package.swift @@ -0,0 +1,18 @@ +// swift-tools-version: 5.9 +import PackageDescription + +let package = Package( + name: "FchatyApp", + platforms: [.macOS(.v14)], + targets: [ + .executableTarget( + name: "FchatyApp", + path: "Sources/FchatyApp" + ), + .testTarget( + name: "FchatyAppTests", + dependencies: ["FchatyApp"], + path: "Tests/FchatyAppTests" + ) + ] +) diff --git a/FchatiApp/Resources/AppIcon.png b/FchatyApp/Resources/AppIcon.png similarity index 100% rename from FchatiApp/Resources/AppIcon.png rename to FchatyApp/Resources/AppIcon.png diff --git a/FchatiApp/Resources/Fchati.icns b/FchatyApp/Resources/Fchaty.icns similarity index 100% rename from FchatiApp/Resources/Fchati.icns rename to FchatyApp/Resources/Fchaty.icns diff --git a/FchatiApp/Resources/MenuBarIcon.png b/FchatyApp/Resources/MenuBarIcon.png similarity index 100% rename from FchatiApp/Resources/MenuBarIcon.png rename to FchatyApp/Resources/MenuBarIcon.png diff --git a/FchatiApp/Sources/FchatiApp/FchatiApp.swift b/FchatyApp/Sources/FchatyApp/FchatyApp.swift similarity index 94% rename from FchatiApp/Sources/FchatiApp/FchatiApp.swift rename to FchatyApp/Sources/FchatyApp/FchatyApp.swift index 2dd52a7..a68eb79 100644 --- a/FchatiApp/Sources/FchatiApp/FchatiApp.swift +++ b/FchatyApp/Sources/FchatyApp/FchatyApp.swift @@ -3,9 +3,9 @@ import AppKit import SwiftUI @main -struct FchatiApp: App { +struct FchatyApp: App { var body: some Scene { - WindowGroup("Fchati") { + WindowGroup("Fchaty") { RootView() } .defaultSize(width: 420, height: 560) @@ -79,8 +79,8 @@ private struct RootView: View { } label: { Image(systemName: "power") } - .accessibilityLabel("Quit Fchati") - .help("Quit Fchati") + .accessibilityLabel("Quit Fchaty") + .help("Quit Fchaty") } .padding() diff --git a/FchatiApp/Sources/FchatiApp/Features/Chat/ChatView.swift b/FchatyApp/Sources/FchatyApp/Features/Chat/ChatView.swift similarity index 73% rename from FchatiApp/Sources/FchatiApp/Features/Chat/ChatView.swift rename to FchatyApp/Sources/FchatyApp/Features/Chat/ChatView.swift index aab3b5e..34e15c1 100644 --- a/FchatiApp/Sources/FchatiApp/Features/Chat/ChatView.swift +++ b/FchatyApp/Sources/FchatyApp/Features/Chat/ChatView.swift @@ -16,6 +16,19 @@ struct ChatView: View { } var body: some View { + switch session.state { + case .unpaired: + notConnectedPlaceholder(icon: "person.2.slash", message: "Pair with someone to start chatting.") + case .connecting: + notConnectedPlaceholder(icon: "antenna.radiowaves.left.and.right", message: "Connecting...") + case .error(let msg): + notConnectedPlaceholder(icon: "exclamationmark.triangle", message: msg) + case .connected: + chatContent + } + } + + private var chatContent: some View { VStack(spacing: 0) { ScrollViewReader { proxy in ScrollView { @@ -61,9 +74,7 @@ struct ChatView: View { TextField("Message", text: $viewModel.draft) .textFieldStyle(.roundedBorder) .onSubmit { - Task { - await viewModel.sendText() - } + Task { await viewModel.sendText() } } Image(systemName: viewModel.isRecording ? "mic.fill" : "mic") @@ -72,19 +83,13 @@ struct ChatView: View { .contentShape(Circle()) .gesture( DragGesture(minimumDistance: 0) - .onChanged { _ in - viewModel.startRecording() - } - .onEnded { _ in - viewModel.stopRecordingAndSend() - } + .onChanged { _ in viewModel.startRecording() } + .onEnded { _ in viewModel.stopRecordingAndSend() } ) .help("Hold to record audio") Button("Send") { - Task { - await viewModel.sendText() - } + Task { await viewModel.sendText() } } .buttonStyle(.borderedProminent) .disabled( @@ -97,6 +102,20 @@ struct ChatView: View { } } + private func notConnectedPlaceholder(icon: String, message: String) -> some View { + VStack(spacing: 12) { + Image(systemName: icon) + .font(.system(size: 36)) + .foregroundStyle(.secondary) + Text(message) + .font(.callout) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding() + } + private func chooseFile() { let panel = NSOpenPanel() panel.canChooseDirectories = false diff --git a/FchatiApp/Sources/FchatiApp/Features/Chat/ChatViewModel.swift b/FchatyApp/Sources/FchatyApp/Features/Chat/ChatViewModel.swift similarity index 88% rename from FchatiApp/Sources/FchatiApp/Features/Chat/ChatViewModel.swift rename to FchatyApp/Sources/FchatyApp/Features/Chat/ChatViewModel.swift index 67b6f9b..43967d2 100644 --- a/FchatiApp/Sources/FchatiApp/Features/Chat/ChatViewModel.swift +++ b/FchatyApp/Sources/FchatyApp/Features/Chat/ChatViewModel.swift @@ -58,6 +58,19 @@ final class ChatViewModel: ObservableObject { return } + // Request microphone permission before attempting to record. + AVCaptureDevice.requestAccess(for: .audio) { granted in + DispatchQueue.main.async { + if granted { + self.beginRecording() + } else { + self.errorMessage = "Microphone access denied. Enable it in System Settings." + } + } + } + } + + private func beginRecording() { do { let url = try makeRecordingURL() let settings: [String: Any] = [ @@ -83,6 +96,7 @@ final class ChatViewModel: ObservableObject { } } + func stopRecordingAndSend() { guard isRecording, let recorder, let recordingURL else { return @@ -106,7 +120,7 @@ final class ChatViewModel: ObservableObject { create: true ) let recordingsDirectory = cachesDirectory.appendingPathComponent( - "FchatiRecordings", + "FchatyRecordings", isDirectory: true ) diff --git a/FchatiApp/Sources/FchatiApp/Features/Chat/MessageBubble.swift b/FchatyApp/Sources/FchatyApp/Features/Chat/MessageBubble.swift similarity index 100% rename from FchatiApp/Sources/FchatiApp/Features/Chat/MessageBubble.swift rename to FchatyApp/Sources/FchatyApp/Features/Chat/MessageBubble.swift diff --git a/FchatiApp/Sources/FchatiApp/Features/Pairing/PairingView.swift b/FchatyApp/Sources/FchatyApp/Features/Pairing/PairingView.swift similarity index 94% rename from FchatiApp/Sources/FchatiApp/Features/Pairing/PairingView.swift rename to FchatyApp/Sources/FchatyApp/Features/Pairing/PairingView.swift index 19fd552..29cad2a 100644 --- a/FchatiApp/Sources/FchatiApp/Features/Pairing/PairingView.swift +++ b/FchatyApp/Sources/FchatyApp/Features/Pairing/PairingView.swift @@ -57,9 +57,6 @@ struct PairingView: View { Text("Pair a new device") .font(.title3.weight(.semibold)) - TextField("Your display name", text: $viewModel.displayName) - .textFieldStyle(.roundedBorder) - if let generatedCode = viewModel.generatedCode { VStack(alignment: .leading, spacing: 8) { Text("Share this code") @@ -109,9 +106,6 @@ struct PairingView: View { Text("Join a pairing") .font(.title3.weight(.semibold)) - TextField("Your display name", text: $viewModel.displayName) - .textFieldStyle(.roundedBorder) - TextField("Pairing code", text: $viewModel.code) .textFieldStyle(.roundedBorder) diff --git a/FchatiApp/Sources/FchatiApp/Features/Pairing/PairingViewModel.swift b/FchatyApp/Sources/FchatyApp/Features/Pairing/PairingViewModel.swift similarity index 96% rename from FchatiApp/Sources/FchatiApp/Features/Pairing/PairingViewModel.swift rename to FchatyApp/Sources/FchatyApp/Features/Pairing/PairingViewModel.swift index 8fccbef..a3c2246 100644 --- a/FchatiApp/Sources/FchatiApp/Features/Pairing/PairingViewModel.swift +++ b/FchatyApp/Sources/FchatyApp/Features/Pairing/PairingViewModel.swift @@ -1,5 +1,6 @@ import Combine import Foundation +import AppKit enum PairingMode: String, CaseIterable, Identifiable { case create = "Create Code" @@ -17,7 +18,7 @@ final class PairingViewModel: ObservableObject { isWaitingForPeer = false } } - @Published var displayName = "" + @Published var displayName = Host.current().localizedName ?? ProcessInfo.processInfo.hostName @Published var code = "" { didSet { let uppercasedCode = code.uppercased() diff --git a/FchatiApp/Sources/FchatiApp/Features/Settings/SettingsView.swift b/FchatyApp/Sources/FchatyApp/Features/Settings/SettingsView.swift similarity index 97% rename from FchatiApp/Sources/FchatiApp/Features/Settings/SettingsView.swift rename to FchatyApp/Sources/FchatyApp/Features/Settings/SettingsView.swift index e18af61..5ad504e 100644 --- a/FchatiApp/Sources/FchatiApp/Features/Settings/SettingsView.swift +++ b/FchatyApp/Sources/FchatyApp/Features/Settings/SettingsView.swift @@ -55,7 +55,7 @@ struct SettingsView: View { } Section { - Button("Quit Fchati", role: .destructive) { + Button("Quit Fchaty", role: .destructive) { NSApplication.shared.terminate(nil) } } diff --git a/FchatiApp/Sources/FchatiApp/Networking/RelayAPI.swift b/FchatyApp/Sources/FchatyApp/Networking/RelayAPI.swift similarity index 100% rename from FchatiApp/Sources/FchatiApp/Networking/RelayAPI.swift rename to FchatyApp/Sources/FchatyApp/Networking/RelayAPI.swift diff --git a/FchatiApp/Sources/FchatiApp/Networking/WSClient.swift b/FchatyApp/Sources/FchatyApp/Networking/WSClient.swift similarity index 100% rename from FchatiApp/Sources/FchatiApp/Networking/WSClient.swift rename to FchatyApp/Sources/FchatyApp/Networking/WSClient.swift diff --git a/FchatiApp/Sources/FchatiApp/Notifications/NotificationManager.swift b/FchatyApp/Sources/FchatyApp/Notifications/NotificationManager.swift similarity index 100% rename from FchatiApp/Sources/FchatiApp/Notifications/NotificationManager.swift rename to FchatyApp/Sources/FchatyApp/Notifications/NotificationManager.swift diff --git a/FchatiApp/Sources/FchatiApp/Session/AppSession.swift b/FchatyApp/Sources/FchatyApp/Session/AppSession.swift similarity index 99% rename from FchatiApp/Sources/FchatiApp/Session/AppSession.swift rename to FchatyApp/Sources/FchatyApp/Session/AppSession.swift index 52dcb05..6fb5c1f 100644 --- a/FchatiApp/Sources/FchatiApp/Session/AppSession.swift +++ b/FchatyApp/Sources/FchatyApp/Session/AppSession.swift @@ -209,7 +209,7 @@ final class AppSession: ObservableObject { KeychainStore.peerID = peerID KeychainStore.peerName = peerName state = .connected(peerID: peerID, peerName: peerName) - NotificationManager.shared.notify(from: "Fchati", body: "\(peerName) connected") + NotificationManager.shared.notify(from: "Fchaty", body: "\(peerName) connected") } case "read": diff --git a/FchatiApp/Sources/FchatiApp/Storage/KeychainStore.swift b/FchatyApp/Sources/FchatyApp/Storage/KeychainStore.swift similarity index 98% rename from FchatiApp/Sources/FchatiApp/Storage/KeychainStore.swift rename to FchatyApp/Sources/FchatyApp/Storage/KeychainStore.swift index 8ca8c4c..b59b137 100644 --- a/FchatiApp/Sources/FchatiApp/Storage/KeychainStore.swift +++ b/FchatyApp/Sources/FchatyApp/Storage/KeychainStore.swift @@ -33,7 +33,7 @@ enum KeychainStore { delete(.peerName) } - private static let service = "de.diyaa.fchati" + private static let service = "de.diyaa.fchaty" private enum Key: String { case installationID diff --git a/FchatiApp/Sources/FchatiApp/Storage/MessageStore.swift b/FchatyApp/Sources/FchatyApp/Storage/MessageStore.swift similarity index 98% rename from FchatiApp/Sources/FchatiApp/Storage/MessageStore.swift rename to FchatyApp/Sources/FchatyApp/Storage/MessageStore.swift index 31495ac..1c426aa 100644 --- a/FchatiApp/Sources/FchatiApp/Storage/MessageStore.swift +++ b/FchatyApp/Sources/FchatyApp/Storage/MessageStore.swift @@ -82,7 +82,7 @@ actor MessageStore { create: true ) let directory = applicationSupportDirectory.appendingPathComponent( - "de.diyaa.fchati", + "de.diyaa.fchaty", isDirectory: true ) diff --git a/FchatiApp/Tests/FchatiAppTests/AppTabTests.swift b/FchatyApp/Tests/FchatyAppTests/AppTabTests.swift similarity index 92% rename from FchatiApp/Tests/FchatiAppTests/AppTabTests.swift rename to FchatyApp/Tests/FchatyAppTests/AppTabTests.swift index 42cd06e..59f2c15 100644 --- a/FchatiApp/Tests/FchatiAppTests/AppTabTests.swift +++ b/FchatyApp/Tests/FchatyAppTests/AppTabTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import FchatiApp +@testable import FchatyApp final class AppTabTests: XCTestCase { func testTabsMatchTheAppShell() { diff --git a/FchatiApp/Tests/FchatiAppTests/ChatMessageBubbleTests.swift b/FchatyApp/Tests/FchatyAppTests/ChatMessageBubbleTests.swift similarity index 94% rename from FchatiApp/Tests/FchatiAppTests/ChatMessageBubbleTests.swift rename to FchatyApp/Tests/FchatyAppTests/ChatMessageBubbleTests.swift index cbb2a31..1b95647 100644 --- a/FchatiApp/Tests/FchatiAppTests/ChatMessageBubbleTests.swift +++ b/FchatyApp/Tests/FchatyAppTests/ChatMessageBubbleTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import FchatiApp +@testable import FchatyApp final class ChatMessageBubbleTests: XCTestCase { func testMessageKeepsMarkdownBodyForRendering() { diff --git a/FchatiApp/Tests/FchatiAppTests/MessageStoreModelTests.swift b/FchatyApp/Tests/FchatyAppTests/MessageStoreModelTests.swift similarity index 97% rename from FchatiApp/Tests/FchatiAppTests/MessageStoreModelTests.swift rename to FchatyApp/Tests/FchatyAppTests/MessageStoreModelTests.swift index b86ded4..9338989 100644 --- a/FchatiApp/Tests/FchatiAppTests/MessageStoreModelTests.swift +++ b/FchatyApp/Tests/FchatyAppTests/MessageStoreModelTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import FchatiApp +@testable import FchatyApp final class MessageStoreModelTests: XCTestCase { func testMessageRoundTripPreservesAttachmentAndReadStatus() throws { diff --git a/FchatiApp/Tests/FchatiAppTests/PairingViewModelTests.swift b/FchatyApp/Tests/FchatyAppTests/PairingViewModelTests.swift similarity index 93% rename from FchatiApp/Tests/FchatiAppTests/PairingViewModelTests.swift rename to FchatyApp/Tests/FchatyAppTests/PairingViewModelTests.swift index eebd618..b76ff6f 100644 --- a/FchatiApp/Tests/FchatiAppTests/PairingViewModelTests.swift +++ b/FchatyApp/Tests/FchatyAppTests/PairingViewModelTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import FchatiApp +@testable import FchatyApp final class PairingViewModelTests: XCTestCase { func testPairingCodeIsUppercasedAndPrefixed() { diff --git a/FchatiApp/Tests/FchatiAppTests/RelayAPIResponseTests.swift b/FchatyApp/Tests/FchatyAppTests/RelayAPIResponseTests.swift similarity index 97% rename from FchatiApp/Tests/FchatiAppTests/RelayAPIResponseTests.swift rename to FchatyApp/Tests/FchatyAppTests/RelayAPIResponseTests.swift index 7bacd4f..22f47e2 100644 --- a/FchatiApp/Tests/FchatiAppTests/RelayAPIResponseTests.swift +++ b/FchatyApp/Tests/FchatyAppTests/RelayAPIResponseTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import FchatiApp +@testable import FchatyApp final class RelayAPIResponseTests: XCTestCase { func testPairingCreateResponseDecodesServerPayload() throws { diff --git a/FchatiApp/Tests/FchatiAppTests/WSMessageTests.swift b/FchatyApp/Tests/FchatyAppTests/WSMessageTests.swift similarity index 95% rename from FchatiApp/Tests/FchatiAppTests/WSMessageTests.swift rename to FchatyApp/Tests/FchatyAppTests/WSMessageTests.swift index 231f5ac..87cf4e7 100644 --- a/FchatiApp/Tests/FchatiAppTests/WSMessageTests.swift +++ b/FchatyApp/Tests/FchatyAppTests/WSMessageTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import FchatiApp +@testable import FchatyApp final class WSMessageTests: XCTestCase { func testMessageRoundTripPreservesServerFields() throws { diff --git a/IMPLEMENTATION_STATUS.md b/IMPLEMENTATION_STATUS.md index 39a756d..dcf399b 100644 --- a/IMPLEMENTATION_STATUS.md +++ b/IMPLEMENTATION_STATUS.md @@ -7,18 +7,18 @@ ### What was built -- `FchatiApp/Package.swift` — SPM package, macOS 14+, no Xcode -- `FchatiApp/Sources/FchatiApp/FchatiApp.swift` — MenuBarExtra (.window style), 360pt wide, 480pt min height, three tabs (Chat / Pairing / Settings) with segmented picker -- `FchatiApp/FchatiApp.entitlements` — sandbox + outgoing/incoming network -- `FchatiApp/Info.plist` — bundle ID `de.diyaa.fchati`, version 0.1.0, `LSUIElement = YES` (hides Dock icon) -- `FchatiApp/Tests/FchatiAppTests/AppTabTests.swift` — verifies all three tabs and their icons exist +- `FchatyApp/Package.swift` — SPM package, macOS 14+, no Xcode +- `FchatyApp/Sources/FchatyApp/FchatyApp.swift` — MenuBarExtra (.window style), 360pt wide, 480pt min height, three tabs (Chat / Pairing / Settings) with segmented picker +- `FchatyApp/FchatyApp.entitlements` — sandbox + outgoing/incoming network +- `FchatyApp/Info.plist` — bundle ID `de.diyaa.fchaty`, version 0.1.0, `LSUIElement = YES` (hides Dock icon) +- `FchatyApp/Tests/FchatyAppTests/AppTabTests.swift` — verifies all three tabs and their icons exist - `scripts/run-macos-app.sh` — clean → test → release build → bundle → codesign → install to /Applications → open ### Verified - `swift build` passes (debug) - `swift test` passes (1 test) -- Script installs and opens `/Applications/Fchati.app` successfully +- Script installs and opens `/Applications/Fchaty.app` successfully ### Fixes applied after agent review @@ -33,14 +33,14 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Storage/KeychainStore.swift` — direct Security.framework wrapper for the installation ID, auth token, peer ID, and peer name. +- `FchatyApp/Sources/FchatyApp/Storage/KeychainStore.swift` — direct Security.framework wrapper for the installation ID, auth token, peer ID, and peer name. - The installation ID is created once as a UUID and persisted in Keychain. - Token and peer properties support read, update, and deletion through Swift property syntax. - `clearAll()` removes pairing credentials while preserving the installation ID. ### Verified -- `swift build` passes from `FchatiApp/`. +- `swift build` passes from `FchatyApp/`. ## TASK-03 — RelayAPI (HTTP) ✅ @@ -48,15 +48,15 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Networking/RelayAPI.swift` — actor-based HTTP client for pairing creation, pairing join, file upload, and file download. +- `FchatyApp/Sources/FchatyApp/Networking/RelayAPI.swift` — actor-based HTTP client for pairing creation, pairing join, file upload, and file download. - Pairing requests use JSON and decode the exact relay response payloads. - File uploads use `multipart/form-data` with the required `file` field and bearer-token authorization. - Non-success HTTP responses, transport failures, and response decoding failures map to `RelayAPIError`. -- `FchatiApp/Tests/FchatiAppTests/RelayAPIResponseTests.swift` — response-decoding coverage for relay pairing and upload payloads. +- `FchatyApp/Tests/FchatyAppTests/RelayAPIResponseTests.swift` — response-decoding coverage for relay pairing and upload payloads. ### Verified -- `swift build` passes from `FchatiApp/`. +- `swift build` passes from `FchatyApp/`. - `swift test` passes with the relay response tests. ## TASK-04 — WSClient (WebSocket) ✅ @@ -65,15 +65,15 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Networking/WSClient.swift` — actor-based WebSocket client using `URLSessionWebSocketTask`. +- `FchatyApp/Sources/FchatyApp/Networking/WSClient.swift` — actor-based WebSocket client using `URLSessionWebSocketTask`. - Connect sends authentication immediately, requires an `auth.ok` response within 10 seconds, and exposes authenticated incoming messages through `AsyncStream`. - The client sends a ping every 30 seconds and reconnects after unexpected disconnects with delays of 2, 4, 8, 16, and 32 seconds. - Explicit disconnects cancel reconnect attempts and retain the incoming stream for a later connection. -- `FchatiApp/Tests/FchatiAppTests/WSMessageTests.swift` — serialization coverage for relay WebSocket messages. +- `FchatyApp/Tests/FchatyAppTests/WSMessageTests.swift` — serialization coverage for relay WebSocket messages. ### Verified -- `swift build` passes from `FchatiApp/`. +- `swift build` passes from `FchatyApp/`. - `swift test` passes with the WebSocket message test. ## TASK-05 — AppSession (State Machine) ✅ @@ -82,7 +82,7 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Session/AppSession.swift` — main-actor application state for pairing, session restoration, message sending, file sending, read receipts, and incoming message handling. +- `FchatyApp/Sources/FchatyApp/Session/AppSession.swift` — main-actor application state for pairing, session restoration, message sending, file sending, read receipts, and incoming message handling. - Existing credentials restore the WebSocket connection at launch. - Pairing and message failures update the published state with an error description. - File uploads are converted to attachment metadata and forwarded through relay chat messages. @@ -93,9 +93,9 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Storage/MessageStore.swift` — actor-backed JSON persistence at the required Application Support location. +- `FchatyApp/Sources/FchatyApp/Storage/MessageStore.swift` — actor-backed JSON persistence at the required Application Support location. - Message writes are atomic, deduplicated by message ID, sorted by timestamp, and retain read-receipt state. -- `FchatiApp/Tests/FchatiAppTests/MessageStoreModelTests.swift` — serialization coverage for messages and attachment metadata. +- `FchatyApp/Tests/FchatyAppTests/MessageStoreModelTests.swift` — serialization coverage for messages and attachment metadata. ## TASK-10 — New Message Notifications ✅ @@ -103,12 +103,12 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Notifications/NotificationManager.swift` — notification permission request and background message notification delivery. +- `FchatyApp/Sources/FchatyApp/Notifications/NotificationManager.swift` — notification permission request and background message notification delivery. - Notifications use the sender name, limit the preview to 100 characters, and are suppressed while the app is active. ### Verified -- `swift build` passes from `FchatiApp/`. +- `swift build` passes from `FchatyApp/`. - `swift test` passes with session storage coverage. ## TASK-06 — PairingView ✅ @@ -117,15 +117,15 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Features/Pairing/PairingView.swift` — create and join pairing screens with a segmented mode selector, code copy action, loading states, and inline errors. -- `FchatiApp/Sources/FchatiApp/Features/Pairing/PairingViewModel.swift` — pairing request orchestration, code normalization, and display-state management. +- `FchatyApp/Sources/FchatyApp/Features/Pairing/PairingView.swift` — create and join pairing screens with a segmented mode selector, code copy action, loading states, and inline errors. +- `FchatyApp/Sources/FchatyApp/Features/Pairing/PairingViewModel.swift` — pairing request orchestration, code normalization, and display-state management. - Join codes are uppercased automatically and accept values with or without the `FCHT-` prefix. - A connection callback allows the app shell to switch to the chat screen when pairing succeeds. -- `FchatiApp/Tests/FchatiAppTests/PairingViewModelTests.swift` — normalization coverage for pairing code input. +- `FchatyApp/Tests/FchatyAppTests/PairingViewModelTests.swift` — normalization coverage for pairing code input. ### Verified -- `swift build` passes from `FchatiApp/`. +- `swift build` passes from `FchatyApp/`. - `swift test` passes with pairing view-model coverage. ## TASK-07 — ChatView ✅ @@ -134,15 +134,15 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Features/Chat/ChatView.swift` — scrollable chat UI with automatic scrolling, text submission on Return, file selection, and hold-to-record voice input. -- `FchatiApp/Sources/FchatiApp/Features/Chat/ChatViewModel.swift` — message and file sending, `.m4a` recording lifecycle, temporary recording cleanup, and inline send errors. -- `FchatiApp/Sources/FchatiApp/Features/Chat/MessageBubble.swift` — left and right message bubbles, Markdown body rendering, attachment metadata, and timestamps. -- `FchatiApp/FchatiApp.entitlements` and `FchatiApp/Info.plist` — microphone sandbox entitlement and privacy usage description required for voice recording. -- `FchatiApp/Tests/FchatiAppTests/ChatMessageBubbleTests.swift` — Markdown rendering coverage for message bodies. +- `FchatyApp/Sources/FchatyApp/Features/Chat/ChatView.swift` — scrollable chat UI with automatic scrolling, text submission on Return, file selection, and hold-to-record voice input. +- `FchatyApp/Sources/FchatyApp/Features/Chat/ChatViewModel.swift` — message and file sending, `.m4a` recording lifecycle, temporary recording cleanup, and inline send errors. +- `FchatyApp/Sources/FchatyApp/Features/Chat/MessageBubble.swift` — left and right message bubbles, Markdown body rendering, attachment metadata, and timestamps. +- `FchatyApp/FchatyApp.entitlements` and `FchatyApp/Info.plist` — microphone sandbox entitlement and privacy usage description required for voice recording. +- `FchatyApp/Tests/FchatyAppTests/ChatMessageBubbleTests.swift` — Markdown rendering coverage for message bodies. ### Verified -- `swift build` passes from `FchatiApp/`. +- `swift build` passes from `FchatyApp/`. - `swift test` passes with chat view coverage. ## TASK-09 — SettingsView ✅ @@ -151,12 +151,12 @@ ### What was built -- `FchatiApp/Sources/FchatiApp/Features/Settings/SettingsView.swift` — editable display name, published session connection state, destructive unpair action, and bundled application version display. +- `FchatyApp/Sources/FchatyApp/Features/Settings/SettingsView.swift` — editable display name, published session connection state, destructive unpair action, and bundled application version display. - The view reads and writes the display name through `KeychainStore.peerName` and calls `AppSession.unpair()` to clear pairing credentials. ### Verified -- `swift build` passes from `FchatiApp/`. +- `swift build` passes from `FchatyApp/`. - `swift test` passes with all current application tests. ## Integration Update ✅ @@ -165,13 +165,13 @@ ### What was updated -- `FchatiApp/Sources/FchatiApp/FchatiApp.swift` now renders `PairingView`, `ChatView`, and `SettingsView` in the menu-bar tabs instead of placeholder content. +- `FchatyApp/Sources/FchatyApp/FchatyApp.swift` now renders `PairingView`, `ChatView`, and `SettingsView` in the menu-bar tabs instead of placeholder content. - The initial tab is pairing for first-time setup. - A successful pairing switches the selected tab to chat. ### Verified -- `swift build` passes from `FchatiApp/`. +- `swift build` passes from `FchatyApp/`. - `swift test` passes with all current application tests. ## Server File Cleanup ✅ @@ -199,7 +199,7 @@ - `relay-server/Dockerfile` initializes all persistent-data paths as the unprivileged `node` user, preventing pairing, queue, and upload write failures on newly created volumes. - The macOS app now has an always-visible quit control beside the top-level tabs, in addition to the existing Settings action. - The macOS app now includes a normal main window and Dock presence while retaining the menu-bar shortcut. -- `FchatiApp/Resources/AppIcon.png` and `FchatiApp/Resources/Fchati.icns` provide the bundled Dock and application icon, and the installer copies the icon into the application bundle. +- `FchatyApp/Resources/AppIcon.png` and `FchatyApp/Resources/Fchaty.icns` provide the bundled Dock and application icon, and the installer copies the icon into the application bundle. - The supplied Teamwork Chat icon replaces the app icon; the menu bar uses the native monochrome message symbol for visual consistency with macOS. ## Bug Fixes — Post-Review ✅ @@ -208,7 +208,7 @@ ### Fix 1 — `isSent` detection in ChatView -**File:** `FchatiApp/Sources/FchatiApp/Features/Chat/ChatView.swift` +**File:** `FchatyApp/Sources/FchatyApp/Features/Chat/ChatView.swift` **Problem:** `MessageBubble` was deciding which side to render on using `message.fromName == "You"`. If the user ever set their display name to "You" this would misidentify received messages as sent. @@ -219,8 +219,8 @@ ### Fix 2 — Message timestamps for queued/offline messages **Files:** -- `FchatiApp/Sources/FchatiApp/Networking/WSClient.swift` — added `sentAt: String?` field to `WSMessage` -- `FchatiApp/Sources/FchatiApp/Session/AppSession.swift` — sender embeds ISO-8601 timestamp; receiver parses it +- `FchatyApp/Sources/FchatyApp/Networking/WSClient.swift` — added `sentAt: String?` field to `WSMessage` +- `FchatyApp/Sources/FchatyApp/Session/AppSession.swift` — sender embeds ISO-8601 timestamp; receiver parses it **Problem:** When a message was delivered from the offline queue, `sentAt` was set to `Date()` (arrival time). Messages sent at 11pm but delivered after a restart showed a wrong timestamp. @@ -236,7 +236,7 @@ **Files:** - `relay-server/src/index.js` — after WebSocket auth, notify the partner with `{ type: "peer.joined", peerID, peerName }` -- `FchatiApp/Sources/FchatiApp/Session/AppSession.swift` — handle `peer.joined`: save peerID/peerName to Keychain, transition state to `.connected` +- `FchatyApp/Sources/FchatyApp/Session/AppSession.swift` — handle `peer.joined`: save peerID/peerName to Keychain, transition state to `.connected` **Problem:** When the creator generates a pairing code and connects via WebSocket, their state gets stuck at `.connecting` forever. The server never told the creator that the joiner joined — it only responded to the joiner's HTTP request. The creator had no way to learn the joiner's identity or transition to `.connected`. @@ -247,8 +247,8 @@ ### Fix 4 — Read receipts were never sent (important) **Files:** -- `FchatiApp/Sources/FchatiApp/Session/AppSession.swift` — added `markVisibleMessagesRead()`: marks all incoming unread messages as read and sends `{ type: "read", messageIDs: [...] }` via WebSocket -- `FchatiApp/Sources/FchatiApp/Features/Chat/ChatView.swift` — calls `markVisibleMessagesRead()` on appear and when new messages arrive +- `FchatyApp/Sources/FchatyApp/Session/AppSession.swift` — added `markVisibleMessagesRead()`: marks all incoming unread messages as read and sends `{ type: "read", messageIDs: [...] }` via WebSocket +- `FchatyApp/Sources/FchatyApp/Features/Chat/ChatView.swift` — calls `markVisibleMessagesRead()` on appear and when new messages arrive **Problem:** The code handled *incoming* read receipts correctly but never *sent* them. The partner's messages were never acknowledged as read, so the partner never saw "read" status on their messages. diff --git a/TASKS.md b/TASKS.md index 57baa12..7e08702 100644 --- a/TASKS.md +++ b/TASKS.md @@ -1,4 +1,4 @@ -# Fchati — Current State & Tasks +# Fchaty — Current State & Tasks > **Important:** The Mac app must be built entirely without opening Xcode. > Use `Package.swift` (Swift Package Manager) as the project structure. @@ -24,7 +24,7 @@ This tells the next agent not to redo your work. Update `IMPLEMENTATION_STATUS.md` with: what files you created or changed, what was verified (tests, build), and anything you found missing or left incomplete. Write in English. This file is how the next session picks up exactly where you left off. **4. Verify before finishing.** -Run `swift build` from `FchatiApp/` before declaring the task complete. If it does not compile, fix it. Do not leave broken code. +Run `swift build` from `FchatyApp/` before declaring the task complete. If it does not compile, fix it. Do not leave broken code. **5. Do not touch completed tasks.** If a task is marked `[x]`, do not modify those files unless explicitly instructed. @@ -73,7 +73,7 @@ These are decisions made during planning — do not change without revisiting th ### Not built yet -The Mac app (`FchatiApp/`) does not exist yet. +The Mac app (`FchatyApp/`) does not exist yet. ### Pending server tasks @@ -90,11 +90,11 @@ The Mac app (`FchatiApp/`) does not exist yet. ``` f-chaty-native-new/ ├── relay-server/ ✅ done -└── FchatiApp/ +└── FchatyApp/ ├── Package.swift no Xcode — SPM only ├── Sources/ - │ └── FchatiApp/ - │ ├── FchatiApp.swift + │ └── FchatyApp/ + │ ├── FchatyApp.swift │ ├── Session/ │ │ └── AppSession.swift │ ├── Networking/ @@ -114,7 +114,7 @@ f-chaty-native-new/ │ │ └── PairingViewModel.swift │ └── Settings/ │ └── SettingsView.swift - └── FchatiApp.entitlements + └── FchatyApp.entitlements ``` --- @@ -132,31 +132,31 @@ f-chaty-native-new/ **Requirements:** -Create `FchatiApp/Package.swift`: +Create `FchatyApp/Package.swift`: ```swift // swift-tools-version: 5.9 import PackageDescription let package = Package( - name: "FchatiApp", + name: "FchatyApp", platforms: [.macOS(.v14)], targets: [ .executableTarget( - name: "FchatiApp", - path: "Sources/FchatiApp" + name: "FchatyApp", + path: "Sources/FchatyApp" ) ] ) ``` -Create `FchatiApp/Sources/FchatiApp/FchatiApp.swift`: +Create `FchatyApp/Sources/FchatyApp/FchatyApp.swift`: - `@main` SwiftUI App - Uses `MenuBarExtra` with `.window` style - Menu bar icon: `Image(systemName: "message")` - Popover width: 360pt, min height: 480pt - A root view with three tabs: Chat, Pairing, Settings (segmented picker at top) -Create `FchatiApp/FchatiApp.entitlements`: +Create `FchatyApp/FchatyApp.entitlements`: ```xml @@ -172,7 +172,7 @@ Create `FchatiApp/FchatiApp.entitlements`: ``` -Verify: `swift build` succeeds from `FchatiApp/` directory. +Verify: `swift build` succeeds from `FchatyApp/` directory. --- @@ -201,7 +201,7 @@ Verify: `swift build` succeeds from `FchatiApp/` directory. - [x] TASK-05 — AppSession (State Machine) (done 2026-07-26) **Priority:** Third — after TASK-02, TASK-03, TASK-04 -**File:** `FchatiApp/Sources/FchatiApp/Session/AppSession.swift` +**File:** `FchatyApp/Sources/FchatyApp/Session/AppSession.swift` **Depends on:** `KeychainStore`, `RelayAPI`, `WSClient` **Requirements:** @@ -296,7 +296,7 @@ Both modes observe `AppSession.shared.state` and navigate to `ChatView` when sta - [x] TASK-08 — MessageStore (Local Persistence) (done 2026-07-26) **Priority:** Fourth (parallel) -**File:** `FchatiApp/Sources/FchatiApp/Storage/MessageStore.swift` +**File:** `FchatyApp/Sources/FchatyApp/Storage/MessageStore.swift` **Independent:** Yes **Requirements:** @@ -328,7 +328,7 @@ actor MessageStore { } ``` -Storage: JSON file at `FileManager.default.urls(for: .applicationSupportDirectory, ...).first!.appendingPathComponent("de.diyaa.fchati/messages.json")`. +Storage: JSON file at `FileManager.default.urls(for: .applicationSupportDirectory, ...).first!.appendingPathComponent("de.diyaa.fchaty/messages.json")`. Create the directory if it does not exist. --- @@ -355,7 +355,7 @@ Simple settings screen: - [x] TASK-10 — New Message Notifications (done 2026-07-26) **Priority:** Fifth (independent) -**File:** `FchatiApp/Sources/FchatiApp/Notifications/NotificationManager.swift` +**File:** `FchatyApp/Sources/FchatyApp/Notifications/NotificationManager.swift` **Requirements:** @@ -381,14 +381,14 @@ Copy the block below, fill in the task number and paste it to your agent: ``` Context: - Repo: https://git.mohfarawati.de/diyaa/fchaty.git -- App folder: FchatiApp/ (Swift Package, no Xcode, SPM only) +- App folder: FchatyApp/ (Swift Package, no Xcode, SPM only) - macOS 14+, Swift 5.9+, no third-party libraries - Relay server is already running at https://fchaty.diyaa.de Task: [paste the full task section here, e.g. TASK-04] After writing the code: -1. Make sure it compiles: run `swift build` from the FchatiApp/ directory +1. Make sure it compiles: run `swift build` from the FchatyApp/ directory 2. Fix any compiler errors before finishing 3. Do not open Xcode or create any .xcodeproj or .xcworkspace files ``` diff --git a/relay-server/docker-compose.yml b/relay-server/docker-compose.yml index bd17cb3..6ba6375 100644 --- a/relay-server/docker-compose.yml +++ b/relay-server/docker-compose.yml @@ -1,26 +1,26 @@ services: - fchati-relay: + fchaty-relay: build: . - image: fchati-relay:latest + image: fchaty-relay:latest restart: unless-stopped env_file: .env volumes: - - fchati-files:/data/files - - fchati-queue:/data/queue - - fchati-pairing:/data/pairing + - fchaty-files:/data/files + - fchaty-queue:/data/queue + - fchaty-pairing:/data/pairing networks: - traefik-net labels: - "traefik.enable=true" - - "traefik.http.routers.fchati.rule=Host(`fchaty.diyaa.de`)" - - "traefik.http.routers.fchati.entrypoints=websecure" - - "traefik.http.routers.fchati.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}" - - "traefik.http.services.fchati.loadbalancer.server.port=3000" + - "traefik.http.routers.fchaty.rule=Host(`fchaty.diyaa.de`)" + - "traefik.http.routers.fchaty.entrypoints=websecure" + - "traefik.http.routers.fchaty.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}" + - "traefik.http.services.fchaty.loadbalancer.server.port=3000" volumes: - fchati-files: - fchati-queue: - fchati-pairing: + fchaty-files: + fchaty-queue: + fchaty-pairing: networks: traefik-net: diff --git a/relay-server/package.json b/relay-server/package.json index 8af3561..2fa5066 100644 --- a/relay-server/package.json +++ b/relay-server/package.json @@ -1,5 +1,5 @@ { - "name": "fchati-relay", + "name": "fchaty-relay", "version": "1.0.0", "private": true, "scripts": { diff --git a/relay-server/src/index.js b/relay-server/src/index.js index 95ee831..b612455 100644 --- a/relay-server/src/index.js +++ b/relay-server/src/index.js @@ -485,5 +485,5 @@ wss.on('connection', (ws) => { // ─── Start ──────────────────────────────────────────────────────────────────── server.listen(PORT, () => { - console.log(`Fchati Relay listening on port ${PORT}`); + console.log(`Fchaty Relay listening on port ${PORT}`); }); diff --git a/scripts/run-macos-app.sh b/scripts/run-macos-app.sh index c93449a..e839209 100755 --- a/scripts/run-macos-app.sh +++ b/scripts/run-macos-app.sh @@ -3,13 +3,13 @@ set -euo pipefail REPOSITORY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -PROJECT_DIR="$REPOSITORY_DIR/FchatiApp" +PROJECT_DIR="$REPOSITORY_DIR/FchatyApp" BUILD_DIR="$PROJECT_DIR/.build" -PRODUCT_NAME="FchatiApp" -INSTALL_PATH="/Applications/Fchati.app" -STAGING_DIR="$(mktemp -d "${TMPDIR:-/tmp}/fchati-install.XXXXXX")" -STAGING_APP="$STAGING_DIR/Fchati.app" -ICON_PATH="$PROJECT_DIR/Resources/Fchati.icns" +PRODUCT_NAME="FchatyApp" +INSTALL_PATH="/Applications/Fchaty.app" +STAGING_DIR="$(mktemp -d "${TMPDIR:-/tmp}/fchaty-install.XXXXXX")" +STAGING_APP="$STAGING_DIR/Fchaty.app" +ICON_PATH="$PROJECT_DIR/Resources/Fchaty.icns" cleanup() { rm -rf "$STAGING_DIR" @@ -17,7 +17,7 @@ cleanup() { trap cleanup EXIT if [[ ! -f "$PROJECT_DIR/Package.swift" ]]; then - echo "FchatiApp package not found: $PROJECT_DIR" >&2 + echo "FchatyApp package not found: $PROJECT_DIR" >&2 exit 1 fi @@ -50,8 +50,8 @@ echo "Creating the application bundle..." mkdir -p "$STAGING_APP/Contents/MacOS" "$STAGING_APP/Contents/Resources" cp "$PROJECT_DIR/Info.plist" "$STAGING_APP/Contents/Info.plist" cp "$EXECUTABLE_PATH" "$STAGING_APP/Contents/MacOS/$PRODUCT_NAME" -cp "$ICON_PATH" "$STAGING_APP/Contents/Resources/Fchati.icns" -codesign --force --sign - --timestamp=none --entitlements "$PROJECT_DIR/FchatiApp.entitlements" "$STAGING_APP" +cp "$ICON_PATH" "$STAGING_APP/Contents/Resources/Fchaty.icns" +codesign --force --sign - --timestamp=none --entitlements "$PROJECT_DIR/FchatyApp.entitlements" "$STAGING_APP" if [[ -d "$INSTALL_PATH" ]]; then echo "Removing the installed application..." @@ -61,7 +61,7 @@ fi echo "Installing the application..." mv "$STAGING_APP" "$INSTALL_PATH" -echo "Opening Fchati..." +echo "Opening Fchaty..." open "$INSTALL_PATH" -echo "Fchati was installed and opened successfully." +echo "Fchaty was installed and opened successfully."