diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 028b7aa4..0f77db16 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -137,7 +137,7 @@ android:name=".components.report.ReportActivity" android:windowSoftInputMode="stateAlwaysHidden|adjustResize" /> - + (R.id.toolbar) - - setSupportActionBar(toolbar) - val bar = supportActionBar - if (bar != null) { - bar.title = getString(R.string.title_scheduled_toot) - bar.setDisplayHomeAsUpEnabled(true) - bar.setDisplayShowHomeEnabled(true) - } - - swipe_refresh_layout.setOnRefreshListener(this::refreshStatuses) - - scheduled_toot_list.setHasFixedSize(true) - val layoutManager = LinearLayoutManager(this) - scheduled_toot_list.layoutManager = layoutManager - val divider = DividerItemDecoration(this, layoutManager.orientation) - scheduled_toot_list.addItemDecoration(divider) - adapter = ScheduledTootAdapter(this) - scheduled_toot_list.adapter = adapter - - loadStatuses() - - eventHub.events - .observeOn(AndroidSchedulers.mainThread()) - .`as`(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) - .subscribe { event -> - if (event is StatusScheduledEvent) { - refreshStatuses() - } - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> { - onBackPressed() - return true - } - } - return super.onOptionsItemSelected(item) - } - - fun loadStatuses() { - progress_bar.visibility = View.VISIBLE - mastodonApi.scheduledStatuses() - .enqueue(object : Callback> { - override fun onResponse(call: Call>, response: Response>) { - progress_bar.visibility = View.GONE - if (response.body().isNullOrEmpty()) { - errorMessageView.show() - errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, - null) - } else { - show(response.body()!!) - } - } - - override fun onFailure(call: Call>, t: Throwable) { - progress_bar.visibility = View.GONE - errorMessageView.show() - errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) { - errorMessageView.hide() - loadStatuses() - } - } - }) - } - - private fun refreshStatuses() { - swipe_refresh_layout.isRefreshing = true - mastodonApi.scheduledStatuses() - .enqueue(object : Callback> { - override fun onResponse(call: Call>, response: Response>) { - swipe_refresh_layout.isRefreshing = false - if (response.body().isNullOrEmpty()) { - errorMessageView.show() - errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, - null) - } else { - show(response.body()!!) - } - } - - override fun onFailure(call: Call>, t: Throwable) { - swipe_refresh_layout.isRefreshing = false - } - }) - } - - fun show(statuses: List) { - adapter.setItems(statuses) - adapter.notifyDataSetChanged() - } - - override fun edit(position: Int, item: ScheduledStatus?) { - if (item == null) { - return - } - val intent = ComposeActivity.startIntent(this, ComposeActivity.ComposeOptions( - tootText = item.params.text, - contentWarning = item.params.spoilerText, - mediaAttachments = item.mediaAttachments, - inReplyToId = item.params.inReplyToId, - visibility = item.params.visibility, - scheduledAt = item.scheduledAt, - sensitive = item.params.sensitive - )) - startActivity(intent) - delete(position, item) - } - - override fun delete(position: Int, item: ScheduledStatus?) { - if (item == null) { - return - } - mastodonApi.deleteScheduledStatus(item.id) - .enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - adapter.removeItem(position) - } - - override fun onFailure(call: Call, t: Throwable) { - - } - }) - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ScheduledTootAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/ScheduledTootAdapter.java deleted file mode 100644 index 6698dd37..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/ScheduledTootAdapter.java +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2019 kyori19 - * - * This file is a part of Tusky. - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 3 of the - * License, or (at your option) any later version. - * - * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. - * - * You should have received a copy of the GNU General Public License along with Tusky; if not, - * see . */ - -package com.keylesspalace.tusky.adapter; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -import com.keylesspalace.tusky.R; -import com.keylesspalace.tusky.entity.ScheduledStatus; - -import java.util.ArrayList; -import java.util.List; - -public class ScheduledTootAdapter extends RecyclerView.Adapter { - private List list; - private ScheduledTootAction handler; - - public ScheduledTootAdapter(Context context) { - super(); - list = new ArrayList<>(); - handler = (ScheduledTootAction) context; - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_scheduled_toot, parent, false); - return new TootViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { - TootViewHolder holder = (TootViewHolder) viewHolder; - holder.bind(getItem(position)); - } - - @Override - public int getItemCount() { - return list.size(); - } - - public void setItems(List newToot) { - list = new ArrayList<>(); - list.addAll(newToot); - } - - @Nullable - public ScheduledStatus removeItem(int position) { - if (position < 0 || position >= list.size()) { - return null; - } - ScheduledStatus toot = list.remove(position); - notifyItemRemoved(position); - return toot; - } - - private ScheduledStatus getItem(int position) { - if (position >= 0 && position < list.size()) { - return list.get(position); - } - return null; - } - - public interface ScheduledTootAction { - void edit(int position, ScheduledStatus item); - - void delete(int position, ScheduledStatus item); - } - - private class TootViewHolder extends RecyclerView.ViewHolder { - View view; - TextView text; - ImageButton edit; - ImageButton delete; - - TootViewHolder(View view) { - super(view); - this.view = view; - this.text = view.findViewById(R.id.text); - this.edit = view.findViewById(R.id.edit); - this.delete = view.findViewById(R.id.delete); - } - - void bind(final ScheduledStatus item) { - edit.setEnabled(true); - delete.setEnabled(true); - - if (item != null) { - text.setText(item.getParams().getText()); - - edit.setOnClickListener(v -> { - v.setEnabled(false); - handler.edit(getAdapterPosition(), item); - }); - - delete.setOnClickListener(v -> { - v.setEnabled(false); - handler.delete(getAdapterPosition(), item); - }); - } - } - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt index 6e200dfa..b4901669 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt @@ -21,7 +21,6 @@ import androidx.core.net.toUri import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModel import com.keylesspalace.tusky.adapter.ComposeAutoCompleteAdapter import com.keylesspalace.tusky.components.compose.ComposeActivity.QueuedMedia import com.keylesspalace.tusky.components.search.SearchType @@ -34,23 +33,11 @@ import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.service.ServiceClient import com.keylesspalace.tusky.service.TootToSend import com.keylesspalace.tusky.util.* -import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable import io.reactivex.rxkotlin.Singles import java.util.* import javax.inject.Inject -open class RxAwareViewModel : ViewModel() { - private val disposables = CompositeDisposable() - - fun Disposable.autoDispose() = disposables.add(this) - - override fun onCleared() { - super.onCleared() - disposables.clear() - } -} - /** * Throw when trying to add an image when video is already present or the other way around */ diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootActivity.kt new file mode 100644 index 00000000..17682ad3 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootActivity.kt @@ -0,0 +1,150 @@ +/* Copyright 2019 Tusky Contributors + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.components.scheduled + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.MenuItem +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import com.keylesspalace.tusky.BaseActivity +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.components.compose.ComposeActivity +import com.keylesspalace.tusky.di.Injectable +import com.keylesspalace.tusky.di.ViewModelFactory +import com.keylesspalace.tusky.entity.ScheduledStatus +import com.keylesspalace.tusky.util.Status +import com.keylesspalace.tusky.util.ThemeUtils +import com.keylesspalace.tusky.util.hide +import com.keylesspalace.tusky.util.show +import kotlinx.android.synthetic.main.activity_scheduled_toot.* +import kotlinx.android.synthetic.main.toolbar_basic.* +import javax.inject.Inject + +class ScheduledTootActivity : BaseActivity(), ScheduledTootActionListener, Injectable { + + @Inject + lateinit var viewModelFactory: ViewModelFactory + + lateinit var viewModel: ScheduledTootViewModel + + private val adapter = ScheduledTootAdapter(this) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_scheduled_toot) + + setSupportActionBar(toolbar) + supportActionBar?.run { + title = getString(R.string.title_scheduled_toot) + setDisplayHomeAsUpEnabled(true) + setDisplayShowHomeEnabled(true) + } + + swipeRefreshLayout.setOnRefreshListener(this::refreshStatuses) + swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue) + swipeRefreshLayout.setProgressBackgroundColorSchemeColor( + ThemeUtils.getColor(this, android.R.attr.colorBackground)) + + scheduledTootList.setHasFixedSize(true) + scheduledTootList.layoutManager = LinearLayoutManager(this) + val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) + scheduledTootList.addItemDecoration(divider) + scheduledTootList.adapter = adapter + + viewModel = ViewModelProvider(this, viewModelFactory)[ScheduledTootViewModel::class.java] + + viewModel.data.observe(this, Observer { + adapter.submitList(it) + }) + + viewModel.networkState.observe(this, Observer { (status) -> + when(status) { + Status.SUCCESS -> { + progressBar.hide() + swipeRefreshLayout.isRefreshing = false + if(viewModel.data.value?.loadedCount == 0) { + errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_scheduled_status) + errorMessageView.show() + } else { + errorMessageView.hide() + } + } + Status.RUNNING -> { + errorMessageView.hide() + if(viewModel.data.value?.loadedCount ?: 0 > 0) { + swipeRefreshLayout.isRefreshing = true + } else { + progressBar.show() + } + } + Status.FAILED -> { + if(viewModel.data.value?.loadedCount ?: 0 >= 0) { + progressBar.hide() + swipeRefreshLayout.isRefreshing = false + errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) { + refreshStatuses() + } + errorMessageView.show() + } + } + } + + }) + + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + onBackPressed() + return true + } + } + return super.onOptionsItemSelected(item) + } + + private fun refreshStatuses() { + viewModel.reload() + } + + override fun edit(item: ScheduledStatus) { + val intent = ComposeActivity.startIntent(this, ComposeActivity.ComposeOptions( + tootText = item.params.text, + contentWarning = item.params.spoilerText, + mediaAttachments = item.mediaAttachments, + inReplyToId = item.params.inReplyToId, + visibility = item.params.visibility, + scheduledAt = item.scheduledAt, + sensitive = item.params.sensitive + )) + startActivity(intent) + } + + override fun delete(item: ScheduledStatus) { + viewModel.deleteScheduledStatus(item) + } + + companion object { + @JvmStatic + fun newIntent(context: Context): Intent { + return Intent(context, ScheduledTootActivity::class.java) + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootAdapter.kt new file mode 100644 index 00000000..ea12d1ff --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootAdapter.kt @@ -0,0 +1,85 @@ +/* Copyright 2019 Tusky Contributors + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.components.scheduled + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.TextView +import androidx.paging.PagedListAdapter +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.entity.ScheduledStatus + +interface ScheduledTootActionListener { + fun edit(item: ScheduledStatus) + fun delete(item: ScheduledStatus) +} + +class ScheduledTootAdapter( + val listener: ScheduledTootActionListener +) : PagedListAdapter( + object: DiffUtil.ItemCallback(){ + override fun areItemsTheSame(oldItem: ScheduledStatus, newItem: ScheduledStatus): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: ScheduledStatus, newItem: ScheduledStatus): Boolean { + return oldItem == newItem + } + + } +) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TootViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_scheduled_toot, parent, false) + return TootViewHolder(view) + } + + override fun onBindViewHolder(viewHolder: TootViewHolder, position: Int) { + getItem(position)?.let{ + viewHolder.bind(it) + } + } + + + inner class TootViewHolder(view: View) : RecyclerView.ViewHolder(view) { + + private val text: TextView = view.findViewById(R.id.text) + private val edit: ImageButton = view.findViewById(R.id.edit) + private val delete: ImageButton = view.findViewById(R.id.delete) + + fun bind(item: ScheduledStatus) { + edit.isEnabled = true + delete.isEnabled = true + text.text = item.params.text + edit.setOnClickListener { v: View -> + v.isEnabled = false + listener.edit(item) + } + delete.setOnClickListener { v: View -> + v.isEnabled = false + listener.delete(item) + } + + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootDataSource.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootDataSource.kt new file mode 100644 index 00000000..6c9ba31b --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootDataSource.kt @@ -0,0 +1,102 @@ +/* Copyright 2019 Tusky Contributors + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.components.scheduled + +import android.util.Log +import androidx.lifecycle.MutableLiveData +import androidx.paging.DataSource +import androidx.paging.ItemKeyedDataSource +import com.keylesspalace.tusky.entity.ScheduledStatus +import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.util.NetworkState +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.addTo + +class ScheduledTootDataSourceFactory( + private val mastodonApi: MastodonApi, + private val disposables: CompositeDisposable +): DataSource.Factory() { + + private val scheduledTootsCache = mutableListOf() + + private var dataSource: ScheduledTootDataSource? = null + + val networkState = MutableLiveData() + + override fun create(): DataSource { + return ScheduledTootDataSource(mastodonApi, disposables, scheduledTootsCache, networkState).also { + dataSource = it + } + } + + fun reload() { + scheduledTootsCache.clear() + dataSource?.invalidate() + } + + fun remove(status: ScheduledStatus) { + scheduledTootsCache.remove(status) + dataSource?.invalidate() + } + +} + + +class ScheduledTootDataSource( + private val mastodonApi: MastodonApi, + private val disposables: CompositeDisposable, + private val scheduledTootsCache: MutableList, + private val networkState: MutableLiveData +): ItemKeyedDataSource() { + override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) { + if(scheduledTootsCache.isNotEmpty()) { + callback.onResult(scheduledTootsCache.toList()) + } else { + networkState.postValue(NetworkState.LOADING) + mastodonApi.scheduledStatuses(limit = params.requestedLoadSize) + .subscribe({ newData -> + scheduledTootsCache.addAll(newData) + callback.onResult(newData) + networkState.postValue(NetworkState.LOADED) + }, { throwable -> + Log.w("ScheduledTootDataSource", "Error loading scheduled statuses", throwable) + networkState.postValue(NetworkState.error(throwable.message)) + }) + .addTo(disposables) + } + } + + override fun loadAfter(params: LoadParams, callback: LoadCallback) { + mastodonApi.scheduledStatuses(limit = params.requestedLoadSize, maxId = params.key) + .subscribe({ newData -> + scheduledTootsCache.addAll(newData) + callback.onResult(newData) + }, { throwable -> + Log.w("ScheduledTootDataSource", "Error loading scheduled statuses", throwable) + networkState.postValue(NetworkState.error(throwable.message)) + }) + .addTo(disposables) + } + + override fun loadBefore(params: LoadParams, callback: LoadCallback) { + // we are always loading from beginning to end + } + + override fun getKey(item: ScheduledStatus): String { + return item.id + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootViewModel.kt new file mode 100644 index 00000000..4d531a45 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootViewModel.kt @@ -0,0 +1,71 @@ +/* Copyright 2019 Tusky Contributors + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.components.scheduled + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.paging.Config +import androidx.paging.toLiveData +import com.keylesspalace.tusky.appstore.EventHub +import com.keylesspalace.tusky.appstore.StatusScheduledEvent +import com.keylesspalace.tusky.entity.ScheduledStatus +import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.util.RxAwareViewModel +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.addTo +import javax.inject.Inject + +class ScheduledTootViewModel @Inject constructor( + val mastodonApi: MastodonApi, + val eventHub: EventHub +): RxAwareViewModel() { + + private val dataSourceFactory = ScheduledTootDataSourceFactory(mastodonApi, disposables) + + val data = dataSourceFactory.toLiveData( + config = Config(pageSize = 20, initialLoadSizeHint = 20, enablePlaceholders = false) + ) + + val networkState = dataSourceFactory.networkState + + init { + eventHub.events + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { event -> + if (event is StatusScheduledEvent) { + reload() + } + } + .autoDispose() + } + + fun reload() { + dataSourceFactory.reload() + } + + fun deleteScheduledStatus(status: ScheduledStatus) { + mastodonApi.deleteScheduledStatus(status.id) + .subscribe({ + dataSourceFactory.remove(status) + },{ throwable -> + Log.w("ScheduledTootViewModel", "Error deleting scheduled status", throwable) + }) + .autoDispose() + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt index e2c4dfc7..1f958e5a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt @@ -19,6 +19,7 @@ import com.keylesspalace.tusky.* import com.keylesspalace.tusky.components.compose.ComposeActivity import com.keylesspalace.tusky.components.instancemute.InstanceListActivity import com.keylesspalace.tusky.components.report.ReportActivity +import com.keylesspalace.tusky.components.scheduled.ScheduledTootActivity import com.keylesspalace.tusky.components.search.SearchActivity import dagger.Module import dagger.android.ContributesAndroidInjector diff --git a/app/src/main/java/com/keylesspalace/tusky/di/ViewModelFactory.kt b/app/src/main/java/com/keylesspalace/tusky/di/ViewModelFactory.kt index 8381d526..f4929463 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/ViewModelFactory.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/ViewModelFactory.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModelProvider import com.keylesspalace.tusky.components.compose.ComposeViewModel import com.keylesspalace.tusky.components.conversation.ConversationsViewModel import com.keylesspalace.tusky.components.report.ReportViewModel +import com.keylesspalace.tusky.components.scheduled.ScheduledTootViewModel import com.keylesspalace.tusky.components.search.SearchViewModel import com.keylesspalace.tusky.viewmodel.AccountViewModel import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel @@ -79,5 +80,10 @@ abstract class ViewModelModule { @ViewModelKey(ComposeViewModel::class) internal abstract fun composeViewModel(viewModel: ComposeViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(ScheduledTootViewModel::class) + internal abstract fun scheduledTootViewModel(viewModel: ScheduledTootViewModel): ViewModel + //Add more ViewModels here } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt index 65d096e8..da61cb6a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt @@ -201,12 +201,15 @@ interface MastodonApi { ): Single @GET("api/v1/scheduled_statuses") - fun scheduledStatuses(): Call> + fun scheduledStatuses( + @Query("limit") limit: Int? = null, + @Query("max_id") maxId: String? = null + ): Single> @DELETE("api/v1/scheduled_statuses/{id}") fun deleteScheduledStatus( @Path("id") scheduledStatusId: String - ): Call + ): Single @GET("api/v1/accounts/verify_credentials") fun accountVerifyCredentials(): Single diff --git a/app/src/main/java/com/keylesspalace/tusky/util/RxAwareViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/util/RxAwareViewModel.kt new file mode 100644 index 00000000..2ad4b825 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/RxAwareViewModel.kt @@ -0,0 +1,16 @@ +package com.keylesspalace.tusky.util + +import androidx.lifecycle.ViewModel +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable + +open class RxAwareViewModel : ViewModel() { + val disposables = CompositeDisposable() + + fun Disposable.autoDispose() = disposables.add(this) + + override fun onCleared() { + super.onCleared() + disposables.clear() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/view/BackgroundMessageView.kt b/app/src/main/java/com/keylesspalace/tusky/view/BackgroundMessageView.kt index 1b73e6f7..e39566f4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/BackgroundMessageView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/BackgroundMessageView.kt @@ -37,7 +37,7 @@ class BackgroundMessageView @JvmOverloads constructor( * If [clickListener] is `null` then the button will be hidden. */ fun setup(@DrawableRes imageRes: Int, @StringRes messageRes: Int, - clickListener: ((v: View) -> Unit)?) { + clickListener: ((v: View) -> Unit)? = null) { messageTextView.setText(messageRes) messageTextView.setCompoundDrawablesWithIntrinsicBounds(0, imageRes, 0, 0) button.setOnClickListener(clickListener) diff --git a/app/src/main/res/layout/activity_scheduled_toot.xml b/app/src/main/res/layout/activity_scheduled_toot.xml index 937c2db3..373a519a 100644 --- a/app/src/main/res/layout/activity_scheduled_toot.xml +++ b/app/src/main/res/layout/activity_scheduled_toot.xml @@ -2,7 +2,7 @@ @@ -15,7 +15,7 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + + + + + - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6d6dab14..3d8efafc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -546,4 +546,6 @@ Edit Error looking up post %s + You don\'t have any scheduled statuses. +