Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ jobs:
shell: bash
run: echo "BUILD_NUMBER=$(expr $GITHUB_RUN_NUMBER + 5210)" >> $GITHUB_ENV

- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: set up JDK
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 19
distribution: zulu
java-version: 21

- name: Make checksum executable
run: chmod +x .github/checksum.sh
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/scheduled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ jobs:
timeout-minutes: 20

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 11
distribution: zulu
java-version: 21

- name: Copy CI gradle.properties
run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
Expand All @@ -26,7 +27,7 @@ jobs:
run: ./gradlew dependencyUpdates

- name: Upload dependency updates report
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: dependency-updates
path: build/dependencyUpdates
109 changes: 109 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Barnee — Agent Guide

Cocktail recipe app built with **Kotlin Multiplatform (KMP)** and **Compose Multiplatform**,
targeting Android and iOS.

## Project Structure

```
app/ Android application entry point (MainActivity, App)
shared/ KMP business logic (data, domain, DI) — shared between Android & iOS
ui/ KMP Compose Multiplatform UI (screens, components, theme)
iosApp/ iOS host app (SwiftUI + CocoaPods)
```

Source sets inside `shared/` and `ui/`:

- `commonMain` — shared Kotlin code
- `androidMain` — Android-specific implementations
- `iosMain` — iOS-specific implementations

## Architecture

**MVI** with a reactive `StateMachine` base class (in
`shared/src/commonMain/.../domain/StateMachine.kt`):

- Each feature has a `*StateMachine` that implements `State`, `Action`, and a `Reducer`
- `StateMachine` extends Voyager's `ScreenModel` and is managed by Koin DI
- Navigation is handled via `Router` and Voyager screens defined in `ui/`

**Layers:**

- `data/model` — pure data classes (serializable, `@Parcelize`)
- `data/remote` — Ktor-based API clients (`Api`, `AiApi`, `CloudinaryApi`)
- `data/repository` — repositories bridging remote + local
- `data/local` — `LocalStore` backed by Multiplatform Settings / DataStore
- `domain/` — state machines and use cases
- `ui/screen/` — Compose Multiplatform screens, one per feature

## Key Dependencies

| Library | Purpose |
|------------------------|--------------------------------------------------|
| Voyager | Navigation + `ScreenModel` (ViewModel) |
| Koin | Dependency injection (multiplatform) |
| Ktor | HTTP client (cocktail API, OpenAI, Cloudinary) |
| Kotlinx Serialization | JSON parsing |
| Multiplatform Settings | Local persistence (DataStore on Android) |
| OpenAI client | AI cocktail generation (`BartenderStateMachine`) |
| Compose Multiplatform | Shared UI for Android and iOS |

## Build Setup

**Prerequisites:**

- JDK 17+ on `PATH` (install via `brew install --cask zulu@17`)
- Android SDK at `sdk.dir` set in `local.properties`
- API keys in `local.properties`:
```
OPEN_AI_API_KEY=...
CLOUDINARY_API_SECRET=...
```
- For iOS: CocoaPods installed; run `pod install` inside `iosApp/`

**Build commands:**

```bash
# Android debug APK
./gradlew :app:assembleDebug

# Run Android unit tests
./gradlew :shared:testDebugUnitTest
./gradlew :ui:testDebugUnitTest

# Check for dependency updates
./gradlew dependencyUpdates

# iOS — generate the shared framework first
./gradlew :shared:podPublishDebugXCFramework
```

## Coding Conventions

- **New feature checklist:**
1. Add `State`, `Action`, `Reducer` → create `*StateMachine` in `shared/.../domain/`
2. Register the state machine as a `factory` in `commonModule` (`shared/.../di/Koin.kt`)
3. Add a Voyager `Screen` in `ui/.../screen/`
4. Register the screen in `ui/.../navigation/ScreenRegistry.kt`
5. Add a route in `shared/.../domain/navigation/AppScreens.kt` if deep-linkable

- **Platform-specific code:** use `expect`/`actual` with implementations in `androidMain` /
`iosMain`
- **All copyright headers** must use the MIT license block matching the rest of the project
- **Package name:** `com.popalay.barnee`
- **Kotlin style:** official (`kotlin.code.style=official` in `gradle.properties`)
- Do not add inline imports; keep all imports at the top of the file

## Screens

| Screen | StateMachine |
|--------------------------|--------------------------------------|
| Discovery | `DiscoveryStateMachine` |
| Drink detail | `DrinkStateMachine` |
| Search | `SearchStateMachine` |
| Parameterized drink list | `ParameterizedDrinkListStateMachine` |
| Collection | `CollectionStateMachine` |
| Collection list | `CollectionListStateMachine` |
| Add to collection | `AddToCollectionStateMachine` |
| Bartender (AI) | `BartenderStateMachine` |
| Shake to drink | `ShakeToDrinkStateMachine` |
13 changes: 6 additions & 7 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Denys Nykyforov
* Copyright (c) 2026 Denys Nykyforov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -22,6 +22,7 @@

