1. edge to edge
2. se quitan imagenes y audios del perfil
3. se quita markdown de posts
4. se agrega pantalla de licencias
5. se agrega soporte para dar clic en hashtags
6. pantalla de hashtags ahora es una actividad
7. se comienza a implementar nuevo flujo de imagenes
This commit is contained in:
erik-everardo 2024-11-09 01:22:18 -06:00
parent a2c9091598
commit 7c073b80c6
38 changed files with 206 additions and 157 deletions

View File

@ -9,6 +9,7 @@ plugins {
id 'androidx.navigation.safeargs.kotlin' id 'androidx.navigation.safeargs.kotlin'
id 'com.google.gms.google-services' id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics' id 'com.google.firebase.crashlytics'
id 'com.google.android.gms.oss-licenses-plugin'
} }
android { android {
@ -73,7 +74,7 @@ dependencies {
// Material 3 // Material 3
implementation "com.google.android.material:material:1.11.0" implementation "com.google.android.material:material:1.12.0"
// Navigation // Navigation
def nav_version = "2.7.7" def nav_version = "2.7.7"
@ -133,4 +134,7 @@ dependencies {
implementation("com.google.firebase:firebase-crashlytics") implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.firebase:firebase-analytics") implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-messaging") implementation("com.google.firebase:firebase-messaging")
// OSS screen
implementation 'com.google.android.gms:play-services-oss-licenses:17.1.0'
} }

View File

@ -16,6 +16,15 @@
android:theme="@style/Theme.Isolaatti" android:theme="@style/Theme.Isolaatti"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:targetApi="31"> tools:targetApi="31">
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:theme="@style/Theme.Isolaatti.OSS" />
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
android:theme="@style/Theme.Isolaatti.OSS" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@ -45,6 +54,7 @@
<activity android:name=".audio.recorder.ui.AudioRecorderActivity" android:theme="@style/Theme.Isolaatti" /> <activity android:name=".audio.recorder.ui.AudioRecorderActivity" android:theme="@style/Theme.Isolaatti" />
<activity android:name=".audio.audio_selector.ui.AudioSelectorActivity" android:theme="@style/Theme.Isolaatti" /> <activity android:name=".audio.audio_selector.ui.AudioSelectorActivity" android:theme="@style/Theme.Isolaatti" />
<activity android:name=".posting.posts.ui.PostInfoActivity" android:theme="@style/Theme.Isolaatti"/> <activity android:name=".posting.posts.ui.PostInfoActivity" android:theme="@style/Theme.Isolaatti"/>
<activity android:name=".hashtags.ui.HashtagsPostsActivity" android:theme="@style/Theme.Isolaatti" />
<provider <provider
android:authorities="com.isolaatti.provider" android:authorities="com.isolaatti.provider"
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"

View File

@ -1,9 +1,11 @@
package com.isolaatti.about package com.isolaatti.about
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.net.toUri import androidx.core.net.toUri
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import com.isolaatti.BuildConfig import com.isolaatti.BuildConfig
import com.isolaatti.R import com.isolaatti.R
import com.isolaatti.databinding.ActivityAboutBinding import com.isolaatti.databinding.ActivityAboutBinding
@ -36,9 +38,8 @@ class AboutActivity : AppCompatActivity() {
} }
binding.openSourceLicences.setOnClickListener { binding.openSourceLicences.setOnClickListener {
CustomTabsIntent.Builder() startActivity(Intent(this, OssLicensesMenuActivity::class.java))
.build()
.launchUrl(this, BuildConfig.openSourceLicences.toUri())
} }
binding.privacyPolicyButton.setOnClickListener { binding.privacyPolicyButton.setOnClickListener {

View File

@ -10,4 +10,5 @@ interface OnUserInteractedWithPostCallback : OnUserInteractedCallback {
fun onPlay(audio: Audio) fun onPlay(audio: Audio)
fun onMoreInfo(postId: Long) fun onMoreInfo(postId: Long)
fun onShare(postId: Long) fun onShare(postId: Long)
fun hashtagClicked(hashtag: String)
} }

View File

@ -0,0 +1,4 @@
package com.isolaatti.common
val hashtagRegex = "#(\\w|-|_)+".toRegex()
val userTagRegex = "@(\\w|-|_)+".toRegex()

View File

@ -6,16 +6,18 @@ import com.isolaatti.audio.drafts.data.AudioDraftEntity
import com.isolaatti.audio.drafts.data.AudiosDraftsDao import com.isolaatti.audio.drafts.data.AudiosDraftsDao
import com.isolaatti.auth.data.local.UserInfoDao import com.isolaatti.auth.data.local.UserInfoDao
import com.isolaatti.auth.data.local.UserInfoEntity import com.isolaatti.auth.data.local.UserInfoEntity
import com.isolaatti.images.common.data.dao.ImagesDraftsDao
import com.isolaatti.images.common.data.entity.ImageDraftEntity
import com.isolaatti.search.data.SearchDao import com.isolaatti.search.data.SearchDao
import com.isolaatti.search.data.SearchHistoryEntity import com.isolaatti.search.data.SearchHistoryEntity
import com.isolaatti.settings.data.KeyValueDao import com.isolaatti.settings.data.KeyValueDao
import com.isolaatti.settings.data.KeyValueEntity import com.isolaatti.settings.data.KeyValueEntity
@Database(entities = [KeyValueEntity::class, UserInfoEntity::class, AudioDraftEntity::class, SearchHistoryEntity::class], version = 7) @Database(entities = [KeyValueEntity::class, UserInfoEntity::class, AudioDraftEntity::class, SearchHistoryEntity::class, ImageDraftEntity::class], version = 8)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun keyValueDao(): KeyValueDao abstract fun keyValueDao(): KeyValueDao
abstract fun userInfoDao(): UserInfoDao abstract fun userInfoDao(): UserInfoDao
abstract fun audioDrafts(): AudiosDraftsDao abstract fun audioDrafts(): AudiosDraftsDao
abstract fun searchHistoryDao(): SearchDao abstract fun searchHistoryDao(): SearchDao
abstract fun imagesDraftsDao(): ImagesDraftsDao
} }

