From ce01e6de22422fea40188a9f837b5b21cab109ac Mon Sep 17 00:00:00 2001 From: Levi Bard Date: Sun, 4 Aug 2019 20:25:07 +0200 Subject: [PATCH] Add support for instance property `max_bio_chars` (#1414) * Migrate getInstance from Call to Single * Add support for instance max_bio_chars. Addresses #1329 --- .../keylesspalace/tusky/ComposeActivity.java | 32 +++++------ .../tusky/EditProfileActivity.kt | 13 +++++ .../keylesspalace/tusky/entity/Instance.kt | 3 +- .../tusky/network/MastodonApi.java | 2 +- .../tusky/viewmodel/EditProfileViewModel.kt | 17 ++++++ .../main/res/layout/activity_edit_profile.xml | 1 + .../tusky/ComposeActivityTest.kt | 53 ++++++------------- 7 files changed, 65 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index b49b4c687..2c459fd81 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -313,22 +313,9 @@ public final class ComposeActivity getString(R.string.compose_active_account_description, activeAccount.getFullName())); - mastodonApi.getInstance().enqueue(new Callback() { - @Override - public void onResponse(@NonNull Call call, @NonNull Response response) { - if (response.isSuccessful() && response.body().getMaxTootChars() != null) { - maximumTootCharacters = response.body().getMaxTootChars(); - updateVisibleCharactersLeft(); - cacheInstanceMetadata(activeAccount); - } - } - - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - Log.w(TAG, "error loading instance data", t); - loadCachedInstanceMetadata(activeAccount); - } - }); + mastodonApi.getInstance() + .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) + .subscribe(this::onFetchInstanceSuccess, this::onFetchInstanceFailure); mastodonApi.getCustomEmojis().enqueue(new Callback>() { @Override @@ -1851,6 +1838,19 @@ public final class ComposeActivity (mimeType.startsWith("image/") || mimeType.startsWith("video/") || mimeType.equals("text/plain"))); } + private void onFetchInstanceSuccess(Instance instance) { + if (instance != null && instance.getMaxTootChars() != null) { + maximumTootCharacters = instance.getMaxTootChars(); + updateVisibleCharactersLeft(); + cacheInstanceMetadata(accountManager.getActiveAccount()); + } + } + + private void onFetchInstanceFailure(Throwable throwable) { + Log.w(TAG, "error loading instance data", throwable); + loadCachedInstanceMetadata(accountManager.getActiveAccount()); + } + public static final class QueuedMedia { Type type; ProgressImageView preview; diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 60537d89d..06f9c9b51 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -41,6 +41,7 @@ import com.keylesspalace.tusky.adapter.AccountFieldEditAdapter import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.entity.Account +import com.keylesspalace.tusky.entity.Instance import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.viewmodel.EditProfileViewModel import com.mikepenz.google_material_typeface_library.GoogleMaterial @@ -164,6 +165,18 @@ class EditProfileActivity : BaseActivity(), Injectable { } }) + viewModel.obtainInstance() + viewModel.instanceData.observe(this, Observer> { result -> + when (result) { + is Success -> { + val instance = result.data + if (instance?.maxBioChars != null && instance.maxBioChars > 0) { + noteEditTextLayout.counterMaxLength = instance.maxBioChars + } + } + } + }) + observeImage(viewModel.avatarData, avatarPreview, avatarProgressBar, true) observeImage(viewModel.headerData, headerPreview, headerProgressBar, false) diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt index 959af1fad..ef0933f1d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt @@ -28,7 +28,8 @@ data class Instance ( val thumbnail: String?, val languages: List, @SerializedName("contact_account") val contactAccount: Account, - @SerializedName("max_toot_chars") val maxTootChars: Int? + @SerializedName("max_toot_chars") val maxTootChars: Int?, + @SerializedName("max_bio_chars") val maxBioChars: Int? ) { override fun hashCode(): Int { return uri.hashCode() diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java index 71606d3aa..cb30099ad 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java @@ -358,7 +358,7 @@ public interface MastodonApi { Call> getCustomEmojis(); @GET("api/v1/instance") - Call getInstance(); + Single getInstance(); @GET("/api/v1/conversations") Call> getConversations(@Nullable @Query("max_id") String maxId, @Query("limit") int limit); diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt index d9c9274b2..54078e477 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt @@ -27,6 +27,7 @@ import com.keylesspalace.tusky.EditProfileActivity.Companion.HEADER_WIDTH import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.ProfileEditedEvent import com.keylesspalace.tusky.entity.Account +import com.keylesspalace.tusky.entity.Instance import com.keylesspalace.tusky.entity.StringField import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.* @@ -64,6 +65,7 @@ class EditProfileViewModel @Inject constructor( val avatarData = MutableLiveData>() val headerData = MutableLiveData>() val saveData = MutableLiveData>() + val instanceData = MutableLiveData>() private var oldProfileData: Account? = null @@ -267,5 +269,20 @@ class EditProfileViewModel @Inject constructor( disposeables.dispose() } + fun obtainInstance() { + if(instanceData.value == null || instanceData.value is Error) { + instanceData.postValue(Loading()) + + mastodonApi.instance.subscribe( + {instance -> + instanceData.postValue(Success(instance)) + }, + { + instanceData.postValue(Error()) + }) + .addTo(disposeables) + } + } + } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_edit_profile.xml b/app/src/main/res/layout/activity_edit_profile.xml index 50617b61b..36ec77b95 100644 --- a/app/src/main/res/layout/activity_edit_profile.xml +++ b/app/src/main/res/layout/activity_edit_profile.xml @@ -111,6 +111,7 @@ ?, Callback?)->Unit)? = null + var instanceResponseCallback: (()->Instance)? = null @Before fun setupActivity() { @@ -109,28 +111,14 @@ class ComposeActivityTest { override fun enqueue(callback: Callback>?) {} }) - `when`(apiMock.instance).thenReturn(object: Call { - override fun isExecuted(): Boolean { - return false - } - override fun clone(): Call { - throw Error("not implemented") - } - override fun isCanceled(): Boolean { - throw Error("not implemented") - } - override fun cancel() { - throw Error("not implemented") - } - override fun execute(): Response { - throw Error("not implemented") - } - override fun request(): Request { - throw Error("not implemented") - } - - override fun enqueue(callback: Callback?) { - instanceResponseCallback?.invoke(this, callback) + `when`(apiMock.instance).thenReturn(object: Single() { + override fun subscribeActual(observer: SingleObserver) { + val instance = instanceResponseCallback?.invoke() + if (instance == null) { + observer.onError(Throwable()) + } else { + observer.onSuccess(instance) + } } }) @@ -181,7 +169,7 @@ class ComposeActivityTest { @Test fun whenMaximumTootCharsIsNull_defaultLimitIsUsed() { - instanceResponseCallback = getSuccessResponseCallbackWithMaximumTootCharacters(null) + instanceResponseCallback = { getInstanceWithMaximumTootCharacters(null) } setupActivity() assertEquals(ComposeActivity.STATUS_CHARACTER_LIMIT, activity.maximumTootCharacters) } @@ -189,23 +177,11 @@ class ComposeActivityTest { @Test fun whenMaximumTootCharsIsPopulated_customLimitIsUsed() { val customMaximum = 1000 - instanceResponseCallback = getSuccessResponseCallbackWithMaximumTootCharacters(customMaximum) + instanceResponseCallback = { getInstanceWithMaximumTootCharacters(customMaximum) } setupActivity() assertEquals(customMaximum, activity.maximumTootCharacters) } - @Test - fun whenInitialInstanceRequestFails_defaultValueIsUsed() { - instanceResponseCallback = { - call: Call?, callback: Callback? -> - if (call != null) { - callback?.onResponse(call, Response.error(400, ResponseBody.create(null, ""))) - } - } - setupActivity() - assertEquals(ComposeActivity.STATUS_CHARACTER_LIMIT, activity.maximumTootCharacters) - } - @Test fun whenTextContainsNoUrl_everyCharacterIsCounted() { val content = "This is test content please ignore thx " @@ -281,7 +257,8 @@ class ComposeActivityTest { emptyList(), emptyList() ), - maximumTootCharacters + maximumTootCharacters, + null ) }