-
Notifications
You must be signed in to change notification settings - Fork 2
user's favorites #114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
user's favorites #114
Changes from all commits
b265bc6
2065f2c
6f0f685
e577262
63af404
3261b3a
03cd28a
cf102fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,8 @@ import com.esp.localjobs.data.repository.userFirebaseRepository | |
| import com.esp.localjobs.databinding.ItemJobBinding | ||
| import com.esp.localjobs.fragments.JobDetailsFragment | ||
| import com.esp.localjobs.fragments.JobsFragmentDirections | ||
| import com.esp.localjobs.utils.IFavoritesManager | ||
| import com.esp.localjobs.utils.favoritesManager | ||
| import com.xwray.groupie.databinding.BindableItem | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.GlobalScope | ||
|
|
@@ -22,6 +24,10 @@ import kotlinx.coroutines.launch | |
| @InternalCoroutinesApi | ||
| class JobItem(val job: Job) : BindableItem<ItemJobBinding>() { | ||
|
|
||
| companion object { | ||
| private val favManager: IFavoritesManager = favoritesManager | ||
| } | ||
|
|
||
| override fun getId() = job.uid.hashCode().toLong() | ||
|
|
||
| @InternalCoroutinesApi | ||
|
|
@@ -35,6 +41,19 @@ class JobItem(val job: Job) : BindableItem<ItemJobBinding>() { | |
| setAuthor(author) | ||
| } | ||
|
|
||
| GlobalScope.launch(Dispatchers.IO) { | ||
| val favorites = favManager.get() | ||
| if (favorites.contains([email protected])) { | ||
| favToggle.isFavorite = true | ||
| } | ||
| favToggle.setOnFavoriteChangeListener { _, isChecked -> | ||
| if (!isChecked) | ||
| favManager.remove([email protected]) | ||
| else | ||
| favManager.add([email protected]) | ||
| } | ||
| } | ||
|
|
||
| [email protected]()?.let { | ||
| Glide.with(cardView.context).load(it).placeholder(R.drawable.placeholder).into(imageView) | ||
| } ?: Glide.with(cardView.context).load("https://picsum.photos/400").placeholder(R.drawable.placeholder).into( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,36 +8,51 @@ import android.view.MenuInflater | |
| import android.view.View | ||
| import android.view.ViewGroup | ||
| import android.widget.SearchView | ||
| import androidx.core.view.forEach | ||
| import androidx.fragment.app.Fragment | ||
| import androidx.fragment.app.activityViewModels | ||
| import androidx.lifecycle.Observer | ||
| import androidx.navigation.fragment.FragmentNavigatorExtras | ||
| import androidx.navigation.fragment.findNavController | ||
| import androidx.navigation.fragment.navArgs | ||
| import androidx.recyclerview.widget.RecyclerView | ||
| import com.esp.localjobs.R | ||
| import com.esp.localjobs.adapters.JobItem | ||
| import com.esp.localjobs.data.models.Location | ||
| import com.esp.localjobs.data.models.User | ||
| import com.esp.localjobs.data.repository.JobsRepository | ||
| import com.esp.localjobs.fragments.FiltersFragment.Companion.FILTER_FRAGMENT_TAG | ||
| import com.esp.localjobs.fragments.map.LocationPickerFragment | ||
| import com.esp.localjobs.utils.favoritesManager | ||
| import com.esp.localjobs.viewModels.FilterViewModel | ||
| import com.esp.localjobs.viewModels.JobsViewModel | ||
| import com.xwray.groupie.GroupAdapter | ||
| import com.xwray.groupie.ViewHolder | ||
| import kotlinx.android.synthetic.main.fragment_filter_status.* | ||
| import kotlinx.android.synthetic.main.fragment_jobs.* | ||
| import kotlinx.android.synthetic.main.fragment_jobs.view.* | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.InternalCoroutinesApi | ||
| import kotlinx.coroutines.async | ||
| import kotlinx.coroutines.launch | ||
| import kotlin.coroutines.CoroutineContext | ||
|
|
||
| /** | ||
| * Fragment used to display a list of jobs | ||
| * Fragment used to display a list of jobs. If arguments include an User then the fragment | ||
| * shows the user's jobs/proposals | ||
| */ | ||
| @InternalCoroutinesApi | ||
| class JobsFragment : Fragment(), LocationPickerFragment.OnLocationPickedListener { | ||
| class JobsFragment : Fragment(), LocationPickerFragment.OnLocationPickedListener, CoroutineScope { | ||
| private lateinit var mJob: kotlinx.coroutines.Job | ||
| override val coroutineContext: CoroutineContext | ||
| get() = mJob + Dispatchers.Main | ||
|
|
||
| private val jobsViewModel: JobsViewModel by activityViewModels() | ||
| private val filterViewModel: FilterViewModel by activityViewModels() | ||
|
|
||
| private val args: JobsFragmentArgs by navArgs() | ||
|
|
||
| val adapter = GroupAdapter<ViewHolder>() | ||
|
|
||
| override fun onCreateView( | ||
|
|
@@ -56,7 +71,17 @@ class JobsFragment : Fragment(), LocationPickerFragment.OnLocationPickedListener | |
| setupAdapter() | ||
|
|
||
| observeChangesInJobList() | ||
| observeFilters() | ||
|
|
||
| when { | ||
| args.user != null -> setupUserJobsView(args.user as User) | ||
| args.showFavorites -> showFavorites() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mmm e' ok, ma invece di fare cosi', farei una sottoclasse di questa per i preferiti, e aprirei direttamente quella There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. si probabilmente è più pulito |
||
| else -> observeFilters() | ||
| } | ||
| } | ||
|
|
||
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
| mJob = kotlinx.coroutines.Job() | ||
| } | ||
|
|
||
| private fun setupUI(view: View) = with(view) { | ||
|
|
@@ -137,6 +162,11 @@ class JobsFragment : Fragment(), LocationPickerFragment.OnLocationPickedListener | |
| } | ||
|
|
||
| override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { | ||
| // hide menu actions if we are showing some user's jobs/proposals or favorites | ||
| if (args.user != null || args.showFavorites) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Facendo una sottoclasse non dovremmo necessariamente usare questi if (: |
||
| menu.forEach { it.isVisible = false } | ||
| return | ||
| } | ||
| inflater.inflate(R.menu.menu_search, menu) | ||
| val searchView = menu.findItem(R.id.action_search_item).actionView as SearchView | ||
| setupSearchView(searchView) | ||
|
|
@@ -181,6 +211,55 @@ class JobsFragment : Fragment(), LocationPickerFragment.OnLocationPickedListener | |
| } | ||
| } | ||
|
|
||
| private fun setupUserJobsView(user: User) { | ||
| activity?.title = getString(R.string.user_jobs_title, user.displayName) | ||
| val fromJobs = filterViewModel.filteringJobs ?: true | ||
|
|
||
| val toCheck = if (fromJobs) | ||
| R.id.radio_job | ||
| else | ||
| R.id.radio_proposal | ||
|
|
||
| jobs_type_radio_group.check(toCheck) | ||
nicomazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| jobs_type_radio_group.setOnCheckedChangeListener { _, checkedId -> | ||
| if (checkedId == R.id.radio_job) { | ||
| loadJobs(JobsRepository.JobFilter( | ||
| uid = user.uid, | ||
| filteringJobs = true | ||
| )) | ||
| } else { | ||
| loadJobs(JobsRepository.JobFilter( | ||
| uid = user.uid, | ||
| filteringJobs = false | ||
| )) | ||
| } | ||
| } | ||
|
|
||
| fabAdd.visibility = View.GONE | ||
| active_filters.visibility = View.GONE | ||
| jobs_type_radio_group.visibility = View.VISIBLE | ||
|
|
||
| loadJobs(JobsRepository.JobFilter( | ||
| uid = user.uid, | ||
| filteringJobs = fromJobs | ||
| )) | ||
| } | ||
|
|
||
| private fun showFavorites() = launch { | ||
| activity?.title = getString(R.string.favorites_title) | ||
| fabAdd.visibility = View.GONE | ||
| active_filters.visibility = View.GONE | ||
|
|
||
| val deferredJobs = async(Dispatchers.IO) { favoritesManager.get() } | ||
| adapter.clear() | ||
| val jobs = deferredJobs.await() | ||
| if (jobs.isEmpty()) { | ||
| no_jobs_title.text = getString(R.string.empty_favorites_title) | ||
| no_jobs_message.text = "" | ||
| } else | ||
| adapter.update(jobs.map { JobItem(it) }) | ||
| } | ||
|
|
||
| override fun onLocationPicked(location: Location, distance: Int?) { | ||
| Log.d(TAG, "location: $location") | ||
| filterViewModel.setLocation(location) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.esp.localjobs.utils | ||
|
|
||
| import com.esp.localjobs.data.models.Job | ||
|
|
||
| interface IFavoritesManager { | ||
| fun add(job: Job) | ||
| fun remove(job: Job) | ||
| suspend fun get(): Set<Job> | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package com.esp.localjobs.utils | ||
|
|
||
| import android.content.Context | ||
| import android.content.SharedPreferences | ||
| import android.util.Log | ||
| import androidx.core.content.edit | ||
| import com.esp.localjobs.LocalJobsApplication | ||
| import com.esp.localjobs.data.base.BaseRepository | ||
| import com.esp.localjobs.data.models.Job | ||
| import com.esp.localjobs.data.repository.JobsRepository | ||
|
|
||
| object favoritesManager : IFavoritesManager { | ||
| private const val FAV_KEY = "favourites_ids" | ||
|
|
||
| private val loader: BaseRepository<Job> by lazy { JobsRepository() } | ||
| private val sharedPreferences: SharedPreferences by lazy { | ||
| LocalJobsApplication.applicationContext() | ||
| .getSharedPreferences("favorites", Context.MODE_PRIVATE) | ||
| } | ||
|
|
||
| private var favorites: MutableSet<Job>? = null | ||
|
|
||
| override fun add(job: Job) { | ||
| val favKeys = sharedPreferences.getStringSet(FAV_KEY, mutableSetOf<String>()) | ||
| ?: mutableSetOf<String>() | ||
| favKeys.add(job.id) | ||
| Log.d("favorites", "adding: ${job.id}") | ||
| sharedPreferences.edit(commit = true) { | ||
| // stringSet is bugged so i must do this :/ | ||
| remove(FAV_KEY) | ||
| apply() | ||
| putStringSet(FAV_KEY, favKeys) | ||
| apply() | ||
| } | ||
| favorites?.add(job) | ||
| } | ||
|
|
||
| override fun remove(job: Job) { | ||
| val favKeys = sharedPreferences.getStringSet(FAV_KEY, mutableSetOf<String>()) ?: return | ||
| favKeys.remove(job.id) | ||
| Log.d("favorites", "removing: ${job.id}") | ||
| sharedPreferences.edit(commit = true) { | ||
| // stringSet is bugged so i must do this :/ | ||
| remove(FAV_KEY) | ||
| apply() | ||
| putStringSet(FAV_KEY, favKeys) | ||
| apply() | ||
| } | ||
| favorites?.remove(job) | ||
| } | ||
|
|
||
| override suspend fun get(): Set<Job> { | ||
| if (favorites == null) { | ||
| favorites = load() | ||
| } | ||
| return (favorites as MutableSet<Job>).toSet() | ||
| } | ||
|
|
||
| private suspend fun load(): MutableSet<Job> { | ||
| val favKeys = sharedPreferences.getStringSet(FAV_KEY, mutableSetOf<String>()) | ||
| Log.d("favorites", "loading: $favKeys") | ||
| val favList = mutableSetOf<Job>() | ||
| favKeys?.forEach { key -> loader.get(key)?.let { favList.add(it) } } | ||
| return favList | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.