View File

@ -1,46 +0,0 @@
package com.isolaatti.hashtags.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import com.isolaatti.databinding.FragmentPostsHashtagBinding
import androidx.navigation.fragment.navArgs
import com.isolaatti.R
import com.isolaatti.posting.posts.ui.PostListingFragment
class HashtagPostsFragment : Fragment() {
private lateinit var binding: FragmentPostsHashtagBinding
private val navArgs: HashtagPostsFragmentArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentPostsHashtagBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.title = "#${navArgs.hashtag}"
setupListeners()
// show feed
(childFragmentManager.findFragmentById(R.id.post_list_fragment_container) as NavHostFragment)
.navController.setGraph(R.navigation.post_listing_navigation, Bundle().apply { putString(PostListingFragment.ARG_HASHTAG, navArgs.hashtag) })
}
private fun setupListeners() {
binding.toolbar.setNavigationOnClickListener {
findNavController().popBackStack()
}
}
}

View File

@ -0,0 +1,46 @@
package com.isolaatti.hashtags.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.NavHostFragment
import com.isolaatti.R
import com.isolaatti.databinding.ActivityPostsHashtagBinding
import com.isolaatti.posting.posts.ui.PostListingFragment
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class HashtagsPostsActivity : AppCompatActivity() {
lateinit var binding: ActivityPostsHashtagBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPostsHashtagBinding.inflate(layoutInflater)
setContentView(binding.root)
setupListeners()
val hashtag = intent.extras?.getString(EXTRA_HASHTAG)!!
binding.toolbar.title = "#$hashtag"
(supportFragmentManager.findFragmentById(R.id.post_list_fragment_container) as NavHostFragment)
.navController.setGraph(R.navigation.post_listing_navigation, Bundle().apply { putString(
PostListingFragment.ARG_HASHTAG, hashtag) })
}
private fun setupListeners() {
binding.toolbar.setNavigationOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
companion object {
const val EXTRA_HASHTAG = "hashtag"
fun startActivity(context: Context, hashtag: String) {
val intent = Intent(context, HashtagsPostsActivity::class.java)
intent.putExtra(EXTRA_HASHTAG, hashtag)
context.startActivity(intent)
}
}
}

