diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 398dcc5..d66df83 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -27,6 +27,7 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/MainModule.kt b/app/src/main/java/com/isolaatti/MainModule.kt
index 3dbf0e8..a06d544 100644
--- a/app/src/main/java/com/isolaatti/MainModule.kt
+++ b/app/src/main/java/com/isolaatti/MainModule.kt
@@ -25,5 +25,4 @@ class MainModule {
return RetrofitClient(authenticationInterceptor)
}
-
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/auth/Module.kt b/app/src/main/java/com/isolaatti/auth/Module.kt
index d326293..e54f335 100644
--- a/app/src/main/java/com/isolaatti/auth/Module.kt
+++ b/app/src/main/java/com/isolaatti/auth/Module.kt
@@ -1,13 +1,17 @@
package com.isolaatti.auth
+import android.content.Context
import com.isolaatti.auth.data.AuthRepositoryImpl
+import com.isolaatti.auth.data.local.KeyValueDao
import com.isolaatti.auth.data.local.TokenStorage
import com.isolaatti.auth.data.remote.AuthApi
import com.isolaatti.auth.domain.AuthRepository
import com.isolaatti.connectivity.RetrofitClient
+import com.isolaatti.database.AppDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
@Module
@@ -18,7 +22,12 @@ class Module {
return retrofitClient.client.create(AuthApi::class.java)
}
@Provides
- fun provideAuthRepository(tokenStorage: TokenStorage, authApi: AuthApi): AuthRepository {
- return AuthRepositoryImpl(tokenStorage, authApi)
+ fun provideKeyValueDao(database: AppDatabase): KeyValueDao {
+ return database.keyValueDao()
+ }
+
+ @Provides
+ fun provideAuthRepository(tokenStorage: TokenStorage, authApi: AuthApi, keyValueDao: KeyValueDao): AuthRepository {
+ return AuthRepositoryImpl(tokenStorage, authApi, keyValueDao)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/auth/data/AuthRepositoryImpl.kt b/app/src/main/java/com/isolaatti/auth/data/AuthRepositoryImpl.kt
index 8994698..d851c59 100644
--- a/app/src/main/java/com/isolaatti/auth/data/AuthRepositoryImpl.kt
+++ b/app/src/main/java/com/isolaatti/auth/data/AuthRepositoryImpl.kt
@@ -1,5 +1,7 @@
package com.isolaatti.auth.data
+import com.isolaatti.auth.data.local.KeyValueDao
+import com.isolaatti.auth.data.local.KeyValueEntity
import com.isolaatti.auth.data.remote.AuthTokenDto
import com.isolaatti.auth.data.local.TokenStorage
import com.isolaatti.auth.data.remote.AuthApi
@@ -8,16 +10,18 @@ import com.isolaatti.auth.domain.AuthRepository
import com.isolaatti.utils.Resource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
-import retrofit2.await
import retrofit2.awaitResponse
-import java.io.IOException
import javax.inject.Inject
class AuthRepositoryImpl @Inject constructor(
private val tokenStorage: TokenStorage,
- private val authApi: AuthApi
+ private val authApi: AuthApi,
+ private val keyValueDao: KeyValueDao
) : AuthRepository {
+ companion object {
+ val KEY_USERID = "user_id"
+ }
override fun authWithEmailAndPassword(
email: String,
password: String
@@ -33,6 +37,7 @@ class AuthRepositoryImpl @Inject constructor(
return@flow
}
tokenStorage.storeToken(dto)
+ keyValueDao.setValue(KeyValueEntity(KEY_USERID, dto.userId.toString()))
emit(Resource.Success(true))
return@flow
}
@@ -60,4 +65,8 @@ class AuthRepositoryImpl @Inject constructor(
return tokenStorage.token
}
+ override fun getUserId(): Flow = flow {
+ emit(keyValueDao.getValue(KEY_USERID).toIntOrNull())
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/auth/data/local/KeyValueDao.kt b/app/src/main/java/com/isolaatti/auth/data/local/KeyValueDao.kt
new file mode 100644
index 0000000..c4a7c87
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/auth/data/local/KeyValueDao.kt
@@ -0,0 +1,15 @@
+package com.isolaatti.auth.data.local
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+
+@Dao
+interface KeyValueDao {
+ @Query("SELECT value FROM key_values WHERE id = :key")
+ fun getValue(key: String): String
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun setValue(entity: KeyValueEntity)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/auth/data/local/KeyValueEntity.kt b/app/src/main/java/com/isolaatti/auth/data/local/KeyValueEntity.kt
new file mode 100644
index 0000000..a7c734c
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/auth/data/local/KeyValueEntity.kt
@@ -0,0 +1,10 @@
+package com.isolaatti.auth.data.local
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "key_values")
+data class KeyValueEntity(
+ @PrimaryKey val id: String,
+ val value: String
+)
diff --git a/app/src/main/java/com/isolaatti/auth/data/remote/AuthTokenDto.kt b/app/src/main/java/com/isolaatti/auth/data/remote/AuthTokenDto.kt
index af44141..13ff700 100644
--- a/app/src/main/java/com/isolaatti/auth/data/remote/AuthTokenDto.kt
+++ b/app/src/main/java/com/isolaatti/auth/data/remote/AuthTokenDto.kt
@@ -1,6 +1,6 @@
package com.isolaatti.auth.data.remote
-data class AuthTokenDto(val token: String) {
+data class AuthTokenDto(val token: String, val userId: Int) {
override fun toString(): String {
return token
}
diff --git a/app/src/main/java/com/isolaatti/auth/domain/AuthRepository.kt b/app/src/main/java/com/isolaatti/auth/domain/AuthRepository.kt
index 4edd198..c7a3e37 100644
--- a/app/src/main/java/com/isolaatti/auth/domain/AuthRepository.kt
+++ b/app/src/main/java/com/isolaatti/auth/domain/AuthRepository.kt
@@ -8,4 +8,6 @@ interface AuthRepository {
fun authWithEmailAndPassword(email: String, password: String): Flow>
fun logout(): Flow
fun getCurrentToken(): AuthTokenDto?
+ fun getUserId(): Flow
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/common/IsolaattiBaseActivity.kt b/app/src/main/java/com/isolaatti/common/IsolaattiBaseActivity.kt
index dc3d4a0..c046cef 100644
--- a/app/src/main/java/com/isolaatti/common/IsolaattiBaseActivity.kt
+++ b/app/src/main/java/com/isolaatti/common/IsolaattiBaseActivity.kt
@@ -13,6 +13,7 @@ import androidx.lifecycle.Observer
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.isolaatti.R
import com.isolaatti.home.HomeActivity
+import com.isolaatti.login.LogInActivity
import com.isolaatti.utils.Resource
import dagger.hilt.android.AndroidEntryPoint
@@ -46,7 +47,7 @@ abstract class IsolaattiBaseActivity : AppCompatActivity() {
abstract fun onRetry()
private val onAcceptReAuthClick = DialogInterface.OnClickListener { _, _ ->
-
+ signInActivityResult.launch(Intent(this, LogInActivity::class.java))
}
private fun showReAuthDialog() {
diff --git a/app/src/main/java/com/isolaatti/database/AppDatabase.kt b/app/src/main/java/com/isolaatti/database/AppDatabase.kt
new file mode 100644
index 0000000..efe4a95
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/database/AppDatabase.kt
@@ -0,0 +1,12 @@
+package com.isolaatti.database
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import com.isolaatti.auth.data.local.KeyValueDao
+import com.isolaatti.auth.data.local.KeyValueEntity
+
+@Database(entities = [KeyValueEntity::class], version = 1)
+abstract class AppDatabase : RoomDatabase() {
+ abstract fun keyValueDao(): KeyValueDao
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/database/Module.kt b/app/src/main/java/com/isolaatti/database/Module.kt
new file mode 100644
index 0000000..f16aa64
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/database/Module.kt
@@ -0,0 +1,20 @@
+package com.isolaatti.database
+
+import android.content.Context
+import androidx.room.Room
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+class Module {
+ @Provides
+ @Singleton
+ fun provideAppDatabase(@ApplicationContext applicationContext: Context): AppDatabase {
+ return Room.databaseBuilder(applicationContext, AppDatabase::class.java, "database.db").build()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/home/FeedFragment.kt b/app/src/main/java/com/isolaatti/home/FeedFragment.kt
index dc53e08..24dec8f 100644
--- a/app/src/main/java/com/isolaatti/home/FeedFragment.kt
+++ b/app/src/main/java/com/isolaatti/home/FeedFragment.kt
@@ -1,5 +1,6 @@
package com.isolaatti.home
+import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
@@ -7,20 +8,30 @@ import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.activityViewModels
+import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.isolaatti.BuildConfig
import com.isolaatti.R
import com.isolaatti.common.ErrorMessageViewModel
import com.isolaatti.databinding.FragmentFeedBinding
+import com.isolaatti.home.presentation.FeedViewModel
import com.isolaatti.posting.posts.presentation.PostsViewModel
import com.isolaatti.posting.comments.presentation.BottomSheetPostComments
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
import com.isolaatti.posting.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
+import com.isolaatti.posting.posts.ui.CreatePostActivity
import com.isolaatti.profile.ui.ProfileActivity
import com.isolaatti.settings.ui.SettingsActivity
import com.isolaatti.utils.PicassoImagesPluginDef
+import com.isolaatti.utils.UrlGen
+import com.squareup.picasso.Picasso
import dagger.hilt.android.AndroidEntryPoint
import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.Markwon
@@ -37,9 +48,16 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
private val viewModel: PostsViewModel by activityViewModels()
private val errorViewModel: ErrorMessageViewModel by activityViewModels()
+ private val screenViewModel: FeedViewModel by viewModels()
private lateinit var viewBinding: FragmentFeedBinding
private lateinit var adapter: PostsRecyclerViewAdapter
+ private val createDiscussion = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ if(it.resultCode == Activity.RESULT_OK) {
+ Toast.makeText(requireContext(), R.string.posted_successfully, Toast.LENGTH_SHORT).show()
+ }
+ }
+
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@@ -99,6 +117,33 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
viewModel.getFeed(refresh = false)
}
+ viewBinding.topAppBar.setOnMenuItemClickListener {
+ when(it.itemId) {
+ R.id.menu_item_new_discussion -> {
+ createDiscussion.launch(Intent(requireContext(),CreatePostActivity::class.java))
+ true
+ }
+ else -> {
+ false
+ }
+ }
+ }
+
+ screenViewModel.userProfile.observe(viewLifecycleOwner) {
+ val header = viewBinding.homeDrawer.getHeaderView(0) as? ConstraintLayout
+
+ val image: ImageView? = header?.findViewById(R.id.profileImageView)
+ val textViewName: TextView? = header?.findViewById(R.id.textViewName)
+ val textViewEmail: TextView? = header?.findViewById(R.id.textViewEmail)
+
+ Picasso.get().load(UrlGen.userProfileImage(it.id)).into(image)
+
+ textViewName?.text = it.name
+ textViewEmail?.text = it.email
+ }
+
+
+
viewModel.posts.observe(viewLifecycleOwner){
if (it != null) {
@@ -128,6 +173,7 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
PostsRecyclerViewAdapter.UpdateEvent.UpdateType.POST_LIKED, it.postId))
}
}
+ screenViewModel.getProfile()
}
fun onNewMenuItemClicked(v: View) {
diff --git a/app/src/main/java/com/isolaatti/home/presentation/FeedViewModel.kt b/app/src/main/java/com/isolaatti/home/presentation/FeedViewModel.kt
new file mode 100644
index 0000000..aff554c
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/home/presentation/FeedViewModel.kt
@@ -0,0 +1,42 @@
+package com.isolaatti.home.presentation
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.isolaatti.auth.domain.AuthRepository
+import com.isolaatti.profile.data.remote.UserProfileDto
+import com.isolaatti.profile.domain.use_case.GetProfile
+import com.isolaatti.utils.Resource
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class FeedViewModel @Inject constructor(private val getProfileUseCase: GetProfile, private val authRepository: AuthRepository) : ViewModel() {
+
+ // User profile
+ private val _userProfile: MutableLiveData = MutableLiveData()
+ val userProfile: LiveData get() = _userProfile
+
+ fun getProfile() {
+ viewModelScope.launch {
+ authRepository.getUserId().onEach {userId ->
+ userId?.let {
+ getProfileUseCase(userId).onEach {profile ->
+ if(profile is Resource.Success) {
+ profile.data?.let { _userProfile.postValue(it) }
+ }
+ }.flowOn(Dispatchers.IO).launchIn(this)
+ }
+
+ }.flowOn(Dispatchers.IO).launchIn(this)
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/Module.kt b/app/src/main/java/com/isolaatti/posting/Module.kt
index a09a669..2f0cb0d 100644
--- a/app/src/main/java/com/isolaatti/posting/Module.kt
+++ b/app/src/main/java/com/isolaatti/posting/Module.kt
@@ -2,12 +2,14 @@ package com.isolaatti.posting
import com.isolaatti.connectivity.RetrofitClient
import com.isolaatti.posting.posts.data.remote.FeedsApi
+import com.isolaatti.posting.posts.data.remote.PostApi
import com.isolaatti.posting.posts.data.repository.PostsRepositoryImpl
import com.isolaatti.posting.posts.domain.PostsRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
+import retrofit2.create
@Module
@InstallIn(SingletonComponent::class)
@@ -18,7 +20,12 @@ class Module {
}
@Provides
- fun providePostsRepository(feedsApi: FeedsApi): PostsRepository {
- return PostsRepositoryImpl(feedsApi)
+ fun providePostApi(retrofitClient: RetrofitClient): PostApi {
+ return retrofitClient.client.create(PostApi::class.java)
+ }
+
+ @Provides
+ fun providePostsRepository(feedsApi: FeedsApi, postApi: PostApi): PostsRepository {
+ return PostsRepositoryImpl(feedsApi, postApi)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/data/remote/CreatePostDto.kt b/app/src/main/java/com/isolaatti/posting/posts/data/remote/CreatePostDto.kt
new file mode 100644
index 0000000..05fdf0c
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/posting/posts/data/remote/CreatePostDto.kt
@@ -0,0 +1,8 @@
+package com.isolaatti.posting.posts.data.remote
+
+data class CreatePostDto(
+ val privacy: Int,
+ val content: String,
+ val audioId: String?,
+ val squadId: String?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/data/remote/DeletePostDto.kt b/app/src/main/java/com/isolaatti/posting/posts/data/remote/DeletePostDto.kt
new file mode 100644
index 0000000..52d99f0
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/posting/posts/data/remote/DeletePostDto.kt
@@ -0,0 +1,3 @@
+package com.isolaatti.posting.posts.data.remote
+
+data class DeletePostDto(val id: Long)
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/data/remote/EditPostDto.kt b/app/src/main/java/com/isolaatti/posting/posts/data/remote/EditPostDto.kt
new file mode 100644
index 0000000..daf4832
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/posting/posts/data/remote/EditPostDto.kt
@@ -0,0 +1,15 @@
+package com.isolaatti.posting.posts.data.remote
+
+data class EditPostDto(
+ val privacy: Int,
+ val content: String,
+ val audioId: String?,
+ val squadId: String?,
+ val postId: Long
+) {
+ companion object {
+ const val PRIVACY_PRIVATE = 1
+ const val PRIVACY_ISOLAATTI = 2
+ const val PRIVACY_PUBLIC = 3
+ }
+}
diff --git a/app/src/main/java/com/isolaatti/posting/posts/data/remote/FeedDto.kt b/app/src/main/java/com/isolaatti/posting/posts/data/remote/FeedDto.kt
index 59749e1..7a33375 100644
--- a/app/src/main/java/com/isolaatti/posting/posts/data/remote/FeedDto.kt
+++ b/app/src/main/java/com/isolaatti/posting/posts/data/remote/FeedDto.kt
@@ -1,5 +1,9 @@
package com.isolaatti.posting.posts.data.remote
+import android.os.Parcel
+import android.os.Parcelable
+import java.io.Serializable
+
data class FeedDto(
val data: MutableList,
var moreContent: Boolean
@@ -20,17 +24,89 @@ data class FeedDto(
val userName: String,
val squadName: String?,
var liked: Boolean
- ) {
+ ): Parcelable {
+
+ constructor(parcel: Parcel) : this(
+ parcel.readParcelable(Post::class.java.classLoader)!!,
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readString()!!,
+ parcel.readString(),
+ parcel.readByte() != 0.toByte()
+ )
+
data class Post(
val id: Long,
var textContent: String,
val userId: Int,
val privacy: Int,
val date: String,
- var audioId: String,
- val squadId: String,
+ var audioId: String?,
+ val squadId: String?,
val linkedDiscussionId: Long,
val linkedCommentId: Long
- )
+ ) : Parcelable {
+ constructor(parcel: Parcel) : this(
+ parcel.readLong(),
+ parcel.readString() ?: "",
+ parcel.readInt(),
+ parcel.readInt(),
+ parcel.readString() ?: "",
+ parcel.readString(),
+ parcel.readString(),
+ parcel.readLong(),
+ parcel.readLong()
+ ) {
+ }
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeLong(id)
+ parcel.writeString(textContent)
+ parcel.writeInt(userId)
+ parcel.writeInt(privacy)
+ parcel.writeString(date)
+ parcel.writeString(audioId)
+ parcel.writeString(squadId)
+ parcel.writeLong(linkedDiscussionId)
+ parcel.writeLong(linkedCommentId)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): Post {
+ return Post(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeParcelable(post, flags)
+ parcel.writeInt(numberOfLikes)
+ parcel.writeInt(numberOfComments)
+ parcel.writeString(userName)
+ parcel.writeString(squadName)
+ parcel.writeByte(if (liked) 1 else 0)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): PostDto {
+ return PostDto(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/data/remote/PostApi.kt b/app/src/main/java/com/isolaatti/posting/posts/data/remote/PostApi.kt
new file mode 100644
index 0000000..739cea9
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/posting/posts/data/remote/PostApi.kt
@@ -0,0 +1,17 @@
+package com.isolaatti.posting.posts.data.remote
+
+import retrofit2.Call
+import retrofit2.http.Body
+import retrofit2.http.POST
+
+interface PostApi {
+ @POST("Posting/Make")
+ fun makePost(@Body post: CreatePostDto): Call
+
+ @POST("Posting/Edit")
+ fun editPost(@Body editedPost: EditPostDto): Call
+
+ @POST("Posting/Delete")
+ fun deletePost(@Body postToDelete: DeletePostDto): Call
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/data/repository/PostsRepositoryImpl.kt b/app/src/main/java/com/isolaatti/posting/posts/data/repository/PostsRepositoryImpl.kt
index 35eaf99..9f728ac 100644
--- a/app/src/main/java/com/isolaatti/posting/posts/data/repository/PostsRepositoryImpl.kt
+++ b/app/src/main/java/com/isolaatti/posting/posts/data/repository/PostsRepositoryImpl.kt
@@ -1,9 +1,12 @@
package com.isolaatti.posting.posts.data.repository
import com.google.gson.Gson
+import com.isolaatti.posting.posts.data.remote.CreatePostDto
+import com.isolaatti.posting.posts.data.remote.EditPostDto
import com.isolaatti.posting.posts.data.remote.FeedDto
import com.isolaatti.posting.posts.data.remote.FeedFilterDto
import com.isolaatti.posting.posts.data.remote.FeedsApi
+import com.isolaatti.posting.posts.data.remote.PostApi
import com.isolaatti.posting.posts.domain.PostsRepository
import com.isolaatti.utils.Resource
import kotlinx.coroutines.flow.Flow
@@ -16,7 +19,7 @@ import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
-class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi) : PostsRepository {
+class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, private val postApi: PostApi) : PostsRepository {
override fun getFeed(lastId: Long): Flow> = flow {
emit(Resource.Loading())
try {
@@ -55,4 +58,42 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi) :
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
}
}
+
+ override fun makePost(createPostDto: CreatePostDto): Flow> = flow {
+ emit(Resource.Loading())
+ try {
+ val result = postApi.makePost(createPostDto).execute()
+ if(result.isSuccessful) {
+ emit(Resource.Success(result.body()))
+ return@flow
+ }
+ when(result.code()) {
+ 401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
+ 404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
+ 500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
+ else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
+ }
+ } catch(_: Exception) {
+ emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
+ }
+ }
+
+ override fun editPost(editPostDto: EditPostDto): Flow> = flow {
+ emit(Resource.Loading())
+ try {
+ val result = postApi.editPost(editPostDto).execute()
+ if(result.isSuccessful) {
+ emit(Resource.Success(result.body()))
+ return@flow
+ }
+ when(result.code()) {
+ 401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
+ 404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
+ 500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
+ else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
+ }
+ } catch(_: Exception) {
+ emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/domain/PostsRepository.kt b/app/src/main/java/com/isolaatti/posting/posts/domain/PostsRepository.kt
index 68c92cf..69ca2b5 100644
--- a/app/src/main/java/com/isolaatti/posting/posts/domain/PostsRepository.kt
+++ b/app/src/main/java/com/isolaatti/posting/posts/domain/PostsRepository.kt
@@ -1,5 +1,7 @@
package com.isolaatti.posting.posts.domain
+import com.isolaatti.posting.posts.data.remote.CreatePostDto
+import com.isolaatti.posting.posts.data.remote.EditPostDto
import com.isolaatti.posting.posts.data.remote.FeedDto
import com.isolaatti.posting.posts.data.remote.FeedFilterDto
import com.isolaatti.utils.Resource
@@ -10,4 +12,7 @@ interface PostsRepository {
fun getFeed(lastId: Long): Flow>
fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto): Flow>
+
+ fun makePost(createPostDto: CreatePostDto): Flow>
+ fun editPost(editPostDto: EditPostDto): Flow>
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/domain/use_case/EditPost.kt b/app/src/main/java/com/isolaatti/posting/posts/domain/use_case/EditPost.kt
new file mode 100644
index 0000000..0dc02f5
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/posting/posts/domain/use_case/EditPost.kt
@@ -0,0 +1,20 @@
+package com.isolaatti.posting.posts.domain.use_case
+
+import com.isolaatti.posting.posts.data.remote.EditPostDto
+import com.isolaatti.posting.posts.data.remote.FeedDto
+import com.isolaatti.posting.posts.domain.PostsRepository
+import com.isolaatti.utils.Resource
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+
+class EditPost @Inject constructor(private val postsRepository: PostsRepository) {
+ operator fun invoke(
+ postId: Long,
+ privacy: Int,
+ content: String,
+ audioId: String?,
+ squadId: String?
+ ): Flow> {
+ return postsRepository.editPost(EditPostDto(privacy, content, audioId, squadId, postId))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/domain/use_case/MakePost.kt b/app/src/main/java/com/isolaatti/posting/posts/domain/use_case/MakePost.kt
index 0a6ffa9..6c6c67a 100644
--- a/app/src/main/java/com/isolaatti/posting/posts/domain/use_case/MakePost.kt
+++ b/app/src/main/java/com/isolaatti/posting/posts/domain/use_case/MakePost.kt
@@ -1,4 +1,19 @@
package com.isolaatti.posting.posts.domain.use_case
-class MakePost {
+import com.isolaatti.posting.posts.data.remote.CreatePostDto
+import com.isolaatti.posting.posts.data.remote.FeedDto
+import com.isolaatti.posting.posts.domain.PostsRepository
+import com.isolaatti.utils.Resource
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+
+class MakePost @Inject constructor(private val postsRepository: PostsRepository) {
+ operator fun invoke(
+ privacy: Int,
+ content: String,
+ audioId: String?,
+ squadId: String?
+ ): Flow> {
+ return postsRepository.makePost(CreatePostDto(privacy, content, audioId, squadId))
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/presentation/CreatePostViewModel.kt b/app/src/main/java/com/isolaatti/posting/posts/presentation/CreatePostViewModel.kt
new file mode 100644
index 0000000..e48dccb
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/posting/posts/presentation/CreatePostViewModel.kt
@@ -0,0 +1,67 @@
+package com.isolaatti.posting.posts.presentation
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.isolaatti.posting.posts.data.remote.CreatePostDto
+import com.isolaatti.posting.posts.data.remote.EditPostDto
+import com.isolaatti.posting.posts.data.remote.FeedDto
+import com.isolaatti.posting.posts.domain.use_case.EditPost
+import com.isolaatti.posting.posts.domain.use_case.MakePost
+import com.isolaatti.utils.Resource
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class CreatePostViewModel @Inject constructor(private val makePost: MakePost, private val editPost: EditPost) : ViewModel() {
+ val validation: MutableLiveData = MutableLiveData(false)
+ val posted: MutableLiveData = MutableLiveData()
+ val error: MutableLiveData = MutableLiveData()
+ val loading: MutableLiveData = MutableLiveData(false)
+
+ fun postDiscussion(content: String) {
+ viewModelScope.launch {
+ makePost(EditPostDto.PRIVACY_ISOLAATTI, content, null, null).onEach {
+ when(it) {
+ is Resource.Success -> {
+ loading.postValue(false)
+ posted.postValue(it.data)
+ }
+ is Resource.Error -> {
+ loading.postValue(false)
+ error.postValue(it.errorType)
+ }
+ is Resource.Loading -> {
+ loading.postValue(true)
+ }
+ }
+ }.flowOn(Dispatchers.IO).launchIn(this)
+ }
+ }
+
+ fun editDiscussion(postId: Long, content: String) {
+ viewModelScope.launch {
+ editPost(postId, EditPostDto.PRIVACY_ISOLAATTI, content, null, null).onEach {
+ when(it) {
+ is Resource.Success -> {
+ loading.postValue(false)
+ posted.postValue(it.data)
+ }
+ is Resource.Error -> {
+ loading.postValue(false)
+ error.postValue(it.errorType)
+ }
+ is Resource.Loading -> {
+ loading.postValue(true)
+ }
+ }
+ }.flowOn(Dispatchers.IO).launchIn(this)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/ui/CreatePostActivity.kt b/app/src/main/java/com/isolaatti/posting/posts/ui/CreatePostActivity.kt
new file mode 100644
index 0000000..c744bc3
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/posting/posts/ui/CreatePostActivity.kt
@@ -0,0 +1,117 @@
+package com.isolaatti.posting.posts.ui
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.os.PersistableBundle
+import android.view.View
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.res.ResourcesCompat
+import androidx.core.widget.doOnTextChanged
+import com.isolaatti.R
+import com.isolaatti.common.IsolaattiBaseActivity
+import com.isolaatti.databinding.ActivityCreatePostBinding
+import com.isolaatti.posting.posts.presentation.CreatePostViewModel
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class CreatePostActivity : IsolaattiBaseActivity() {
+ companion object {
+ const val EXTRA_MODE_CREATE = 0
+ /**
+ * Post activity in edit mode
+ */
+ const val EXTRA_MODE_EDIT = 1
+ const val EXTRA_KEY_MODE = "mode"
+
+ /**
+ * post id, pass long
+ */
+ const val EXTRA_KEY_POST_ID = "postId"
+
+ const val EXTRA_KEY_POST_POSTED = "post"
+ }
+
+ lateinit var binding: ActivityCreatePostBinding
+ val viewModel: CreatePostViewModel by viewModels()
+ var mode: Int = EXTRA_MODE_CREATE
+ var postId: Long = 0L
+ override fun onRetry() {
+ if(mode == EXTRA_MODE_EDIT && postId != 0L) {
+ viewModel.editDiscussion(postId, binding.filledTextField.editText?.text.toString())
+ } else {
+ viewModel.postDiscussion(binding.filledTextField.editText?.text.toString())
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ intent.extras?.let {
+ mode = it.getInt(EXTRA_KEY_MODE, EXTRA_MODE_CREATE)
+ postId = it.getLong(EXTRA_KEY_POST_ID)
+ }
+
+
+ binding = ActivityCreatePostBinding.inflate(layoutInflater)
+
+
+
+ setupUI()
+ setListeners()
+ setObservers()
+
+ setContentView(binding.root)
+
+ }
+
+ private fun setupUI() {
+ binding.toolbar.setTitle(if(mode == EXTRA_MODE_EDIT && postId != 0L) R.string.edit else R.string.new_post)
+ binding.filledTextField.requestFocus()
+ }
+
+ private fun setListeners() {
+ binding.toolbar.setNavigationOnClickListener {
+ exit()
+ }
+ binding.filledTextField.editText?.doOnTextChanged { text, _, _, _ ->
+ // make better validation :)
+ viewModel.validation.postValue(!text.isNullOrEmpty())
+ }
+
+ binding.postButton.setOnClickListener {
+ if(mode == EXTRA_MODE_EDIT && postId != 0L) {
+ viewModel.editDiscussion(postId, binding.filledTextField.editText?.text.toString())
+ } else {
+ viewModel.postDiscussion(binding.filledTextField.editText?.text.toString())
+ }
+ }
+ }
+
+ private fun setObservers() {
+ viewModel.validation.observe(this@CreatePostActivity) {
+ binding.postButton.isEnabled = it
+ }
+
+ viewModel.error.observe(this@CreatePostActivity) {
+ errorViewModel.error.postValue(it)
+ }
+
+ viewModel.posted.observe(this@CreatePostActivity) {
+ setResult(Activity.RESULT_OK, Intent().apply{
+ putExtra(EXTRA_KEY_POST_POSTED, it)
+ })
+ finish()
+ }
+
+ viewModel.loading.observe(this@CreatePostActivity) {
+ binding.progressBarLoading.visibility = if(it) View.VISIBLE else View.GONE
+ }
+ }
+
+ private fun exit() {
+ setResult(Activity.RESULT_CANCELED)
+ finish()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/posting/posts/ui/CreatePostDialogFragment.kt b/app/src/main/java/com/isolaatti/posting/posts/ui/CreatePostDialogFragment.kt
deleted file mode 100644
index 4d4f1de..0000000
--- a/app/src/main/java/com/isolaatti/posting/posts/ui/CreatePostDialogFragment.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.isolaatti.posting.posts.ui
-
-class CreatePostDialogFragment {
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/profile/data/remote/ProfileApi.kt b/app/src/main/java/com/isolaatti/profile/data/remote/ProfileApi.kt
index 155fed7..7ff4901 100644
--- a/app/src/main/java/com/isolaatti/profile/data/remote/ProfileApi.kt
+++ b/app/src/main/java/com/isolaatti/profile/data/remote/ProfileApi.kt
@@ -1,12 +1,12 @@
package com.isolaatti.profile.data.remote
import retrofit2.Call
-import retrofit2.http.GET
+import retrofit2.http.POST
import retrofit2.http.Path
interface ProfileApi {
- @GET("Fetch/UserProfile/{userId}")
+ @POST("Fetch/UserProfile/{userId}")
fun userProfile(@Path("userId") userId: Int): Call
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/profile/data/repository/ProfileRepositoryImpl.kt b/app/src/main/java/com/isolaatti/profile/data/repository/ProfileRepositoryImpl.kt
index 78997a6..5af4f67 100644
--- a/app/src/main/java/com/isolaatti/profile/data/repository/ProfileRepositoryImpl.kt
+++ b/app/src/main/java/com/isolaatti/profile/data/repository/ProfileRepositoryImpl.kt
@@ -3,13 +3,29 @@ package com.isolaatti.profile.data.repository
import com.isolaatti.profile.data.remote.ProfileApi
import com.isolaatti.profile.data.remote.UserProfileDto
import com.isolaatti.profile.domain.ProfileRepository
+import com.isolaatti.utils.Resource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import retrofit2.await
import javax.inject.Inject
class ProfileRepositoryImpl @Inject constructor(private val profileApi: ProfileApi) : ProfileRepository {
- override fun getProfile(): Flow = flow {
+ override fun getProfile(userId: Int): Flow> = flow {
+ try {
+ val result = profileApi.userProfile(userId).execute()
+ if(result.isSuccessful) {
+ emit(Resource.Success(result.body()))
+ return@flow
+ }
+ when(result.code()) {
+ 401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
+ 404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
+ 500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
+ else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
+ }
+ } catch(_: Exception) {
+ emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/profile/domain/ProfileRepository.kt b/app/src/main/java/com/isolaatti/profile/domain/ProfileRepository.kt
index 8fd59fe..dd3cb20 100644
--- a/app/src/main/java/com/isolaatti/profile/domain/ProfileRepository.kt
+++ b/app/src/main/java/com/isolaatti/profile/domain/ProfileRepository.kt
@@ -2,8 +2,9 @@ package com.isolaatti.profile.domain
import com.isolaatti.profile.data.remote.ProfileApi
import com.isolaatti.profile.data.remote.UserProfileDto
+import com.isolaatti.utils.Resource
import kotlinx.coroutines.flow.Flow
interface ProfileRepository {
- fun getProfile(): Flow
+ fun getProfile(userId: Int): Flow>
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/profile/domain/use_case/GetProfile.kt b/app/src/main/java/com/isolaatti/profile/domain/use_case/GetProfile.kt
index 12ef136..c135802 100644
--- a/app/src/main/java/com/isolaatti/profile/domain/use_case/GetProfile.kt
+++ b/app/src/main/java/com/isolaatti/profile/domain/use_case/GetProfile.kt
@@ -1,4 +1,11 @@
package com.isolaatti.profile.domain.use_case
-class GetProfile {
+import com.isolaatti.profile.data.remote.UserProfileDto
+import com.isolaatti.profile.domain.ProfileRepository
+import com.isolaatti.utils.Resource
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+
+class GetProfile @Inject constructor(private val profileRepository: ProfileRepository) {
+ operator fun invoke(userId: Int): Flow> = profileRepository.getProfile(userId)
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/baseline_check_24.xml b/app/src/main/res/drawable/baseline_check_24.xml
new file mode 100644
index 0000000..cf143d4
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_check_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/baseline_image_24.xml b/app/src/main/res/drawable/baseline_image_24.xml
new file mode 100644
index 0000000..f757acc
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_image_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/baseline_mic_24.xml b/app/src/main/res/drawable/baseline_mic_24.xml
index 5eb92eb..81b0217 100644
--- a/app/src/main/res/drawable/baseline_mic_24.xml
+++ b/app/src/main/res/drawable/baseline_mic_24.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/drawable/baseline_post_add_24.xml b/app/src/main/res/drawable/baseline_post_add_24.xml
index 141a0a9..80a084e 100644
--- a/app/src/main/res/drawable/baseline_post_add_24.xml
+++ b/app/src/main/res/drawable/baseline_post_add_24.xml
@@ -1,9 +1,9 @@
-
-
-
-
-
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_create_post.xml b/app/src/main/res/layout/activity_create_post.xml
new file mode 100644
index 0000000..ecc640a
--- /dev/null
+++ b/app/src/main/res/layout/activity_create_post.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/header_drawer_home.xml b/app/src/main/res/layout/header_drawer_home.xml
index b0b5e63..5412e19 100644
--- a/app/src/main/res/layout/header_drawer_home.xml
+++ b/app/src/main/res/layout/header_drawer_home.xml
@@ -1,15 +1,48 @@
-
+ android:layout_height="match_parent"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:background="@color/purple">
+
+
-
\ No newline at end of file
+ android:layout_marginStart="8dp"
+ android:layout_marginTop="16dp"
+ android:text="TextView"
+ android:textAlignment="textStart"
+ android:textColor="@android:color/white"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toEndOf="@+id/profileImageView"
+ app:layout_constraintTop_toTopOf="@+id/profileImageView"
+ tools:text="Name" />
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/discussion_creator_menu.xml b/app/src/main/res/menu/discussion_creator_menu.xml
new file mode 100644
index 0000000..1e686cc
--- /dev/null
+++ b/app/src/main/res/menu/discussion_creator_menu.xml
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/home_topbar_menu.xml b/app/src/main/res/menu/home_topbar_menu.xml
deleted file mode 100644
index 1bc9bae..0000000
--- a/app/src/main/res/menu/home_topbar_menu.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index b98fab3..76e2552 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -1,7 +1,7 @@
#4d3b68
- #7015ea
+ #3F0095
#1D1725
#FFFFFF
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ad05424..68f6eb4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -43,4 +43,8 @@
New audio
Load more
There is no more content to show
+ Post
+ Add image
+ What do you want to talk about? Markdown is compatible. You can record an audio if you want.
+ Posted!
\ No newline at end of file