Skip to content

Commit

Permalink
[1.50.*] Pre-release merge (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
tramline-github[bot] authored Jan 31, 2024
2 parents d6bd270 + 0cc4310 commit a8f5b5a
Show file tree
Hide file tree
Showing 21 changed files with 300 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ios_prod_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
build:
runs-on: tramline-macos-sonoma-md
runs-on: macos-latest
env:
TERM: dumb
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
Expand Down
120 changes: 120 additions & 0 deletions .github/workflows/ios_prod_release_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: iOS Prod Release Test

on:
workflow_dispatch:
inputs:
tramline-input:
description: "Tramline input"
required: false

jobs:
build:
runs-on: tramline-macos-sonoma-md
env:
TERM: dumb
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
steps:
- name: Configure Tramline
id: tramline
uses: tramlinehq/[email protected]
with:
input: ${{ github.event.inputs.tramline-input }}

- name: Setup JDK 20
uses: actions/setup-java@v4
with:
java-version: 20
distribution: zulu
cache: 'gradle'

- name: Install private API key P8
env:
PRIVATE_API_KEY_BASE64: ${{ secrets.APP_STORE_API_PRIVATE_KEY }}
API_KEY: ${{ secrets.APP_STORE_KEY_ID }}
run: |
mkdir -p ~/private_keys
echo -n "$PRIVATE_API_KEY_BASE64" | base64 --decode --output ~/private_keys/AuthKey_$API_KEY.p8
- name: Install the Apple certificate and provisioning profile
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
- name: Update Archive Version
run: |
/usr/libexec/Plistbuddy -c "Set CFBundleVersion ${{ steps.tramline.outputs.version_code }}" "iosApp/iosApp/Info.plist"
/usr/libexec/Plistbuddy -c "Set CFBundleShortVersionString ${{ steps.tramline.outputs.version_name }}" "iosApp/iosApp/Info.plist"
- name: Pod Install
run: |
./gradlew --no-daemon podInstall;
- name: Build Archive
run: |
xcodebuild -workspace ./iosApp/iosApp.xcworkspace \
-scheme iosApp \
-archivePath $RUNNER_TEMP/twine.xcarchive \
-sdk iphoneos \
-configuration Release \
-destination generic/platform=iOS \
DEVELOPMENT_TEAM=6XCS8KZXDA \
PROVISIONING_PROFILE=${{ secrets.PROVISION_PROFILE_ID }} \
clean archive
CODE_SIGN_IDENTITY="iPhone Distribution: Sasi Kanth (6XCS8KZXDA)"
- name: Export ipa
env:
EXPORT_OPTIONS_PLIST: ${{ secrets.EXPORT_OPTIONS_PLIST }}
run: |
EXPORT_OPTS_PATH=$RUNNER_TEMP/ExportOptions.plist
echo -n "$EXPORT_OPTIONS_PLIST" | base64 --decode -o $EXPORT_OPTS_PATH
xcodebuild -exportArchive -archivePath $RUNNER_TEMP/twine.xcarchive -exportOptionsPlist $EXPORT_OPTS_PATH -exportPath $RUNNER_TEMP/build
- name: Upload debug symbols to Sentry
run: |
curl -sL https://sentry.io/get-cli/ | SENTRY_CLI_VERSION=2.21.2 bash;
sentry-cli debug-files upload --auth-token ${{ secrets.SENTRY_AUTH_TOKEN }} \
--include-sources \
--org ${{ secrets.SENTRY_ORG }} \
--project ${{ secrets.SENTRY_PROJECT }} \
$RUNNER_TEMP/twine.xcarchive/dSYMs
- name: Clean up keychain and provisioning profile
if: ${{ always() }}
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision
- name: Upload app to TestFlight
env:
API_KEY: ${{ secrets.APP_STORE_KEY_ID }}
API_ISSUER: ${{ secrets.APP_STORE_ISSUER_ID }}
APP_PATH: ${{ runner.temp }}/build/twine.ipa
run: |
xcrun altool --upload-app --type ios -f $APP_PATH --apiKey $API_KEY --apiIssuer $API_ISSUER
- name: Upload application
uses: actions/upload-artifact@v4
with:
name: app
path: ${{ runner.temp }}/build/twine.ipa
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ user interface and experience to browse through the feeds, and supports Material
## Features ✨

- Supports RSS & Atom feeds
- Bookmarks
- Search
- Gorgeous home feed
- Pin frequently visited feeds
- Smart fetching: Twine looks for feeds when given any website homepage
- Reading view with shortcut to fetch full article
- Bookmark posts to read later
- Search posts
- Background sync
- Feed management: Add, Edit & Pin feeds
- Reader view with article fetching
- Import and exports your feeds with OPML

## Tech Stack 📚

Expand Down
5 changes: 3 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ stately = "2.0.6"
xmlutil = "0.86.3"
ktxml = "0.2.3"
uri = "0.0.16"
webview = "1.8.4"
webview = "1.8.6"
uuid = "0.8.2"

[libraries]
Expand All @@ -56,6 +56,7 @@ compose_ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "compose" }
compose_ui_util = { module = "org.jetbrains.compose.ui:ui-util", version.ref = "compose" }
compose_material = { module = "org.jetbrains.compose.material:material", version.ref = "compose" }
compose_material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "compose" }
compose_material_icons_extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "compose" }
compose_resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "compose" }
ktor_core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor_client_okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
Expand Down Expand Up @@ -127,7 +128,7 @@ buildKonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildKonfig"
sentry_android = { id = "io.sentry.android.gradle", version.ref = "sentry_android" }