View File

@ -10,6 +10,9 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -136,5 +139,11 @@ class FeedFragment : Fragment() {
} }
} }
} }
ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) {_, insets ->
val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
viewBinding.homeDrawer.getHeaderView(0).updatePadding(top = systemBarsInsets.top)
insets
}
} }
} }

View File

@ -6,6 +6,7 @@ import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -36,6 +37,7 @@ class HomeActivity : IsolaattiBaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewBinding = ActivityHomeBinding.inflate(layoutInflater) viewBinding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(viewBinding.root) setContentView(viewBinding.root)

View File

@ -0,0 +1,19 @@
package com.isolaatti.images.common.data.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.isolaatti.images.common.data.entity.ImageDraftEntity
@Dao
interface ImagesDraftsDao {
@Query("SELECT * FROM image_drafts WHERE postId IS NULL")
fun getDetachedImages(): List<ImageDraftEntity>
@Insert
fun insertImageDraft(imageDraftEntity: ImageDraftEntity)
@Query("DELETE FROM image_drafts WHERE id = :id")
fun deleteById(id: Long)
}

View File

@ -0,0 +1,12 @@
package com.isolaatti.images.common.data.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "image_drafts")
data class ImageDraftEntity(
@PrimaryKey(autoGenerate = true)
val id: Long,
val uri: String,
val postId: Long? = null
)

View File

