diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineFragment.kt b/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineFragment.kt index 9044bdb..56df629 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineFragment.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineFragment.kt @@ -71,7 +71,7 @@ class TimelineFragment : DaggerFragment(R.layout.fragment_timeline), TimeLineAct } override fun onBoost(post: StatusEntity) { - TODO("Not yet implemented") + viewModel.onBoost(post) } override fun onReply(status: StatusEntity) { diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineListAdapter.kt b/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineListAdapter.kt index b577192..582baad 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineListAdapter.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineListAdapter.kt @@ -67,6 +67,8 @@ class TimelineListAdapter( true } + holder.binding.postBoostButton.isChecked = status.reblogged + holder.binding.postBoostButton.setEventListener { button, buttonState -> listener.onBoost(status) true diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineViewModel.kt b/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineViewModel.kt index db05bc3..00495de 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineViewModel.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineViewModel.kt @@ -9,7 +9,10 @@ import androidx.paging.cachedIn import at.connyduck.pixelcat.db.AccountManager import at.connyduck.pixelcat.db.AppDatabase import at.connyduck.pixelcat.db.entitity.StatusEntity +import at.connyduck.pixelcat.db.entitity.toEntity +import at.connyduck.pixelcat.model.Status import at.connyduck.pixelcat.network.FediverseApi +import at.connyduck.pixelcat.network.calladapter.NetworkResponse import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.flatMapConcat @@ -19,7 +22,7 @@ import javax.inject.Inject class TimelineViewModel @Inject constructor( private val accountManager: AccountManager, private val db: AppDatabase, - fediverseApi: FediverseApi + private val fediverseApi: FediverseApi ) : ViewModel() { @OptIn(FlowPreview::class) @@ -36,13 +39,46 @@ class TimelineViewModel @Inject constructor( fun onFavorite(status: StatusEntity) { viewModelScope.launch { - // repository.onFavorite(status, accountManager.activeAccount()?.id!!) + val alreadyFavourited = status.favourited + if (alreadyFavourited) { + fediverseApi.unfavouriteStatus(status.actionableId) + } else { + fediverseApi.favouriteStatus(status.actionableId) + }.updateStatusInDb() + } + } + + fun onBoost(status: StatusEntity) { + viewModelScope.launch { + val alreadyBoosted = status.reblogged + if (alreadyBoosted) { + fediverseApi.unreblogStatus(status.actionableId) + } else { + fediverseApi.reblogStatus(status.actionableId) + }.updateStatusInDb() } } fun onMediaVisibilityChanged(status: StatusEntity) { viewModelScope.launch { - db.statusDao().changeMediaVisibility(!status.mediaVisible, status.id, accountManager.activeAccount()?.id!!) + db.statusDao().changeMediaVisibility( + !status.mediaVisible, + status.id, + accountManager.activeAccount()?.id!! + ) } } + + private suspend fun NetworkResponse.updateStatusInDb() { + fold( + { updatedStatus -> + val accountId = accountManager.activeAccount()?.id!! + val updatedStatusEntity = updatedStatus.toEntity(accountId) + db.statusDao().insertOrReplace(updatedStatusEntity) + }, + { + // Todo + } + ) + } } diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/db/TimelineDao.kt b/app/src/main/kotlin/at/connyduck/pixelcat/db/TimelineDao.kt index ae8bf3f..1e15206 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/db/TimelineDao.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/db/TimelineDao.kt @@ -12,7 +12,7 @@ import at.connyduck.pixelcat.db.entitity.StatusEntity interface TimelineDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertOrReplace(status: StatusEntity): Long + suspend fun insertOrReplace(status: StatusEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertOrReplace(statuses: List) diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/db/entitity/TimelineStatusEntity.kt b/app/src/main/kotlin/at/connyduck/pixelcat/db/entitity/TimelineStatusEntity.kt index 936e9fa..d598411 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/db/entitity/TimelineStatusEntity.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/db/entitity/TimelineStatusEntity.kt @@ -15,6 +15,7 @@ import java.util.Date data class StatusEntity( val accountId: Long, val id: String, + val actionableId: String, val url: String?, // not present if it's reblog @Embedded(prefix = "a_") val account: TimelineAccountEntity, val content: String, @@ -27,7 +28,6 @@ data class StatusEntity( val spoilerText: String, val visibility: Status.Visibility, val attachments: List, - val mediaPosition: Int, val mediaVisible: Boolean ) @@ -35,6 +35,7 @@ data class StatusEntity( fun Status.toEntity(accountId: Long, mediaPosition: Int = 0, mediaVisible: Boolean = !this.sensitive) = StatusEntity( accountId = accountId, id = id, + actionableId = actionableStatus.id, url = actionableStatus.url, account = actionableStatus.account.toEntity(accountId), content = actionableStatus.content, diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/network/FediverseApi.kt b/app/src/main/kotlin/at/connyduck/pixelcat/network/FediverseApi.kt index c0286be..e4b7986 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/network/FediverseApi.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/network/FediverseApi.kt @@ -154,4 +154,14 @@ interface FediverseApi { suspend fun unfavouriteStatus( @Path("id") statusId: String ): NetworkResponse + + @POST("api/v1/statuses/{id}/reblog") + suspend fun reblogStatus( + @Path("id") statusId: String + ): NetworkResponse + + @POST("api/v1/statuses/{id}/unreblog") + suspend fun unreblogStatus( + @Path("id") statusId: String + ): NetworkResponse } diff --git a/app/src/main/res/drawable/ic_heart_filled.xml b/app/src/main/res/drawable/ic_heart_filled.xml index de285a3..3f29fdd 100644 --- a/app/src/main/res/drawable/ic_heart_filled.xml +++ b/app/src/main/res/drawable/ic_heart_filled.xml @@ -7,7 +7,7 @@ android:pathData="M20.84,4.61a5.5,5.5 0,0 0,-7.78 0L12,5.67l-1.06,-1.06a5.5,5.5 0,0 0,-7.78 7.78l1.06,1.06L12,21.23l7.78,-7.78 1.06,-1.06a5.5,5.5 0,0 0,0 -7.78z" android:strokeLineJoin="round" android:strokeWidth="2" - android:fillColor="#f00" - android:strokeColor="#f00" + android:fillColor="@color/pixelcat_gradient_warm_middle" + android:strokeColor="@color/pixelcat_gradient_warm_middle" android:strokeLineCap="round"/> diff --git a/app/src/main/res/drawable/ic_repeat.xml b/app/src/main/res/drawable/ic_repeat.xml index 3679e7c..32a5942 100644 --- a/app/src/main/res/drawable/ic_repeat.xml +++ b/app/src/main/res/drawable/ic_repeat.xml @@ -1,34 +1,34 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#00000000" + android:pathData="M17,1l4,4l-4,4" + android:strokeWidth="2" + android:strokeColor="?android:attr/textColorTertiary" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + android:fillColor="#00000000" + android:pathData="M3,11V9a4,4 0,0 1,4 -4h14" + android:strokeWidth="2" + android:strokeColor="?android:attr/textColorTertiary" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + android:fillColor="#00000000" + android:pathData="M7,23l-4,-4l4,-4" + android:strokeWidth="2" + android:strokeColor="?android:attr/textColorTertiary" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + android:fillColor="#00000000" + android:pathData="M21,13v2a4,4 0,0 1,-4 4H3" + android:strokeWidth="2" + android:strokeColor="?android:attr/textColorTertiary" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> diff --git a/app/src/main/res/drawable/ic_repeat_active.xml b/app/src/main/res/drawable/ic_repeat_active.xml new file mode 100644 index 0000000..82d8a47 --- /dev/null +++ b/app/src/main/res/drawable/ic_repeat_active.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/layout/item_status.xml b/app/src/main/res/layout/item_status.xml index 363d4e3..1c0ca27 100644 --- a/app/src/main/res/layout/item_status.xml +++ b/app/src/main/res/layout/item_status.xml @@ -5,6 +5,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:clipChildren="false" android:paddingBottom="8dp"> @@ -90,7 +91,7 @@ android:layout_marginEnd="16dp" app:layout_constraintStart_toEndOf="@id/postLikeButton" app:layout_constraintTop_toBottomOf="@id/postImages" - sparkbutton:activeImage="@drawable/ic_repeat" + sparkbutton:activeImage="@drawable/ic_repeat_active" sparkbutton:iconSize="24dp" sparkbutton:inactiveImage="@drawable/ic_repeat" sparkbutton:primaryColor="@color/boost_button_primary" diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5554cc8..8f0244a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -14,10 +14,10 @@ #e0e0e0 - #f00 - #f90 + @color/pixelcat_gradient_warm_end + @color/pixelcat_gradient_warm_start - #1A2980 - #207da7 + @color/pixelcat_gradient_cold_end + @color/pixelcat_gradient_cold_start