From 1be976e118a21c2f01933aa4f0dabe2c4e7796ae Mon Sep 17 00:00:00 2001 From: peterfriese Date: Wed, 28 Jan 2026 19:25:14 +0100 Subject: [PATCH 1/2] feat: Implement iOS MVP with SwiftUI, Firestore, and Anonymous Auth --- ccdr/makeitso/PRD.md | 100 +++++ ccdr/makeitso/iOS/MakeItSo/.gitignore | 7 + .../MakeItSo.xcodeproj/project.pbxproj | 373 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/swiftpm/Package.resolved | 123 ++++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../MakeItSo/Assets.xcassets/Contents.json | 6 + .../MakeItSo/GoogleService-Info.plist | 30 ++ .../iOS/MakeItSo/MakeItSo/MakeItSoApp.swift | 22 ++ .../iOS/MakeItSo/MakeItSo/Models/Task.swift | 18 + .../Repositories/TaskRepository.swift | 87 ++++ .../Services/AuthenticationService.swift | 18 + .../MakeItSo/MakeItSo/Views/AddTaskView.swift | 46 +++ .../MakeItSo/Views/TaskListView.swift | 41 ++ .../MakeItSo/MakeItSo/Views/TaskRowView.swift | 29 ++ 16 files changed, 953 insertions(+) create mode 100644 ccdr/makeitso/PRD.md create mode 100644 ccdr/makeitso/iOS/MakeItSo/.gitignore create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.pbxproj create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/Contents.json create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/GoogleService-Info.plist create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/MakeItSoApp.swift create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Models/Task.swift create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Repositories/TaskRepository.swift create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Services/AuthenticationService.swift create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/AddTaskView.swift create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/TaskListView.swift create mode 100644 ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/TaskRowView.swift diff --git a/ccdr/makeitso/PRD.md b/ccdr/makeitso/PRD.md new file mode 100644 index 00000000..36b512fe --- /dev/null +++ b/ccdr/makeitso/PRD.md @@ -0,0 +1,100 @@ +# Product Requirements Document (PRD): Productivity App MVP + +| Document Details | | +| --- | --- | +| **Project Name** | **Make It So** | +| **Version** | 1.2 | +| **Status** | Draft | +| **Backend** | Google Cloud Firestore | +| **UI Style** | Platform-Specific / Native | + +## 1. Executive Summary + +"Make It So" is a productivity application designed to help users capture and complete tasks efficiently. The app leverages **Firebase** for cloud storage and real-time syncing. The UI is strictly **Platform Native**, ensuring that an Android user feels at home with Material Design patterns, while an iOS user interacts with standard Apple interface elements. + +--- + +## 2. Functional Requirements + +### 2.1 Task Management + +* **Create:** Users can add a new task with a title, priority, and due date. +* **Read:** Users view a filtered list of *only* their own tasks. +* **Update:** Users can edit task details or toggle the "Complete" status. +* **Delete:** Users can permanently remove tasks. + +### 2.2 User Experience (UX) + +* **Native Metaphors:** +* **iOS:** Uses standard navigation stacks, "Back" buttons with text, and bottom sheets for actions. +* **Android:** Uses the physical/gesture back button, potentially a Floating Action Button (FAB) for "Add", and standard toolbars. + + +* **List Interaction:** A clean, vertical list. No Kanban boards or grids. + +--- + +## 3. Technical Architecture & Data Model + +### 3.1 Collection Structure + +**Collection Name:** `tasks` +**Path:** `tasks/{documentId}` + +* **Security:** A Firestore Security Rule ensures users can only access documents where `userId` matches their Authentication UID. + +### 3.2 Data Schema (The "Item" Object) + +| Field Name | Data Type | Description | +| --- | --- | --- | +| `userId` | String | **Owner ID.** Matches the Auth UID. | +| `title` | String | The task name. | +| `isCompleted` | Boolean | `false` = Active, `true` = Done. | +| `priority` | String | "Low", "Medium", "High". | +| `dueDate` | Timestamp | Date/time the task is due. | +| `createdAt` | Timestamp | Used for sorting order. | + +--- + +## 4. User Interface (UI) Guidelines + +This section defines how the "Native Look" is achieved on both platforms for the main task list. + +### 4.1 iOS Implementation (Cupertino) + +* **List Style:** `UITableView` or Sidebar style. +* **Separators:** Thin gray lines between items, inset from the left. +* **Typography:** San Francisco font. +* **Add Action:** A "+" icon in the top right of the Navigation Bar. +* **Edit Action:** Swipe-left on a row to reveal "Edit" and "Delete" buttons. + +### 4.2 Android Implementation (Material) + +* **List Style:** `RecyclerView` / LazyColumn. +* **Density/Padding:** Items generally have slightly more vertical whitespace (min 48dp-56dp height) to ensure comfortable touch targets. +* **Separators:** Often omitted in modern Material design, relying on whitespace, or full-width if used. +* **Add Action:** A Floating Action Button (FAB) in the bottom right corner. +* **Edit Action:** Long-press on an item to enter "Selection Mode," or tap to open details. + +--- + +## 5. Security & Logic + +### 5.1 Firestore Security Rules + +* **Read/Write:** Allowed only if `request.auth.uid == resource.data.userId`. + +### 5.2 Business Logic + +* **Sorting:** Primary sort by `isCompleted` (Active first), Secondary sort by `dueDate`. +* **Date Formats:** Display dates relative to the user's locale (e.g., MM/DD/YYYY in US, DD/MM/YYYY elsewhere). + +--- + +## 6. Acceptance Criteria + +1. App is named "Make It So" on the home screen. +2. Android version features a Floating Action Button (FAB) or native toolbar for adding tasks. +3. iOS version features a standard Navigation Bar button for adding tasks. +4. Tasks persist to the `tasks` root collection in Firestore. +5. Query filters ensure I never see another user's data. \ No newline at end of file diff --git a/ccdr/makeitso/iOS/MakeItSo/.gitignore b/ccdr/makeitso/iOS/MakeItSo/.gitignore new file mode 100644 index 00000000..ad146c76 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/.gitignore @@ -0,0 +1,7 @@ + +# xcode-build-server files +buildServer.json +.compile + +# Local build artifacts +.build/ diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.pbxproj b/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.pbxproj new file mode 100644 index 00000000..1e0c1a19 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.pbxproj @@ -0,0 +1,373 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 887FFFA12F2A8791005FF8AE /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 887FFFA02F2A8791005FF8AE /* FirebaseAuth */; }; + 887FFFA32F2A8791005FF8AE /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 887FFFA22F2A8791005FF8AE /* FirebaseFirestore */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 88DE712D2F2A832500BB4BF6 /* MakeItSo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MakeItSo.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 88DE712F2F2A832500BB4BF6 /* MakeItSo */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = MakeItSo; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 88DE712A2F2A832500BB4BF6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 887FFFA12F2A8791005FF8AE /* FirebaseAuth in Frameworks */, + 887FFFA32F2A8791005FF8AE /* FirebaseFirestore in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 88DE71242F2A832500BB4BF6 = { + isa = PBXGroup; + children = ( + 88DE712F2F2A832500BB4BF6 /* MakeItSo */, + 88DE712E2F2A832500BB4BF6 /* Products */, + ); + sourceTree = ""; + }; + 88DE712E2F2A832500BB4BF6 /* Products */ = { + isa = PBXGroup; + children = ( + 88DE712D2F2A832500BB4BF6 /* MakeItSo.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 88DE712C2F2A832500BB4BF6 /* MakeItSo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 88DE71382F2A832600BB4BF6 /* Build configuration list for PBXNativeTarget "MakeItSo" */; + buildPhases = ( + 88DE71292F2A832500BB4BF6 /* Sources */, + 88DE712A2F2A832500BB4BF6 /* Frameworks */, + 88DE712B2F2A832500BB4BF6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 88DE712F2F2A832500BB4BF6 /* MakeItSo */, + ); + name = MakeItSo; + packageProductDependencies = ( + 887FFFA02F2A8791005FF8AE /* FirebaseAuth */, + 887FFFA22F2A8791005FF8AE /* FirebaseFirestore */, + ); + productName = MakeItSo; + productReference = 88DE712D2F2A832500BB4BF6 /* MakeItSo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 88DE71252F2A832500BB4BF6 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2620; + LastUpgradeCheck = 2620; + TargetAttributes = { + 88DE712C2F2A832500BB4BF6 = { + CreatedOnToolsVersion = 26.2; + }; + }; + }; + buildConfigurationList = 88DE71282F2A832500BB4BF6 /* Build configuration list for PBXProject "MakeItSo" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 88DE71242F2A832500BB4BF6; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 887FFF9F2F2A8791005FF8AE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 88DE712E2F2A832500BB4BF6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 88DE712C2F2A832500BB4BF6 /* MakeItSo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 88DE712B2F2A832500BB4BF6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 88DE71292F2A832500BB4BF6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 88DE71362F2A832600BB4BF6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = YGAZHQXHH4; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 88DE71372F2A832600BB4BF6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = YGAZHQXHH4; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 88DE71392F2A832600BB4BF6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = YGAZHQXHH4; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.example.MakeItSo; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 88DE713A2F2A832600BB4BF6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = YGAZHQXHH4; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.example.MakeItSo; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 88DE71282F2A832500BB4BF6 /* Build configuration list for PBXProject "MakeItSo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 88DE71362F2A832600BB4BF6 /* Debug */, + 88DE71372F2A832600BB4BF6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 88DE71382F2A832600BB4BF6 /* Build configuration list for PBXNativeTarget "MakeItSo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 88DE71392F2A832600BB4BF6 /* Debug */, + 88DE713A2F2A832600BB4BF6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 887FFF9F2F2A8791005FF8AE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk/"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 12.8.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 887FFFA02F2A8791005FF8AE /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 887FFF9F2F2A8791005FF8AE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; + 887FFFA22F2A8791005FF8AE /* FirebaseFirestore */ = { + isa = XCSwiftPackageProductDependency; + package = 887FFF9F2F2A8791005FF8AE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseFirestore; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 88DE71252F2A832500BB4BF6 /* Project object */; +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..5e02bf43 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,123 @@ +{ + "originHash" : "982d6975f1623415c991bb4e7758e60bc97378fdfee9880ed6c057cb048551ff", + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", + "version" : "1.2024072200.0" + } + }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk/", + "state" : { + "revision" : "674d9a7ee9858207181a3dd0b42c77298c6fb71b", + "version" : "12.8.0" + } + }, + { + "identity" : "google-ads-on-device-conversion-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk", + "state" : { + "revision" : "35b601a60fbbea2de3ea461f604deaaa4d8bbd0c", + "version" : "3.2.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "2ffd220823f3716904733162e9ae685545c276d1", + "version" : "12.8.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "75b31c842f664a0f46a2e590a570e370249fd8f6", + "version" : "1.69.1" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "fb7f2740b1570d2f7599c6bb9531bf4fad6974b7", + "version" : "5.0.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + } + ], + "version" : 3 +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/AccentColor.colorset/Contents.json b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/AppIcon.appiconset/Contents.json b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/Contents.json b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/GoogleService-Info.plist b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/GoogleService-Info.plist new file mode 100644 index 00000000..d8a169c7 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/GoogleService-Info.plist @@ -0,0 +1,30 @@ + + + + + API_KEY + AIzaSyAEizdoF84eYJQnokvf2jT07oFvJ7UTJRY + GCM_SENDER_ID + 439311565653 + PLIST_VERSION + 1 + BUNDLE_ID + com.google.firebase.example.MakeItSo + PROJECT_ID + make-it-so-live-ccdr-01 + STORAGE_BUCKET + make-it-so-live-ccdr-01.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:439311565653:ios:fd87509708ecce371fa5dc + + \ No newline at end of file diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/MakeItSoApp.swift b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/MakeItSoApp.swift new file mode 100644 index 00000000..01a3295e --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/MakeItSoApp.swift @@ -0,0 +1,22 @@ +import SwiftUI +import FirebaseCore + +class AppDelegate: NSObject, UIApplicationDelegate { + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + FirebaseApp.configure() + AuthenticationService.shared.signIn() + return true + } +} + +@main +struct MakeItSoApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate + + var body: some Scene { + WindowGroup { + TaskListView() + } + } +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Models/Task.swift b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Models/Task.swift new file mode 100644 index 00000000..0c9d0615 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Models/Task.swift @@ -0,0 +1,18 @@ +import Foundation +import FirebaseFirestore + +struct Task: Codable, Identifiable { + @DocumentID var id: String? + var title: String + var isCompleted: Bool + var priority: TaskPriority + var dueDate: Date? // Optional, as tasks might not have a deadline + @ServerTimestamp var createdAt: Date? + var userId: String? +} + +enum TaskPriority: String, Codable, CaseIterable { + case low = "Low" + case medium = "Medium" + case high = "High" +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Repositories/TaskRepository.swift b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Repositories/TaskRepository.swift new file mode 100644 index 00000000..e8faf38d --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Repositories/TaskRepository.swift @@ -0,0 +1,87 @@ +import Foundation +import FirebaseFirestore +import Observation +import FirebaseAuth + +@Observable +class TaskRepository { + var tasks = [Task]() + + private var db = Firestore.firestore() + private var listenerRegistration: ListenerRegistration? + + init() { + subscribe() + } + + deinit { + unsubscribe() + } + + func subscribe() { + if listenerRegistration == nil { + // Assuming we filter by userId. For anonymous auth, we might need to wait for auth state. + // For MVP, we'll just listen to the collection and trust security rules or client-side filtering once auth is active. + // Ideally, we'd wait for Auth to be ready. + + let query = db.collection("tasks") + .order(by: "isCompleted") // pending first + .order(by: "priority") // then by priority + + // Note: Real filtering by userId requires the user to be logged in. + // We often need a "userId" field in the query if rules require it. + + listenerRegistration = query + .addSnapshotListener { [weak self] querySnapshot, error in + guard let documents = querySnapshot?.documents else { + print("No documents") + return + } + + self?.tasks = documents.compactMap { queryDocumentSnapshot in + try? queryDocumentSnapshot.data(as: Task.self) + } + } + } + } + + func unsubscribe() { + listenerRegistration?.remove() + listenerRegistration = nil + } + + func addTask(_ task: Task) { + do { + var newTask = task + // Assign current user ID if available + if let userId = Auth.auth().currentUser?.uid { + newTask.userId = userId + } + let _ = try db.collection("tasks").addDocument(from: newTask) + } + catch { + print("Unable to add task: \(error.localizedDescription)") + } + } + + func updateTask(_ task: Task) { + if let taskID = task.id { + do { + try db.collection("tasks").document(taskID).setData(from: task) + } + catch { + print("Unable to update task: \(error.localizedDescription)") + } + } + } + + func deleteTask(_ task: Task) { + if let taskID = task.id { + db.collection("tasks").document(taskID).delete { error in + if let error = error { + print("Unable to remove document: \(error.localizedDescription)") + } + } + } + } +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Services/AuthenticationService.swift b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Services/AuthenticationService.swift new file mode 100644 index 00000000..91a1f52a --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Services/AuthenticationService.swift @@ -0,0 +1,18 @@ +import Foundation +import FirebaseAuth + +class AuthenticationService { + static let shared = AuthenticationService() + + private init() { } + + func signIn() { + if Auth.auth().currentUser == nil { + Auth.auth().signInAnonymously { authResult, error in + if let error = error { + print("Error signing in anonymously: \(error.localizedDescription)") + } + } + } + } +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/AddTaskView.swift b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/AddTaskView.swift new file mode 100644 index 00000000..4c9d0eb5 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/AddTaskView.swift @@ -0,0 +1,46 @@ +import SwiftUI + +struct AddTaskView: View { + @Environment(\.dismiss) var dismiss + var onAdd: (Task) -> Void + + @State private var title = "" + @State private var priority: TaskPriority = .medium + + var body: some View { + NavigationStack { + Form { + TextField("Task Title", text: $title) + .onSubmit { + submit() + } + + Picker("Priority", selection: $priority) { + ForEach(TaskPriority.allCases, id: \.self) { priority in + Text(priority.rawValue).tag(priority) + } + } + } + .navigationTitle("New Task") + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { + dismiss() + } + } + ToolbarItem(placement: .confirmationAction) { + Button("Add") { + submit() + } + .disabled(title.isEmpty) + } + } + } + } + + private func submit() { + let task = Task(title: title, isCompleted: false, priority: priority) + onAdd(task) + dismiss() + } +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/TaskListView.swift b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/TaskListView.swift new file mode 100644 index 00000000..75748b5b --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/TaskListView.swift @@ -0,0 +1,41 @@ +import SwiftUI + +struct TaskListView: View { + @State private var repository = TaskRepository() + @State private var isPresentingAddTask = false + + var body: some View { + NavigationStack { + List { + ForEach(repository.tasks) { task in + TaskRowView(task: task) { task in + var updatedTask = task + updatedTask.isCompleted.toggle() + repository.updateTask(updatedTask) + } + } + .onDelete { indexSet in + indexSet.forEach { index in + let task = repository.tasks[index] + repository.deleteTask(task) + } + } + } + .navigationTitle("Tasks") + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button { + isPresentingAddTask = true + } label: { + Image(systemName: "plus") + } + } + } + .sheet(isPresented: $isPresentingAddTask) { + AddTaskView { task in + repository.addTask(task) + } + } + } + } +} diff --git a/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/TaskRowView.swift b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/TaskRowView.swift new file mode 100644 index 00000000..95b5cf73 --- /dev/null +++ b/ccdr/makeitso/iOS/MakeItSo/MakeItSo/Views/TaskRowView.swift @@ -0,0 +1,29 @@ +import SwiftUI + +struct TaskRowView: View { + let task: Task + var onToggleCompleted: (Task) -> Void + + var body: some View { + HStack { + Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle") + .resizable() + .frame(width: 20, height: 20) + .onTapGesture { + onToggleCompleted(task) + } + + VStack(alignment: .leading) { + Text(task.title) + .strikethrough(task.isCompleted) + if task.priority != .medium { + Text(task.priority.rawValue) + .font(.caption) + .foregroundStyle(.secondary) + } + } + + Spacer() + } + } +} From 279ebe21fc5f2efbcc8c95aa6b7a5202838c73d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:26:17 +0000 Subject: [PATCH 2/2] Bump lodash in /javascript-firestore-auth-crash-course Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../package-lock.json | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/javascript-firestore-auth-crash-course/package-lock.json b/javascript-firestore-auth-crash-course/package-lock.json index 74f30170..c1fac16a 100644 --- a/javascript-firestore-auth-crash-course/package-lock.json +++ b/javascript-firestore-auth-crash-course/package-lock.json @@ -61,7 +61,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -844,7 +843,6 @@ "version": "0.7.20", "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.20.tgz", "integrity": "sha512-tTVrEYCbEKBcMp/bj5rUa35iM32W5z9l3jbLAqDh0ZM2yO4JvF08a3hHacZ32XDh9Av/yCgla0QmVPp/Z2klNg==", - "peer": true, "dependencies": { "@firebase/component": "0.5.12", "@firebase/logger": "0.3.2", @@ -896,7 +894,6 @@ "version": "0.1.21", "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.21.tgz", "integrity": "sha512-zKRjOt6JXZ6gBdl3ELdjvEQ7cdrsrCjLEaLttBxioqW9VxXZfBOgP38uUb0baJk2XNEUA6YWM+H/bg1y+FGFYA==", - "peer": true, "dependencies": { "@firebase/app": "0.7.20", "@firebase/component": "0.5.12", @@ -908,8 +905,7 @@ "node_modules/@firebase/app-types": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", - "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==", - "peer": true + "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==" }, "node_modules/@firebase/auth": { "version": "0.19.11", @@ -1270,7 +1266,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.5.1.tgz", "integrity": "sha512-ojwPg8sKVcoU/kC1QdTrD+eUDyjQkZyiH9tlouXeZdAeDddCYNvHgIeBQhZt62WIcjlNhy1zro/xdV5nUUU38A==", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -2014,7 +2009,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -2394,6 +2388,29 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -6897,7 +6914,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", "dev": true, - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -10134,10 +10150,11 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -10410,7 +10427,6 @@ "version": "10.7.0", "resolved": "https://registry.npmjs.org/preact/-/preact-10.7.0.tgz", "integrity": "sha512-9MEURwzNMKpAil/t6+wabDIJI6oG6GnwypYxiJDvQnW+fHDTt51PYuLZ1QUM31hFr7sDaj9qTaShAF9VIxuxGQ==", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -10623,7 +10639,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "devOptional": true }, "node_modules/selenium-webdriver": { "version": "4.0.0-rc-1", @@ -10805,7 +10821,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -10931,7 +10946,6 @@ "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -11043,7 +11057,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" },