@ -51,9 +51,6 @@ class ImageMakerActivity : IsolaattiBaseActivity() {
binding.uploadPhotoFab.setOnClickListener { binding.uploadPhotoFab.setOnClickListener {
viewModel.uploadPicture() viewModel.uploadPicture()
} }
binding.textImageName.editText?.doOnTextChanged { text, _, _, _ ->
viewModel.name = text.toString()
}
binding.toolbar.setNavigationOnClickListener { binding.toolbar.setNavigationOnClickListener {
showExitConfirmationDialog() showExitConfirmationDialog()
} }
@ -66,12 +63,10 @@ class ImageMakerActivity : IsolaattiBaseActivity() {
errorViewModel.error.value = it.errorType errorViewModel.error.value = it.errorType
binding.progressBarLoading.visibility = View.GONE binding.progressBarLoading.visibility = View.GONE
binding.uploadPhotoFab.visibility = View.VISIBLE binding.uploadPhotoFab.visibility = View.VISIBLE
binding.textImageName.isEnabled = true
} }
is Resource.Loading -> { is Resource.Loading -> {
binding.progressBarLoading.visibility = View.VISIBLE binding.progressBarLoading.visibility = View.VISIBLE
binding.uploadPhotoFab.visibility = View.INVISIBLE binding.uploadPhotoFab.visibility = View.INVISIBLE
binding.textImageName.isEnabled = false
} }
is Resource.Success -> { is Resource.Success -> {
binding.progressBarLoading.visibility = View.GONE binding.progressBarLoading.visibility = View.GONE

View File

@ -4,8 +4,8 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import com.isolaatti.audio.common.domain.Audio import com.isolaatti.audio.common.domain.Audio
import com.isolaatti.common.Ownable import com.isolaatti.common.Ownable
import com.isolaatti.common.hashtagRegex
import com.isolaatti.posting.posts.data.remote.FeedDto import com.isolaatti.posting.posts.data.remote.FeedDto
import java.io.Serializable
data class Post( data class Post(
val id: Long, val id: Long,
@ -108,4 +108,8 @@ data class Post(
override fun describeContents(): Int { override fun describeContents(): Int {
return 0 return 0
} }
val hashtagsSpans by lazy {
hashtagRegex.findAll(textContent).map { it.range }.toList()
}
} }

View File

@ -1,6 +1,9 @@
package com.isolaatti.posting.posts.presentation package com.isolaatti.posting.posts.presentation
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -8,7 +11,7 @@ import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.res.ResourcesCompat import androidx.core.text.set
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.recyclerview.widget.RecyclerView.ViewHolder
import coil.load import coil.load
@ -21,10 +24,9 @@ import com.isolaatti.common.OnUserInteractedWithPostCallback
import com.isolaatti.databinding.PostLayoutBinding import com.isolaatti.databinding.PostLayoutBinding
import com.isolaatti.posting.posts.domain.entity.Post import com.isolaatti.posting.posts.domain.entity.Post
import com.isolaatti.utils.UrlGen.userProfileImage import com.isolaatti.utils.UrlGen.userProfileImage
import io.noties.markwon.Markwon
class PostsRecyclerViewAdapter ( class PostsRecyclerViewAdapter (
private val markwon: Markwon,
private val callback: OnUserInteractedWithPostCallback private val callback: OnUserInteractedWithPostCallback
) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){ ) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){
private var postList: List<Post>? = null private var postList: List<Post>? = null
@ -102,7 +104,17 @@ class PostsRecyclerViewAdapter (
dateTextView.text = post.date dateTextView.text = post.date
val content: TextView = itemView.findViewById(R.id.post_content) val content: TextView = itemView.findViewById(R.id.post_content)
markwon.setMarkdown(content, post.textContent) content.movementMethod = LinkMovementMethod.getInstance()
val spannableString = SpannableString(post.textContent).apply {
post.hashtagsSpans.forEach {
set(it.first, it.last + 1, object: ClickableSpan() {
override fun onClick(widget: View) {
callback.hashtagClicked(post.textContent.substring(it.first + 1, it.last + 1))
}
})
}
}
content.text = spannableString
itemBinding.likeButton.isEnabled = true itemBinding.likeButton.isEnabled = true

View File

@ -88,7 +88,7 @@ class CreatePostActivity : IsolaattiBaseActivity() {
TabLayoutMediator(binding.tabs, binding.pager) { tab, position -> TabLayoutMediator(binding.tabs, binding.pager) { tab, position ->
when(position) { when(position) {
0 -> tab.setText(R.string.markdown) 0 -> tab.setText(R.string.create_a_new_discussion)
1 -> tab.setText(R.string.preview) 1 -> tab.setText(R.string.preview)
} }
}.attach() }.attach()

View File

@ -2,7 +2,6 @@ package com.isolaatti.posting.posts.ui
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
class CreatePostFragmentStateAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) { class CreatePostFragmentStateAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
@ -12,7 +11,7 @@ class CreatePostFragmentStateAdapter(fragmentActivity: FragmentActivity) : Fragm
override fun createFragment(position: Int): Fragment { override fun createFragment(position: Int): Fragment {
return if(position == 0) { return if(position == 0) {
MarkdownEditingFragment() PostEditingFragment()
} else { } else {
MarkdownPreviewFragment() MarkdownPreviewFragment()
} }

View File

@ -15,7 +15,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import com.isolaatti.R import com.isolaatti.R
import com.isolaatti.audio.audio_selector.ui.AudioSelectorActivity
import com.isolaatti.audio.audio_selector.ui.AudioSelectorContract import com.isolaatti.audio.audio_selector.ui.AudioSelectorContract
import com.isolaatti.audio.common.domain.Audio import com.isolaatti.audio.common.domain.Audio
import com.isolaatti.audio.common.domain.Playable import com.isolaatti.audio.common.domain.Playable
@ -28,9 +27,9 @@ import com.isolaatti.posting.link_creator.presentation.LinkCreatorViewModel
import com.isolaatti.posting.link_creator.ui.LinkCreatorFragment import com.isolaatti.posting.link_creator.ui.LinkCreatorFragment
import com.isolaatti.posting.posts.presentation.CreatePostViewModel import com.isolaatti.posting.posts.presentation.CreatePostViewModel
class MarkdownEditingFragment : Fragment(){ class PostEditingFragment : Fragment(){
companion object { companion object {
const val LOG_TAG = "MarkdownEditingFragment" const val LOG_TAG = "PostEditingFragment"
} }
@ -48,7 +47,7 @@ class MarkdownEditingFragment : Fragment(){
} }
private val imageChooserLauncher = registerForActivityResult(ImageChooserContract()) { image -> private val imageChooserLauncher = registerForActivityResult(ImageChooserContract()) { image ->
Log.d("MarkdownEditingFragment", "${image?.markdown}") Log.d(LOG_TAG, "${image?.markdown}")
if(image != null) { if(image != null) {
viewModel.content += "\n\n ${image.markdown}" viewModel.content += "\n\n ${image.markdown}"

View File

@ -27,6 +27,7 @@ import com.isolaatti.common.options_bottom_sheet.domain.Options
import com.isolaatti.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel import com.isolaatti.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
import com.isolaatti.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment import com.isolaatti.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
import com.isolaatti.databinding.FragmentPostListingBinding import com.isolaatti.databinding.FragmentPostListingBinding
import com.isolaatti.hashtags.ui.HashtagsPostsActivity
import com.isolaatti.home.ui.FeedFragment import com.isolaatti.home.ui.FeedFragment
import com.isolaatti.posting.comments.ui.BottomSheetPostComments import com.isolaatti.posting.comments.ui.BottomSheetPostComments
import com.isolaatti.posting.posts.domain.entity.Post import com.isolaatti.posting.posts.domain.entity.Post
@ -124,7 +125,7 @@ class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
.usePlugin(CoilImagesPlugin.create(requireContext(), CoilImageLoader.imageLoader)) .usePlugin(CoilImagesPlugin.create(requireContext(), CoilImageLoader.imageLoader))
.usePlugin(LinkifyPlugin.create()) .usePlugin(LinkifyPlugin.create())
.build() .build()
adapter = PostsRecyclerViewAdapter(markwon, this) adapter = PostsRecyclerViewAdapter(this)
viewBinding.feedRecyclerView.adapter = adapter viewBinding.feedRecyclerView.adapter = adapter
viewBinding.feedRecyclerView.setItemViewCacheSize(7) viewBinding.feedRecyclerView.setItemViewCacheSize(7)
viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext()) viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext())
@ -192,6 +193,10 @@ class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
startActivity(intent) startActivity(intent)
} }
override fun hashtagClicked(hashtag: String) {
HashtagsPostsActivity.startActivity(requireContext(), hashtag)
}
override fun onProfileClick(userId: Int) { override fun onProfileClick(userId: Int) {
ProfileActivity.startActivity(requireContext(), userId) ProfileActivity.startActivity(requireContext(), userId)
} }

View File

@ -75,7 +75,7 @@ class PostViewerActivity : IsolaattiBaseActivity() {
} }
viewModel.post.observe(this) { viewModel.post.observe(this) {
markwon.setMarkdown(binding.markwonContainer, it.textContent) binding.markwonContainer.text = it.textContent
binding.author.text = it.userName binding.author.text = it.userName
binding.profileImageView.load(UrlGen.userProfileImage(it.userId), imageLoader) binding.profileImageView.load(UrlGen.userProfileImage(it.userId), imageLoader)
binding.commentsInfo.text = getString(R.string.comments_info, it.numberOfComments) binding.commentsInfo.text = getString(R.string.comments_info, it.numberOfComments)

View File

@ -4,6 +4,7 @@ import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.core.app.TaskStackBuilder import androidx.core.app.TaskStackBuilder
import com.isolaatti.common.IsolaattiBaseActivity import com.isolaatti.common.IsolaattiBaseActivity
import com.isolaatti.databinding.ActivityProfileBinding import com.isolaatti.databinding.ActivityProfileBinding
@ -15,6 +16,7 @@ class ProfileActivity : IsolaattiBaseActivity() {
lateinit var viewBinding: ActivityProfileBinding lateinit var viewBinding: ActivityProfileBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewBinding = ActivityProfileBinding.inflate(layoutInflater) viewBinding = ActivityProfileBinding.inflate(layoutInflater)
setContentView(viewBinding.root) setContentView(viewBinding.root)

View File

@ -37,6 +37,7 @@ import com.isolaatti.common.options_bottom_sheet.presentation.BottomSheetPostOpt
import com.isolaatti.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment import com.isolaatti.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
import com.isolaatti.databinding.FragmentDiscussionsBinding import com.isolaatti.databinding.FragmentDiscussionsBinding
import com.isolaatti.followers.domain.FollowingState import com.isolaatti.followers.domain.FollowingState
import com.isolaatti.hashtags.ui.HashtagsPostsActivity
import com.isolaatti.images.common.domain.entity.Image import com.isolaatti.images.common.domain.entity.Image
import com.isolaatti.images.image_chooser.ui.ImageChooserContract import com.isolaatti.images.image_chooser.ui.ImageChooserContract
import com.isolaatti.images.image_list.ui.ImagesFragment import com.isolaatti.images.image_list.ui.ImagesFragment
@ -378,16 +379,6 @@ class ProfileMainFragment : Fragment() {
} }
} }
viewBinding.audiosButton.setOnClickListener {
findNavController().navigate(ProfileMainFragmentDirections.actionDiscussionsFragmentToAudiosFragment(AudiosFragment.SOURCE_PROFILE, userId.toString()))
}
viewBinding.imagesButton.setOnClickListener {
findNavController().navigate(
ProfileMainFragmentDirections.actionDiscussionsFragmentToImagesFragment(ImagesFragment.SOURCE_PROFILE, userId.toString())
)
}
viewBinding.createPostButton.setOnClickListener { viewBinding.createPostButton.setOnClickListener {
createDiscussion.launch(Unit) createDiscussion.launch(Unit)
} }
@ -456,7 +447,7 @@ class ProfileMainFragment : Fragment() {
.usePlugin(LinkifyPlugin.create()) .usePlugin(LinkifyPlugin.create())
.build() .build()
postsAdapter = PostsRecyclerViewAdapter(markwon,postListingRecyclerViewAdapterWiring ) postsAdapter = PostsRecyclerViewAdapter(postListingRecyclerViewAdapterWiring )
} }
@ -528,6 +519,10 @@ class ProfileMainFragment : Fragment() {
}, getString(R.string.share_post)) }, getString(R.string.share_post))
startActivity(intent) startActivity(intent)
} }
override fun hashtagClicked(hashtag: String) {
HashtagsPostsActivity.startActivity(requireContext(), hashtag)
}
} }
} }