[bundles]
compose = [ "compose_runtime", "compose_foundation", "compose_material", "compose_material3", "compose_resources", "compose_ui", "compose_ui_util" ]
compose = [ "compose_runtime", "compose_foundation", "compose_material", "compose_material3", "compose_resources", "compose_ui", "compose_ui_util", "compose_material_icons_extended" ]
kotlinx = [ "kotlinx_coroutines", "kotlinx_datetime", "kotlinx_immutable_collections" ]
androidx_test = [ "androidx_test_runner", "androidx_test_rules" ]
xmlutil = [ "xmlutil-core", "xmlutil-serialization" ]
Binary file modified readme_images/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,7 @@ val EnTwineStrings =
postsToday = "Today",
openSource = "Open Source",
openSourceDesc =
"Twine is built on open source technologies and is completely free to use, you can find the source code of Twine and some of my other popular projects on GitHub. Click here to head over there."
"Twine is built on open source technologies and is completely free to use, you can find the source code of Twine and some of my other popular projects on GitHub. Click here to head over there.",
markAsRead = "Mark as Read",
markAsUnRead = "Mark as Unread"
)
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ data class TwineStrings(
val postsToday: String,
val openSource: String,
val openSourceDesc: String,
val markAsRead: String,
val markAsUnRead: String
)

