diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml deleted file mode 100644 index a12225a..0000000 --- a/.github/workflows/claude-code-review.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Claude Code Review - -on: - pull_request: - types: [opened, synchronize] - # Optional: Only run on specific file changes - # paths: - # - "src/**/*.ts" - # - "src/**/*.tsx" - # - "src/**/*.js" - # - "src/**/*.jsx" - -jobs: - claude-review: - # Optional: Filter by PR author - # if: | - # github.event.pull_request.user.login == 'external-contributor' || - # github.event.pull_request.user.login == 'new-developer' || - # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' - - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Claude Code Review - id: claude-review - uses: anthropics/claude-code-action@beta - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1) - # model: "claude-opus-4-1-20250805" - - # Direct prompt for automated review (no @claude mention needed) - direct_prompt: | - Please review this pull request and provide feedback on: - - Code quality and best practices - - Potential bugs or issues - - Performance considerations - - Security concerns - - Test coverage - - Be constructive and helpful in your feedback. - - # Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR - # use_sticky_comment: true - - # Optional: Customize review based on file types - # direct_prompt: | - # Review this PR focusing on: - # - For TypeScript files: Type safety and proper interface usage - # - For API endpoints: Security, input validation, and error handling - # - For React components: Performance, accessibility, and best practices - # - For tests: Coverage, edge cases, and test quality - - # Optional: Different prompts for different authors - # direct_prompt: | - # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && - # 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' || - # 'Please provide a thorough code review focusing on our coding standards and best practices.' }} - - # Optional: Add specific tools for running tests or linting - # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)" - - # Optional: Skip review for certain conditions - # if: | - # !contains(github.event.pull_request.title, '[skip-review]') && - # !contains(github.event.pull_request.title, '[WIP]') - diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index bc77307..0000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Claude Code - id: claude - uses: anthropics/claude-code-action@beta - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - # This is an optional setting that allows Claude to read CI results on PRs - additional_permissions: | - actions: read - - # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1) - # model: "claude-opus-4-1-20250805" - - # Optional: Customize the trigger phrase (default: @claude) - # trigger_phrase: "/claude" - - # Optional: Trigger when specific user is assigned to an issue - # assignee_trigger: "claude-bot" - - # Optional: Allow Claude to run specific commands - # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)" - - # Optional: Add custom instructions for Claude to customize its behavior for your project - # custom_instructions: | - # Follow our coding standards - # Ensure all new code has tests - # Use TypeScript for new files - - # Optional: Custom environment variables for Claude - # claude_env: | - # NODE_ENV: test - diff --git a/AGENTS.md b/AGENTS.md index 678e95e..7cf14ee 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -147,7 +147,15 @@ xcrun simctl launch booted com.barronroth.Cubby DISABLE_CLOUDKIT - Use `asc` for App Store Connect status, build/version staging, review submission, and release/distribution operations. - Use Xcode Cloud when local archive/signing is blocked by keychain or certificate access. - `.asc/export-options-app-store.plist` contains App Store Connect export options for local `asc`/Xcode export flows. -- Current project settings should be verified before release; as of this update, marketing version is `1.0.4`, build is `70`, and deployment target is iOS `26.0`. +- Version/build numbers are release-sensitive. Before opening or updating any PR branch that can trigger Xcode Cloud/TestFlight/App Store distribution: + - Read the project values from `Cubby.xcodeproj/project.pbxproj`: + `rg -n "MARKETING_VERSION|CURRENT_PROJECT_VERSION" Cubby.xcodeproj/project.pbxproj` + - Check App Store Connect for already-uploaded builds: + `asc builds list --app 6751732388 --sort -uploadedDate --limit 20 --output table` + - Ensure `CURRENT_PROJECT_VERSION` is greater than every uploaded/submitted build for the same `MARKETING_VERSION`; if not, bump all `CURRENT_PROJECT_VERSION` occurrences to the next unused integer before pushing. + - Do not bump `MARKETING_VERSION` unless the user explicitly asks for a new App Store version or the release train requires it. For normal PR/TestFlight fixes, keep the current marketing version and only bump the build number. + - After pushing, monitor Xcode Cloud from GitHub with `gh pr checks --watch=false`; if it fails, inspect the Xcode Cloud details before bumping again. +- Branches intended to launch the Codex Xcode Cloud/TestFlight workflow should keep the `codex/` prefix. - `fastlane/` is legacy. Historically it provided: - `fastlane beta`: increment build number, build an App Store export, upload to TestFlight, wait for processing, distribute externally. - `fastlane beta_internal`: same upload path without external distribution. diff --git a/Cubby.xcodeproj/project.pbxproj b/Cubby.xcodeproj/project.pbxproj index 9867bdb..2a703e3 100644 --- a/Cubby.xcodeproj/project.pbxproj +++ b/Cubby.xcodeproj/project.pbxproj @@ -316,7 +316,7 @@ CODE_SIGN_ENTITLEMENTS = Cubby/Cubby.Debug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 86; + CURRENT_PROJECT_VERSION = 91; DEVELOPMENT_TEAM = CVE9ZKS33D; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -333,7 +333,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = com.barronroth.Cubby; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -352,7 +352,7 @@ CODE_SIGN_ENTITLEMENTS = Cubby/Cubby.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 86; + CURRENT_PROJECT_VERSION = 91; DEVELOPMENT_TEAM = CVE9ZKS33D; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -369,7 +369,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = com.barronroth.Cubby; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -503,10 +503,10 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 86; + CURRENT_PROJECT_VERSION = 91; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 26.0; - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = com.barronroth.CubbyTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -521,10 +521,10 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 86; + CURRENT_PROJECT_VERSION = 91; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 26.0; - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = com.barronroth.CubbyTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -538,9 +538,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 86; + CURRENT_PROJECT_VERSION = 91; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = com.barronroth.CubbyUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -554,9 +554,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 86; + CURRENT_PROJECT_VERSION = 91; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = com.barronroth.CubbyUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -618,10 +618,10 @@ }; F6E851580DBA413886332B51 /* XCRemoteSwiftPackageReference "MCEmojiPicker" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/izyumkin/MCEmojiPicker"; + repositoryURL = "https://github.com/barronlroth/MCEmojiPicker"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.2.5; + kind = revision; + revision = 2391491cb0dcbba479ec8b3425b726d3e8617d19; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/Cubby.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Cubby.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 30b5441..13daa54 100644 --- a/Cubby.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Cubby.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,13 +1,12 @@ { - "originHash" : "fc6ad783e10454de647eafd2da6092a8279c0eaa2253d5e318b11eea550edf23", + "originHash" : "29321ae2c5875abe034601b5c84a05504a346984fdef54f437857dd3915426c8", "pins" : [ { "identity" : "mcemojipicker", "kind" : "remoteSourceControl", - "location" : "https://github.com/izyumkin/MCEmojiPicker", + "location" : "https://github.com/barronlroth/MCEmojiPicker", "state" : { - "revision" : "e5b3ed359b04370287366a7c15ffa7013cdd4eec", - "version" : "1.2.5" + "revision" : "2391491cb0dcbba479ec8b3425b726d3e8617d19" } }, { diff --git a/Cubby/Views/Items/ItemEmojiPickerButton.swift b/Cubby/Views/Items/ItemEmojiPickerButton.swift index e956ca4..421af69 100644 --- a/Cubby/Views/Items/ItemEmojiPickerButton.swift +++ b/Cubby/Views/Items/ItemEmojiPickerButton.swift @@ -104,8 +104,7 @@ private struct ItemMCEmojiPickerController: UIViewControllerRepresentable { switch isPresented { case true: - if let emojiPicker = representableController.presentedViewController as? MCEmojiPickerViewController { - MCEmojiPickerCategoryBarRepair.schedule(in: emojiPicker.view) + if representableController.presentedViewController is MCEmojiPickerViewController { return } @@ -119,9 +118,7 @@ private struct ItemMCEmojiPickerController: UIViewControllerRepresentable { emojiPicker.feedBackGeneratorStyle = feedBackGeneratorStyle context.coordinator.addPickerDismissingObserver() - representableController.present(emojiPicker, animated: true) { - MCEmojiPickerCategoryBarRepair.schedule(in: emojiPicker.view) - } + representableController.present(emojiPicker, animated: true) case false: if representableController.presentedViewController is MCEmojiPickerViewController, context.coordinator.isPresented { @@ -172,58 +169,3 @@ private struct ItemMCEmojiPickerController: UIViewControllerRepresentable { private static let pickerDidDisappearNotification = Notification.Name("MCEmojiPickerDidDisappear") } } - -private enum MCEmojiPickerCategoryBarRepair { - // MCEmojiPicker 1.2.5 appends category controls from draw(_:), so popover redraws can duplicate them. - private static let categoryCount = 9 - private static let repairDelays: [TimeInterval] = [0, 0.05, 0.2, 0.45] - - static func schedule(in rootView: UIView) { - repairDelays.forEach { delay in - DispatchQueue.main.asyncAfter(deadline: .now() + delay) { - repair(in: rootView) - } - } - } - - private static func repair(in rootView: UIView) { - stackViews(in: rootView).forEach(repair) - } - - private static func repair(_ stackView: UIStackView) { - let arrangedSubviews = stackView.arrangedSubviews - guard arrangedSubviews.count > categoryCount, - arrangedSubviews.allSatisfy(isCategoryView) - else { return } - - let viewsToKeep = Set(arrangedSubviews.suffix(categoryCount).map { ObjectIdentifier($0) }) - for view in arrangedSubviews where !viewsToKeep.contains(ObjectIdentifier(view)) { - stackView.removeArrangedSubview(view) - view.removeFromSuperview() - } - - stackView.setNeedsLayout() - stackView.layoutIfNeeded() - stackView.arrangedSubviews.forEach { - $0.setNeedsLayout() - $0.setNeedsDisplay() - } - } - - private static func isCategoryView(_ view: UIView) -> Bool { - String(reflecting: type(of: view)).contains("MCTouchableEmojiCategoryView") - } - - private static func stackViews(in rootView: UIView) -> [UIStackView] { - var result = [UIStackView]() - if let stackView = rootView as? UIStackView { - result.append(stackView) - } - - rootView.subviews.forEach { - result.append(contentsOf: stackViews(in: $0)) - } - - return result - } -}