View File

@ -1,6 +1,6 @@
package com.isolaatti.search.ui package com.isolaatti.search.ui
import android.content.Intent import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
@ -10,7 +10,6 @@ import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.carousel.CarouselLayoutManager import com.google.android.material.carousel.CarouselLayoutManager
@ -18,12 +17,11 @@ import com.google.android.material.carousel.UncontainedCarouselStrategy
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import com.isolaatti.R import com.isolaatti.R
import com.isolaatti.databinding.FragmentSearchBinding import com.isolaatti.databinding.FragmentSearchBinding
import com.isolaatti.hashtags.ui.HashtagsPostsActivity
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
import com.isolaatti.profile.domain.entity.ProfileListItem import com.isolaatti.profile.domain.entity.ProfileListItem
import com.isolaatti.profile.profile_listing.ui.ProfileListingFragment
import com.isolaatti.profile.ui.ProfileActivity import com.isolaatti.profile.ui.ProfileActivity
import com.isolaatti.search.data.HashtagsDto import com.isolaatti.search.data.HashtagsDto
import com.isolaatti.search.data.NewestUsersDto
import com.isolaatti.search.data.SearchDto import com.isolaatti.search.data.SearchDto
import com.isolaatti.search.data.SearchHistoryEntity import com.isolaatti.search.data.SearchHistoryEntity
import com.isolaatti.search.data.SearchResultType import com.isolaatti.search.data.SearchResultType
@ -63,7 +61,7 @@ class SearchFragment : Fragment() {
viewBinding.chipGroup.addView(Chip(requireContext()).apply { viewBinding.chipGroup.addView(Chip(requireContext()).apply {
text = "#$hashtag" text = "#$hashtag"
setOnClickListener { setOnClickListener {
findNavController().navigate(SearchFragmentDirections.actionSearchFragmentToHashtagPostsFragment(hashtag)) HashtagsPostsActivity.startActivity(requireContext(), hashtag)
} }
}) })
} }
@ -100,6 +98,7 @@ class SearchFragment : Fragment() {
viewBinding.searchBar.menu.findItem(R.id.close_button)?.setVisible(false) viewBinding.searchBar.menu.findItem(R.id.close_button)?.setVisible(false)
} }
} }
@SuppressLint("RestrictedApi")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupListeners() setupListeners()
@ -141,7 +140,7 @@ class SearchFragment : Fragment() {
it.resourceId.toLongOrNull()?.also { PostViewerActivity.startActivity(requireContext(), it) } it.resourceId.toLongOrNull()?.also { PostViewerActivity.startActivity(requireContext(), it) }
} }
SearchResultType.Hashtag -> { SearchResultType.Hashtag -> {
findNavController().navigate(SearchFragmentDirections.actionSearchFragmentToHashtagPostsFragment(it.resourceId)) HashtagsPostsActivity.startActivity(requireContext(), it.resourceId)
} }
SearchResultType.Unknown -> {} SearchResultType.Unknown -> {}
} }

