diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml index 62894d9..8a92aa3 100644 --- a/.idea/assetWizardSettings.xml +++ b/.idea/assetWizardSettings.xml @@ -18,7 +18,7 @@ @@ -28,7 +28,7 @@ diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 2b8a50f..69e8615 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml index 0f89bc0..e6f581c 100644 --- a/.idea/navEditor.xml +++ b/.idea/navEditor.xml @@ -135,8 +135,20 @@ + + + + + + + @@ -147,10 +159,44 @@ + @@ -159,8 +205,44 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index 7dc536e..3ba3d0e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,6 +6,7 @@ plugins { id 'com.google.dagger.hilt.android' id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.0' id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' + id 'androidx.navigation.safeargs.kotlin' } android { @@ -32,6 +33,7 @@ android { } } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } @@ -41,6 +43,7 @@ android { } dependencies { + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' implementation 'androidx.core:core-ktx:1.10.1' implementation 'androidx.appcompat:appcompat:1.6.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 85956c2..a08e38f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,6 +29,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/drafts/ui/DraftsActivity.kt b/app/src/main/java/com/isolaatti/drafts/ui/DraftsActivity.kt new file mode 100644 index 0000000..ea342a6 --- /dev/null +++ b/app/src/main/java/com/isolaatti/drafts/ui/DraftsActivity.kt @@ -0,0 +1,17 @@ +package com.isolaatti.drafts.ui + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.isolaatti.databinding.ActivityDraftsBinding + +class DraftsActivity : AppCompatActivity() { + + lateinit var binding: ActivityDraftsBinding + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityDraftsBinding.inflate(layoutInflater) + + setContentView(binding.root) + } +} \ 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 c333752..3157dae 100644 --- a/app/src/main/java/com/isolaatti/home/FeedFragment.kt +++ b/app/src/main/java/com/isolaatti/home/FeedFragment.kt @@ -21,6 +21,7 @@ import com.isolaatti.BuildConfig import com.isolaatti.R import com.isolaatti.common.ErrorMessageViewModel import com.isolaatti.databinding.FragmentFeedBinding +import com.isolaatti.drafts.ui.DraftsActivity import com.isolaatti.home.presentation.FeedViewModel import com.isolaatti.posting.PostViewerActivity import com.isolaatti.posting.posts.presentation.PostsViewModel @@ -104,6 +105,10 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback { startActivity(Intent(requireActivity(), ProfileActivity::class.java)) true } + R.id.drafts_menu_item -> { + startActivity(Intent(requireActivity(), DraftsActivity::class.java)) + true + } R.id.settings_menu_item -> { startActivity(Intent(requireActivity(), SettingsActivity::class.java)) true @@ -128,18 +133,18 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback { viewBinding.feedRecyclerView.adapter = adapter viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext()) - viewBinding.refreshButton.setOnClickListener { - viewModel.getFeed(refresh = true) - } +// viewBinding.refreshButton.setOnClickListener { +// viewModel.getFeed(refresh = true) +// } viewBinding.swipeToRefresh.setOnRefreshListener { viewModel.getFeed(refresh = true) viewBinding.swipeToRefresh.isRefreshing = false } - viewBinding.loadMoreButton.setOnClickListener { - viewModel.getFeed(refresh = false) - } +// viewBinding.loadMoreButton.setOnClickListener { +// viewModel.getFeed(refresh = false) +// } viewBinding.topAppBar.setOnMenuItemClickListener { when(it.itemId) { @@ -175,17 +180,17 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback { } } - viewModel.loadingPosts.observe(viewLifecycleOwner) { - viewBinding.progressBarLoading.visibility = if(it) View.VISIBLE else View.GONE - viewBinding.loadMoreButton.visibility = if(it) View.GONE else View.VISIBLE - } - - viewModel.noMoreContent.observe(viewLifecycleOwner) { - val visibility = if(it) View.VISIBLE else View.GONE - viewBinding.noMoreContentToShowTextView.visibility = visibility - viewBinding.refreshButton.visibility = visibility - viewBinding.loadMoreButton.visibility = if(it) View.GONE else View.VISIBLE - } +// viewModel.loadingPosts.observe(viewLifecycleOwner) { +// viewBinding.progressBarLoading.visibility = if(it) View.VISIBLE else View.GONE +// viewBinding.loadMoreButton.visibility = if(it) View.GONE else View.VISIBLE +// } +// +// viewModel.noMoreContent.observe(viewLifecycleOwner) { +// val visibility = if(it) View.VISIBLE else View.GONE +// viewBinding.noMoreContentToShowTextView.visibility = visibility +// viewBinding.refreshButton.visibility = visibility +// viewBinding.loadMoreButton.visibility = if(it) View.GONE else View.VISIBLE +// } viewModel.errorLoading.observe(viewLifecycleOwner) { errorViewModel.error.postValue(it) diff --git a/app/src/main/java/com/isolaatti/posting/posts/data/remote/FeedFilterDto.kt b/app/src/main/java/com/isolaatti/posting/posts/data/remote/FeedFilterDto.kt index d1408ca..c5eb7c5 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/data/remote/FeedFilterDto.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/data/remote/FeedFilterDto.kt @@ -1,13 +1,36 @@ package com.isolaatti.posting.posts.data.remote +import com.google.gson.TypeAdapter +import com.google.gson.annotations.JsonAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + data class FeedFilterDto( val includeAudio: String, val includeFromSquads: String, - val dataRange: DataRange + val dateRange: DataRange ) { + + class LocalDateTimeJsonAdapter : TypeAdapter() { + override fun write(out: JsonWriter?, value: LocalDate?) { + if(value != null) { + out?.jsonValue("\"${value.format(DateTimeFormatter.ISO_LOCAL_DATE)}\"") + } + } + + override fun read(`in`: JsonReader?): LocalDate { + return LocalDate.parse(`in`.toString()) + } + + } data class DataRange( val enabled: Boolean, - val from: String, - val to: String + @JsonAdapter(LocalDateTimeJsonAdapter::class) + val from: LocalDate, + @JsonAdapter(LocalDateTimeJsonAdapter::class) + val to: LocalDate ) } diff --git a/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt b/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt index f16b793..607eeaf 100644 --- a/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt +++ b/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.isolaatti.posting.posts.data.remote.FeedDto +import com.isolaatti.posting.posts.data.remote.FeedFilterDto import com.isolaatti.profile.data.remote.UserProfileDto import com.isolaatti.profile.domain.ProfileRepository import com.isolaatti.profile.domain.use_case.GetProfile @@ -16,6 +17,10 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.Month import javax.inject.Inject @HiltViewModel @@ -37,6 +42,25 @@ class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetPro } fun getPosts(profileId: Int, refresh: Boolean) { - + viewModelScope.launch { + getProfilePosts( + profileId, + -1, + false, + FeedFilterDto( + "both", + "both", + FeedFilterDto.DataRange( + false, + LocalDate.of(2020, 1, 1), + LocalDate.now() + ) + ) + ).onEach { + if(it is Resource.Success) { + _posts.postValue(it.data!!) + } + }.flowOn(Dispatchers.IO).launchIn(this) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/ui/BlockProfileFragment.kt b/app/src/main/java/com/isolaatti/profile/ui/BlockProfileFragment.kt new file mode 100644 index 0000000..58a182b --- /dev/null +++ b/app/src/main/java/com/isolaatti/profile/ui/BlockProfileFragment.kt @@ -0,0 +1,8 @@ +package com.isolaatti.profile.ui + +import androidx.fragment.app.Fragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class BlockProfileFragment : Fragment() { +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/ui/DiscussionsFragment.kt b/app/src/main/java/com/isolaatti/profile/ui/DiscussionsFragment.kt index 15b99f2..5041a93 100644 --- a/app/src/main/java/com/isolaatti/profile/ui/DiscussionsFragment.kt +++ b/app/src/main/java/com/isolaatti/profile/ui/DiscussionsFragment.kt @@ -4,13 +4,62 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.activity.viewModels import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.Observer +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.appbar.AppBarLayout +import com.isolaatti.BuildConfig +import com.isolaatti.R import com.isolaatti.databinding.FragmentDiscussionsBinding +import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback +import com.isolaatti.posting.posts.data.remote.FeedDto +import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter +import com.isolaatti.profile.data.remote.UserProfileDto +import com.isolaatti.profile.presentation.ProfileViewModel +import com.isolaatti.utils.PicassoImagesPluginDef +import com.isolaatti.utils.UrlGen +import com.squareup.picasso.Picasso +import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.lifecycle.HiltViewModel +import io.noties.markwon.AbstractMarkwonPlugin +import io.noties.markwon.Markwon +import io.noties.markwon.MarkwonConfiguration +import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute +import io.noties.markwon.linkify.LinkifyPlugin -class DiscussionsFragment : Fragment() { +@AndroidEntryPoint +class DiscussionsFragment : Fragment(), OnUserInteractedWithPostCallback { lateinit var viewBinding: FragmentDiscussionsBinding + private val viewModel: ProfileViewModel by viewModels() + private var userId: Int? = null + + private var title = "" + lateinit var feedAdapter: PostsRecyclerViewAdapter + + private val profileObserver = Observer { profile -> + Picasso.get() + .load(UrlGen.userProfileImage(profile.id)) + .into(viewBinding.profileImageView) + + title = profile.name + viewBinding.textViewUsername.text = profile.name + viewBinding.textViewDescription.text = profile.descriptionText + } + + private val postsObserver: Observer = Observer { + feedAdapter.updateList(it, null) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + userId = (requireActivity()).intent.extras?.getInt(ProfileActivity.EXTRA_USER_ID) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -20,4 +69,89 @@ class DiscussionsFragment : Fragment() { return viewBinding.root } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + var scrollRange = -1 + var isShow = false + viewBinding.topAppBarLayout.addOnOffsetChangedListener { appBarLayout, verticalOffset -> + if (scrollRange == -1) scrollRange = appBarLayout.totalScrollRange + if (scrollRange + verticalOffset == 0) { + viewBinding.collapsingToolbarLayout.title = title + isShow = true + } else if (isShow) { + viewBinding.collapsingToolbarLayout.title = " " + } + } + + viewBinding.topAppBar.setNavigationOnClickListener { + findNavController().popBackStack() + } + + + viewModel.profile.observe(viewLifecycleOwner, profileObserver) + viewModel.posts.observe(viewLifecycleOwner, postsObserver) + + userId?.let { + viewModel.getProfile(it) + viewModel.getPosts(it, true) + } + + val markwon = Markwon.builder(requireContext()) + .usePlugin(object: AbstractMarkwonPlugin() { + override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { + builder + .imageDestinationProcessor( + ImageDestinationProcessorRelativeToAbsolute + .create(BuildConfig.backend)) + } + }) + .usePlugin(PicassoImagesPluginDef.picassoImagePlugin) + .usePlugin(LinkifyPlugin.create()) + .build() + + feedAdapter = PostsRecyclerViewAdapter(markwon, this, null) + + viewBinding.feedRecyclerView.adapter = feedAdapter + viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + + viewBinding.bottomAppBar.setOnMenuItemClickListener { + when(it.itemId) { + R.id.audios_menu_item -> { + findNavController().navigate(DiscussionsFragmentDirections.actionDiscussionsFragmentToAudiosFragment()) + true + } + R.id.images_menu_item -> { + findNavController().navigate(DiscussionsFragmentDirections.actionDiscussionsFragmentToImagesFragment()) + true + } + else -> { false } + } + } + + } + + override fun onLiked(postId: Long) { + TODO("Not yet implemented") + } + + override fun onUnLiked(postId: Long) { + TODO("Not yet implemented") + } + + override fun onComment(postId: Long) { + TODO("Not yet implemented") + } + + override fun onOpenPost(postId: Long) { + TODO("Not yet implemented") + } + + override fun onOptions(postId: Long) { + TODO("Not yet implemented") + } + + override fun onProfileClick(userId: Int) { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/ui/MainFollowersFragment.kt b/app/src/main/java/com/isolaatti/profile/ui/MainFollowersFragment.kt new file mode 100644 index 0000000..8c184bc --- /dev/null +++ b/app/src/main/java/com/isolaatti/profile/ui/MainFollowersFragment.kt @@ -0,0 +1,8 @@ +package com.isolaatti.profile.ui + +import androidx.fragment.app.Fragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class MainFollowersFragment : Fragment() { +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/ui/ProfileActivity.kt b/app/src/main/java/com/isolaatti/profile/ui/ProfileActivity.kt index 80b1461..b464514 100644 --- a/app/src/main/java/com/isolaatti/profile/ui/ProfileActivity.kt +++ b/app/src/main/java/com/isolaatti/profile/ui/ProfileActivity.kt @@ -7,73 +7,49 @@ import android.util.Log import androidx.activity.addCallback import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContentProviderCompat.requireContext import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider +import androidx.navigation.findNavController +import androidx.navigation.fragment.NavHostFragment +import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewpager.widget.PagerAdapter import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentViewHolder import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener import com.google.android.material.tabs.TabLayoutMediator +import com.isolaatti.BuildConfig import com.isolaatti.R import com.isolaatti.databinding.ActivityProfileBinding +import com.isolaatti.posting.common.domain.OnUserInteractedCallback +import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback +import com.isolaatti.posting.posts.data.remote.FeedDto +import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter import com.isolaatti.profile.data.remote.UserProfileDto import com.isolaatti.profile.presentation.ProfileViewModel +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 +import io.noties.markwon.MarkwonConfiguration +import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute +import io.noties.markwon.linkify.LinkifyPlugin @AndroidEntryPoint -class ProfileActivity : AppCompatActivity() { +class ProfileActivity : FragmentActivity() { lateinit var viewBinding: ActivityProfileBinding - private val viewModel: ProfileViewModel by viewModels() - private var userId: Int? = null - private var title = "" - private val profileObserver = Observer { profile -> - Picasso.get() - .load(UrlGen.userProfileImage(profile.id)) - .into(viewBinding.profileImageView) - - title = profile.name - viewBinding.textViewUsername.text = profile.name - viewBinding.textViewDescription.text = profile.descriptionText - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityProfileBinding.inflate(layoutInflater) setContentView(viewBinding.root) - userId = intent.extras?.getInt(EXTRA_USER_ID) - var scrollRange = -1 - var isShow = false - viewBinding.topAppBarLayout.addOnOffsetChangedListener(object: OnOffsetChangedListener { - - - override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) { - if (scrollRange == -1) scrollRange = appBarLayout.totalScrollRange - if(scrollRange + verticalOffset == 0) { - viewBinding.collapsingToolbarLayout.title = title - isShow = true - } else if(isShow) { - viewBinding.collapsingToolbarLayout.title = " " - } - - } - - }) - - viewBinding.topAppBar.setNavigationOnClickListener { - finish() - } - - - viewModel.profile.observe(this, profileObserver) - - userId?.let { viewModel.getProfile(it) } } diff --git a/app/src/main/java/com/isolaatti/profile/ui/ReportProfileFragment.kt b/app/src/main/java/com/isolaatti/profile/ui/ReportProfileFragment.kt new file mode 100644 index 0000000..58e605c --- /dev/null +++ b/app/src/main/java/com/isolaatti/profile/ui/ReportProfileFragment.kt @@ -0,0 +1,8 @@ +package com.isolaatti.profile.ui + +import androidx.fragment.app.Fragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class ReportProfileFragment : Fragment() { +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/ui/UserLinkFragment.kt b/app/src/main/java/com/isolaatti/profile/ui/UserLinkFragment.kt new file mode 100644 index 0000000..3ec6e0c --- /dev/null +++ b/app/src/main/java/com/isolaatti/profile/ui/UserLinkFragment.kt @@ -0,0 +1,8 @@ +package com.isolaatti.profile.ui + +import androidx.fragment.app.Fragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class UserLinkFragment : Fragment() { +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/utils/Resource.kt b/app/src/main/java/com/isolaatti/utils/Resource.kt index 5842b25..06c2cbf 100644 --- a/app/src/main/java/com/isolaatti/utils/Resource.kt +++ b/app/src/main/java/com/isolaatti/utils/Resource.kt @@ -1,6 +1,6 @@ package com.isolaatti.utils -abstract class Resource { +sealed class Resource { class Success(val data: T?): Resource() class Loading: Resource() class Error(val errorType: ErrorType? = null): Resource() { diff --git a/app/src/main/res/drawable/baseline_audio_file_24.xml b/app/src/main/res/drawable/baseline_audio_file_24.xml new file mode 100644 index 0000000..a948544 --- /dev/null +++ b/app/src/main/res/drawable/baseline_audio_file_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_block_24.xml b/app/src/main/res/drawable/baseline_block_24.xml new file mode 100644 index 0000000..1e478d2 --- /dev/null +++ b/app/src/main/res/drawable/baseline_block_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_expand_more_24.xml b/app/src/main/res/drawable/baseline_expand_more_24.xml new file mode 100644 index 0000000..48368f3 --- /dev/null +++ b/app/src/main/res/drawable/baseline_expand_more_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_sort_24.xml b/app/src/main/res/drawable/baseline_sort_24.xml new file mode 100644 index 0000000..3b18419 --- /dev/null +++ b/app/src/main/res/drawable/baseline_sort_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout-land/fragment_feed.xml b/app/src/main/res/layout-land/fragment_feed.xml index 88f0eaf..148463f 100644 --- a/app/src/main/res/layout-land/fragment_feed.xml +++ b/app/src/main/res/layout-land/fragment_feed.xml @@ -19,102 +19,46 @@ app:layout_constraintTop_toTopOf="parent" tools:context=".home.FeedFragment"> + - - - - - - - - + app:title="@string/app_name" + app:titleCentered="true" + app:menu="@menu/new_menu" + tools:layout_conversion_absoluteHeight="64dp" + tools:layout_conversion_absoluteWidth="531dp" + tools:layout_editor_absoluteX="360dp" + tools:layout_editor_absoluteY="0dp" /> - + - + - + - - - - - - - - -