diff --git a/FchatiApp/FchatiApp.entitlements b/FchatiApp/FchatiApp.entitlements new file mode 100644 index 0000000..e2ae37c --- /dev/null +++ b/FchatiApp/FchatiApp.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/FchatiApp/Info.plist b/FchatiApp/Info.plist new file mode 100644 index 0000000..2efe6f5 --- /dev/null +++ b/FchatiApp/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + FchatiApp + CFBundleIdentifier + de.diyaa.fchati + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Fchati + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 14.0 + NSHighResolutionCapable + + + diff --git a/FchatiApp/Package.swift b/FchatiApp/Package.swift new file mode 100644 index 0000000..7bdc10d --- /dev/null +++ b/FchatiApp/Package.swift @@ -0,0 +1,18 @@ +// 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/Sources/FchatiApp/FchatiApp.swift b/FchatiApp/Sources/FchatiApp/FchatiApp.swift new file mode 100644 index 0000000..5663ce1 --- /dev/null +++ b/FchatiApp/Sources/FchatiApp/FchatiApp.swift @@ -0,0 +1,95 @@ +import SwiftUI + +@main +struct FchatiApp: App { + var body: some Scene { + MenuBarExtra("Fchati", systemImage: "message") { + RootView() + } + .menuBarExtraStyle(.window) + } +} + +enum AppTab: String, CaseIterable, Identifiable { + case chat = "Chat" + case pairing = "Pairing" + case settings = "Settings" + + var id: Self { self } + + var title: String { + rawValue + } + + var icon: String { + switch self { + case .chat: + "message" + case .pairing: + "link" + case .settings: + "gearshape" + } + } +} + +private struct RootView: View { + @State private var selectedTab: AppTab = .chat + + var body: some View { + VStack(spacing: 0) { + Picker("Section", selection: $selectedTab) { + ForEach(AppTab.allCases) { tab in + Label(tab.title, systemImage: tab.icon) + .tag(tab) + } + } + .pickerStyle(.segmented) + .labelsHidden() + .padding() + + Divider() + + Group { + switch selectedTab { + case .chat: + PlaceholderView( + icon: "message", + title: "No messages yet", + description: "Pair with someone to start chatting." + ) + case .pairing: + PlaceholderView( + icon: "link", + title: "Pair a device", + description: "Create or join a pairing session." + ) + case .settings: + PlaceholderView( + icon: "gearshape", + title: "Settings", + description: "Your preferences will appear here." + ) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + .frame(width: 360) + .frame(minHeight: 480) + } +} + +private struct PlaceholderView: View { + let icon: String + let title: String + let description: String + + var body: some View { + ContentUnavailableView( + title, + systemImage: icon, + description: Text(description) + ) + .padding() + } +} diff --git a/FchatiApp/Tests/FchatiAppTests/AppTabTests.swift b/FchatiApp/Tests/FchatiAppTests/AppTabTests.swift new file mode 100644 index 0000000..42cd06e --- /dev/null +++ b/FchatiApp/Tests/FchatiAppTests/AppTabTests.swift @@ -0,0 +1,11 @@ +import XCTest +@testable import FchatiApp + +final class AppTabTests: XCTestCase { + func testTabsMatchTheAppShell() { + XCTAssertEqual(AppTab.allCases, [.chat, .pairing, .settings]) + XCTAssertEqual(AppTab.chat.icon, "message") + XCTAssertEqual(AppTab.pairing.icon, "link") + XCTAssertEqual(AppTab.settings.icon, "gearshape") + } +} diff --git a/IMPLEMENTATION_STATUS.md b/IMPLEMENTATION_STATUS.md new file mode 100644 index 0000000..dfbf0ac --- /dev/null +++ b/IMPLEMENTATION_STATUS.md @@ -0,0 +1,50 @@ +# حالة التنفيذ + +## آخر تحديث + +تم إنشاء الهيكل الأولي لتطبيق ماك، مع سكربت محلي لاختباره وبنائه وتثبيته وفتحه. + +## ما تم إنجازه + +- إنشاء حزمة سويفت لتطبيق ماك يدعم نظام ماك 14 فما فوق. +- إنشاء تطبيق شريط القوائم مع واجهة بعرض 360 نقطة وارتفاع أدنى 480 نقطة. +- إضافة تبويبات للدردشة والاقتران والإعدادات، مع حالات بداية مؤقتة لكل تبويب. +- إضافة صلاحيات الشبكة وصندوق الحماية للتطبيق. +- إضافة اختبار يتحقق من وجود التبويبات الثلاثة وأيقوناتها. +- إضافة ملف معلومات لتجميع الملف التنفيذي داخل حزمة تطبيق قابلة للتشغيل. +- إضافة سكربت للتنظيف والاختبار والبناء والتثبيت والفتح. + +## السكربت المحلي + +المسار: + +`scripts/run-macos-app.sh` + +ينفذ السكربت الخطوات التالية بالترتيب: + +1. يحذف نواتج البناء الحالية الخاصة بتطبيق ماك فقط. +2. يشغّل الاختبارات. +3. يبني نسخة إصدار من التطبيق. +4. ينشئ حزمة تطبيق ماك مؤقتة ويوقعها محليًا. +5. يحذف النسخة الموجودة من التطبيق في مجلد التطبيقات، إن وجدت. +6. ينقل النسخة الجديدة إلى مجلد التطبيقات. +7. يفتح التطبيق الجديد. + +مسار التثبيت الناتج: + +`/Applications/Fchati.app` + +## التحقق المنفذ + +- نجح اختبار الحزمة، مع اختبار واحد ناجح ودون إخفاقات. +- تم تنفيذ سكربت التثبيت كاملًا بنجاح، بما في ذلك البناء والتثبيت والفتح. +- تمت إضافة اختبار للحفاظ على عقد واجهة البداية. +- لم يتم تعديل ملف المهام الموجود مسبقًا. + +## الحالة الحالية + +واجهة البداية وحزمة التطبيق جاهزتان للتشغيل المحلي. + +لا تزال ميزات التخزين الآمن والاقتران والاتصال بالخادم والدردشة الفعلية غير منفذة. + +المهمة التالية المقترحة هي إضافة التخزين الآمن للهوية والرموز، ثم ربط شاشة الاقتران بالخادم. diff --git a/scripts/run-macos-app.sh b/scripts/run-macos-app.sh new file mode 100755 index 0000000..3c28e4b --- /dev/null +++ b/scripts/run-macos-app.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +set -euo pipefail + +REPOSITORY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PROJECT_DIR="$REPOSITORY_DIR/FchatiApp" +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" + +cleanup() { + rm -rf "$STAGING_DIR" +} +trap cleanup EXIT + +if [[ ! -f "$PROJECT_DIR/Package.swift" ]]; then + echo "FchatiApp package not found: $PROJECT_DIR" >&2 + exit 1 +fi + +if [[ -e "$INSTALL_PATH" && ! -d "$INSTALL_PATH" ]]; then + echo "Expected an application bundle at $INSTALL_PATH, but found a non-directory." >&2 + exit 1 +fi + +echo "Removing the current Swift build artifacts..." +rm -rf "$BUILD_DIR" + +echo "Running tests..." +swift test --package-path "$PROJECT_DIR" + +echo "Building the release executable..." +swift build --configuration release --package-path "$PROJECT_DIR" + +EXECUTABLE_PATH="$PROJECT_DIR/.build/release/$PRODUCT_NAME" +if [[ ! -x "$EXECUTABLE_PATH" ]]; then + echo "Release executable not found: $EXECUTABLE_PATH" >&2 + exit 1 +fi + +echo "Creating the application bundle..." +mkdir -p "$STAGING_APP/Contents/MacOS" +cp "$PROJECT_DIR/Info.plist" "$STAGING_APP/Contents/Info.plist" +cp "$EXECUTABLE_PATH" "$STAGING_APP/Contents/MacOS/$PRODUCT_NAME" +codesign --force --sign - --timestamp=none "$STAGING_APP" + +if [[ -d "$INSTALL_PATH" ]]; then + echo "Removing the installed application..." + rm -rf "$INSTALL_PATH" +fi + +echo "Installing the application..." +mv "$STAGING_APP" "$INSTALL_PATH" + +echo "Opening Fchati..." +open "$INSTALL_PATH" + +echo "Fchati was installed and opened successfully."