View File

@ -26,28 +26,6 @@
android:background="@color/translucent_purple" android:background="@color/translucent_purple"
android:translationZ="2dp"> android:translationZ="2dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textImageName"
style="?attr/textInputFilledDenseStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="32dp"
android:layout_marginTop="16dp"
android:hint="@string/picture_name"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/upload_photo_fab"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/upload_photo_fab" android:id="@+id/upload_photo_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -56,7 +34,9 @@
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginBottom="32dp" android:layout_marginBottom="32dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"/>
<ProgressBar <ProgressBar
android:id="@+id/progress_bar_loading" android:id="@+id/progress_bar_loading"

View File

@ -63,7 +63,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"/> android:layout_marginBottom="8dp"
android:linksClickable="true"/>
</LinearLayout> </LinearLayout>

View File

@ -10,7 +10,8 @@
android:id="@+id/topAppBar_layout" android:id="@+id/topAppBar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:liftOnScrollColor="@color/on_surface"> app:liftOnScrollColor="@color/on_surface"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout <com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout" android:id="@+id/collapsing_toolbar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -156,23 +157,7 @@
android:text="@string/new_post" android:text="@string/new_post"
app:icon="@drawable/baseline_post_add_24"/> app:icon="@drawable/baseline_post_add_24"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/images_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_marginStart="16dp"
android:text="@string/images"
app:icon="@drawable/baseline_image_24"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/audios_button"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text="@string/audios"
app:icon="@drawable/baseline_audio_file_24" />
</LinearLayout> </LinearLayout>
</HorizontalScrollView> </HorizontalScrollView>
@ -194,7 +179,8 @@
android:id="@+id/swipe_to_refresh" android:id="@+id/swipe_to_refresh"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
android:clipToPadding="false">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/feed_recycler_view" android:id="@+id/feed_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -19,7 +19,8 @@
android:id="@+id/topAppBar_layout" android:id="@+id/topAppBar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:liftOnScroll="true"> app:liftOnScroll="true"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/topAppBar" android:id="@+id/topAppBar"