object Locales {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ sealed interface BookmarksEvent {
data class OnPostBookmarkClick(val post: PostWithMetadata) : BookmarksEvent

data class OnPostClicked(val post: PostWithMetadata) : BookmarksEvent

data class TogglePostReadStatus(val postLink: String, val postRead: Boolean) : BookmarksEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ class BookmarksPresenter(
is BookmarksEvent.OnPostClicked -> {
// no-op
}
is BookmarksEvent.TogglePostReadStatus ->
togglePostReadStatus(event.postLink, event.postRead)
}
}

private fun togglePostReadStatus(postLink: String, postRead: Boolean) {
coroutineScope.launch {
rssRepository.updatePostReadStatus(read = !postRead, link = postLink)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ internal fun BookmarksScreen(
},
onPostSourceClick = {
// no-op
},
togglePostReadClick = {
bookmarksPresenter.dispatch(
BookmarksEvent.TogglePostReadStatus(post.link, post.read)
)
}
)
if (index != bookmarks.itemCount - 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ sealed interface HomeEvent {
data object EditFeedsClicked : HomeEvent

data object ExitFeedsEdit : HomeEvent

data class TogglePostReadStatus(val postLink: String, val postRead: Boolean) : HomeEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ class HomePresenter(
HomeEvent.ExitFeedsEdit -> exitFeedsEdit()
is HomeEvent.OnPostSourceClicked -> postSourceClicked(event.feedLink)
is HomeEvent.OnPostsTypeChanged -> onPostsTypeChanged(event.postsType)
is HomeEvent.TogglePostReadStatus -> togglePostReadStatus(event.postLink, event.postRead)
}
}

private fun togglePostReadStatus(postLink: String, postRead: Boolean) {
coroutineScope.launch {
rssRepository.updatePostReadStatus(read = !postRead, link = postLink)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ internal fun FeaturedPostItem(
onClick: () -> Unit,
onBookmarkClick: () -> Unit,
onCommentsClick: () -> Unit,
onSourceClick: () -> Unit
onSourceClick: () -> Unit,
onTogglePostReadClick: () -> Unit,
) {
val isLargeScreenLayout =
LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded
Expand All @@ -85,7 +86,8 @@ internal fun FeaturedPostItem(
pagerState = pagerState,
onBookmarkClick = onBookmarkClick,
onCommentsClick = onCommentsClick,
onSourceClick = onSourceClick
onSourceClick = onSourceClick,
onTogglePostReadClick = onTogglePostReadClick
)
} else {
DefaultFeaturedPostItem(
Expand All @@ -94,7 +96,8 @@ internal fun FeaturedPostItem(
pagerState = pagerState,
onBookmarkClick = onBookmarkClick,
onCommentsClick = onCommentsClick,
onSourceClick = onSourceClick
onSourceClick = onSourceClick,
onTogglePostReadClick = onTogglePostReadClick
)
}
}
Expand All @@ -107,7 +110,8 @@ private fun DefaultFeaturedPostItem(
pagerState: PagerState,
onBookmarkClick: () -> Unit,
onCommentsClick: () -> Unit,
onSourceClick: () -> Unit
onSourceClick: () -> Unit,
onTogglePostReadClick: () -> Unit,
) {
Column {
AsyncImage(
Expand Down Expand Up @@ -167,6 +171,7 @@ private fun DefaultFeaturedPostItem(
onBookmarkClick = onBookmarkClick,
onCommentsClick = onCommentsClick,
onSourceClick = onSourceClick,
onTogglePostReadClick = onTogglePostReadClick,
modifier = Modifier.padding(start = 16.dp, end = 0.dp)
)

Expand All @@ -181,7 +186,8 @@ private fun LargeScreenFeaturedPostItem(
pagerState: PagerState,
onBookmarkClick: () -> Unit,
onCommentsClick: () -> Unit,
onSourceClick: () -> Unit
onSourceClick: () -> Unit,
onTogglePostReadClick: () -> Unit,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
AsyncImage(
Expand Down Expand Up @@ -241,7 +247,8 @@ private fun LargeScreenFeaturedPostItem(
enablePostSource = true,
onBookmarkClick = onBookmarkClick,
onCommentsClick = onCommentsClick,
onSourceClick = onSourceClick
onSourceClick = onSourceClick,
onTogglePostReadClick = onTogglePostReadClick
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ internal fun FeaturedSection(
onPostBookmarkClick: (PostWithMetadata) -> Unit,
onPostCommentsClick: (String) -> Unit,
onPostSourceClick: (String) -> Unit,
onTogglePostReadClick: (String, Boolean) -> Unit,
) {
Box(modifier = modifier) {
if (featuredPosts.isNotEmpty()) {
Expand Down Expand Up @@ -179,7 +180,8 @@ internal fun FeaturedSection(
onClick = { onItemClick(featuredPost) },
onBookmarkClick = { onPostBookmarkClick(featuredPost) },
onCommentsClick = { onPostCommentsClick(featuredPost.commentsLink!!) },
onSourceClick = { onPostSourceClick(featuredPost.feedLink) }
onSourceClick = { onPostSourceClick(featuredPost.feedLink) },
onTogglePostReadClick = { onTogglePostReadClick(featuredPost.link, featuredPost.read) }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ internal fun HomeScreen(homePresenter: HomePresenter, modifier: Modifier = Modif
homePresenter.dispatch(HomeEvent.OnPostSourceClicked(feedLink))
},
onNoFeedsSwipeUp = { coroutineScope.launch { bottomSheetState.expand() } },
onTogglePostReadStatus = { postLink, postRead ->
homePresenter.dispatch(HomeEvent.TogglePostReadStatus(postLink, postRead))
}
)
},
sheetContent = {
Expand Down Expand Up @@ -278,6 +281,7 @@ private fun HomeScreenContent(
onPostCommentsClick: (String) -> Unit,
onPostSourceClick: (String) -> Unit,
onNoFeedsSwipeUp: () -> Unit,
onTogglePostReadStatus: (String, Boolean) -> Unit,
) {
val featuredPosts = state.featuredPosts
val posts = state.posts?.collectAsLazyPagingItems()
Expand Down Expand Up @@ -312,6 +316,7 @@ private fun HomeScreenContent(
onPostBookmarkClick = onPostBookmarkClick,
onPostCommentsClick = onPostCommentsClick,
onPostSourceClick = onPostSourceClick,
onTogglePostReadClick = onTogglePostReadStatus
)
}
!hasFeeds -> {
Expand Down
Loading

0 comments on commit a8f5b5a

Please sign in to comment.