creacion de post, se ve informacion de perfil en drawer y otros cambios
This commit is contained in:
parent
1e6be6db5b
commit
72290eb50f
@ -27,6 +27,7 @@
|
||||
<activity android:name=".login.LogInActivity" android:theme="@style/Theme.Isolaatti" />
|
||||
<activity android:name=".profile.ui.ProfileActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||
<activity android:name=".settings.ui.SettingsActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||
<activity android:name=".posting.posts.ui.CreatePostActivity" android:theme="@style/Theme.Isolaatti" android:windowSoftInputMode="adjustResize"/>
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@ -25,5 +25,4 @@ class MainModule {
|
||||
return RetrofitClient(authenticationInterceptor)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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<Int?> = flow {
|
||||
emit(keyValueDao.getValue(KEY_USERID).toIntOrNull())
|
||||
}
|
||||
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
)
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -8,4 +8,6 @@ interface AuthRepository {
|
||||
fun authWithEmailAndPassword(email: String, password: String): Flow<Resource<Boolean>>
|
||||
fun logout(): Flow<Boolean>
|
||||
fun getCurrentToken(): AuthTokenDto?
|
||||
fun getUserId(): Flow<Int?>
|
||||
|
||||
}
|
||||
@ -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() {
|
||||
|
||||
12
app/src/main/java/com/isolaatti/database/AppDatabase.kt
Normal file
12
app/src/main/java/com/isolaatti/database/AppDatabase.kt
Normal file
@ -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
|
||||
|
||||
}
|
||||
20
app/src/main/java/com/isolaatti/database/Module.kt
Normal file
20
app/src/main/java/com/isolaatti/database/Module.kt
Normal file
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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<UserProfileDto> = MutableLiveData()
|
||||
val userProfile: LiveData<UserProfileDto> 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)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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?
|
||||
)
|
||||
@ -0,0 +1,3 @@
|
||||
package com.isolaatti.posting.posts.data.remote
|
||||
|
||||
data class DeletePostDto(val id: Long)
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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<PostDto>,
|
||||
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<Post> {
|
||||
override fun createFromParcel(parcel: Parcel): Post {
|
||||
return Post(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Post?> {
|
||||
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<PostDto> {
|
||||
override fun createFromParcel(parcel: Parcel): PostDto {
|
||||
return PostDto(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<PostDto?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<FeedDto.PostDto>
|
||||
|
||||
@POST("Posting/Edit")
|
||||
fun editPost(@Body editedPost: EditPostDto): Call<FeedDto.PostDto>
|
||||
|
||||
@POST("Posting/Delete")
|
||||
fun deletePost(@Body postToDelete: DeletePostDto): Call<Any>
|
||||
|
||||
}
|
||||
@ -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<Resource<FeedDto>> = 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<Resource<FeedDto.PostDto>> = 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<Resource<FeedDto.PostDto>> = 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<Resource<FeedDto>>
|
||||
|
||||
fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto): Flow<Resource<FeedDto>>
|
||||
|
||||
fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>>
|
||||
fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>>
|
||||
}
|
||||
@ -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<Resource<FeedDto.PostDto>> {
|
||||
return postsRepository.editPost(EditPostDto(privacy, content, audioId, squadId, postId))
|
||||
}
|
||||
}
|
||||
@ -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<Resource<FeedDto.PostDto>> {
|
||||
return postsRepository.makePost(CreatePostDto(privacy, content, audioId, squadId))
|
||||
}
|
||||
}
|
||||
@ -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<Boolean> = MutableLiveData(false)
|
||||
val posted: MutableLiveData<FeedDto.PostDto?> = MutableLiveData()
|
||||
val error: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||
val loading: MutableLiveData<Boolean> = 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
package com.isolaatti.posting.posts.ui
|
||||
|
||||
class CreatePostDialogFragment {
|
||||
}
|
||||
@ -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<UserProfileDto>
|
||||
|
||||
}
|
||||
@ -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<UserProfileDto> = flow {
|
||||
override fun getProfile(userId: Int): Flow<Resource<UserProfileDto>> = 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<UserProfileDto>
|
||||
fun getProfile(userId: Int): Flow<Resource<UserProfileDto>>
|
||||
}
|
||||
@ -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<Resource<UserProfileDto>> = profileRepository.getProfile(userId)
|
||||
}
|
||||
5
app/src/main/res/drawable/baseline_check_24.xml
Normal file
5
app/src/main/res/drawable/baseline_check_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/baseline_image_24.xml
Normal file
5
app/src/main/res/drawable/baseline_image_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@color/on_surface" android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||
</vector>
|
||||
@ -1,5 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
|
||||
<path android:fillColor="@color/on_surface" android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
|
||||
</vector>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17,19.22H5V7h7V5H5C3.9,5 3,5.9 3,7v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-7h-2V19.22z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,2h-2v3h-3c0.01,0.01 0,2 0,2h3v2.99c0.01,0.01 2,0 2,0V7h3V5h-3V2z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,9h8v2h-8z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,12l0,2l8,0l0,-2l-3,0z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,15h8v2h-8z"/>
|
||||
<path android:fillColor="@color/on_surface" android:pathData="M17,19.22H5V7h7V5H5C3.9,5 3,5.9 3,7v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-7h-2V19.22z"/>
|
||||
<path android:fillColor="@color/on_surface" android:pathData="M19,2h-2v3h-3c0.01,0.01 0,2 0,2h3v2.99c0.01,0.01 2,0 2,0V7h3V5h-3V2z"/>
|
||||
<path android:fillColor="@color/on_surface" android:pathData="M7,9h8v2h-8z"/>
|
||||
<path android:fillColor="@color/on_surface" android:pathData="M7,12l0,2l8,0l0,-2l-3,0z"/>
|
||||
<path android:fillColor="@color/on_surface" android:pathData="M7,15h8v2h-8z"/>
|
||||
</vector>
|
||||
|
||||
70
app/src/main/res/layout/activity_create_post.xml
Normal file
70
app/src/main/res/layout/activity_create_post.xml
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:navigationIcon="@drawable/baseline_close_24"
|
||||
app:navigationIconTint="@color/on_surface"
|
||||
app:title="@string/new_post"
|
||||
app:titleCentered="true">
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="100dp"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
android:clipToPadding="false">
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="?attr/textInputFilledStyle"
|
||||
android:id="@+id/filledTextField"
|
||||
android:layout_width="match_parent"
|
||||
app:boxBackgroundMode="none"
|
||||
android:layout_height="wrap_content"
|
||||
app:hintEnabled="false">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/what_do_you_want_to_talk_about_markdown_is_compatible_you_can_record_an_audio_if_you_want"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<com.google.android.material.bottomappbar.BottomAppBar
|
||||
android:id="@+id/bottomAppBar"
|
||||
style="@style/Widget.Material3.BottomAppBar"
|
||||
app:menu="@menu/discussion_creator_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom" />
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/postButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/baseline_send_24"
|
||||
app:layout_anchor="@id/bottomAppBar" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@ -1,15 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:background="@color/purple">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/profileImageView"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/textViewName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:text="@string/app_name" />
|
||||
</LinearLayout>
|
||||
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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewEmail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="TextView"
|
||||
android:textAlignment="textStart"
|
||||
android:textColor="@android:color/white"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/profileImageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textViewName" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
11
app/src/main/res/menu/discussion_creator_menu.xml
Normal file
11
app/src/main/res/menu/discussion_creator_menu.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:title="@string/add_audio"
|
||||
android:icon="@drawable/baseline_mic_24"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item android:title="@string/add_image"
|
||||
android:icon="@drawable/baseline_image_24"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
</menu>
|
||||
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:icon="@drawable/baseline_add_24"
|
||||
app:showAsAction="always"
|
||||
android:title="@string/new_post" />
|
||||
</menu>
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple">#4d3b68</color>
|
||||
<color name="purple_lighter">#7015ea</color>
|
||||
<color name="purple_lighter">#3F0095</color>
|
||||
<color name="surface">#1D1725</color>
|
||||
<color name="on_surface">#FFFFFF</color>
|
||||
</resources>
|
||||
@ -43,4 +43,8 @@
|
||||
<string name="add_audio">New audio</string>
|
||||
<string name="load_more">Load more</string>
|
||||
<string name="there_is_no_more_content_to_show">There is no more content to show</string>
|
||||
<string name="post">Post</string>
|
||||
<string name="add_image">Add image</string>
|
||||
<string name="what_do_you_want_to_talk_about_markdown_is_compatible_you_can_record_an_audio_if_you_want">What do you want to talk about? Markdown is compatible. You can record an audio if you want.</string>
|
||||
<string name="posted_successfully">Posted!</string>
|
||||
</resources>
|
||||
Loading…
x
Reference in New Issue
Block a user