View File

@ -6,7 +6,8 @@
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout" android:id="@+id/appBarLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -20,7 +20,7 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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"/> android:hint="@string/what_do_you_want_to_talk_about_you_can_record_an_audio_if_you_want"/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<LinearLayout <LinearLayout

View File

@ -5,7 +5,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/topAppBar" android:id="@+id/topAppBar"

View File

@ -113,7 +113,8 @@
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.search.SearchBar <com.google.android.material.search.SearchBar
android:id="@+id/search_bar" android:id="@+id/search_bar"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"> android:orientation="vertical">
@ -11,7 +12,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="?attr/materialCardViewFilledStyle" style="?attr/materialCardViewFilledStyle"
android:layout_margin="8dp"> android:layout_margin="8dp"
>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -39,11 +41,14 @@
android:id="@+id/text_view_username" android:id="@+id/text_view_username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"/> android:textStyle="bold"
tools:text="Erik"
android:textSize="16sp"/>
<TextView <TextView
android:id="@+id/text_view_date" android:id="@+id/text_view_date"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
tools:text="Date"/>
</LinearLayout> </LinearLayout>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
@ -67,7 +72,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp" /> android:layout_marginTop="8dp"
android:linksClickable="true"
android:textSize="16sp"
tools:text="Hola"
android:fontFamily="sans-serif"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -34,9 +34,6 @@
<action <action
android:id="@+id/action_searchFragment_to_hashtagsFragment" android:id="@+id/action_searchFragment_to_hashtagsFragment"
app:destination="@id/hashtagsFragment" /> app:destination="@id/hashtagsFragment" />
<action
android:id="@+id/action_searchFragment_to_hashtagPostsFragment"
app:destination="@id/hashtagPostsFragment2" />
<action <action
android:id="@+id/action_searchFragment_to_browseProfilesFragment" android:id="@+id/action_searchFragment_to_browseProfilesFragment"
app:destination="@id/browseProfilesFragment" /> app:destination="@id/browseProfilesFragment" />
@ -44,19 +41,7 @@
<fragment <fragment
android:id="@+id/hashtagsFragment" android:id="@+id/hashtagsFragment"
android:name="com.isolaatti.hashtags.ui.HashtagsFragment" android:name="com.isolaatti.hashtags.ui.HashtagsFragment"
android:label="HashtagsFragment" > android:label="HashtagsFragment" />
<action
android:id="@+id/action_hashtagsFragment2_to_hashtagPostsFragment2"
app:destination="@id/hashtagPostsFragment2" />
</fragment>
<fragment
android:id="@+id/hashtagPostsFragment2"
android:name="com.isolaatti.hashtags.ui.HashtagPostsFragment"
android:label="HashtagPostsFragment">
<argument
android:name="hashtag"
app:argType="string" />
</fragment>
<fragment <fragment
android:id="@+id/browseProfilesFragment" android:id="@+id/browseProfilesFragment"
android:name="com.isolaatti.profile.ui.BrowseProfilesFragment" android:name="com.isolaatti.profile.ui.BrowseProfilesFragment"

