Skip to content

Commit 087fd8d

Browse files
authored
Merge pull request #2224 from DimensionDev/feature/native-macos
Feature/native macos
2 parents ebce41b + 60c547c commit 087fd8d

787 files changed

Lines changed: 39408 additions & 37606 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/crowdin-upload.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ on:
55
branches: [master]
66
paths:
77
- "**/values/strings.xml"
8-
- "iosApp/flare/Localizable.xcstrings"
8+
- "crowdin.yml"
9+
- "appleApp/Shared/FlareAppleResource/Resources/Localizable.xcstrings"
910

1011
jobs:
1112
crowdin:

.github/workflows/desktop.yml

Lines changed: 121 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ concurrency:
2222
jobs:
2323
build-macos:
2424
runs-on: [macos-26]
25-
timeout-minutes: 30
2625
steps:
2726
- uses: yumis-coconudge/clean-workspace-action@v1
2827

@@ -34,55 +33,150 @@ jobs:
3433
env:
3534
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3635
with:
37-
distribution: 'jetbrains'
36+
distribution: "jetbrains"
3837
java-version: 25
3938
- name: Setup Xcode
4039
uses: maxim-lobanov/setup-xcode@v1
4140
with:
4241
xcode-version: latest-stable
42+
- name: Install XcodeGen
43+
run: brew install xcodegen
4344
- name: import certs
44-
id: import-cert
4545
if: startsWith(github.ref, 'refs/tags/')
4646
uses: apple-actions/import-codesign-certs@v6
4747
with:
4848
p12-file-base64: ${{ secrets.MACOS_CERT_BASE64 }}
4949
p12-password: ${{ secrets.MACOS_CERT_PASSWORD }}
50-
- name: Create Embedded Provision Profile
50+
- name: Install macOS Provision Profile
5151
if: startsWith(github.ref, 'refs/tags/')
5252
run: |
53-
echo "$MACOS_EMBEDDED_PROVISION" > desktopApp/embedded.provisionprofile.b64
54-
base64 -d -i desktopApp/embedded.provisionprofile.b64 > desktopApp/embedded.provisionprofile
53+
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
54+
echo "$MACOS_EMBEDDED_PROVISION" > "$RUNNER_TEMP/native-macos.provisionprofile.b64"
55+
base64 -d -i "$RUNNER_TEMP/native-macos.provisionprofile.b64" > "$RUNNER_TEMP/native-macos.provisionprofile"
56+
security cms -D -i "$RUNNER_TEMP/native-macos.provisionprofile" > "$RUNNER_TEMP/native-macos-profile.plist"
57+
profile_uuid=$(/usr/libexec/PlistBuddy -c "Print UUID" "$RUNNER_TEMP/native-macos-profile.plist")
58+
profile_name=$(/usr/libexec/PlistBuddy -c "Print Name" "$RUNNER_TEMP/native-macos-profile.plist")
59+
cp "$RUNNER_TEMP/native-macos.provisionprofile" "$HOME/Library/MobileDevice/Provisioning Profiles/$profile_uuid.provisionprofile"
60+
echo "NATIVE_MACOS_PROVISIONING_PROFILE=$profile_uuid" >> "$GITHUB_ENV"
61+
echo "NATIVE_MACOS_PROVISIONING_PROFILE_SPECIFIER=$profile_name" >> "$GITHUB_ENV"
5562
env:
5663
MACOS_EMBEDDED_PROVISION: ${{ secrets.MACOS_EMBEDDED_PROVISION }}
57-
- name: Create Runtime Provision Profile
64+
- name: Resolve native macOS signing identities
5865
if: startsWith(github.ref, 'refs/tags/')
5966
run: |
60-
echo "$MACOS_RUNTIME_PROVISION" > desktopApp/runtime.provisionprofile.b64
61-
base64 -d -i desktopApp/runtime.provisionprofile.b64 > desktopApp/runtime.provisionprofile
67+
set -euo pipefail
68+
69+
security find-identity -v -p codesigning
70+
security find-identity -v -p basic
71+
72+
app_identity=$(security find-identity -v -p codesigning | sed -n 's/.*"\(3rd Party Mac Developer Application: .* (7LFDZ96332)\)".*/\1/p' | head -n 1)
73+
installer_identity=$(security find-identity -v -p basic | sed -n 's/.*"\(3rd Party Mac Developer Installer: .* (7LFDZ96332)\)".*/\1/p' | head -n 1)
74+
75+
if [ -z "$app_identity" ]; then
76+
echo "No 3rd Party Mac Developer Application identity found for team 7LFDZ96332."
77+
exit 1
78+
fi
79+
80+
if [ -z "$installer_identity" ]; then
81+
echo "No 3rd Party Mac Developer Installer identity found for team 7LFDZ96332."
82+
exit 1
83+
fi
84+
85+
echo "NATIVE_MACOS_APP_SIGNING_IDENTITY=$app_identity" >> "$GITHUB_ENV"
86+
echo "NATIVE_MACOS_INSTALLER_SIGNING_IDENTITY=$installer_identity" >> "$GITHUB_ENV"
87+
- name: Add Firebase config
6288
env:
63-
MACOS_RUNTIME_PROVISION: ${{ secrets.MACOS_RUNTIME_PROVISION }}
64-
# Run tests
65-
- name: Run Tests
66-
run: ./gradlew jvmTest
67-
- name: Build with Gradle
89+
GOOGLE_SERVICE_INFO_PLIST_B64: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST_B64 }}
90+
run: |
91+
if [ -n "${GOOGLE_SERVICE_INFO_PLIST_B64:-}" ]; then
92+
mkdir -p appleApp/Firebase
93+
printf '%s' "$GOOGLE_SERVICE_INFO_PLIST_B64" | base64 -D > appleApp/Firebase/GoogleService-Info.plist
94+
else
95+
echo "GOOGLE_SERVICE_INFO_PLIST_B64 is not set; Firebase config will not be bundled."
96+
fi
97+
- name: Generate Xcode project
98+
run: xcodegen generate --spec appleApp/project.yml
99+
- name: Resolve native macOS version
100+
run: |
101+
marketing_version="$(sed -n 's/^versionName=//p' fdroid.properties | head -n 1)"
102+
103+
if [[ "$GITHUB_REF_NAME" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
104+
marketing_version="$GITHUB_REF_NAME"
105+
fi
106+
107+
if [ -z "$marketing_version" ]; then
108+
echo "Unable to resolve native macOS marketing version"
109+
exit 1
110+
fi
111+
112+
echo "NATIVE_MACOS_MARKETING_VERSION=$marketing_version" >> "$GITHUB_ENV"
113+
echo "NATIVE_MACOS_BUILD_NUMBER=$GITHUB_RUN_NUMBER" >> "$GITHUB_ENV"
114+
- name: Build native macOS app
115+
if: ${{ ! startsWith(github.ref, 'refs/tags/') }}
116+
working-directory: ./appleApp
68117
env:
69-
BUILD_NUMBER: ${{github.run_number}}
70-
BUILD_VERSION: ${{github.ref_name}}
71118
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
72-
run: ./gradlew :desktop:check :desktop:build --stacktrace
73-
- name: Package with Gradle
119+
run: |
120+
xcodebuild build \
121+
-project Flare.xcodeproj \
122+
-scheme macOS \
123+
-configuration Release \
124+
-destination "generic/platform=macOS" \
125+
-derivedDataPath build/DerivedData \
126+
-skipPackagePluginValidation \
127+
-skipMacroValidation \
128+
GRADLE_JVM_ARGS="-Xmx16G -Dfile.encoding=UTF-8" \
129+
JAVA_OPTS="-Xmx16G" \
130+
MARKETING_VERSION="$NATIVE_MACOS_MARKETING_VERSION" \
131+
CURRENT_PROJECT_VERSION="$NATIVE_MACOS_BUILD_NUMBER" \
132+
CODE_SIGNING_ALLOWED=NO
133+
- name: Archive native macOS app
134+
if: startsWith(github.ref, 'refs/tags/')
135+
working-directory: ./appleApp
74136
env:
75-
BUILD_NUMBER: ${{github.run_number}}
76-
BUILD_VERSION: ${{github.ref_name}}
137+
FIREBASE_CRASHLYTICS_UPLOAD_SYMBOLS: ${{ startsWith(github.ref, 'refs/tags/') && '1' || '0' }}
77138
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
78-
run: ./gradlew :desktop:packageReleasePkg --stacktrace
79-
- name: Archive macOS artifacts Product
139+
run: |
140+
xcodebuild archive \
141+
-project Flare.xcodeproj \
142+
-scheme macOS \
143+
-configuration Release \
144+
-destination "generic/platform=macOS" \
145+
-archivePath "$RUNNER_TEMP/Flare-native-macOS.xcarchive" \
146+
-derivedDataPath build/DerivedData \
147+
-skipPackagePluginValidation \
148+
-skipMacroValidation \
149+
DEVELOPMENT_TEAM=7LFDZ96332 \
150+
CODE_SIGN_STYLE=Manual \
151+
CODE_SIGN_IDENTITY="$NATIVE_MACOS_APP_SIGNING_IDENTITY" \
152+
NATIVE_MACOS_PROVISIONING_PROFILE_SPECIFIER="$NATIVE_MACOS_PROVISIONING_PROFILE_SPECIFIER" \
153+
NATIVE_MACOS_PROVISIONING_PROFILE="$NATIVE_MACOS_PROVISIONING_PROFILE" \
154+
GRADLE_JVM_ARGS="-Xmx16G -Dfile.encoding=UTF-8" \
155+
JAVA_OPTS="-Xmx16G" \
156+
MARKETING_VERSION="$NATIVE_MACOS_MARKETING_VERSION" \
157+
CURRENT_PROJECT_VERSION="$NATIVE_MACOS_BUILD_NUMBER"
158+
- name: Package native macOS app
159+
if: startsWith(github.ref, 'refs/tags/')
160+
run: |
161+
set -euo pipefail
162+
163+
app_path="$RUNNER_TEMP/Flare-native-macOS.xcarchive/Products/Applications/Flare.app"
164+
if [ ! -d "$app_path" ]; then
165+
echo "No archived app found at $app_path"
166+
exit 1
167+
fi
168+
169+
mkdir -p appleApp/build/native-macos/pkg
170+
safe_ref="${GITHUB_REF_NAME//\//-}"
171+
pkg_path="appleApp/build/native-macos/pkg/Flare-native-macOS-$safe_ref.pkg"
172+
productbuild --sign "$NATIVE_MACOS_INSTALLER_SIGNING_IDENTITY" --component "$app_path" /Applications "$pkg_path"
173+
- name: Upload native macOS package
80174
if: startsWith(github.ref, 'refs/tags/')
81175
uses: actions/upload-artifact@v6
82176
with:
83-
name: macOS-package
84-
path: desktopApp/build/compose/binaries/main-release/pkg/*.pkg
85-
177+
name: Native-macOS-package
178+
path: appleApp/build/native-macos/pkg/*.pkg
179+
86180
build-windows:
87181
runs-on: [windows-latest]
88182
timeout-minutes: 30
@@ -97,7 +191,7 @@ jobs:
97191
env:
98192
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
99193
with:
100-
distribution: 'jetbrains'
194+
distribution: "jetbrains"
101195
java-version: 25
102196
# Run tests
103197
- name: Run Tests
@@ -198,7 +292,7 @@ jobs:
198292
env:
199293
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
200294
with:
201-
distribution: 'jetbrains'
295+
distribution: "jetbrains"
202296
java-version: 25
203297
# Run tests
204298
- name: Run Tests

.github/workflows/ios.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,22 @@ jobs:
4343
run: xcodebuild -downloadPlatform iOS
4444
- name: Install XcodeGen
4545
run: brew install xcodegen
46+
- name: Add Firebase config
47+
env:
48+
GOOGLE_SERVICE_INFO_PLIST_B64: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST_B64 }}
49+
run: |
50+
if [ -n "${GOOGLE_SERVICE_INFO_PLIST_B64:-}" ]; then
51+
mkdir -p appleApp/Firebase
52+
printf '%s' "$GOOGLE_SERVICE_INFO_PLIST_B64" | base64 -D > appleApp/Firebase/GoogleService-Info.plist
53+
else
54+
echo "GOOGLE_SERVICE_INFO_PLIST_B64 is not set; Firebase config will not be bundled."
55+
fi
4656
- name: Generate Xcode project
47-
run: xcodegen generate --spec iosApp/project.yml
57+
run: xcodegen generate --spec appleApp/project.yml
4858
- name: Build
49-
working-directory: ./iosApp
59+
working-directory: ./appleApp
5060
env:
51-
scheme: Flare
61+
scheme: iOS
5262
platform: ${{ 'iOS Simulator' }}
5363
run: |
5464
# xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)

.gitignore

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ local.properties
1818
signing.properties
1919

2020
build/
21-
iosApp/Podfile.lock
22-
iosApp/Pods/*
23-
iosApp/Flare.xcworkspace/*
24-
iosApp/Flare.xcodeproj/*
21+
*/Podfile.lock
22+
*/Pods/*
23+
*/Flare.xcworkspace/*
24+
*/Flare.xcodeproj/*
2525
shared/shared.podspec
2626
.kotlin
2727
.history
2828
.lh
29-
iosApp/K_D/
29+
appleApp/K_D/
3030
*.sh
3131
!.github/scripts/*.sh
3232
.env

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Flare uses [ktlint](https://github.com/pinterest/ktlint) to check the code style
3434
### iOS
3535
- Make sure you have JDK 25 installed
3636
- Make sure you have a Mac with Xcode 26 installed
37-
- open `iosApp/Flare.xcodeproj` in Xcode
37+
- open `appleApp/Flare.xcodeproj` in Xcode
3838
- Build and run the app
3939

4040
### Desktop
@@ -46,10 +46,10 @@ The project is split into theses parts:
4646
- `shared`: The common code, including bussiness logic.
4747
- `compose-ui`: The Compose UI code that shared between Android, iOS, Desktop.
4848
- `app`: The Android app.
49-
- `iosApp`: The iOS app.
49+
- `appleApp`: The iOS and macOS apps.
5050
- `desktopApp`: The desktop app for Windows/macOS.
5151

52-
Most of the business logic is in `shared`, and the platform specific code and UI is in `app` and `iosApp`.
52+
Most of the business logic is in `shared`, and the platform specific code and UI is in `app` and `appleApp`.
5353
Flare uses [Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html) to share code between platforms, [Jetpack Compose](https://developer.android.com/jetpack/compose) for the UI on Android, [SwiftUI](https://developer.apple.com/xcode/swiftui/) for the UI on iOS.
5454

5555
### Business logic

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ Here're some features we're planning to implement in the future.
5555
### iOS
5656
- Make sure you have JDK 25 installed
5757
- Make sure you have a Mac with Xcode 26 installed
58-
- Install XcodeGen with `brew install xcodegen`
59-
- Run `xcodegen generate --spec iosApp/project.yml`
60-
- open `iosApp/Flare.xcodeproj` in Xcode
58+
- open `appleApp/Flare.xcodeproj` in Xcode
6159
- Build and run the app
6260

6361
### Desktop

app/src/main/java/dev/dimension/flare/ui/screen/profile/ProfileScreen.kt

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import androidx.compose.ui.graphics.Color
5353
import androidx.compose.ui.graphics.graphicsLayer
5454
import androidx.compose.ui.input.nestedscroll.nestedScroll
5555
import androidx.compose.ui.platform.LocalDensity
56+
import androidx.compose.ui.platform.LocalUriHandler
5657
import androidx.compose.ui.platform.LocalWindowInfo
5758
import androidx.compose.ui.res.stringResource
5859
import androidx.compose.ui.unit.dp
@@ -87,6 +88,7 @@ import dev.dimension.flare.ui.component.status.LazyStatusVerticalStaggeredGrid
8788
import dev.dimension.flare.ui.component.status.MediaItem
8889
import dev.dimension.flare.ui.component.status.StatusPlaceholder
8990
import dev.dimension.flare.ui.component.status.status
91+
import dev.dimension.flare.ui.model.ClickContext
9092
import dev.dimension.flare.ui.model.UiMedia
9193
import dev.dimension.flare.ui.model.UiRelation
9294
import dev.dimension.flare.ui.model.UiStrings
@@ -628,6 +630,7 @@ private fun ProfileMediaTab(
628630
modifier: Modifier = Modifier,
629631
) {
630632
val isBigScreen = isBigScreen()
633+
val uriHandler = LocalUriHandler.current
631634
CompositionLocalProvider(
632635
LocalTimelineAppearance provides
633636
LocalTimelineAppearance.current.copy(
@@ -672,16 +675,26 @@ private fun ProfileMediaTab(
672675
.clickable {
673676
val content = item.status
674677
if (content is UiTimelineV2.Post) {
675-
onItemClicked(
676-
item.statusKey,
677-
item.index,
678-
when (media) {
679-
is UiMedia.Image -> media.previewUrl
680-
is UiMedia.Video -> media.thumbnailUrl
681-
is UiMedia.Gif -> media.previewUrl
682-
else -> null
683-
},
684-
)
678+
when (content.mediaClickPolicy) {
679+
UiTimelineV2.Post.MediaClickPolicy.OpenStatusMedia -> {
680+
onItemClicked(
681+
item.statusKey,
682+
item.index,
683+
when (media) {
684+
is UiMedia.Image -> media.previewUrl
685+
is UiMedia.Video -> media.thumbnailUrl
686+
is UiMedia.Gif -> media.previewUrl
687+
else -> null
688+
},
689+
)
690+
}
691+
692+
UiTimelineV2.Post.MediaClickPolicy.OpenPostClickEvent -> {
693+
content.onClicked.invoke(
694+
ClickContext(launcher = uriHandler::openUri),
695+
)
696+
}
697+
}
685698
}
686699
},
687700
)

0 commit comments

Comments
 (0)