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