View File

@ -10,6 +10,6 @@
android:label="MarkdownPreviewFragment" /> android:label="MarkdownPreviewFragment" />
<fragment <fragment
android:id="@+id/markdownEditingFragment" android:id="@+id/markdownEditingFragment"
android:name="com.isolaatti.posting.posts.ui.MarkdownEditingFragment" android:name="com.isolaatti.posting.posts.ui.PostEditingFragment"
android:label="MarkdownEditingFragment" /> android:label="MarkdownEditingFragment" />
</navigation> </navigation>

View File

@ -58,7 +58,7 @@
<string name="there_is_no_more_content_to_show">There is no more content to show</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="post">Post</string>
<string name="add_image">Add image</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="what_do_you_want_to_talk_about_you_can_record_an_audio_if_you_want">What do you want to talk about? You can record an audio if you want.</string>
<string name="posted_successfully">Posted!</string> <string name="posted_successfully">Posted!</string>
<string name="drafts">Drafts</string> <string name="drafts">Drafts</string>
<string name="report_profile">Report profile</string> <string name="report_profile">Report profile</string>

View File

@ -33,4 +33,13 @@
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">false</item>
<item name="android:windowBackground">#D9000000</item> <item name="android:windowBackground">#D9000000</item>
</style> </style>
<style name="Theme.Isolaatti.OSS" parent="Theme.Material3.DayNight">
<item name="android:colorPrimary">@color/purple</item>
<item name="colorSurface">@color/surface</item>
<item name="colorOnSurface">@color/on_surface</item>
<item name="android:statusBarColor">@color/purple</item>
<item name="windowActionModeOverlay">true</item>
<item name="actionModeCloseDrawable">@drawable/baseline_close_24</item>
</style>
</resources> </resources>

View File

@ -10,6 +10,7 @@ buildscript {
def nav_version = "2.6.0" def nav_version = "2.6.0"
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1" classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6'
} }
} }
plugins { plugins {