From 8265ac2d621a2585817bb0f230aa5c16dc6d8811 Mon Sep 17 00:00:00 2001 From: Andrew Dobis Date: Thu, 23 Apr 2020 17:48:45 +0200 Subject: [PATCH] Reblogging and HTML text (#107) * Changed Post interaction icons and added click feedback * added reblog and unreblog api implementations * Use fancy animated buttons * WIP reposter * WIP reblog button * renamed ViewHolder => PostViewHolder * activated reblogger in feed * added custom html parser, still need to fix clickable links * added parsed HTML in notifications * fixed mention click * added tests for reblog and clickable mentions * adapted unit tests to work with new html parser * changed incoherent comment * made hashtags slightly less useless * removed unit test that were no longer valid * removed useless test * trying to fix tests * fixing tests * trying to improve coverage a little * removed unused code to improve coverage * changed cast to type converter * added failure responses to help coverage * added mock server response for reblogging * fixed broken json * trying to fix a broken test * Tweak tests * Typo in test * Add scrolls to make tests pass on small screens * fixed old JSON in mockserver * fixed linter issue Co-authored-by: Matthieu <61561059+Wv5twkFEKh54vo4tta9yu7dHa3@users.noreply.github.com> --- app/build.gradle | 1 + .../java/com/h/pixeldroid/IntentTest.kt | 106 ++++- .../com/h/pixeldroid/LoginInstrumentedTest.kt | 33 +- .../java/com/h/pixeldroid/MockedServerTest.kt | 235 +++++------ .../pixeldroid/testUtility/CustomMatchers.kt | 95 +++++ .../h/pixeldroid/testUtility/MockServer.kt | 128 ++++-- .../java/com/h/pixeldroid/api/PixelfedAPI.kt | 20 + .../h/pixeldroid/fragments/PostFragment.kt | 11 +- .../fragments/feeds/HomeFragment.kt | 31 +- .../fragments/feeds/NotificationsFragment.kt | 15 +- .../java/com/h/pixeldroid/objects/Account.kt | 136 ++++--- .../java/com/h/pixeldroid/objects/Mention.kt | 9 +- .../java/com/h/pixeldroid/objects/Status.kt | 89 ++++- .../java/com/h/pixeldroid/utils/HtmlUtils.kt | 126 ++++++ .../java/com/h/pixeldroid/utils/PostUtils.kt | 105 ++++- .../customSpans/ClickableSpanNoUnderline.kt | 11 + app/src/main/res/drawable/ic_comment_blue.xml | 5 + .../main/res/drawable/ic_comment_empty.xml | 9 + app/src/main/res/drawable/ic_comment_full.xml | 9 + app/src/main/res/drawable/ic_like_empty.xml | 6 + app/src/main/res/drawable/ic_like_full.xml | 5 + app/src/main/res/drawable/ic_reblog.xml | 15 + app/src/main/res/drawable/ic_reblog_blue.xml | 15 + app/src/main/res/drawable/ic_send_blue.xml | 5 + app/src/main/res/drawable/ic_share.xml | 4 - app/src/main/res/layout/post_fragment.xml | 365 +++++++++--------- app/src/main/res/values/values.xml | 11 + .../java/com/h/pixeldroid/PostUnitTest.kt | 12 - build.gradle | 4 +- 29 files changed, 1142 insertions(+), 474 deletions(-) create mode 100644 app/src/androidTest/java/com/h/pixeldroid/testUtility/CustomMatchers.kt create mode 100644 app/src/main/java/com/h/pixeldroid/utils/HtmlUtils.kt create mode 100644 app/src/main/java/com/h/pixeldroid/utils/customSpans/ClickableSpanNoUnderline.kt create mode 100644 app/src/main/res/drawable/ic_comment_blue.xml create mode 100644 app/src/main/res/drawable/ic_comment_empty.xml create mode 100644 app/src/main/res/drawable/ic_comment_full.xml create mode 100644 app/src/main/res/drawable/ic_like_empty.xml create mode 100644 app/src/main/res/drawable/ic_like_full.xml create mode 100644 app/src/main/res/drawable/ic_reblog.xml create mode 100644 app/src/main/res/drawable/ic_reblog_blue.xml create mode 100644 app/src/main/res/drawable/ic_send_blue.xml delete mode 100644 app/src/main/res/drawable/ic_share.xml diff --git a/app/build.gradle b/app/build.gradle index 035c762d..2f937641 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -64,6 +64,7 @@ dependencies { implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation "androidx.browser:browser:1.2.0" implementation 'com.google.android.material:material:1.1.0' + implementation 'com.github.connyduck:sparkbutton:3.0.0' def room_version = "2.2.5" implementation "androidx.room:room-runtime:$room_version" diff --git a/app/src/androidTest/java/com/h/pixeldroid/IntentTest.kt b/app/src/androidTest/java/com/h/pixeldroid/IntentTest.kt index 278dc67b..1fe52e37 100644 --- a/app/src/androidTest/java/com/h/pixeldroid/IntentTest.kt +++ b/app/src/androidTest/java/com/h/pixeldroid/IntentTest.kt @@ -2,19 +2,39 @@ package com.h.pixeldroid import android.content.Context import android.content.Intent +import android.text.SpannableString +import android.text.style.ClickableSpan +import android.view.View +import android.widget.TextView import androidx.test.core.app.ActivityScenario import androidx.test.espresso.Espresso +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.NoMatchingViewException +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents.intended +import androidx.test.espresso.intent.matcher.BundleMatchers.hasValue import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.intent.rule.IntentsTestRule import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.rule.ActivityTestRule import com.google.android.material.tabs.TabLayout +import com.h.pixeldroid.fragments.feeds.PostViewHolder +import com.h.pixeldroid.objects.Account +import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_TAG +import com.h.pixeldroid.testUtility.CustomMatchers +import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first import com.h.pixeldroid.testUtility.MockServer import org.hamcrest.CoreMatchers import org.hamcrest.Matcher +import org.hamcrest.Matchers +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -32,7 +52,7 @@ class IntentTest { @get:Rule var mLoginActivityActivityTestRule = - IntentsTestRule( + ActivityTestRule( LoginActivity::class.java ) @@ -44,8 +64,87 @@ class IntentTest { .targetContext.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE) preferences.edit().putString("accessToken", "azerty").apply() preferences.edit().putString("domain", baseUrl.toString()).apply() + Intents.init() } + @Test + fun clickingMentionOpensProfile() { + ActivityScenario.launch(MainActivity::class.java) + + val account = Account("1450", "deerbard_photo", "deerbard_photo", + "https://pixelfed.social/deerbard_photo", "deerbard photography", + "", + "https://pixelfed.social/storage/avatars/000/000/001/450/SMSep5NoabDam1W8UDMh_avatar.png?v=4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a", + "https://pixelfed.social/storage/avatars/000/000/001/450/SMSep5NoabDam1W8UDMh_avatar.png?v=4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a", + "", "", false, emptyList(), false, + "2018-08-01T12:58:21.000000Z", 72, 68, 27, + null, null, false, null) + val expectedIntent: Matcher = CoreMatchers.allOf( + IntentMatchers.hasExtra(ACCOUNT_TAG, account) + ) + + Thread.sleep(1000) + + //Click the mention + Espresso.onView(ViewMatchers.withId(R.id.list)) + .perform(RecyclerViewActions.actionOnItemAtPosition + (0, clickClickableSpanInDescription("@Dobios"))) + + //Wait a bit + Thread.sleep(1000) + + //Check that the Profile is shown + intended(expectedIntent) + } + + fun clickClickableSpanInDescription(textToClick: CharSequence): ViewAction { + return object : ViewAction { + + override fun getConstraints(): Matcher { + return Matchers.instanceOf(TextView::class.java) + } + + override fun getDescription(): String { + return "clicking on a ClickableSpan"; + } + + override fun perform(uiController: UiController, view: View) { + val textView = view.findViewById(R.id.description) as TextView + val spannableString = textView.text as SpannableString + + if (spannableString.isEmpty()) { + // TextView is empty, nothing to do + throw NoMatchingViewException.Builder() + .includeViewHierarchy(true) + .withRootView(textView) + .build(); + } + + // Get the links inside the TextView and check if we find textToClick + val spans = spannableString.getSpans(0, spannableString.length, ClickableSpan::class.java) + if (spans.isNotEmpty()) { + var spanCandidate: ClickableSpan + for (span: ClickableSpan in spans) { + spanCandidate = span + val start = spannableString.getSpanStart(spanCandidate) + val end = spannableString.getSpanEnd(spanCandidate) + val sequence = spannableString.subSequence(start, end) + if (textToClick.toString().equals(sequence.toString())) { + span.onClick(textView) + return; + } + } + } + + // textToClick not found in TextView + throw NoMatchingViewException.Builder() + .includeViewHierarchy(true) + .withRootView(textView) + .build() + + } + } + } @Test fun launchesIntent() { @@ -63,7 +162,10 @@ class IntentTest { Thread.sleep(1000) intended(expectedIntent) + } - + @After + fun after() { + Intents.release() } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/h/pixeldroid/LoginInstrumentedTest.kt b/app/src/androidTest/java/com/h/pixeldroid/LoginInstrumentedTest.kt index 9108df65..5480ee63 100644 --- a/app/src/androidTest/java/com/h/pixeldroid/LoginInstrumentedTest.kt +++ b/app/src/androidTest/java/com/h/pixeldroid/LoginInstrumentedTest.kt @@ -5,10 +5,13 @@ import android.content.Context import android.content.Intent import android.content.Intent.ACTION_VIEW import android.net.Uri +import androidx.test.core.app.ActivityScenario import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction import androidx.test.espresso.intent.matcher.IntentMatchers.hasDataString @@ -24,6 +27,7 @@ import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.anyOf import org.hamcrest.CoreMatchers.containsString import org.hamcrest.Matcher +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -52,14 +56,14 @@ class LoginInstrumentedTest { @Test fun invalidURL() { onView(withId(R.id.editText)).perform(ViewActions.replaceText("/jdi"), ViewActions.closeSoftKeyboard()) - onView(withId(R.id.connect_instance_button)).perform(click()) + onView(withId(R.id.connect_instance_button)).perform(scrollTo()).perform(click()) onView(withId(R.id.editText)).check(matches(hasErrorText("Invalid domain"))) } @Test fun notPixelfedInstance() { onView(withId(R.id.editText)).perform(ViewActions.replaceText("localhost"), ViewActions.closeSoftKeyboard()) - onView(withId(R.id.connect_instance_button)).perform(click()) + onView(withId(R.id.connect_instance_button)).perform(scrollTo()).perform(click()) onView(withId(R.id.editText)).check(matches(hasErrorText("Could not register the application with this server"))) } } @@ -69,37 +73,50 @@ class LoginCheckIntent { @get:Rule var globalTimeout: Timeout = Timeout.seconds(100) @get:Rule - val intentsTestRule = IntentsTestRule(LoginActivity::class.java) + val intentsTestRule = ActivityTestRule(LoginActivity::class.java) + + @Before + fun before() { + Intents.init() + } @Test fun launchesOAuthIntent() { + ActivityScenario.launch(LoginActivity::class.java) val expectedIntent: Matcher = allOf( hasAction(ACTION_VIEW), hasDataString(containsString("pixelfed.social")) ) + Thread.sleep(1000) - onView(withId(R.id.editText)).perform(ViewActions.replaceText("pixelfed.social"), ViewActions.closeSoftKeyboard()) - onView(withId(R.id.connect_instance_button)).perform(click()) + onView(withId(R.id.editText)).perform(scrollTo()).perform(ViewActions.replaceText("pixelfed.social"), ViewActions.closeSoftKeyboard()) + onView(withId(R.id.connect_instance_button)).perform(scrollTo()).perform(click()) - Thread.sleep(5000) + Thread.sleep(3000) intended(expectedIntent) } @Test fun launchesInstanceInfo() { + ActivityScenario.launch(LoginActivity::class.java) val expectedIntent: Matcher = allOf( hasAction(ACTION_VIEW), hasDataString(containsString("pixelfed.org/join")) ) - onView(withId(R.id.whatsAnInstanceTextView)).perform(click()) + onView(withId(R.id.whatsAnInstanceTextView)).perform(scrollTo()).perform(click()) - Thread.sleep(1000) + Thread.sleep(10000) intended(expectedIntent) } + + @After + fun after() { + Intents.release() + } } @RunWith(AndroidJUnit4::class) diff --git a/app/src/androidTest/java/com/h/pixeldroid/MockedServerTest.kt b/app/src/androidTest/java/com/h/pixeldroid/MockedServerTest.kt index 64c3e1c6..fa6aeb9f 100644 --- a/app/src/androidTest/java/com/h/pixeldroid/MockedServerTest.kt +++ b/app/src/androidTest/java/com/h/pixeldroid/MockedServerTest.kt @@ -1,29 +1,23 @@ package com.h.pixeldroid import android.content.Context -import android.view.Gravity -import android.view.View -import android.widget.EditText -import android.widget.TextView import androidx.test.core.app.ActivityScenario import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.UiController -import androidx.test.espresso.ViewAction -import androidx.test.espresso.action.* +import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.contrib.DrawerActions -import androidx.test.espresso.contrib.DrawerMatchers -import androidx.test.espresso.contrib.NavigationViewActions import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition import androidx.test.espresso.matcher.ViewMatchers.* import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.google.android.material.tabs.TabLayout -import com.h.pixeldroid.fragments.feeds.ViewHolder +import com.h.pixeldroid.fragments.feeds.PostViewHolder +import com.h.pixeldroid.testUtility.CustomMatchers.Companion.clickChildViewWithId +import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first +import com.h.pixeldroid.testUtility.CustomMatchers.Companion.getText +import com.h.pixeldroid.testUtility.CustomMatchers.Companion.slowSwipeUp +import com.h.pixeldroid.testUtility.CustomMatchers.Companion.typeTextInViewWithId import com.h.pixeldroid.testUtility.MockServer -import org.hamcrest.BaseMatcher -import org.hamcrest.Matcher import org.junit.Before import org.junit.Rule import org.junit.Test @@ -33,84 +27,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class MockedServerTest { - private fun first(matcher: Matcher): Matcher? { - return object : BaseMatcher() { - var isFirst = true - override fun describeTo(description: org.hamcrest.Description?) { - description?.appendText("first matching item") - } - - override fun matches(item: Any?): Boolean { - if (isFirst && matcher.matches(item)) { - isFirst = false - return true - } - return false - } - - } - } - - /** - * @param percent can be 1 or 0 - * 1: swipes all the way up - * 0: swipes half way up - */ - private fun slowSwipeUp(percent: Boolean) : ViewAction { - return ViewActions.actionWithAssertions( - GeneralSwipeAction( - Swipe.SLOW, - GeneralLocation.BOTTOM_CENTER, - if(percent) GeneralLocation.TOP_CENTER else GeneralLocation.CENTER, - Press.FINGER) - ) - } - - fun getText(matcher: Matcher?): String? { - val stringHolder = arrayOf(null) - onView(matcher).perform(object : ViewAction { - override fun getConstraints(): Matcher { - return isAssignableFrom(TextView::class.java) - } - - override fun getDescription(): String { - return "getting text from a TextView" - } - - override fun perform( - uiController: UiController, - view: View - ) { - val tv = view as TextView //Save, because of check in getConstraints() - stringHolder[0] = tv.text.toString() - } - }) - return stringHolder[0] - } - - private fun clickChildViewWithId(id: Int) = object : ViewAction { - - override fun getConstraints() = null - - override fun getDescription() = "click child view with id $id" - - override fun perform(uiController: UiController, view: View) { - val v = view.findViewById(id) - v.performClick() - } - } - - private fun typeTextInViewWithId(id: Int, text: String) = object : ViewAction { - - override fun getConstraints() = null - - override fun getDescription() = "click child view with id $id" - - override fun perform(uiController: UiController, view: View) { - val v = view.findViewById(id) - v.text.append(text) - } - } private val mockServer = MockServer() @@ -234,22 +150,43 @@ class MockedServerTest { Thread.sleep(1000) //Get initial like count - val likes = getText(withId(R.id.nlikes)) + val likes = getText(first(withId(R.id.nlikes))) //Like the post onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, clickChildViewWithId(R.id.liker))) Thread.sleep(100) //Unlike the post onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, clickChildViewWithId(R.id.liker))) //... Thread.sleep(100) //Profit - onView(withId(R.id.nlikes)).check(matches((withText(likes)))) + onView(first(withId(R.id.nlikes))).check(matches((withText(likes)))) + } + + @Test + fun clickingLikeButtonFails() { + ActivityScenario.launch(MainActivity::class.java) + Thread.sleep(1000) + + //Get initial like count + val likes = getText(first(withId(R.id.nlikes))) + + //Like the post + onView(withId(R.id.list)) + .perform(actionOnItemAtPosition + (2, clickChildViewWithId(R.id.liker))) + Thread.sleep(100) + + //... + Thread.sleep(100) + + //Profit + onView((withId(R.id.list))).check(matches(isDisplayed())) } @Test @@ -259,7 +196,7 @@ class MockedServerTest { //Get initial like count onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, clickChildViewWithId(R.id.username))) Thread.sleep(1000) @@ -275,7 +212,7 @@ class MockedServerTest { //Get initial like count onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, clickChildViewWithId(R.id.profilePic))) Thread.sleep(1000) @@ -284,16 +221,86 @@ class MockedServerTest { onView(withId(R.id.accountNameTextView)).check(matches(isDisplayed())) } + @Test + fun clickingReblogButtonWorks() { + ActivityScenario.launch(MainActivity::class.java) + Thread.sleep(1000) + + //Get initial like count + val shares = getText(first(withId(R.id.nshares))) + + //Reblog the post + onView(withId(R.id.list)) + .perform(actionOnItemAtPosition + (0, clickChildViewWithId(R.id.reblogger))) + Thread.sleep(100) + + //UnReblog the post + onView(withId(R.id.list)) + .perform(actionOnItemAtPosition + (0, clickChildViewWithId(R.id.reblogger))) + //... + Thread.sleep(100) + + //Profit + onView(first(withId(R.id.nshares))).check(matches((withText(shares)))) + } + + @Test + fun clickingMentionOpensProfile() { + ActivityScenario.launch(MainActivity::class.java) + Thread.sleep(1000) + + //Click the mention + onView(withId(R.id.list)) + .perform(actionOnItemAtPosition + (0, clickChildViewWithId(R.id.description))) + + //Wait a bit + Thread.sleep(1000) + + //Check that the Profile is shown + onView(first(withId(R.id.username))).check(matches(isDisplayed())) + } + + @Test + fun clickingHashTagsWorks() { + ActivityScenario.launch(MainActivity::class.java) + Thread.sleep(1000) + + //Click the hashtag + onView(withId(R.id.list)) + .perform(actionOnItemAtPosition + (1, clickChildViewWithId(R.id.description))) + + //Wait a bit + Thread.sleep(1000) + + //Check that the HashTag was indeed clicked + //Doesn't do anything for now + onView(withId(R.id.list)).check(matches(isDisplayed())) + } + + @Test fun clickingCommentButtonOpensCommentSection() { ActivityScenario.launch(MainActivity::class.java) Thread.sleep(1000) - //Click comment button and then try to see if the commenter exists + + //Click comment button 3 times and then try to see if the commenter exists onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, clickChildViewWithId(R.id.commenter))) - Thread.sleep(1000) - onView(withId(R.id.commentIn)) + Thread.sleep(100) + onView(withId(R.id.list)) + .perform(actionOnItemAtPosition + (0, clickChildViewWithId(R.id.commenter))) + Thread.sleep(100) + onView(withId(R.id.list)) + .perform(actionOnItemAtPosition + (0, clickChildViewWithId(R.id.commenter))) + + onView(first(withId(R.id.commentIn))) .check(matches(hasDescendant(withId(R.id.editComment)))) } @@ -303,13 +310,25 @@ class MockedServerTest { Thread.sleep(1000) //Open the comment section onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, clickChildViewWithId(R.id.ViewComments))) Thread.sleep(1000) - onView(withId(R.id.commentContainer)) + onView(first(withId(R.id.commentContainer))) .check(matches(hasDescendant(withId(R.id.comment)))) } + @Test + fun clickingViewCommentFails() { + ActivityScenario.launch(MainActivity::class.java) + Thread.sleep(1000) + //Open the comment section + onView(withId(R.id.list)) + .perform(actionOnItemAtPosition + (2, clickChildViewWithId(R.id.ViewComments))) + Thread.sleep(1000) + onView(withId(R.id.list)).check(matches(isDisplayed())) + } + @Test fun postingACommentWorks() { ActivityScenario.launch(MainActivity::class.java) @@ -317,23 +336,21 @@ class MockedServerTest { //Open the comment section onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, clickChildViewWithId(R.id.commenter))) - onView(withId(R.id.list)).perform(slowSwipeUp(true)) - onView(withId(R.id.list)).perform(slowSwipeUp(false)) onView(withId(R.id.list)).perform(slowSwipeUp(false)) Thread.sleep(1000) onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, typeTextInViewWithId(R.id.editComment, "test"))) onView(withId(R.id.list)) - .perform(actionOnItemAtPosition + .perform(actionOnItemAtPosition (0, clickChildViewWithId(R.id.submitComment))) Thread.sleep(1000) - onView(withId(R.id.commentContainer)) + onView(first(withId(R.id.commentContainer))) .check(matches(hasDescendant(withId(R.id.comment)))) } } diff --git a/app/src/androidTest/java/com/h/pixeldroid/testUtility/CustomMatchers.kt b/app/src/androidTest/java/com/h/pixeldroid/testUtility/CustomMatchers.kt new file mode 100644 index 00000000..b5a69db8 --- /dev/null +++ b/app/src/androidTest/java/com/h/pixeldroid/testUtility/CustomMatchers.kt @@ -0,0 +1,95 @@ +package com.h.pixeldroid.testUtility + +import android.view.View +import android.widget.EditText +import android.widget.TextView +import androidx.test.espresso.Espresso +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.action.* +import androidx.test.espresso.matcher.ViewMatchers +import org.hamcrest.BaseMatcher +import org.hamcrest.Matcher + +abstract class CustomMatchers { + companion object { + fun first(matcher: Matcher): Matcher? { + return object : BaseMatcher() { + var isFirst = true + override fun describeTo(description: org.hamcrest.Description?) { + description?.appendText("first matching item") + } + + override fun matches(item: Any?): Boolean { + if (isFirst && matcher.matches(item)) { + isFirst = false + return true + } + return false + } + + } + } + + /** + * @param percent can be 1 or 0 + * 1: swipes all the way up + * 0: swipes half way up + */ + fun slowSwipeUp(percent: Boolean) : ViewAction { + return ViewActions.actionWithAssertions( + GeneralSwipeAction( + Swipe.SLOW, + GeneralLocation.BOTTOM_CENTER, + if(percent) GeneralLocation.TOP_CENTER else GeneralLocation.CENTER, + Press.FINGER) + ) + } + + fun getText(matcher: Matcher?): String? { + val stringHolder = arrayOf(null) + Espresso.onView(matcher).perform(object : ViewAction { + override fun getConstraints(): Matcher { + return ViewMatchers.isAssignableFrom(TextView::class.java) + } + + override fun getDescription(): String { + return "getting text from a TextView" + } + + override fun perform( + uiController: UiController, + view: View + ) { + val tv = view as TextView //Save, because of check in getConstraints() + stringHolder[0] = tv.text.toString() + } + }) + return stringHolder[0] + } + + fun clickChildViewWithId(id: Int) = object : ViewAction { + + override fun getConstraints() = null + + override fun getDescription() = "click child view with id $id" + + override fun perform(uiController: UiController, view: View) { + val v = view.findViewById(id) + v.performClick() + } + } + + fun typeTextInViewWithId(id: Int, text: String) = object : ViewAction { + + override fun getConstraints() = null + + override fun getDescription() = "click child view with id $id" + + override fun perform(uiController: UiController, view: View) { + val v = view.findViewById(id) + v.text.append(text) + } + } + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/h/pixeldroid/testUtility/MockServer.kt b/app/src/androidTest/java/com/h/pixeldroid/testUtility/MockServer.kt index db124836..9ee4cae2 100644 --- a/app/src/androidTest/java/com/h/pixeldroid/testUtility/MockServer.kt +++ b/app/src/androidTest/java/com/h/pixeldroid/testUtility/MockServer.kt @@ -1,5 +1,6 @@ package com.h.pixeldroid.testUtility +import com.h.pixeldroid.objects.Account import okhttp3.HttpUrl import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse @@ -33,7 +34,8 @@ class MockServer { " \"software\": \"pixelfed\",\n" + " \"is_admin\": false\n" + " }" - private val feedJson = """[{"id":"140364967936397312","uri":"https:\/\/pixelfed.de\/p\/Miike\/140364967936397312","url":"https:\/\/pixelfed.de\/p\/Miike\/140364967936397312","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"Day 8 #rotavicentina<\/a> #hiking<\/a> #nature<\/a>","created_at":"2020-03-03T08:00:16.000000Z","emojis":[],"replies_count":1,"reblogs_count":0,"favourites_count":0,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[{"name":"hiking","url":"https:\/\/pixelfed.de\/discover\/tags\/hiking"},{"name":"nature","url":"https:\/\/pixelfed.de\/discover\/tags\/nature"},{"name":"rotavicentina","url":"https:\/\/pixelfed.de\/discover\/tags\/rotavicentina"}],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"115114166443970560","username":"Miike","acct":"Miike","display_name":"Miike Duart","locked":false,"created_at":"2019-12-24T15:42:35.000000Z","followers_count":14,"following_count":0,"statuses_count":71,"note":"","url":"https:\/\/pixelfed.de\/Miike","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/011\/511\/416\/644\/397\/056\/0\/ZhaopLJWTWJ3hsVCS5pS_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/011\/511\/416\/644\/397\/056\/0\/ZhaopLJWTWJ3hsVCS5pS_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"15888","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/34dd6d9fb1762dac8c7ddeeaf789d2d8fa083c9f\/JtjO0eAbELpgO1UZqF5ydrKbCKRVyJUM1WAaqIeB.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/34dd6d9fb1762dac8c7ddeeaf789d2d8fa083c9f\/JtjO0eAbELpgO1UZqF5ydrKbCKRVyJUM1WAaqIeB_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"140349785193451520","uri":"https:\/\/pixelfed.de\/p\/stephan\/140349785193451520","url":"https:\/\/pixelfed.de\/p\/stephan\/140349785193451520","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"","created_at":"2020-03-03T06:59:56.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":2,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"908","username":"stephan","acct":"stephan","display_name":"Stephan","locked":false,"created_at":"2019-03-17T07:46:33.000000Z","followers_count":136,"following_count":25,"statuses_count":136,"note":"Musician, software developer & hobby photographer.","url":"https:\/\/pixelfed.de\/stephan","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/000\/000\/000\/908\/5nQzzsB1mkwKaUqQ9GNN_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/000\/000\/000\/908\/5nQzzsB1mkwKaUqQ9GNN_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"15887","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/a1349f5183c2bac7d52880e8f5188df0f3b2d62a\/mvT3nYV6Wdu42Xh56Ny4VYaWU0OzbnC3wjxiqnKS.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/a1349f5183c2bac7d52880e8f5188df0f3b2d62a\/mvT3nYV6Wdu42Xh56Ny4VYaWU0OzbnC3wjxiqnKS_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"140276879742603264","uri":"https:\/\/pixelfed.de\/p\/fegrimaldi\/140276879742603264","url":"https:\/\/pixelfed.de\/p\/fegrimaldi\/140276879742603264","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"february 2 is the day to give flowers to Iemanj\u00e1. #salvador<\/a> #bahia<\/a> #brazil<\/a> #iemanja<\/a>","created_at":"2020-03-03T02:10:14.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[{"name":"salvador","url":"https:\/\/pixelfed.de\/discover\/tags\/salvador"},{"name":"bahia","url":"https:\/\/pixelfed.de\/discover\/tags\/bahia"},{"name":"brazil","url":"https:\/\/pixelfed.de\/discover\/tags\/brazil"},{"name":"iemanja","url":"https:\/\/pixelfed.de\/discover\/tags\/iemanja"}],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"137257212828585984","username":"fegrimaldi","acct":"fegrimaldi","display_name":"Fernanda Grimaldi","locked":false,"created_at":"2020-02-23T18:11:09.000000Z","followers_count":2,"following_count":7,"statuses_count":2,"note":"a little piece of Bahia in the fediverse.","url":"https:\/\/pixelfed.de\/fegrimaldi","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/013\/725\/721\/282\/858\/598\/4\/oUPBit0TJso1xNhJfFqg_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/013\/725\/721\/282\/858\/598\/4\/oUPBit0TJso1xNhJfFqg_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"15886","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/feb878b4bd60b85ac840670c6b9c809fd76b628b\/lYMrx0WF8LDqn0vTRgNJaRs7stMKtAXrgzpMrWEr.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/feb878b4bd60b85ac840670c6b9c809fd76b628b\/lYMrx0WF8LDqn0vTRgNJaRs7stMKtAXrgzpMrWEr_thumb.jpeg","text_url":null,"meta":null,"description":null}]}]""" + + private val feedJson = """[{"id":"156491373246287872","created_at":"2020-04-16T20:00:50.000000Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","url":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","replies_count":1,"reblogs_count":13,"favourites_count":2,"reblogged":false,"favourited":false,"muted":false,"bookmarked":false,"pinned":false,"content":"@Dobios<\/a> @Dante<\/a>","reblog":null,"application":{"name":"web","website":null},"mentions":[{"id":"136800034732773376","url":"https:\/\/pixelfed.de\/Dobios","username":"Dobios","acct":"Dobios"},{"id":"136453537340198912","url":"https:\/\/pixelfed.de\/dante","username":"dante","acct":"dante"}],"tags":[{"name":"mushroom","url":"https:\/\/pixelfed.de\/discover\/tags\/mushroom"},{"name":"commentsstillbroken","url":"https:\/\/pixelfed.de\/discover\/tags\/commentsstillbroken"},{"name":"fixyourapi","url":"https:\/\/pixelfed.de\/discover\/tags\/fixyourapi"},{"name":"pls","url":"https:\/\/pixelfed.de\/discover\/tags\/pls"}],"emojis":[],"card":null,"poll":null,"account":{"id":"145183325781364736","username":"machintuck","acct":"machintuck","display_name":"Arthur","locked":false,"created_at":"2020-03-16T15:06:42.000000Z","followers_count":4,"following_count":4,"statuses_count":5,"note":"","url":"https:\/\/pixelfed.de\/machintuck","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"19228","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"140349785193451520","uri":"https:\/\/pixelfed.de\/p\/stephan\/140349785193451520","url":"https:\/\/pixelfed.de\/p\/stephan\/140349785193451520","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"","created_at":"2020-03-03T06:59:56.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":2,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"908","username":"stephan","acct":"stephan","display_name":"Stephan","locked":false,"created_at":"2019-03-17T07:46:33.000000Z","followers_count":136,"following_count":25,"statuses_count":136,"note":"Musician, software developer & hobby photographer.","url":"https:\/\/pixelfed.de\/stephan","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/000\/000\/000\/908\/5nQzzsB1mkwKaUqQ9GNN_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/000\/000\/000\/908\/5nQzzsB1mkwKaUqQ9GNN_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"15887","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/a1349f5183c2bac7d52880e8f5188df0f3b2d62a\/mvT3nYV6Wdu42Xh56Ny4VYaWU0OzbnC3wjxiqnKS.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/a1349f5183c2bac7d52880e8f5188df0f3b2d62a\/mvT3nYV6Wdu42Xh56Ny4VYaWU0OzbnC3wjxiqnKS_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"0","uri":"https:\/\/pixelfed.de\/p\/fegrimaldi\/140276879742603264","url":"https:\/\/pixelfed.de\/p\/fegrimaldi\/140276879742603264","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"february 2 is the day to give flowers to Iemanj\u00e1. #salvador<\/a> #bahia<\/a> #brazil<\/a> #iemanja<\/a>","created_at":"2020-03-03T02:10:14.000000Z","emojis":[],"replies_count":1,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[{"name":"salvador","url":"https:\/\/pixelfed.de\/discover\/tags\/salvador"},{"name":"bahia","url":"https:\/\/pixelfed.de\/discover\/tags\/bahia"},{"name":"brazil","url":"https:\/\/pixelfed.de\/discover\/tags\/brazil"},{"name":"iemanja","url":"https:\/\/pixelfed.de\/discover\/tags\/iemanja"}],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"137257212828585984","username":"fegrimaldi","acct":"fegrimaldi","display_name":"Fernanda Grimaldi","locked":false,"created_at":"2020-02-23T18:11:09.000000Z","followers_count":2,"following_count":7,"statuses_count":2,"note":"a little piece of Bahia in the fediverse.","url":"https:\/\/pixelfed.de\/fegrimaldi","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/013\/725\/721\/282\/858\/598\/4\/oUPBit0TJso1xNhJfFqg_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/013\/725\/721\/282\/858\/598\/4\/oUPBit0TJso1xNhJfFqg_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"15886","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/feb878b4bd60b85ac840670c6b9c809fd76b628b\/lYMrx0WF8LDqn0vTRgNJaRs7stMKtAXrgzpMrWEr.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/feb878b4bd60b85ac840670c6b9c809fd76b628b\/lYMrx0WF8LDqn0vTRgNJaRs7stMKtAXrgzpMrWEr_thumb.jpeg","text_url":null,"meta":null,"description":null}]}]""" private val notificationsJson = "[{\"id\":\"45945\",\"type\":\"favourite\",\"created_at\":\"2020-03-15T14:49:20.000000Z\",\"account\":{\"id\":\"136800034732773376\",\"username\":\"Dobios\",\"acct\":\"Dobios\",\"display_name\":\"Andrew Dobis\",\"locked\":false,\"created_at\":\"2020-02-22T11:54:29.000000Z\",\"followers_count\":2,\"following_count\":1,\"statuses_count\":0,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Dobios\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"status\":{\"id\":\"144456497894658048\",\"uri\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"url\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"in_reply_to_id\":null,\"in_reply_to_account_id\":null,\"reblog\":null,\"content\":\"Saturn V launch\",\"created_at\":\"2020-03-14T14:58:32.000000Z\",\"emojis\":[],\"replies_count\":0,\"reblogs_count\":1,\"favourites_count\":6,\"reblogged\":null,\"favourited\":null,\"muted\":null,\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"mentions\":[],\"tags\":[],\"card\":null,\"poll\":null,\"application\":{\"name\":\"web\",\"website\":null},\"language\":null,\"pinned\":null,\"account\":{\"id\":\"136453537340198912\",\"username\":\"Dante\",\"acct\":\"dante\",\"display_name\":\"Dante\",\"locked\":false,\"created_at\":\"2020-02-21T12:57:38.000000Z\",\"followers_count\":3,\"following_count\":4,\"statuses_count\":1,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/dante\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"media_attachments\":[{\"id\":\"16583\",\"type\":\"image\",\"url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad.jpeg\",\"remote_url\":null,\"preview_url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad_thumb.jpeg\",\"text_url\":null,\"meta\":null,\"description\":null}]}},{\"id\":\"45944\",\"type\":\"follow\",\"created_at\":\"2020-03-15T14:49:11.000000Z\",\"account\":{\"id\":\"136800034732773376\",\"username\":\"Dobios\",\"acct\":\"Dobios\",\"display_name\":\"Andrew Dobis\",\"locked\":false,\"created_at\":\"2020-02-22T11:54:29.000000Z\",\"followers_count\":2,\"following_count\":1,\"statuses_count\":0,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Dobios\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false}},{\"id\":\"45942\",\"type\":\"reblog\",\"created_at\":\"2020-03-15T14:41:04.000000Z\",\"account\":{\"id\":\"144813993922531328\",\"username\":\"Clement\",\"acct\":\"Clement\",\"display_name\":\"Andrea\",\"locked\":false,\"created_at\":\"2020-03-15T14:39:06.000000Z\",\"followers_count\":0,\"following_count\":2,\"statuses_count\":0,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Clement\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"status\":{\"id\":\"144814478708576256\",\"uri\":\"https:\\/\\/pixelfed.de\\/p\\/Clement\\/144814478708576256\",\"url\":\"https:\\/\\/pixelfed.de\\/p\\/Clement\\/144814478708576256\",\"in_reply_to_id\":null,\"in_reply_to_account_id\":136453537340198912,\"reblog\":null,\"content\":\"\",\"created_at\":\"2020-03-15T14:41:02.000000Z\",\"emojis\":[],\"replies_count\":0,\"reblogs_count\":0,\"favourites_count\":0,\"reblogged\":null,\"favourited\":null,\"muted\":null,\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"mentions\":[],\"tags\":[],\"card\":null,\"poll\":null,\"application\":{\"name\":\"web\",\"website\":null},\"language\":null,\"pinned\":null,\"account\":{\"id\":\"144813993922531328\",\"username\":\"Clement\",\"acct\":\"Clement\",\"display_name\":\"Andrea\",\"locked\":false,\"created_at\":\"2020-03-15T14:39:06.000000Z\",\"followers_count\":0,\"following_count\":2,\"statuses_count\":0,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Clement\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false}}},{\"id\":\"45941\",\"type\":\"mention\",\"created_at\":\"2020-03-15T14:40:52.000000Z\",\"account\":{\"id\":\"144813993922531328\",\"username\":\"Clement\",\"acct\":\"Clement\",\"display_name\":\"Andrea\",\"locked\":false,\"created_at\":\"2020-03-15T14:39:06.000000Z\",\"followers_count\":0,\"following_count\":2,\"statuses_count\":0,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Clement\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"status\":{\"id\":\"144814428691501056\",\"uri\":\"https:\\/\\/pixelfed.de\\/p\\/Clement\\/144814428691501056\",\"url\":\"https:\\/\\/pixelfed.de\\/p\\/Clement\\/144814428691501056\",\"in_reply_to_id\":144456497894658048,\"in_reply_to_account_id\":136453537340198912,\"reblog\":null,\"content\":\"@dante<\\/a> I identify to this pic.\",\"created_at\":\"2020-03-15T14:40:50.000000Z\",\"emojis\":[],\"replies_count\":0,\"reblogs_count\":0,\"favourites_count\":1,\"reblogged\":null,\"favourited\":null,\"muted\":null,\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"mentions\":[{\"id\":\"136453537340198912\",\"url\":\"https:\\/\\/pixelfed.de\\/dante\",\"username\":\"dante\",\"acct\":\"dante\"}],\"tags\":[],\"card\":null,\"poll\":null,\"application\":{\"name\":\"web\",\"website\":null},\"language\":null,\"pinned\":null,\"account\":{\"id\":\"144813993922531328\",\"username\":\"Clement\",\"acct\":\"Clement\",\"display_name\":\"Andrea\",\"locked\":false,\"created_at\":\"2020-03-15T14:39:06.000000Z\",\"followers_count\":0,\"following_count\":2,\"statuses_count\":0,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Clement\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false}}},{\"id\":\"45940\",\"type\":\"favourite\",\"created_at\":\"2020-03-15T14:40:22.000000Z\",\"account\":{\"id\":\"144813993922531328\",\"username\":\"Clement\",\"acct\":\"Clement\",\"display_name\":\"Andrea\",\"locked\":false,\"created_at\":\"2020-03-15T14:39:06.000000Z\",\"followers_count\":0,\"following_count\":2,\"statuses_count\":0,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Clement\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"status\":{\"id\":\"144456497894658048\",\"uri\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"url\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"in_reply_to_id\":null,\"in_reply_to_account_id\":null,\"reblog\":null,\"content\":\"Saturn V launch\",\"created_at\":\"2020-03-14T14:58:32.000000Z\",\"emojis\":[],\"replies_count\":0,\"reblogs_count\":1,\"favourites_count\":6,\"reblogged\":null,\"favourited\":null,\"muted\":null,\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"mentions\":[],\"tags\":[],\"card\":null,\"poll\":null,\"application\":{\"name\":\"web\",\"website\":null},\"language\":null,\"pinned\":null,\"account\":{\"id\":\"136453537340198912\",\"username\":\"dante\",\"acct\":\"dante\",\"display_name\":\"Dante\",\"locked\":false,\"created_at\":\"2020-02-21T12:57:38.000000Z\",\"followers_count\":3,\"following_count\":4,\"statuses_count\":1,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/dante\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"media_attachments\":[{\"id\":\"16583\",\"type\":\"image\",\"url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad.jpeg\",\"remote_url\":null,\"preview_url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad_thumb.jpeg\",\"text_url\":null,\"meta\":null,\"description\":null}]}},{\"id\":\"45939\",\"type\":\"follow\",\"created_at\":\"2020-03-15T14:40:12.000000Z\",\"account\":{\"id\":\"144813993922531328\",\"username\":\"Clement\",\"acct\":\"Clement\",\"display_name\":\"Andrea\",\"locked\":false,\"created_at\":\"2020-03-15T14:39:06.000000Z\",\"followers_count\":0,\"following_count\":2,\"statuses_count\":0,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Clement\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false}},{\"id\":\"45804\",\"type\":\"favourite\",\"created_at\":\"2020-03-15T02:22:47.000000Z\",\"account\":{\"id\":\"131984031779786752\",\"username\":\"joska\",\"acct\":\"joska\",\"display_name\":\"jxzk\",\"locked\":false,\"created_at\":\"2020-02-09T04:57:25.000000Z\",\"followers_count\":3,\"following_count\":2,\"statuses_count\":82,\"note\":\"Feliz :D\",\"url\":\"https:\\/\\/pixelfed.de\\/joska\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/013\\/198\\/403\\/177\\/978\\/675\\/2\\/zhytNrT3ij5cHBXX1mJv_avatar.jpeg?v=4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/013\\/198\\/403\\/177\\/978\\/675\\/2\\/zhytNrT3ij5cHBXX1mJv_avatar.jpeg?v=4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"status\":{\"id\":\"144456497894658048\",\"uri\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"url\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"in_reply_to_id\":null,\"in_reply_to_account_id\":null,\"reblog\":null,\"content\":\"Saturn V launch\",\"created_at\":\"2020-03-14T14:58:32.000000Z\",\"emojis\":[],\"replies_count\":0,\"reblogs_count\":1,\"favourites_count\":6,\"reblogged\":null,\"favourited\":null,\"muted\":null,\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"mentions\":[],\"tags\":[],\"card\":null,\"poll\":null,\"application\":{\"name\":\"web\",\"website\":null},\"language\":null,\"pinned\":null,\"account\":{\"id\":\"136453537340198912\",\"username\":\"dante\",\"acct\":\"dante\",\"display_name\":\"Dante\",\"locked\":false,\"created_at\":\"2020-02-21T12:57:38.000000Z\",\"followers_count\":3,\"following_count\":4,\"statuses_count\":1,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/dante\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"media_attachments\":[{\"id\":\"16583\",\"type\":\"image\",\"url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad.jpeg\",\"remote_url\":null,\"preview_url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad_thumb.jpeg\",\"text_url\":null,\"meta\":null,\"description\":null}]}},{\"id\":\"45783\",\"type\":\"favourite\",\"created_at\":\"2020-03-15T00:45:53.000000Z\",\"account\":{\"id\":\"139939422090170368\",\"username\":\"DrMsch\",\"acct\":\"DrMsch\",\"display_name\":\"Mische\",\"locked\":false,\"created_at\":\"2020-03-02T03:49:18.000000Z\",\"followers_count\":13,\"following_count\":21,\"statuses_count\":9,\"note\":\"TelefonFotos und Malereien von DerMische\",\"url\":\"https:\\/\\/pixelfed.de\\/DrMsch\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/013\\/993\\/942\\/209\\/017\\/036\\/8\\/xQUOq3tBNgOhFItKMZ56_avatar.jpeg?v=4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/013\\/993\\/942\\/209\\/017\\/036\\/8\\/xQUOq3tBNgOhFItKMZ56_avatar.jpeg?v=4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"status\":{\"id\":\"144456497894658048\",\"uri\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"url\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"in_reply_to_id\":null,\"in_reply_to_account_id\":null,\"reblog\":null,\"content\":\"Saturn V launch\",\"created_at\":\"2020-03-14T14:58:32.000000Z\",\"emojis\":[],\"replies_count\":0,\"reblogs_count\":1,\"favourites_count\":6,\"reblogged\":null,\"favourited\":null,\"muted\":null,\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"mentions\":[],\"tags\":[],\"card\":null,\"poll\":null,\"application\":{\"name\":\"web\",\"website\":null},\"language\":null,\"pinned\":null,\"account\":{\"id\":\"136453537340198912\",\"username\":\"dante\",\"acct\":\"dante\",\"display_name\":\"Dante\",\"locked\":false,\"created_at\":\"2020-02-21T12:57:38.000000Z\",\"followers_count\":3,\"following_count\":4,\"statuses_count\":1,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/dante\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"media_attachments\":[{\"id\":\"16583\",\"type\":\"image\",\"url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad.jpeg\",\"remote_url\":null,\"preview_url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad_thumb.jpeg\",\"text_url\":null,\"meta\":null,\"description\":null}]}},{\"id\":\"45768\",\"type\":\"favourite\",\"created_at\":\"2020-03-14T22:43:18.000000Z\",\"account\":{\"id\":\"139819612522024960\",\"username\":\"vitorpires\",\"acct\":\"vitorpires\",\"display_name\":\"Vitor Pires\",\"locked\":false,\"created_at\":\"2020-03-01T19:53:13.000000Z\",\"followers_count\":20,\"following_count\":8,\"statuses_count\":42,\"note\":\"photography\\/graphic design\\/desktop publishing\\/illustration\\/3D animation\\/video\\/content writing\\/teaching\\/woodworking\\/luthier\",\"url\":\"https:\\/\\/pixelfed.de\\/vitorpires\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/013\\/981\\/961\\/252\\/202\\/496\\/0\\/2HB6Gs2m5NaSys7W5ikG_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/013\\/981\\/961\\/252\\/202\\/496\\/0\\/2HB6Gs2m5NaSys7W5ikG_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"status\":{\"id\":\"144456497894658048\",\"uri\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"url\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"in_reply_to_id\":null,\"in_reply_to_account_id\":null,\"reblog\":null,\"content\":\"Saturn V launch\",\"created_at\":\"2020-03-14T14:58:32.000000Z\",\"emojis\":[],\"replies_count\":0,\"reblogs_count\":1,\"favourites_count\":6,\"reblogged\":null,\"favourited\":null,\"muted\":null,\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"mentions\":[],\"tags\":[],\"card\":null,\"poll\":null,\"application\":{\"name\":\"web\",\"website\":null},\"language\":null,\"pinned\":null,\"account\":{\"id\":\"136453537340198912\",\"username\":\"dante\",\"acct\":\"dante\",\"display_name\":\"Dante\",\"locked\":false,\"created_at\":\"2020-02-21T12:57:38.000000Z\",\"followers_count\":3,\"following_count\":4,\"statuses_count\":1,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/dante\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"media_attachments\":[{\"id\":\"16583\",\"type\":\"image\",\"url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad.jpeg\",\"remote_url\":null,\"preview_url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad_thumb.jpeg\",\"text_url\":null,\"meta\":null,\"description\":null}]}},{\"id\":\"45723\",\"type\":\"favourite\",\"created_at\":\"2020-03-14T15:01:49.000000Z\",\"account\":{\"id\":\"79574199701737472\",\"username\":\"Spaziergaenger\",\"acct\":\"Spaziergaenger\",\"display_name\":\"anonymous\",\"locked\":false,\"created_at\":\"2019-09-17T13:59:27.000000Z\",\"followers_count\":40,\"following_count\":0,\"statuses_count\":894,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/Spaziergaenger\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/007\\/957\\/419\\/970\\/173\\/747\\/2\\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/007\\/957\\/419\\/970\\/173\\/747\\/2\\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"status\":{\"id\":\"144456497894658048\",\"uri\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"url\":\"https:\\/\\/pixelfed.de\\/p\\/dante\\/144456497894658048\",\"in_reply_to_id\":null,\"in_reply_to_account_id\":null,\"reblog\":null,\"content\":\"Saturn V launch\",\"created_at\":\"2020-03-14T14:58:32.000000Z\",\"emojis\":[],\"replies_count\":0,\"reblogs_count\":1,\"favourites_count\":6,\"reblogged\":null,\"favourited\":null,\"muted\":null,\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"mentions\":[],\"tags\":[],\"card\":null,\"poll\":null,\"application\":{\"name\":\"web\",\"website\":null},\"language\":null,\"pinned\":null,\"account\":{\"id\":\"136453537340198912\",\"username\":\"dante\",\"acct\":\"dante\",\"display_name\":\"Dante\",\"locked\":false,\"created_at\":\"2020-02-21T12:57:38.000000Z\",\"followers_count\":3,\"following_count\":4,\"statuses_count\":1,\"note\":\"\",\"url\":\"https:\\/\\/pixelfed.de\\/dante\",\"avatar\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"avatar_static\":\"https:\\/\\/pixelfed.de\\/storage\\/avatars\\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9\",\"header\":\"\",\"header_static\":\"\",\"emojis\":[],\"moved\":null,\"fields\":null,\"bot\":false,\"software\":\"pixelfed\",\"is_admin\":false},\"media_attachments\":[{\"id\":\"16583\",\"type\":\"image\",\"url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad.jpeg\",\"remote_url\":null,\"preview_url\":\"https:\\/\\/pixelfed.de\\/storage\\/m\\/113a3e2124a33b1f5511e531953f5ee48456e0c7\\/0fa8bbe19cc23442034913a7c97fbe4527c1d63a\\/vs2vouJ86OvzxhK9ewhPlfPf4Y9IoQ5CHfiBIqad_thumb.jpeg\",\"text_url\":null,\"meta\":null,\"description\":null}]}}]" private val accountStatusesJson = """[{"id":"153848799696130048","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153848799696130048","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153848799696130048","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nn\u00e4her geht es an Neu-Bamberg und den dritten Kontrollpunkt heran","created_at":"2020-04-09T13:00:12.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18521","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/IX5g6bLSCB4lpp0wTr6HKj4mYEpNUhgrjv50InJ3.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/IX5g6bLSCB4lpp0wTr6HKj4mYEpNUhgrjv50InJ3_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"153848420002566144","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153848420002566144","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153848420002566144","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nimmer wieder Wein und im Hintergrund der Steinbruch","created_at":"2020-04-09T12:58:41.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18520","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/g0ScH8shkytKjS9fask1CZHj0TMzWhbgJALApt5u.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/g0ScH8shkytKjS9fask1CZHj0TMzWhbgJALApt5u_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"153848196064481280","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153848196064481280","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153848196064481280","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\ndas Ende der Steigung ist absehbar","created_at":"2020-04-09T12:57:48.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":0,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18518","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/xLvyeeywOdeUTxYW03caxRXzNr4bldmAB1JUXUzv.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/xLvyeeywOdeUTxYW03caxRXzNr4bldmAB1JUXUzv_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"153446932994461696","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153446932994461696","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153446932994461696","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nweiter gehts es steil den Berg hinauf","created_at":"2020-04-08T10:23:19.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":0,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18421","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/VkG4AxsEE8gyWqUxprpna6LNSwRtKjW6A8yoZijI.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/VkG4AxsEE8gyWqUxprpna6LNSwRtKjW6A8yoZijI_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"153446615137521664","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153446615137521664","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153446615137521664","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nUnd hier gibt es neben dem zweiten Stempel auch k\u00fchle Getr\u00e4nke und Reibekuchen zum Sattessen","created_at":"2020-04-08T10:22:03.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":0,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18420","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/BhSqEKyy6iBlJGnrHpvjp2ZCrmCnepM0E3c83vvf.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/BhSqEKyy6iBlJGnrHpvjp2ZCrmCnepM0E3c83vvf_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"153446167173271552","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153446167173271552","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153446167173271552","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nDer zweite Kontrollpunkt: das sogenannte Wingertsh\u00e4uschen","created_at":"2020-04-08T10:20:17.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":4,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18419","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/iPjOwRResYI4BZxSNV7ZNpTsLz7Wna4IRHcu25dz.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/iPjOwRResYI4BZxSNV7ZNpTsLz7Wna4IRHcu25dz_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"153053485812813824","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153053485812813824","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153053485812813824","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nBlick auf die Kreisstra\u00dfe 88","created_at":"2020-04-07T08:19:54.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":2,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18291","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/GdQiZxDAB99NYbq0QpeW4GoNDzhAGuxcylOhWaBF.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/GdQiZxDAB99NYbq0QpeW4GoNDzhAGuxcylOhWaBF_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"153053256745095168","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153053256745095168","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153053256745095168","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nendlich Schatten","created_at":"2020-04-07T08:18:59.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":3,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18290","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/6TIe7Rj5vLFwp7VIRjLapnLqOzQ0WCtfnaVni9QS.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/6TIe7Rj5vLFwp7VIRjLapnLqOzQ0WCtfnaVni9QS_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"153052983712681984","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153052983712681984","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/153052983712681984","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nDer Wald kommt immer n\u00e4her","created_at":"2020-04-07T08:17:54.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":4,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18285","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/N0Dmh4lwjTcI8ACNqnxsS2cuWN5ZvusnZAt8XauE.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/N0Dmh4lwjTcI8ACNqnxsS2cuWN5ZvusnZAt8XauE_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"152657825544409088","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152657825544409088","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152657825544409088","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nnoch immer bei glei\u00dfender Sonne auf offenem Feld kommt Wald in Sicht","created_at":"2020-04-06T06:07:41.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":3,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18180","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/jgnP7gR3rw37yCnF1T5379krsZhRzh6JHiASINSP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/jgnP7gR3rw37yCnF1T5379krsZhRzh6JHiASINSP_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"152657278993043456","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152657278993043456","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152657278993043456","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nBlick durch die Zweige auf die historische Kapelle von Hof Iben","created_at":"2020-04-06T06:05:31.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":2,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18179","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/FyadKaDMpUZkde9cQzrBKbtcAaHKsT4WtDQSnLMg.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/FyadKaDMpUZkde9cQzrBKbtcAaHKsT4WtDQSnLMg_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"152656864235098112","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152656864235098112","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152656864235098112","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nimmer weiter \u00fcber offene Felder","created_at":"2020-04-06T06:03:52.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":4,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18178","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/ExqK24a3Ri3Jn7XEodkoAx7kRLJIQPuWaFzx09Sk.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/ExqK24a3Ri3Jn7XEodkoAx7kRLJIQPuWaFzx09Sk_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"152158673148448768","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152158673148448768","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152158673148448768","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\n... und r\u00fcber nach Neu Bamberg, links der Steinbruch","created_at":"2020-04-04T21:04:14.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18106","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/r29IsMP9TUagGBhr8UzRi7Odb83mMJNotEAZxqsS.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/r29IsMP9TUagGBhr8UzRi7Odb83mMJNotEAZxqsS_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"152158370521026560","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152158370521026560","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152158370521026560","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nDer Blick zur\u00fcck nach F\u00fcrfeld ...","created_at":"2020-04-04T21:03:02.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":2,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18105","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/CDMIhBX34DlG3L4clpHQvdPoBpR8MCgIUSy1cnQU.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/CDMIhBX34DlG3L4clpHQvdPoBpR8MCgIUSy1cnQU_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"152157823550230528","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152157823550230528","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/152157823550230528","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\n... bis zum Horizont ...","created_at":"2020-04-04T21:00:52.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18104","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/JYBeWD99cXadqo385uZjH98y2Cs1tQ0kun1NH9vd.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/JYBeWD99cXadqo385uZjH98y2Cs1tQ0kun1NH9vd_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"151795147183624192","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151795147183624192","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151795147183624192","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nDuch die Weinberge Richtung Hof Iben","created_at":"2020-04-03T20:59:43.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18071","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/VUD4ewNlZ7ofpFiO8wG9SEoaG4TbmRBgNtfEkEKw.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/VUD4ewNlZ7ofpFiO8wG9SEoaG4TbmRBgNtfEkEKw_thumb.jpeg","text_url":null,"meta":null,"description":null},{"id":"18072","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/2tiRGDebac4DvE3a2djRjCHtaJuspwdyaxiu27JY.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/2tiRGDebac4DvE3a2djRjCHtaJuspwdyaxiu27JY_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"151794839434956800","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151794839434956800","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151794839434956800","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nBlick nach S\u00fcden \u00fcber die herrliche Landschaft","created_at":"2020-04-03T20:58:29.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18070","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/vdEd5cyW6SMxectXdDVPdMNNt4TNrtERHSRGWc9R.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/vdEd5cyW6SMxectXdDVPdMNNt4TNrtERHSRGWc9R_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"151794394431885312","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151794394431885312","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151794394431885312","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nBlick \u00fcber F\u00fcrfeld","created_at":"2020-04-03T20:56:43.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":4,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"18069","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/sGtZi6f3Q9cJfc6cou08otJqBoGCcSlot64yttra.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/sGtZi6f3Q9cJfc6cou08otJqBoGCcSlot64yttra_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"151344455130157056","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151344455130157056","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151344455130157056","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nDas Fahrzeug der Veranstaltungsleitung von hinten","created_at":"2020-04-02T15:08:49.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":2,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"17966","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/0bixTrcD8FKkM12kG7k21OzfDDZQIVEyadu7Tv47.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/0bixTrcD8FKkM12kG7k21OzfDDZQIVEyadu7Tv47_thumb.jpeg","text_url":null,"meta":null,"description":null}]},{"id":"151343830426324992","uri":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151343830426324992","url":"https:\/\/pixelfed.de\/p\/Spaziergaenger\/151343830426324992","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"2017-05-28 VG Bad-Kreuznach Wandertag
\n
\nBlick zur\u00fcck zum Eichelberg","created_at":"2020-04-02T15:06:21.000000Z","emojis":[],"replies_count":0,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":null,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"79574199701737472","username":"Spaziergaenger","acct":"Spaziergaenger","display_name":"anonymous","locked":false,"created_at":"2019-09-17T13:59:27.000000Z","followers_count":43,"following_count":0,"statuses_count":966,"note":"","url":"https:\/\/pixelfed.de\/Spaziergaenger","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/007\/957\/419\/970\/173\/747\/2\/KEg4YgCgsmzdgyVztszz_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"17965","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/PgvSJ25k9SS6dhFh5l3v0rgQ5r7SY5JbepkrDkPP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/d75e944e8cc85b953d37d1a9d615f44d6e0cca22\/PgvSJ25k9SS6dhFh5l3v0rgQ5r7SY5JbepkrDkPP_thumb.jpeg","text_url":null,"meta":null,"description":null}]}]""" private val commentStatusesJson = """{ @@ -121,8 +123,11 @@ class MockServer { ] }""" - val likedJson = """{"id":"140364967936397312","uri":"https:\/\/pixelfed.de\/p\/Miike\/140364967936397312","url":"https:\/\/pixelfed.de\/p\/Miike\/140364967936397312","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"Day 8
#rotavicentina<\/a> #hiking<\/a> #nature<\/a>","created_at":"2020-03-03T08:00:16.000000Z","emojis":[],"replies_count":1,"reblogs_count":0,"favourites_count":1,"reblogged":null,"favourited":true,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[{"name":"hiking","url":"https:\/\/pixelfed.de\/discover\/tags\/hiking"},{"name":"nature","url":"https:\/\/pixelfed.de\/discover\/tags\/nature"},{"name":"rotavicentina","url":"https:\/\/pixelfed.de\/discover\/tags\/rotavicentina"}],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"115114166443970560","username":"Miike","acct":"Miike","display_name":"Miike Duart","locked":false,"created_at":"2019-12-24T15:42:35.000000Z","followers_count":14,"following_count":0,"statuses_count":71,"note":"","url":"https:\/\/pixelfed.de\/Miike","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/011\/511\/416\/644\/397\/056\/0\/ZhaopLJWTWJ3hsVCS5pS_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/011\/511\/416\/644\/397\/056\/0\/ZhaopLJWTWJ3hsVCS5pS_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"15888","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/34dd6d9fb1762dac8c7ddeeaf789d2d8fa083c9f\/JtjO0eAbELpgO1UZqF5ydrKbCKRVyJUM1WAaqIeB.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/34dd6d9fb1762dac8c7ddeeaf789d2d8fa083c9f\/JtjO0eAbELpgO1UZqF5ydrKbCKRVyJUM1WAaqIeB_thumb.jpeg","text_url":null,"meta":null,"description":null}]}""" - val unlikeJson = """{"id":"140364967936397312","uri":"https:\/\/pixelfed.de\/p\/Miike\/140364967936397312","url":"https:\/\/pixelfed.de\/p\/Miike\/140364967936397312","in_reply_to_id":null,"in_reply_to_account_id":null,"reblog":null,"content":"Day 8 #rotavicentina<\/a> #hiking<\/a> #nature<\/a>","created_at":"2020-03-03T08:00:16.000000Z","emojis":[],"replies_count":1,"reblogs_count":0,"favourites_count":0,"reblogged":null,"favourited":false,"muted":null,"sensitive":false,"spoiler_text":"","visibility":"public","mentions":[],"tags":[{"name":"hiking","url":"https:\/\/pixelfed.de\/discover\/tags\/hiking"},{"name":"nature","url":"https:\/\/pixelfed.de\/discover\/tags\/nature"},{"name":"rotavicentina","url":"https:\/\/pixelfed.de\/discover\/tags\/rotavicentina"}],"card":null,"poll":null,"application":{"name":"web","website":null},"language":null,"pinned":null,"account":{"id":"115114166443970560","username":"Miike","acct":"Miike","display_name":"Miike Duart","locked":false,"created_at":"2019-12-24T15:42:35.000000Z","followers_count":14,"following_count":0,"statuses_count":71,"note":"","url":"https:\/\/pixelfed.de\/Miike","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/011\/511\/416\/644\/397\/056\/0\/ZhaopLJWTWJ3hsVCS5pS_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/011\/511\/416\/644\/397\/056\/0\/ZhaopLJWTWJ3hsVCS5pS_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"15888","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/34dd6d9fb1762dac8c7ddeeaf789d2d8fa083c9f\/JtjO0eAbELpgO1UZqF5ydrKbCKRVyJUM1WAaqIeB.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/113a3e2124a33b1f5511e531953f5ee48456e0c7\/34dd6d9fb1762dac8c7ddeeaf789d2d8fa083c9f\/JtjO0eAbELpgO1UZqF5ydrKbCKRVyJUM1WAaqIeB_thumb.jpeg","text_url":null,"meta":null,"description":null}]}""" + val likedJson = """{"id":"156491373246287872","created_at":"2020-04-16T20:00:50.000000Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","url":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","replies_count":1,"reblogs_count":13,"favourites_count":3,"reblogged":false,"favourited":true,"muted":false,"bookmarked":false,"pinned":false,"content":"@Dobios<\/a> @Dante<\/a>","reblog":null,"application":{"name":"web","website":null},"mentions":[{"id":"136800034732773376","url":"https:\/\/pixelfed.de\/Dobios","username":"Dobios","acct":"Dobios"},{"id":"136453537340198912","url":"https:\/\/pixelfed.de\/dante","username":"dante","acct":"dante"}],"tags":[{"name":"mushroom","url":"https:\/\/pixelfed.de\/discover\/tags\/mushroom"},{"name":"commentsstillbroken","url":"https:\/\/pixelfed.de\/discover\/tags\/commentsstillbroken"},{"name":"fixyourapi","url":"https:\/\/pixelfed.de\/discover\/tags\/fixyourapi"},{"name":"pls","url":"https:\/\/pixelfed.de\/discover\/tags\/pls"}],"emojis":[],"card":null,"poll":null,"account":{"id":"145183325781364736","username":"machintuck","acct":"machintuck","display_name":"Arthur","locked":false,"created_at":"2020-03-16T15:06:42.000000Z","followers_count":4,"following_count":4,"statuses_count":5,"note":"","url":"https:\/\/pixelfed.de\/machintuck","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"19228","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP_thumb.jpeg","text_url":null,"meta":null,"description":null}]}""" + val unlikeJson = """{"id":"156491373246287872","created_at":"2020-04-16T20:00:50.000000Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","url":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","replies_count":1,"reblogs_count":13,"favourites_count":2,"reblogged":false,"favourited":false,"muted":false,"bookmarked":false,"pinned":false,"content":"@Dobios<\/a> @Dante<\/a>","reblog":null,"application":{"name":"web","website":null},"mentions":[{"id":"136800034732773376","url":"https:\/\/pixelfed.de\/Dobios","username":"Dobios","acct":"Dobios"},{"id":"136453537340198912","url":"https:\/\/pixelfed.de\/dante","username":"dante","acct":"dante"}],"tags":[{"name":"mushroom","url":"https:\/\/pixelfed.de\/discover\/tags\/mushroom"},{"name":"commentsstillbroken","url":"https:\/\/pixelfed.de\/discover\/tags\/commentsstillbroken"},{"name":"fixyourapi","url":"https:\/\/pixelfed.de\/discover\/tags\/fixyourapi"},{"name":"pls","url":"https:\/\/pixelfed.de\/discover\/tags\/pls"}],"emojis":[],"card":null,"poll":null,"account":{"id":"145183325781364736","username":"machintuck","acct":"machintuck","display_name":"Arthur","locked":false,"created_at":"2020-03-16T15:06:42.000000Z","followers_count":4,"following_count":4,"statuses_count":5,"note":"","url":"https:\/\/pixelfed.de\/machintuck","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"19228","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP_thumb.jpeg","text_url":null,"meta":null,"description":null}]}""" + + val reblogJson = """{"id":"156491373246287872","created_at":"2020-04-16T20:00:50.000000Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","url":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","replies_count":1,"reblogs_count":14,"favourites_count":2,"reblogged":true,"favourited":false,"muted":false,"bookmarked":false,"pinned":false,"content":"@Dobios<\/a> @Dante<\/a>","reblog":null,"application":{"name":"web","website":null},"mentions":[{"id":"136800034732773376","url":"https:\/\/pixelfed.de\/Dobios","username":"Dobios","acct":"Dobios"},{"id":"136453537340198912","url":"https:\/\/pixelfed.de\/dante","username":"dante","acct":"dante"}],"tags":[{"name":"mushroom","url":"https:\/\/pixelfed.de\/discover\/tags\/mushroom"},{"name":"commentsstillbroken","url":"https:\/\/pixelfed.de\/discover\/tags\/commentsstillbroken"},{"name":"fixyourapi","url":"https:\/\/pixelfed.de\/discover\/tags\/fixyourapi"},{"name":"pls","url":"https:\/\/pixelfed.de\/discover\/tags\/pls"}],"emojis":[],"card":null,"poll":null,"account":{"id":"145183325781364736","username":"machintuck","acct":"machintuck","display_name":"Arthur","locked":false,"created_at":"2020-03-16T15:06:42.000000Z","followers_count":4,"following_count":4,"statuses_count":5,"note":"","url":"https:\/\/pixelfed.de\/machintuck","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"19228","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP_thumb.jpeg","text_url":null,"meta":null,"description":null}]}""" + fun start() { server.dispatcher = getDispatcher() @@ -137,42 +142,89 @@ class MockServer { "/api/v1/accounts/verify_credentials" -> return MockResponse().addHeader("Content-Type", "application/json; charset=utf-8").setResponseCode(200).setBody(accountJson) "/api/v1/timelines/home" -> return MockResponse().addHeader("Content-Type", "application/json; charset=utf-8").setResponseCode(200).setBody(feedJson) } - if(request.path?.startsWith("/api/v1/notifications") == true) { - return MockResponse() - .addHeader("Content-Type", "application/json; charset=utf-8") - .setResponseCode(200).setBody(notificationsJson) - } else if (request.path?.startsWith("/api/v1/timelines/home") == true) { - return MockResponse().addHeader( - "Content-Type", - "application/json; charset=utf-8" - ).setResponseCode(200).setBody(feedJson) - } else if (request.path?.matches("/api/v1/accounts/[0-9]*/statuses".toRegex()) == true) { - return MockResponse().addHeader( - "Content-Type", - "application/json; charset=utf-8" - ).setResponseCode(200).setBody(accountStatusesJson) - } else if(request.path?.matches("/api/v1/statuses/[0-9]*/context".toRegex()) == true) { - return MockResponse().addHeader( - "Content-Type", - "application/json; charset=utf-8" - ).setResponseCode(200).setBody(commentStatusesJson) - } else if(request.path?.matches("/api/v1/statuses/[0-9]*/favourite".toRegex()) == true) { - return MockResponse().addHeader( - "Content-Type", - "application/json; charset=utf-8" - ).setResponseCode(200).setBody(likedJson) - } else if(request.path?.matches("/api/v1/statuses/[0-9]*/unfavourite".toRegex()) == true) { - return MockResponse().addHeader( - "Content-Type", - "application/json; charset=utf-8" - ).setResponseCode(200).setBody(unlikeJson) - } else if(request.path?.startsWith("/api/v1/statuses") == true) { - return MockResponse().addHeader( - "Content-Type", - "application/json; charset=utf-8" - ).setResponseCode(200).setBody(unlikeJson) + when { + request.path?.startsWith("/api/v1/notifications") == true -> { + return MockResponse() + .addHeader("Content-Type", "application/json; charset=utf-8") + .setResponseCode(200).setBody(notificationsJson) + } + request.path?.startsWith("/api/v1/timelines/home") == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(feedJson) + } + request.path?.startsWith("/api/v1/accounts/0/statuses") == true -> { + return MockResponse().setHttp2ErrorCode(401) + } + request.path?.matches("/api/v1/accounts/[0-9]*/statuses".toRegex()) == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(accountStatusesJson) + } + request.path?.startsWith("/api/v1/statuses/0/context") == true -> { + return MockResponse().setHttp2ErrorCode(401) + } + request.path?.matches("/api/v1/statuses/[0-9]*/context".toRegex()) == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(commentStatusesJson) + } + request.path?.startsWith("/api/v1/statuses/0/favourite") == true -> { + return MockResponse().setHttp2ErrorCode(401) + } + request.path?.matches("/api/v1/statuses/[0-9]*/favourite".toRegex()) == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(likedJson) + } + request.path?.startsWith("/api/v1/statuses/0/unfavourite") == true -> { + return MockResponse().setHttp2ErrorCode(401) + } + request.path?.matches("/api/v1/statuses/[0-9]*/unfavourite".toRegex()) == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(unlikeJson) + } + request.path?.startsWith("/api/v1/statuses") == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(unlikeJson) + } + request.path?.startsWith("/api/v1/accounts/0") == true -> { + return MockResponse().setHttp2ErrorCode(401) + } + request.path?.matches("/api/v1/accounts/[0-9]*".toRegex()) == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(accountJson) + } + request.path?.startsWith("/api/v1/statuses/0/reblog") == true -> { + return MockResponse().setHttp2ErrorCode(401) + } + request.path?.matches("/api/v1/statuses/[0-9]*/reblog".toRegex()) == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(reblogJson) + } + request.path?.startsWith("/api/v1/statuses/0/unreblog") == true -> { + return MockResponse().setHttp2ErrorCode(401) + } + request.path?.matches("/api/v1/statuses/[0-9]*/unreblog".toRegex()) == true -> { + return MockResponse().addHeader( + "Content-Type", + "application/json; charset=utf-8" + ).setResponseCode(200).setBody(unlikeJson) + } + else -> return MockResponse().setResponseCode(404) } - return MockResponse().setResponseCode(404) } } } diff --git a/app/src/main/java/com/h/pixeldroid/api/PixelfedAPI.kt b/app/src/main/java/com/h/pixeldroid/api/PixelfedAPI.kt index 72b076f8..63bcca71 100644 --- a/app/src/main/java/com/h/pixeldroid/api/PixelfedAPI.kt +++ b/app/src/main/java/com/h/pixeldroid/api/PixelfedAPI.kt @@ -77,6 +77,20 @@ interface PixelfedAPI { @Field("language") language : String? = null ) : Call + @FormUrlEncoded + @POST("/api/v1/statuses/{id}/reblog") + fun reblogStatus( + @Header("Authorization") authorization: String, + @Path("id") statusId: String, + @Field("visibility") visibility: String? = null + ) : Call + + @POST("/api/v1/statuses/{id}/unreblog") + fun undoReblogStatus( + @Path("id") statusId: String, + @Header("Authorization") authorization: String + ) : Call + //Used in our case to retrieve comments for a given status @GET("/api/v1/statuses/{id}/context") fun statusComments( @@ -133,6 +147,12 @@ interface PixelfedAPI { @Path("id") account_id: String? = null ): Call> + @GET("/api/v1/accounts/{id}") + fun getAccount( + @Header("Authorization") authorization: String, + @Path("id") accountId : String + ): Call + companion object { fun create(baseUrl: String): PixelfedAPI { return Retrofit.Builder() diff --git a/app/src/main/java/com/h/pixeldroid/fragments/PostFragment.kt b/app/src/main/java/com/h/pixeldroid/fragments/PostFragment.kt index 6897ea60..82100deb 100644 --- a/app/src/main/java/com/h/pixeldroid/fragments/PostFragment.kt +++ b/app/src/main/java/com/h/pixeldroid/fragments/PostFragment.kt @@ -12,9 +12,8 @@ import com.bumptech.glide.Glide import com.h.pixeldroid.BuildConfig import com.h.pixeldroid.R import com.h.pixeldroid.api.PixelfedAPI -import com.h.pixeldroid.fragments.feeds.ViewHolder +import com.h.pixeldroid.fragments.feeds.PostViewHolder import com.h.pixeldroid.objects.Status - import com.h.pixeldroid.objects.Status.Companion.POST_TAG import kotlinx.android.synthetic.main.post_fragment.view.* @@ -34,15 +33,19 @@ class PostFragment : Fragment() { status?.setupPost(root, picRequest, root.postPicture, root.profilePic) //Setup arguments needed for the onclicklisteners - val holder = ViewHolder(root, requireContext()) + val holder = PostViewHolder(root, requireContext()) + val preferences = requireActivity().getSharedPreferences( "${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE ) val accessToken = preferences.getString("accessToken", "") val api = PixelfedAPI.create("${preferences.getString("domain", "")}") + status?.setDescription(root, api, "Bearer $accessToken") + //Activate onclickListeners - status?.activateLiker(holder, api, "Bearer $accessToken") + status?.activateLiker(holder, api, "Bearer $accessToken", status.favourited) + status?.activateReblogger(holder, api, "Bearer $accessToken", status.reblogged) status?.activateCommenter(holder, api, "Bearer $accessToken") status?.showComments(holder, api, "Bearer $accessToken") diff --git a/app/src/main/java/com/h/pixeldroid/fragments/feeds/HomeFragment.kt b/app/src/main/java/com/h/pixeldroid/fragments/feeds/HomeFragment.kt index 36c8f4f0..1659c50a 100644 --- a/app/src/main/java/com/h/pixeldroid/fragments/feeds/HomeFragment.kt +++ b/app/src/main/java/com/h/pixeldroid/fragments/feeds/HomeFragment.kt @@ -13,6 +13,7 @@ import androidx.lifecycle.Observer import androidx.paging.LivePagedListBuilder import androidx.paging.PagedList import androidx.recyclerview.widget.RecyclerView +import at.connyduck.sparkbutton.SparkButton import com.bumptech.glide.Glide import com.bumptech.glide.ListPreloader import com.bumptech.glide.RequestBuilder @@ -23,7 +24,7 @@ import com.h.pixeldroid.objects.Status import retrofit2.Call -class HomeFragment : FeedFragment() { +class HomeFragment : FeedFragment() { lateinit var picRequest: RequestBuilder @@ -81,21 +82,21 @@ class HomeFragment : FeedFragment() { /** * [RecyclerView.Adapter] that can display a list of Statuses */ - inner class HomeRecyclerViewAdapter() - : FeedsRecyclerViewAdapter() { + inner class HomeRecyclerViewAdapter + : FeedsRecyclerViewAdapter() { private val api = pixelfedAPI private val credential = "Bearer $accessToken" - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.post_fragment, parent, false) context = view.context - return ViewHolder(view, context) + return PostViewHolder(view, context) } /** * Binds the different elements of the Post Model to the view holder */ - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder(holder: PostViewHolder, position: Int) { val post = getItem(position) ?: return val metrics = context.resources.displayMetrics //Limit the height of the different images @@ -105,17 +106,20 @@ class HomeFragment : FeedFragment() { //Setup the post layout post.setupPost(holder.postView, picRequest, holder.postPic, holder.profilePic) - //Set initial favorite toggle value - holder.isLiked = post.favourited + //Set the special HTML text + post.setDescription(holder.postView, api, credential) //Activate liker - post.activateLiker(holder, api, credential) + post.activateLiker(holder, api, credential, post.favourited) //Show comments post.showComments(holder, api, credential) //Activate Commenter post.activateCommenter(holder, api, credential) + + //Activate Reblogger + post.activateReblogger(holder, api ,credential, post.reblogged) } override fun getPreloadItems(position: Int): MutableList { @@ -132,7 +136,7 @@ class HomeFragment : FeedFragment() { /** * Represents the posts that will be contained within the feed */ -class ViewHolder(val postView: View, val context: android.content.Context) : RecyclerView.ViewHolder(postView) { +class PostViewHolder(val postView: View, val context: android.content.Context) : RecyclerView.ViewHolder(postView) { val profilePic : ImageView = postView.findViewById(R.id.profilePic) val postPic : ImageView = postView.findViewById(R.id.postPicture) val username : TextView = postView.findViewById(R.id.username) @@ -140,12 +144,15 @@ class ViewHolder(val postView: View, val context: android.content.Context) : Rec val description : TextView = postView.findViewById(R.id.description) val nlikes : TextView = postView.findViewById(R.id.nlikes) val nshares : TextView = postView.findViewById(R.id.nshares) - val liker : ImageView = postView.findViewById(R.id.liker) + + //Spark buttons + val liker : SparkButton = postView.findViewById(R.id.liker) + val reblogger : SparkButton = postView.findViewById(R.id.reblogger) + val submitCmnt : ImageButton = postView.findViewById(R.id.submitComment) val commenter : ImageView = postView.findViewById(R.id.commenter) val comment : EditText = postView.findViewById(R.id.editComment) val commentCont : LinearLayout = postView.findViewById(R.id.commentContainer) val commentIn : LinearLayout = postView.findViewById(R.id.commentIn) val viewComment : TextView = postView.findViewById(R.id.ViewComments) - var isLiked : Boolean = false } diff --git a/app/src/main/java/com/h/pixeldroid/fragments/feeds/NotificationsFragment.kt b/app/src/main/java/com/h/pixeldroid/fragments/feeds/NotificationsFragment.kt index 47e9d266..3d8f49f4 100644 --- a/app/src/main/java/com/h/pixeldroid/fragments/feeds/NotificationsFragment.kt +++ b/app/src/main/java/com/h/pixeldroid/fragments/feeds/NotificationsFragment.kt @@ -27,6 +27,7 @@ import com.h.pixeldroid.R import com.h.pixeldroid.objects.Account import com.h.pixeldroid.objects.Notification import com.h.pixeldroid.objects.Status +import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText import kotlinx.android.synthetic.main.fragment_notifications.view.* import retrofit2.Call @@ -145,7 +146,15 @@ class NotificationsFragment : FeedFragment { - setNotificationTypeTextView(context, R.string.shared_notification, R.drawable.ic_share) + setNotificationTypeTextView(context, R.string.shared_notification, R.drawable.ic_reblog_blue) } Notification.NotificationType.favourite -> { - setNotificationTypeTextView(context, R.string.liked_notification, R.drawable.ic_heart) + setNotificationTypeTextView(context, R.string.liked_notification, R.drawable.ic_like_full) } } textView.text = format.format(username) diff --git a/app/src/main/java/com/h/pixeldroid/objects/Account.kt b/app/src/main/java/com/h/pixeldroid/objects/Account.kt index 1b301c1c..54f071bd 100644 --- a/app/src/main/java/com/h/pixeldroid/objects/Account.kt +++ b/app/src/main/java/com/h/pixeldroid/objects/Account.kt @@ -3,13 +3,18 @@ package com.h.pixeldroid.objects import android.content.Context import android.content.Intent import android.graphics.Typeface +import android.util.Log import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat.startActivity import com.h.pixeldroid.ProfileActivity import com.h.pixeldroid.R +import com.h.pixeldroid.api.PixelfedAPI import com.h.pixeldroid.utils.ImageConverter +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response import java.io.Serializable /* @@ -18,65 +23,92 @@ https://docs.joinmastodon.org/entities/account/ */ data class Account( - //Base attributes - val id: String, - val username: String, - val acct: String, - val url: String, //HTTPS URL - //Display attributes - val display_name: String, - val note: String, //HTML - val avatar: String, //URL - val avatar_static: String, //URL - val header: String, //URL - val header_static: String, //URL - val locked: Boolean, - val emojis: List, - val discoverable: Boolean, - //Statistical attributes - val created_at: String, //ISO 8601 Datetime (maybe can use a date type) - val statuses_count: Int, - val followers_count: Int, - val following_count: Int, - //Optional attributes - val moved: Account? = null, - val fields: List? = emptyList(), - val bot: Boolean = false, - val source: Source? = null + //Base attributes + val id: String, + val username: String, + val acct: String, + val url: String, //HTTPS URL + //Display attributes + val display_name: String, + val note: String, //HTML + val avatar: String, //URL + val avatar_static: String, //URL + val header: String, //URL + val header_static: String, //URL + val locked: Boolean, + val emojis: List, + val discoverable: Boolean, + //Statistical attributes + val created_at: String, //ISO 8601 Datetime (maybe can use a date type) + val statuses_count: Int, + val followers_count: Int, + val following_count: Int, + //Optional attributes + val moved: Account? = null, + val fields: List? = emptyList(), + val bot: Boolean = false, + val source: Source? = null ) : Serializable { - companion object { - const val ACCOUNT_TAG = "AccountTag" + companion object { + const val ACCOUNT_TAG = "AccountTag" + + /** + * @brief Opens an activity of the profile withn the given id + */ + fun getAccountFromId(id: String, api : PixelfedAPI, context: Context, credential: String) { + Log.e("ACCOUNT_ID", id) + api.getAccount(credential, id).enqueue( object : Callback { + override fun onFailure(call: Call, t: Throwable) { + Log.e("GET ACCOUNT ERROR", t.toString()) + } + + override fun onResponse( + call: Call, + response: Response + ) { + if(response.code() == 200) { + val account = response.body()!! + + //Open the account page in a seperate activity + account.openProfile(context) + } else { + Log.e("ERROR CODE", response.code().toString()) + } + } + + }) } + } - // Open profile activity with given account - fun openProfile(context: Context) { - val intent = Intent(context, ProfileActivity::class.java) - intent.putExtra(Account.ACCOUNT_TAG, this) - startActivity(context, intent, null) - } - // Populate myProfile page with user's data - fun setContent(view: View) { - val profilePicture = view.findViewById(R.id.profilePictureImageView) - ImageConverter.setRoundImageFromURL(view, this.avatar, profilePicture) + // Open profile activity with given account + fun openProfile(context: Context) { + val intent = Intent(context, ProfileActivity::class.java) + intent.putExtra(Account.ACCOUNT_TAG, this) + startActivity(context, intent, null) + } + // Populate myProfile page with user's data + fun setContent(view: View) { + val profilePicture = view.findViewById(R.id.profilePictureImageView) + ImageConverter.setRoundImageFromURL(view, this.avatar, profilePicture) - val description = view.findViewById(R.id.descriptionTextView) - description.text = this.note + val description = view.findViewById(R.id.descriptionTextView) + description.text = this.note - val accountName = view.findViewById(R.id.accountNameTextView) - accountName.text = this.username - accountName.setTypeface(null, Typeface.BOLD) + val accountName = view.findViewById(R.id.accountNameTextView) + accountName.text = this.username + accountName.setTypeface(null, Typeface.BOLD) - val nbPosts = view.findViewById(R.id.nbPostsTextView) - nbPosts.text = "${this.statuses_count}\nPosts" - nbPosts.setTypeface(null, Typeface.BOLD) + val nbPosts = view.findViewById(R.id.nbPostsTextView) + nbPosts.text = "${this.statuses_count}\nPosts" + nbPosts.setTypeface(null, Typeface.BOLD) - val nbFollowers = view.findViewById(R.id.nbFollowersTextView) - nbFollowers.text = "${this.followers_count}\nFollowers" - nbFollowers.setTypeface(null, Typeface.BOLD) + val nbFollowers = view.findViewById(R.id.nbFollowersTextView) + nbFollowers.text = "${this.followers_count}\nFollowers" + nbFollowers.setTypeface(null, Typeface.BOLD) - val nbFollowing = view.findViewById(R.id.nbFollowingTextView) - nbFollowing.text = "${this.following_count}\nFollowing" - nbFollowing.setTypeface(null, Typeface.BOLD) - } + val nbFollowing = view.findViewById(R.id.nbFollowingTextView) + nbFollowing.text = "${this.following_count}\nFollowing" + nbFollowing.setTypeface(null, Typeface.BOLD) + } } diff --git a/app/src/main/java/com/h/pixeldroid/objects/Mention.kt b/app/src/main/java/com/h/pixeldroid/objects/Mention.kt index 01134310..4a4e468f 100644 --- a/app/src/main/java/com/h/pixeldroid/objects/Mention.kt +++ b/app/src/main/java/com/h/pixeldroid/objects/Mention.kt @@ -2,5 +2,10 @@ package com.h.pixeldroid.objects import java.io.Serializable -class Mention : Serializable { -} \ No newline at end of file +data class Mention( + //Mentioned user + val id: String, + val username : String, + val acct : String, //URI of mentioned user (username if local, else username@domain) + val url : String //URL of mentioned user's profile +) : Serializable \ No newline at end of file diff --git a/app/src/main/java/com/h/pixeldroid/objects/Status.kt b/app/src/main/java/com/h/pixeldroid/objects/Status.kt index 30c8c12a..5cf2acd3 100644 --- a/app/src/main/java/com/h/pixeldroid/objects/Status.kt +++ b/app/src/main/java/com/h/pixeldroid/objects/Status.kt @@ -1,22 +1,32 @@ package com.h.pixeldroid.objects +import android.content.Context import android.graphics.Typeface import android.graphics.drawable.Drawable +import android.os.Build +import android.text.Html +import android.text.Spanned +import android.text.method.LinkMovementMethod +import android.util.Log import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast +import androidx.core.text.toSpanned import com.bumptech.glide.RequestBuilder import com.h.pixeldroid.R import com.h.pixeldroid.api.PixelfedAPI -import com.h.pixeldroid.fragments.feeds.ViewHolder +import com.h.pixeldroid.fragments.feeds.PostViewHolder +import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText import com.h.pixeldroid.utils.ImageConverter import com.h.pixeldroid.utils.PostUtils.Companion.likePostCall import com.h.pixeldroid.utils.PostUtils.Companion.postComment +import com.h.pixeldroid.utils.PostUtils.Companion.reblogPost import com.h.pixeldroid.utils.PostUtils.Companion.retrieveComments import com.h.pixeldroid.utils.PostUtils.Companion.toggleCommentInput import com.h.pixeldroid.utils.PostUtils.Companion.unLikePostCall +import com.h.pixeldroid.utils.PostUtils.Companion.undoReblogPost import java.io.Serializable /* @@ -70,17 +80,21 @@ data class Status( fun getProfilePicUrl() : String? = account.avatar fun getPostPreviewURL() : String? = media_attachments?.getOrNull(0)?.preview_url - fun getDescription() : CharSequence { - val description = content as CharSequence + /** + * @brief returns the parsed version of the HTML description + */ + private fun getDescription(api: PixelfedAPI, context: Context, credential: String) : Spanned { + val description = content if(description.isEmpty()) { - return "No description" + return "No description".toSpanned() } - return description + return parseHTMLText(description, mentions, api, context, credential) + } fun getUsername() : CharSequence { var name = account?.display_name - if (name.isNullOrEmpty()) { + if (name.isEmpty()) { name = account?.username } return name!! @@ -112,8 +126,6 @@ data class Status( usernameDesc.text = this.getUsername() usernameDesc.setTypeface(null, Typeface.BOLD) - rootView.findViewById(R.id.description).text = this.getDescription() - val nlikes = rootView.findViewById(R.id.nlikes) nlikes.text = this.getNLikes() nlikes.setTypeface(null, Typeface.BOLD) @@ -135,25 +147,60 @@ data class Status( rootView.findViewById(R.id.commentIn).visibility = View.GONE } - fun activateLiker( - holder : ViewHolder, - api: PixelfedAPI, - credential: String + fun setDescription(rootView: View, api : PixelfedAPI, credential: String) { + val desc = rootView.findViewById(R.id.description) + + desc.text = this.getDescription(api, rootView.context, credential) + desc.movementMethod = LinkMovementMethod.getInstance() + } + + fun activateReblogger( + holder : PostViewHolder, + api : PixelfedAPI, + credential: String, + isReblogged : Boolean ) { - //Activate the liker - holder.liker.setOnClickListener { - if (holder.isLiked) { - //Unlike the post - unLikePostCall(holder, api, credential, this) + //Set initial button state + holder.reblogger.isChecked = isReblogged + + //Activate the button + holder.reblogger.setEventListener { _, buttonState -> + if (buttonState) { + Log.e("REBLOG", "Reblogged post") + // Button is active + reblogPost(holder, api, credential, this) } else { - //like the post - likePostCall(holder, api, credential, this) + Log.e("REBLOG", "Undo Reblogged post") + // Button is inactive + undoReblogPost(holder, api, credential, this) } } } + fun activateLiker( + holder : PostViewHolder, + api: PixelfedAPI, + credential: String, + isLiked: Boolean + ) { + //Set initial state + holder.liker.isChecked = isLiked + + //Activate the liker + holder.liker.setEventListener { _, buttonState -> + if (buttonState) { + // Button is active + likePostCall(holder, api, credential, this) + } else { + // Button is inactive + unLikePostCall(holder, api, credential, this) + } + } + } + + fun showComments( - holder : ViewHolder, + holder : PostViewHolder, api: PixelfedAPI, credential: String ) { @@ -172,7 +219,7 @@ data class Status( } fun activateCommenter( - holder : ViewHolder, + holder : PostViewHolder, api: PixelfedAPI, credential: String ) { diff --git a/app/src/main/java/com/h/pixeldroid/utils/HtmlUtils.kt b/app/src/main/java/com/h/pixeldroid/utils/HtmlUtils.kt new file mode 100644 index 00000000..0bd93b17 --- /dev/null +++ b/app/src/main/java/com/h/pixeldroid/utils/HtmlUtils.kt @@ -0,0 +1,126 @@ +package com.h.pixeldroid.utils + +import android.content.Context +import android.os.Build +import android.text.Html +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.style.ClickableSpan +import android.text.style.URLSpan +import android.util.Log +import android.view.View +import android.widget.Toast +import androidx.core.text.toSpanned +import com.h.pixeldroid.api.PixelfedAPI +import com.h.pixeldroid.objects.Account.Companion.getAccountFromId +import com.h.pixeldroid.objects.Mention +import com.h.pixeldroid.utils.customSpans.ClickableSpanNoUnderline +import java.net.URI +import java.net.URISyntaxException +import java.util.Locale + + +class HtmlUtils { + companion object { + + private fun fromHtml(html: String): Spanned { + val result: Spanned = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY) + } else { + Html.fromHtml(html) + } + return result.trim().toSpanned() + } + + private fun getDomain(urlString: String?): String { + val uri: URI + try { + uri = URI(urlString!!) + } catch (e: URISyntaxException) { + return "" + } + val host: String = uri.host + return if (host.startsWith("www.")) { + host.substring(4) + } else { + host + } + } + + fun parseHTMLText( + text : String, + mentions: List?, + api : PixelfedAPI, + context: Context, + credential: String + ) : Spanned { + //Convert text to spannable + val content = fromHtml(text) + + //Retrive all links that should be made clickable + val builder = SpannableStringBuilder(content) + val urlSpans = content.getSpans(0, content.length, URLSpan::class.java) + + for(span in urlSpans) { + val start = builder.getSpanStart(span) + val end = builder.getSpanEnd(span) + val flags = builder.getSpanFlags(span) + val text = builder.subSequence(start, end) + var customSpan: ClickableSpan? = null + + //Handle hashtags + if (text[0] == '#') { + val tag = text.subSequence(1, text.length).toString() + customSpan = object : ClickableSpanNoUnderline() { + override fun onClick(widget: View) { + Toast.makeText(context, tag, Toast.LENGTH_SHORT).show() + } + + } + } + + //Handle mentions + if(text[0] == '@' && !mentions.isNullOrEmpty()) { + val accountUsername = text.subSequence(1, text.length).toString() + var id: String? = null + + //Go through all mentions stored in the status + for (mention in mentions) { + if (mention.username.toLowerCase(Locale.ROOT) + == accountUsername.toLowerCase(Locale.ROOT) + ) { + id = mention.id + + //Mentions can be of users in other domains + if (mention.url.contains(getDomain(span.url))) { + break + } + } + } + + //Check that we found a user for the given mention + if (id != null) { + val accountId: String = id + customSpan = object : ClickableSpanNoUnderline() { + override fun onClick(widget: View) { + Log.e("MENTION", "CLICKED") + //Retrieve the account for the given profile + getAccountFromId(accountId, api, context, credential) + } + } + } + } + + builder.removeSpan(span); + builder.setSpan(customSpan, start, end, flags); + + // Add zero-width space after links in end of line to fix its too large hitbox. + if (end >= builder.length || builder.subSequence(end, end + 1).toString() == "\n") { + builder.insert(end, "\u200B") + } + } + + return builder + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/h/pixeldroid/utils/PostUtils.kt b/app/src/main/java/com/h/pixeldroid/utils/PostUtils.kt index d18c2f50..314532d5 100644 --- a/app/src/main/java/com/h/pixeldroid/utils/PostUtils.kt +++ b/app/src/main/java/com/h/pixeldroid/utils/PostUtils.kt @@ -1,49 +1,110 @@ package com.h.pixeldroid.utils -import android.graphics.Typeface import android.util.Log import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.widget.LinearLayout -import android.widget.TextView import android.widget.Toast -import androidx.cardview.widget.CardView import com.h.pixeldroid.R import com.h.pixeldroid.api.PixelfedAPI -import com.h.pixeldroid.fragments.feeds.ViewHolder -import com.h.pixeldroid.objects.Account +import com.h.pixeldroid.fragments.feeds.PostViewHolder import com.h.pixeldroid.objects.Context import com.h.pixeldroid.objects.Status +import com.h.pixeldroid.utils.ImageConverter.Companion.setImageFromDrawable import kotlinx.android.synthetic.main.comment.view.* import retrofit2.Call import retrofit2.Callback import retrofit2.Response -class PostUtils { +abstract class PostUtils { companion object { fun toggleCommentInput( - holder : ViewHolder + holder : PostViewHolder ) { //Toggle comment button holder.commenter.setOnClickListener { when(holder.commentIn.visibility) { - View.VISIBLE -> holder.commentIn.visibility = View.GONE - View.INVISIBLE -> holder.commentIn.visibility = View.VISIBLE - View.GONE -> holder.commentIn.visibility = View.VISIBLE + View.VISIBLE -> { + holder.commentIn.visibility = View.GONE + setImageFromDrawable(holder.postView, holder.commenter, R.drawable.ic_comment_empty) + } + View.GONE -> { + holder.commentIn.visibility = View.VISIBLE + setImageFromDrawable(holder.postView, holder.commenter, R.drawable.ic_comment_blue) + } } } } - fun likePostCall( - holder : ViewHolder, + fun reblogPost( + holder : PostViewHolder, api: PixelfedAPI, credential: String, post : Status ) { + //Call the api function + api.reblogStatus(credential, post.id).enqueue(object : Callback { + override fun onFailure(call: Call, t: Throwable) { + Log.e("REBLOG ERROR", t.toString()) + holder.reblogger.isChecked = false + } + + override fun onResponse(call: Call, response: Response) { + if(response.code() == 200) { + val resp = response.body()!! + + //Update shown share count + holder.nshares.text = resp.getNShares() + holder.reblogger.isChecked = resp.reblogged + } else { + Log.e("RESPONSE_CODE", response.code().toString()) + holder.reblogger.isChecked = false + } + } + + }) + } + + fun undoReblogPost( + holder : PostViewHolder, + api: PixelfedAPI, + credential: String, + post : Status + ) { + //Call the api function + api.undoReblogStatus(credential, post.id).enqueue(object : Callback { + override fun onFailure(call: Call, t: Throwable) { + Log.e("REBLOG ERROR", t.toString()) + holder.reblogger.isChecked = true + } + + override fun onResponse(call: Call, response: Response) { + if(response.code() == 200) { + val resp = response.body()!! + + //Update shown share count + holder.nshares.text = resp.getNShares() + holder.reblogger.isChecked = resp.reblogged + } else { + Log.e("RESPONSE_CODE", response.code().toString()) + holder.reblogger.isChecked = true + } + } + + }) + } + + fun likePostCall( + holder : PostViewHolder, + api: PixelfedAPI, + credential: String, + post : Status + ) { + //Call the api function api.likePost(credential, post.id).enqueue(object : Callback { override fun onFailure(call: Call, t: Throwable) { Log.e("LIKE ERROR", t.toString()) + holder.liker.isChecked = false } override fun onResponse(call: Call, response: Response) { @@ -52,9 +113,10 @@ class PostUtils { //Update shown like count and internal like toggle holder.nlikes.text = resp.getNLikes() - holder.isLiked = resp.favourited + holder.liker.isChecked = resp.favourited } else { - Log.e("RESPOSE_CODE", response.code().toString()) + Log.e("RESPONSE_CODE", response.code().toString()) + holder.liker.isChecked = false } } @@ -62,14 +124,16 @@ class PostUtils { } fun unLikePostCall( - holder : ViewHolder, + holder : PostViewHolder, api: PixelfedAPI, credential: String, post : Status ) { + //Call the api function api.unlikePost(credential, post.id).enqueue(object : Callback { override fun onFailure(call: Call, t: Throwable) { Log.e("UNLIKE ERROR", t.toString()) + holder.liker.isChecked = true } override fun onResponse(call: Call, response: Response) { @@ -78,9 +142,10 @@ class PostUtils { //Update shown like count and internal like toggle holder.nlikes.text = resp.getNLikes() - holder.isLiked = resp.favourited + holder.liker.isChecked = resp.favourited } else { - Log.e("RESPOSE_CODE", response.code().toString()) + Log.e("RESPONSE_CODE", response.code().toString()) + holder.liker.isChecked = true } } @@ -89,7 +154,7 @@ class PostUtils { } fun postComment( - holder : ViewHolder, + holder : PostViewHolder, api: PixelfedAPI, credential: String, post : Status @@ -131,7 +196,7 @@ class PostUtils { } fun retrieveComments( - holder : ViewHolder, + holder : PostViewHolder, api: PixelfedAPI, credential: String, post : Status diff --git a/app/src/main/java/com/h/pixeldroid/utils/customSpans/ClickableSpanNoUnderline.kt b/app/src/main/java/com/h/pixeldroid/utils/customSpans/ClickableSpanNoUnderline.kt new file mode 100644 index 00000000..786d68ee --- /dev/null +++ b/app/src/main/java/com/h/pixeldroid/utils/customSpans/ClickableSpanNoUnderline.kt @@ -0,0 +1,11 @@ +package com.h.pixeldroid.utils.customSpans + +import android.text.TextPaint +import android.text.style.ClickableSpan + +abstract class ClickableSpanNoUnderline : ClickableSpan() { + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.isUnderlineText = false + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_comment_blue.xml b/app/src/main/res/drawable/ic_comment_blue.xml new file mode 100644 index 00000000..212e3935 --- /dev/null +++ b/app/src/main/res/drawable/ic_comment_blue.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_comment_empty.xml b/app/src/main/res/drawable/ic_comment_empty.xml new file mode 100644 index 00000000..880a1b1a --- /dev/null +++ b/app/src/main/res/drawable/ic_comment_empty.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_comment_full.xml b/app/src/main/res/drawable/ic_comment_full.xml new file mode 100644 index 00000000..3eeab828 --- /dev/null +++ b/app/src/main/res/drawable/ic_comment_full.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_like_empty.xml b/app/src/main/res/drawable/ic_like_empty.xml new file mode 100644 index 00000000..18c836ac --- /dev/null +++ b/app/src/main/res/drawable/ic_like_empty.xml @@ -0,0 +1,6 @@ + + + diff --git a/app/src/main/res/drawable/ic_like_full.xml b/app/src/main/res/drawable/ic_like_full.xml new file mode 100644 index 00000000..b70f7ffb --- /dev/null +++ b/app/src/main/res/drawable/ic_like_full.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_reblog.xml b/app/src/main/res/drawable/ic_reblog.xml new file mode 100644 index 00000000..d712269d --- /dev/null +++ b/app/src/main/res/drawable/ic_reblog.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_reblog_blue.xml b/app/src/main/res/drawable/ic_reblog_blue.xml new file mode 100644 index 00000000..4eed7e83 --- /dev/null +++ b/app/src/main/res/drawable/ic_reblog_blue.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_send_blue.xml b/app/src/main/res/drawable/ic_send_blue.xml new file mode 100644 index 00000000..0a12f1ee --- /dev/null +++ b/app/src/main/res/drawable/ic_send_blue.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml deleted file mode 100644 index 65c012e0..00000000 --- a/app/src/main/res/drawable/ic_share.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/app/src/main/res/layout/post_fragment.xml b/app/src/main/res/layout/post_fragment.xml index 37647ac4..ecfb38f3 100644 --- a/app/src/main/res/layout/post_fragment.xml +++ b/app/src/main/res/layout/post_fragment.xml @@ -1,200 +1,195 @@ - - + android:layout_height="wrap_content" + xmlns:sparkbutton="http://schemas.android.com/apk/res-auto" + tools:context=".fragments.PostFragment"> + - + + + + + + + + android:layout_marginTop="10dp" + android:adjustViewBounds="true" + app:layout_constraintTop_toBottomOf="@+id/profilePic" + tools:src="@color/browser_actions_bg_grey"/> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:layout_marginStart="10dp" + android:layout_marginBottom="10dp" + tools:text="TextView"/> - + - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index 02a4ed79..209bbee1 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -1,4 +1,15 @@ oauth2redirect + + + + + + + + + #c42f0a + #0000ff + #000000 \ No newline at end of file diff --git a/app/src/test/java/com/h/pixeldroid/PostUnitTest.kt b/app/src/test/java/com/h/pixeldroid/PostUnitTest.kt index 498e008c..862145cf 100644 --- a/app/src/test/java/com/h/pixeldroid/PostUnitTest.kt +++ b/app/src/test/java/com/h/pixeldroid/PostUnitTest.kt @@ -32,18 +32,6 @@ class PostUnitTest { @Test fun getProfilePicUrlReturnsAValidURL() = Assert.assertNotNull(status.getProfilePicUrl()) - @Test - fun getDescriptionReturnsDefaultIfEmpty() { - val emptyDescStatus = status.copy(content = "") - Assert.assertEquals( "No description", emptyDescStatus.getDescription()) - } - - @Test - fun getDescriptionReturnsAValidDesc() = Assert.assertNotNull(status.getDescription()) - - @Test - fun getDescriptionReturnsACorrectDesc() = Assert.assertEquals(status.content, status.getDescription()) - @Test fun getUsernameReturnsACorrectName() = Assert.assertEquals(status.account.display_name, status.getUsername()) diff --git a/build.gradle b/build.gradle index 067a3626..98f28901 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,6 @@ buildscript { repositories { google() jcenter() - } dependencies { classpath 'com.android.tools.build:gradle:3.6.2' @@ -20,7 +19,8 @@ allprojects { repositories { google() jcenter() - + maven { url "https://jitpack.io" } + } }