From cdbfb17d945df917c56961ed10c9a6f196ca580d Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 15 Nov 2022 10:51:46 +0100 Subject: [PATCH] Follow Hashtags --- app/src/main/AndroidManifest.xml | 5 + .../app/fedilab/android/BaseMainActivity.java | 4 + .../activities/FollowedTagActivity.java | 202 ++++++++++++++++++ .../android/activities/HashTagActivity.java | 51 ++++- .../client/endpoints/MastodonTagService.java | 13 +- .../android/ui/drawer/FollowedTagAdapter.java | 79 +++++++ .../android/viewmodel/mastodon/ReorderVM.java | 19 ++ .../android/viewmodel/mastodon/TagVM.java | 43 +++- .../res/drawable/ic_baseline_post_add_24.xml | 22 ++ .../main/res/drawable/ic_baseline_tag_24.xml | 10 + .../res/layout/activity_followed_tags.xml | 33 +++ .../res/layout/popup_add_followed_tagt.xml | 17 ++ .../main/res/menu/activity_main_drawer.xml | 4 + app/src/main/res/menu/menu_followed_tag.xml | 9 + app/src/main/res/menu/menu_hashtag.xml | 14 ++ .../main/res/menu/menu_main_followed_tag.xml | 9 + app/src/main/res/values/strings.xml | 9 + 17 files changed, 527 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/app/fedilab/android/activities/FollowedTagActivity.java create mode 100644 app/src/main/java/app/fedilab/android/ui/drawer/FollowedTagAdapter.java create mode 100644 app/src/main/res/drawable/ic_baseline_post_add_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_tag_24.xml create mode 100644 app/src/main/res/layout/activity_followed_tags.xml create mode 100644 app/src/main/res/layout/popup_add_followed_tagt.xml create mode 100644 app/src/main/res/menu/menu_followed_tag.xml create mode 100644 app/src/main/res/menu/menu_hashtag.xml create mode 100644 app/src/main/res/menu/menu_main_followed_tag.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a34f97d5..1b449cde 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -251,6 +251,11 @@ android:configChanges="keyboardHidden|orientation|screenSize" android:label="@string/action_lists" android:theme="@style/AppThemeBar" /> + . */ + + +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.text.InputFilter; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; + +import java.util.ArrayList; + +import app.fedilab.android.BaseMainActivity; +import app.fedilab.android.R; +import app.fedilab.android.client.entities.api.Tag; +import app.fedilab.android.client.entities.app.Timeline; +import app.fedilab.android.databinding.ActivityFollowedTagsBinding; +import app.fedilab.android.databinding.PopupAddFollowedTagtBinding; +import app.fedilab.android.helper.Helper; +import app.fedilab.android.helper.ThemeHelper; +import app.fedilab.android.ui.drawer.FollowedTagAdapter; +import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; +import app.fedilab.android.viewmodel.mastodon.TagVM; +import es.dmoral.toasty.Toasty; + + +public class FollowedTagActivity extends BaseActivity implements FollowedTagAdapter.ActionOnTag { + + + private ActivityFollowedTagsBinding binding; + private boolean canGoBack; + private TagVM tagVM; + private Tag tag; + private ArrayList tagList; + private FollowedTagAdapter followedTagAdapter; + private FragmentMastodonTimeline fragmentMastodonTimeline; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ThemeHelper.applyThemeBar(this); + binding = ActivityFollowedTagsBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + canGoBack = false; + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayShowHomeEnabled(true); + getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary))); + } + tagVM = new ViewModelProvider(FollowedTagActivity.this).get(TagVM.class); + tagVM.followedTags(BaseMainActivity.currentInstance, BaseMainActivity.currentToken) + .observe(FollowedTagActivity.this, tags -> { + if (tags != null && tags.tags != null && tags.tags.size() > 0) { + tagList = new ArrayList<>(tags.tags); + followedTagAdapter = new FollowedTagAdapter(tagList); + followedTagAdapter.actionOnTag = this; + binding.notContent.setVisibility(View.GONE); + binding.recyclerView.setAdapter(followedTagAdapter); + binding.recyclerView.setLayoutManager(new LinearLayoutManager(FollowedTagActivity.this)); + } else { + binding.notContent.setVisibility(View.VISIBLE); + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + return true; + } else if (item.getItemId() == R.id.action_unfollow && tag != null) { + AlertDialog.Builder alt_bld = new AlertDialog.Builder(FollowedTagActivity.this, Helper.dialogStyle()); + alt_bld.setTitle(R.string.action_unfollow_tag); + alt_bld.setMessage(R.string.action_unfollow_tag_confirm); + alt_bld.setPositiveButton(R.string.unfollow, (dialog, id) -> { + tagVM.unfollow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, tag.name); + int position = 0; + for (Tag tagTmp : tagList) { + if (tagTmp.name.equalsIgnoreCase(tag.name)) { + break; + } + position++; + } + tagList.remove(position); + followedTagAdapter.notifyItemRemoved(position); + ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> { + canGoBack = false; + if (fragmentMastodonTimeline != null) { + fragmentMastodonTimeline.onDestroyView(); + } + invalidateOptionsMenu(); + setTitle(R.string.action_lists); + }); + if (tagList.size() == 0) { + binding.notContent.setVisibility(View.VISIBLE); + } else { + binding.notContent.setVisibility(View.GONE); + } + }); + alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); + AlertDialog alert = alt_bld.create(); + alert.show(); + } else if (item.getItemId() == R.id.action_follow_tag) { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(FollowedTagActivity.this, Helper.dialogStyle()); + PopupAddFollowedTagtBinding popupAddFollowedTagtBinding = PopupAddFollowedTagtBinding.inflate(getLayoutInflater()); + dialogBuilder.setView(popupAddFollowedTagtBinding.getRoot()); + popupAddFollowedTagtBinding.addTag.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)}); + dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> { + if (popupAddFollowedTagtBinding.addTag.getText() != null && popupAddFollowedTagtBinding.addTag.getText().toString().trim().length() > 0) { + tagVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, popupAddFollowedTagtBinding.addTag.getText().toString().trim()) + .observe(FollowedTagActivity.this, newTag -> { + if (tagList == null) { + tagList = new ArrayList<>(); + } + if (newTag != null && followedTagAdapter != null) { + tagList.add(0, newTag); + followedTagAdapter.notifyItemInserted(0); + } else { + Toasty.error(FollowedTagActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); + } + }); + dialog.dismiss(); + } else { + popupAddFollowedTagtBinding.addTag.setError(getString(R.string.not_valid_tag_name)); + } + + }); + dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); + dialogBuilder.create().show(); + } + return super.onOptionsItemSelected(item); + } + + + @Override + public boolean onCreateOptionsMenu(@NonNull Menu menu) { + if (!canGoBack) { + getMenuInflater().inflate(R.menu.menu_main_followed_tag, menu); + } else { + getMenuInflater().inflate(R.menu.menu_followed_tag, menu); + } + return true; + } + + @Override + public void onBackPressed() { + if (canGoBack) { + canGoBack = false; + ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> { + if (fragmentMastodonTimeline != null) { + fragmentMastodonTimeline.onDestroyView(); + } + }); + setTitle(R.string.action_lists); + invalidateOptionsMenu(); + } else { + super.onBackPressed(); + } + } + + @Override + public void click(Tag tag) { + this.tag = tag; + canGoBack = true; + ThemeHelper.slideViewsToLeft(binding.recyclerView, binding.fragmentContainer, () -> { + fragmentMastodonTimeline = new FragmentMastodonTimeline(); + Bundle bundle = new Bundle(); + bundle.putSerializable(Helper.ARG_SEARCH_KEYWORD, tag.name); + bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG); + setTitle(tag.name); + fragmentMastodonTimeline.setArguments(bundle); + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = + fragmentManager.beginTransaction(); + fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline); + fragmentTransaction.commit(); + invalidateOptionsMenu(); + }); + } +} diff --git a/app/src/main/java/app/fedilab/android/activities/HashTagActivity.java b/app/src/main/java/app/fedilab/android/activities/HashTagActivity.java index 7e7fdb90..82caadc7 100644 --- a/app/src/main/java/app/fedilab/android/activities/HashTagActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/HashTagActivity.java @@ -28,6 +28,7 @@ import android.view.MenuItem; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.core.content.ContextCompat; +import androidx.lifecycle.ViewModelProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import java.util.ArrayList; @@ -45,6 +46,8 @@ import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; +import app.fedilab.android.viewmodel.mastodon.ReorderVM; +import app.fedilab.android.viewmodel.mastodon.TagVM; import es.dmoral.toasty.Toasty; @@ -53,6 +56,9 @@ public class HashTagActivity extends BaseActivity { public static int position; private String tag; + private boolean pinnedTag; + private boolean followedTag; + private TagVM tagVM; @Override protected void onCreate(Bundle savedInstanceState) { @@ -67,7 +73,8 @@ public class HashTagActivity extends BaseActivity { } if (tag == null) finish(); - + pinnedTag = false; + followedTag = false; setSupportActionBar(binding.toolbar); ActionBar actionBar = getSupportActionBar(); //Remove title @@ -81,6 +88,29 @@ public class HashTagActivity extends BaseActivity { getSupportActionBar().setDisplayShowHomeEnabled(true); } + tagVM = new ViewModelProvider(HashTagActivity.this).get(TagVM.class); + tagVM.getTag(MainActivity.currentInstance, MainActivity.currentToken, tag).observe(this, returnedTag -> { + if (returnedTag != null) { + followedTag = returnedTag.following; + invalidateOptionsMenu(); + } + }); + ReorderVM reorderVM = new ViewModelProvider(HashTagActivity.this).get(ReorderVM.class); + reorderVM.getAllPinned().observe(HashTagActivity.this, pinned -> { + if (pinned != null) { + if (pinned.pinnedTimelines != null) { + for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) { + if (pinnedTimeline.tagTimeline != null) { + if (pinnedTimeline.tagTimeline.name.equalsIgnoreCase(tag)) { + pinnedTag = true; + invalidateOptionsMenu(); + } + } + } + } + } + }); + Bundle bundle = new Bundle(); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG); bundle.putString(Helper.ARG_SEARCH_KEYWORD, tag); @@ -151,10 +181,19 @@ public class HashTagActivity extends BaseActivity { Intent intentBD = new Intent(Helper.BROADCAST_DATA); intentBD.putExtras(b); LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD); + pinnedTag = true; + invalidateOptionsMenu(); } catch (DBException e) { e.printStackTrace(); } }).start(); + } else if (item.getItemId() == R.id.action_follow_tag) { + tagVM.follow(MainActivity.currentInstance, MainActivity.currentToken, tag).observe(this, returnedTag -> { + if (returnedTag != null) { + followedTag = returnedTag.following; + invalidateOptionsMenu(); + } + }); } return super.onOptionsItemSelected(item); @@ -163,7 +202,15 @@ public class HashTagActivity extends BaseActivity { @Override public boolean onCreateOptionsMenu(@NonNull Menu menu) { - getMenuInflater().inflate(R.menu.menu_reorder, menu); + getMenuInflater().inflate(R.menu.menu_hashtag, menu); + MenuItem pin = menu.findItem(R.id.action_add_timeline); + MenuItem follow = menu.findItem(R.id.action_follow_tag); + if (pinnedTag && pin != null) { + pin.setVisible(false); + } + if (followedTag && follow != null) { + follow.setVisible(false); + } return super.onCreateOptionsMenu(menu); } diff --git a/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTagService.java b/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTagService.java index 351e379a..a3f6e466 100644 --- a/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTagService.java +++ b/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTagService.java @@ -23,18 +23,21 @@ import retrofit2.http.GET; import retrofit2.http.Header; import retrofit2.http.POST; import retrofit2.http.Path; -import retrofit2.http.Query; public interface MastodonTagService { //Get followed tags @GET("followed_tags") Call> getFollowedTags( + @Header("Authorization") String token + ); + + + //Get followed tags + @GET("tags/{name}") + Call getTag( @Header("Authorization") String token, - @Query("max_id") String max_id, - @Query("since_id") String since_id, - @Query("min_id") String min_id, - @Query("limit") int limit + @Path("name") String name ); //Follow tag diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/FollowedTagAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/FollowedTagAdapter.java new file mode 100644 index 00000000..11dc4d76 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/ui/drawer/FollowedTagAdapter.java @@ -0,0 +1,79 @@ +package app.fedilab.android.ui.drawer; +/* Copyright 2022 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +import app.fedilab.android.client.entities.api.Tag; +import app.fedilab.android.databinding.DrawerListBinding; + + +public class FollowedTagAdapter extends RecyclerView.Adapter { + private final List tagList; + public ActionOnTag actionOnTag; + + public FollowedTagAdapter(List tagList) { + this.tagList = tagList; + } + + + public int getCount() { + return tagList.size(); + } + + public Tag getItem(int position) { + return tagList.get(position); + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + DrawerListBinding itemBinding = DrawerListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new ListViewHolder(itemBinding); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + Tag tag = tagList.get(position); + ListViewHolder holder = (ListViewHolder) viewHolder; + holder.binding.title.setText(tag.name); + holder.binding.title.setOnClickListener(v -> actionOnTag.click(tag)); + } + + @Override + public int getItemCount() { + return tagList.size(); + } + + + public interface ActionOnTag { + void click(Tag tag); + } + + public static class ListViewHolder extends RecyclerView.ViewHolder { + DrawerListBinding binding; + + ListViewHolder(DrawerListBinding itemView) { + super(itemView.getRoot()); + binding = itemView; + } + } +} diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/ReorderVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/ReorderVM.java index 4f3565b3..49854552 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/ReorderVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/ReorderVM.java @@ -112,6 +112,25 @@ public class ReorderVM extends AndroidViewModel { } + public LiveData getAllPinned() { + pinnedMutableLiveData = new MutableLiveData<>(); + new Thread(() -> { + Pinned pinned = null; + try { + pinned = new Pinned(getApplication().getApplicationContext()).getAllPinned(currentAccount); + + } catch (DBException e) { + e.printStackTrace(); + } + Handler mainHandler = new Handler(Looper.getMainLooper()); + Pinned finalPinned = pinned; + Runnable myRunnable = () -> pinnedMutableLiveData.setValue(finalPinned); + mainHandler.post(myRunnable); + }).start(); + return pinnedMutableLiveData; + } + + public LiveData getBottomMenu() { bottomMenuMutableLiveData = new MutableLiveData<>(); new Thread(() -> { diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TagVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TagVM.java index 8f9b5af3..62b90327 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TagVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TagVM.java @@ -28,8 +28,6 @@ import java.util.concurrent.TimeUnit; import app.fedilab.android.client.endpoints.MastodonTagService; import app.fedilab.android.client.entities.api.Pagination; -import app.fedilab.android.client.entities.api.Status; -import app.fedilab.android.client.entities.api.Statuses; import app.fedilab.android.client.entities.api.Tag; import app.fedilab.android.client.entities.api.Tags; import app.fedilab.android.helper.Helper; @@ -76,19 +74,15 @@ public class TagVM extends AndroidViewModel { /** * Return followed tags with pagination * - * @return {@link LiveData} containing a {@link Statuses}. Note: Not to be confused with {@link Status} + * @return {@link LiveData} containing a {@link Tags}. Note: Not to be confused with {@link Tag} */ - public LiveData followedTags(@NonNull String instance, String token, - String maxId, - String sinceId, - String minId, - int count) { + public LiveData followedTags(@NonNull String instance, String token) { tagsMutableLiveData = new MutableLiveData<>(); MastodonTagService mastodonTagService = init(instance); new Thread(() -> { List tagList = null; Pagination pagination = null; - Call> followedTagsListCall = mastodonTagService.getFollowedTags(token, maxId, sinceId, minId, count); + Call> followedTagsListCall = mastodonTagService.getFollowedTags(token); if (followedTagsListCall != null) { try { Response> tagsResponse = followedTagsListCall.execute(); @@ -111,6 +105,37 @@ public class TagVM extends AndroidViewModel { return tagsMutableLiveData; } + /** + * Return tag + * + * @return {@link LiveData} containing a {@link Tag} + */ + public LiveData getTag(@NonNull String instance, String token, + String tagName) { + tagMutableLiveData = new MutableLiveData<>(); + MastodonTagService mastodonTagService = init(instance); + new Thread(() -> { + Tag tag = null; + Call tagCall = mastodonTagService.getTag(token, tagName); + if (tagCall != null) { + try { + Response tagResponse = tagCall.execute(); + if (tagResponse.isSuccessful()) { + tag = tagResponse.body(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + Handler mainHandler = new Handler(Looper.getMainLooper()); + Tag finalTag = tag; + Runnable myRunnable = () -> tagMutableLiveData.setValue(finalTag); + mainHandler.post(myRunnable); + }).start(); + return tagMutableLiveData; + } + + /** * Follow a tag * diff --git a/app/src/main/res/drawable/ic_baseline_post_add_24.xml b/app/src/main/res/drawable/ic_baseline_post_add_24.xml new file mode 100644 index 00000000..59bf92d4 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_post_add_24.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_baseline_tag_24.xml b/app/src/main/res/drawable/ic_baseline_tag_24.xml new file mode 100644 index 00000000..e996ae23 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_tag_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_followed_tags.xml b/app/src/main/res/layout/activity_followed_tags.xml new file mode 100644 index 00000000..6a1558ee --- /dev/null +++ b/app/src/main/res/layout/activity_followed_tags.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/popup_add_followed_tagt.xml b/app/src/main/res/layout/popup_add_followed_tagt.xml new file mode 100644 index 00000000..2b9bdc0a --- /dev/null +++ b/app/src/main/res/layout/popup_add_followed_tagt.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml index 553d01f4..c82203cc 100644 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -25,6 +25,10 @@ android:id="@+id/nav_list" android:icon="@drawable/ic_baseline_view_list_24" android:title="@string/action_lists" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_hashtag.xml b/app/src/main/res/menu/menu_hashtag.xml new file mode 100644 index 00000000..3a6740ff --- /dev/null +++ b/app/src/main/res/menu/menu_hashtag.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main_followed_tag.xml b/app/src/main/res/menu/menu_main_followed_tag.xml new file mode 100644 index 00000000..90e7f470 --- /dev/null +++ b/app/src/main/res/menu/menu_main_followed_tag.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e630c786..7d33f0c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1974,4 +1974,13 @@ You should restart the application to apply changes. Languages in picker Allow to reduce the list of languages in the picker when composing a message. + You don\'t follow any tags! + Unfollow tag + Are you sure you want to unfollow this tag? + Unfollow + Follow a tag + Write the tag to follow + Tag name is not valid! + Followed tags + Follow tag