plugins {
kotlin("multiplatform")
kotlin("plugin.compose") version libs.versions.kotlin
id("com.android.application")
id("com.google.gms.google-services")
id("kotlin-parcelize")
Expand All @@ -35,7 +36,7 @@ android {
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = properties.getOrDefault("barnee.versioncode", 1).toString().toInt()
versionName = "1.4.0"
versionName = "1.5.0"

signingConfigs {
getByName("debug") {
Expand Down Expand Up @@ -76,8 +77,8 @@ android {
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_19
targetCompatibility = JavaVersion.VERSION_19
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

packaging {
Expand All @@ -86,12 +87,10 @@ android {

namespace = "com.popalay.barnee"

composeOptions {
kotlinCompilerExtensionVersion = libs.versions.jetbrainsComposeCompiler.get()
}
}

kotlin {
jvmToolchain(21)
androidTarget()
sourceSets {
sourceSets["androidMain"].dependencies {
Expand Down
2 changes: 1 addition & 1 deletion app/src/androidMain/kotlin/com/popalay/barnee/App.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Denys Nykyforov
* Copyright (c) 2026 Denys Nykyforov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Denys Nykyforov
* Copyright (c) 2026 Denys Nykyforov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down
6 changes: 4 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Denys Nykyforov
* Copyright (c) 2026 Denys Nykyforov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -33,6 +33,8 @@ buildscript {
}
}

plugins{
plugins {
id("com.github.ben-manes.versions") version libs.versions.dependencyUpdatesPlugin
id("org.jetbrains.compose") version libs.versions.jetbrainsCompose apply false
kotlin("plugin.compose") version libs.versions.kotlin apply false
}
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ android.disableResourceValidation=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false
org.jetbrains.compose.experimental.uikit.enabled=true
kotlin.mpp.androidGradlePluginCompatibility.nowarn=true
28 changes: 13 additions & 15 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
[versions]
android-minSdk = "28"
android-targetSdk = "34"
android-compileSdk = "34"
android-targetSdk = "36"
android-compileSdk = "36"
imageLoader = "1.6.2"
insetsx = "0.1.0-alpha10"
kotlin = "1.9.0"
android-gradlePlugin = "8.2.2"
kotlin = "2.0.21"
android-gradlePlugin = "8.9.1"
google-services-gradlePlugin = "4.3.15"
dependencyUpdatesPlugin = "0.47.0"
buildkonfig-gradle-plugin = "0.13.3"

androidx-activity = "1.7.2"
androidx-lifecycle = "2.6.1"
androidx-activity = "1.13.0"
androidx-lifecycle = "2.8.0"
androidx-datastore = "1.0.0"
androidx-espresso = "3.5.1"
androidx-core-splashscreen = "1.0.1"
Expand All @@ -20,11 +20,11 @@ kotlinxDatetime = "0.4.0"
ktor = "2.3.2"
junit = "4.13.2"
androidx-test = "1.1.5"
logger = "2.0.0-RC5"
coroutines = "1.7.3"
koin = "3.4.3"
logger = "2.0.6"
coroutines = "1.10.2"
koin = "3.5.6"
koin-compose = "1.0.4"
multiplatformsettings = "1.0.0"
multiplatformsettings = "1.3.0"
firebase-dynamicLinks = "21.1.0"
multiplatformpaging = "3.2.0-alpha05-0.2.3"
parcelize = "0.1.2"
Expand All @@ -33,9 +33,8 @@ uri = "0.0.13"
uuid = "0.8.0"
youtubePlayer = "12.0.0"
openai = "3.3.2"
voyager = "1.0.0-rc06"
jetbrainsCompose = "1.4.3"
jetbrainsComposeCompiler = "1.5.0"
voyager = "1.1.0-beta02"
jetbrainsCompose = "1.6.11"

[libraries]
#Core
Expand Down Expand Up @@ -82,7 +81,6 @@ multiplatformsettings-datastore = { module = "com.russhwolf:multiplatform-settin

#Voyager
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
voyager-bottomSheetNavigator = { module = "cafe.adriel.voyager:voyager-bottom-sheet-navigator", version.ref = "voyager" }
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
voyager-koin = { module = "cafe.adriel.voyager:voyager-koin", version.ref = "voyager" }

Expand All @@ -103,5 +101,5 @@ insetsx = { module = "com.moriatsushi.insetsx:insetsx", version.ref = "insetsx"
[bundles]
ktor = ["ktor-core", "ktor-android", "ktor-serialization", "ktor-logging"]
koin = ["koin-android", "koin-core", "koin-compose"]
vojager = ["voyager-navigator", "voyager-bottomSheetNavigator", "voyager-transitions", "voyager-koin"]
vojager = ["voyager-navigator", "voyager-transitions", "voyager-koin"]
kotlinx = ["kotlinx-coroutines-core", "kotlinx-datetime"]
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
18 changes: 0 additions & 18 deletions iosApp/iosApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@
7555FF77242A565900829871 /* Sources */,
7555FF79242A565900829871 /* Resources */,
F85CB1118929364A9C6EFABC /* Frameworks */,
4260E2E22261F2CC8EFA4D0F /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -171,23 +170,6 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
4260E2E22261F2CC8EFA4D0F /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n";
showEnvVarsInLog = 0;
};
98D614C51D2DA07C614CC46E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand Down
Loading
Loading