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.
+