Commit 67f7e775 authored by Sajal Narang's avatar Sajal Narang Committed by GitHub

Merge pull request #248 from wncc/notif

Change notifications to sheet, add animations to recyclerviews
parents 805b324d 39e4261a
...@@ -4,25 +4,21 @@ import java.util.ArrayList; ...@@ -4,25 +4,21 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class UpdatableList<T> extends ArrayList<T> { public class UpdatableList<T> extends ArrayList<T> {
private List<T> cache = new ArrayList<>(); /** Convert a list to updatable list */
public void setList(List<T> list) {
public List<T> getCache() { this.clear();
return cache; this.addAll(list);
}
public void setCache(List<T> mCache) {
cache = mCache;
} }
/** Update existing or add */ /** Update existing or add */
public void updateCache(T t) { public void updateCache(T t) {
for (int i = 0; i < cache.size(); i++) { for (int i = 0; i < this.size(); i++) {
T cachedT = cache.get(i); T cachedT = this.get(i);
if (cachedT.equals(t)) { if (cachedT.equals(t)) {
cache.set(i, t); this.set(i, t);
return; return;
} }
} }
cache.add(t); this.add(t);
} }
} }
...@@ -20,6 +20,7 @@ import app.insti.activity.MainActivity; ...@@ -20,6 +20,7 @@ import app.insti.activity.MainActivity;
import app.insti.api.RetrofitInterface; import app.insti.api.RetrofitInterface;
import app.insti.api.model.Body; import app.insti.api.model.Body;
import app.insti.api.model.Event; import app.insti.api.model.Event;
import app.insti.api.model.Notification;
import app.insti.api.model.User; import app.insti.api.model.User;
import app.insti.fragment.BodyFragment; import app.insti.fragment.BodyFragment;
import app.insti.fragment.EventFragment; import app.insti.fragment.EventFragment;
...@@ -27,6 +28,8 @@ import app.insti.fragment.UserFragment; ...@@ -27,6 +28,8 @@ import app.insti.fragment.UserFragment;
public final class Utils { public final class Utils {
public static UpdatableList<Event> eventCache = new UpdatableList<>(); public static UpdatableList<Event> eventCache = new UpdatableList<>();
public static UpdatableList<Notification> notificationCache = null;
private static String sessionId; private static String sessionId;
private static RetrofitInterface retrofitInterface; private static RetrofitInterface retrofitInterface;
public static Gson gson; public static Gson gson;
......
...@@ -47,6 +47,7 @@ import java.util.List; ...@@ -47,6 +47,7 @@ import java.util.List;
import app.insti.Constants; import app.insti.Constants;
import app.insti.R; import app.insti.R;
import app.insti.SessionManager; import app.insti.SessionManager;
import app.insti.UpdatableList;
import app.insti.Utils; import app.insti.Utils;
import app.insti.api.EmptyCallback; import app.insti.api.EmptyCallback;
import app.insti.api.RetrofitInterface; import app.insti.api.RetrofitInterface;
...@@ -95,8 +96,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ...@@ -95,8 +96,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private User currentUser; private User currentUser;
private BackHandledFragment selectedFragment; private BackHandledFragment selectedFragment;
private Menu menu; private Menu menu;
private RetrofitInterface retrofitInterface;
private List<Notification> notifications = null;
public static void hideKeyboard(Activity activity) { public static void hideKeyboard(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
...@@ -180,7 +179,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ...@@ -180,7 +179,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
*/ */
private void fetchNotifications() { private void fetchNotifications() {
// Try memory cache // Try memory cache
if (notifications != null) { if (Utils.notificationCache != null) {
showNotifications(); showNotifications();
return; return;
} }
...@@ -191,7 +190,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ...@@ -191,7 +190,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
@Override @Override
public void onResponse(Call<List<Notification>> call, Response<List<Notification>> response) { public void onResponse(Call<List<Notification>> call, Response<List<Notification>> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
notifications = response.body(); Utils.notificationCache = new UpdatableList<>();
Utils.notificationCache.setList(response.body());
showNotifications(); showNotifications();
} }
} }
...@@ -202,7 +202,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ...@@ -202,7 +202,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
* Show the right notification icon * Show the right notification icon
*/ */
private void showNotifications() { private void showNotifications() {
if (notifications != null && !notifications.isEmpty()) { if (Utils.notificationCache != null && !Utils.notificationCache.isEmpty()) {
menu.findItem(R.id.action_notifications).setIcon(R.drawable.baseline_notifications_active_white_24); menu.findItem(R.id.action_notifications).setIcon(R.drawable.baseline_notifications_active_white_24);
} else { } else {
menu.findItem(R.id.action_notifications).setIcon(R.drawable.ic_notifications_white_24dp); menu.findItem(R.id.action_notifications).setIcon(R.drawable.ic_notifications_white_24dp);
...@@ -521,7 +521,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ...@@ -521,7 +521,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
if (id == R.id.action_notifications) { if (id == R.id.action_notifications) {
NotificationsFragment notificationsFragment = new NotificationsFragment(); NotificationsFragment notificationsFragment = new NotificationsFragment();
updateFragment(notificationsFragment); notificationsFragment.show(getSupportFragmentManager(), TAG);
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
......
...@@ -38,6 +38,7 @@ public abstract class CardAdapter<T extends CardInterface> extends RecyclerView. ...@@ -38,6 +38,7 @@ public abstract class CardAdapter<T extends CardInterface> extends RecyclerView.
public CardAdapter(List<T> tList, Fragment fragment) { public CardAdapter(List<T> tList, Fragment fragment) {
this.tList = tList; this.tList = tList;
mFragment = fragment; mFragment = fragment;
this.setHasStableIds(true);
} }
@Override @Override
...@@ -114,6 +115,11 @@ public abstract class CardAdapter<T extends CardInterface> extends RecyclerView. ...@@ -114,6 +115,11 @@ public abstract class CardAdapter<T extends CardInterface> extends RecyclerView.
else return 2; else return 2;
} }
@Override
public long getItemId(int position) {
return tList.get(position).getId();
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return tList.size(); return tList.size();
......
...@@ -19,6 +19,7 @@ public class MessMenuAdapter extends RecyclerView.Adapter<MessMenuAdapter.ViewHo ...@@ -19,6 +19,7 @@ public class MessMenuAdapter extends RecyclerView.Adapter<MessMenuAdapter.ViewHo
public MessMenuAdapter(List<MessMenu> messMenus) { public MessMenuAdapter(List<MessMenu> messMenus) {
this.messMenus = messMenus; this.messMenus = messMenus;
this.setHasStableIds(true);
} }
@NonNull @NonNull
...@@ -42,11 +43,20 @@ public class MessMenuAdapter extends RecyclerView.Adapter<MessMenuAdapter.ViewHo ...@@ -42,11 +43,20 @@ public class MessMenuAdapter extends RecyclerView.Adapter<MessMenuAdapter.ViewHo
holder.dinner.setText(messMenu.getDinner()); holder.dinner.setText(messMenu.getDinner());
} }
@Override
public long getItemId(int position) {
return messMenus.get(position).getMealID().hashCode();
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return messMenus.size(); return messMenus.size();
} }
public void setMenu(List<MessMenu> menus) {
messMenus = menus;
}
private String generateDayString(int day) { private String generateDayString(int day) {
switch (day) { switch (day) {
case 1: case 1:
......
...@@ -35,6 +35,7 @@ public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i ...@@ -35,6 +35,7 @@ public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i
public NewsAdapter(List<NewsArticle> newsArticles, ItemClickListener itemClickListener) { public NewsAdapter(List<NewsArticle> newsArticles, ItemClickListener itemClickListener) {
this.newsArticles = newsArticles; this.newsArticles = newsArticles;
this.itemClickListener = itemClickListener; this.itemClickListener = itemClickListener;
this.setHasStableIds(true);
} }
@Override @Override
...@@ -108,6 +109,11 @@ public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i ...@@ -108,6 +109,11 @@ public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i
return newsArticles.size() > position ? VIEW_ITEM : VIEW_PROG; return newsArticles.size() > position ? VIEW_ITEM : VIEW_PROG;
} }
@Override
public long getItemId(int position) {
return newsArticles.get(position).getArticleID().hashCode();
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return NewsFragment.showLoader ? (newsArticles.size() + 1) : newsArticles.size(); return NewsFragment.showLoader ? (newsArticles.size() + 1) : newsArticles.size();
......
...@@ -15,12 +15,16 @@ import app.insti.api.model.Event; ...@@ -15,12 +15,16 @@ import app.insti.api.model.Event;
import app.insti.api.model.Notification; import app.insti.api.model.Notification;
import app.insti.api.model.PlacementBlogPost; import app.insti.api.model.PlacementBlogPost;
import app.insti.fragment.NewsFragment; import app.insti.fragment.NewsFragment;
import app.insti.fragment.NotificationsFragment;
import app.insti.fragment.PlacementBlogFragment; import app.insti.fragment.PlacementBlogFragment;
import app.insti.fragment.TrainingBlogFragment; import app.insti.fragment.TrainingBlogFragment;
public class NotificationsAdapter extends CardAdapter<Notification> { public class NotificationsAdapter extends CardAdapter<Notification> {
private NotificationsFragment notificationsFragment;
public NotificationsAdapter(List<Notification> notifications, Fragment fragment) { public NotificationsAdapter(List<Notification> notifications, Fragment fragment) {
super(notifications, fragment); super(notifications, fragment);
notificationsFragment = (NotificationsFragment) fragment;
} }
@Override @Override
...@@ -30,6 +34,9 @@ public class NotificationsAdapter extends CardAdapter<Notification> { ...@@ -30,6 +34,9 @@ public class NotificationsAdapter extends CardAdapter<Notification> {
String sessId = Utils.getSessionIDHeader(); String sessId = Utils.getSessionIDHeader();
retrofitInterface.markNotificationRead(sessId, notification.getNotificationId().toString()).enqueue(new EmptyCallback<Void>()); retrofitInterface.markNotificationRead(sessId, notification.getNotificationId().toString()).enqueue(new EmptyCallback<Void>());
/* Close the bottom sheet */
notificationsFragment.dismiss();
/* Open event */ /* Open event */
if (notification.isEvent()) { if (notification.isEvent()) {
Gson gson = new Gson(); Gson gson = new Gson();
......
...@@ -32,6 +32,7 @@ public class PlacementBlogAdapter extends RecyclerView.Adapter<RecyclerView.View ...@@ -32,6 +32,7 @@ public class PlacementBlogAdapter extends RecyclerView.Adapter<RecyclerView.View
public PlacementBlogAdapter(List<PlacementBlogPost> posts, ItemClickListener itemClickListener) { public PlacementBlogAdapter(List<PlacementBlogPost> posts, ItemClickListener itemClickListener) {
this.posts = posts; this.posts = posts;
this.itemClickListener = itemClickListener; this.itemClickListener = itemClickListener;
this.setHasStableIds(true);
} }
public List<PlacementBlogPost> getPosts() { public List<PlacementBlogPost> getPosts() {
...@@ -100,6 +101,11 @@ public class PlacementBlogAdapter extends RecyclerView.Adapter<RecyclerView.View ...@@ -100,6 +101,11 @@ public class PlacementBlogAdapter extends RecyclerView.Adapter<RecyclerView.View
} }
} }
@Override
public long getItemId(int position) {
return posts.get(position).getPostID().hashCode();
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder { public static class ProgressViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar; public ProgressBar progressBar;
......
...@@ -34,6 +34,7 @@ public class TrainingBlogAdapter extends RecyclerView.Adapter<RecyclerView.ViewH ...@@ -34,6 +34,7 @@ public class TrainingBlogAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
public TrainingBlogAdapter(List<TrainingBlogPost> posts, ItemClickListener itemClickListener) { public TrainingBlogAdapter(List<TrainingBlogPost> posts, ItemClickListener itemClickListener) {
this.posts = posts; this.posts = posts;
this.itemClickListener = itemClickListener; this.itemClickListener = itemClickListener;
this.setHasStableIds(true);
} }
public List<TrainingBlogPost> getPosts() { public List<TrainingBlogPost> getPosts() {
...@@ -96,6 +97,11 @@ public class TrainingBlogAdapter extends RecyclerView.Adapter<RecyclerView.ViewH ...@@ -96,6 +97,11 @@ public class TrainingBlogAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
return posts.size() > position ? VIEW_ITEM : VIEW_PROG; return posts.size() > position ? VIEW_ITEM : VIEW_PROG;
} }
@Override
public long getItemId(int position) {
return posts.get(position).getPostID().hashCode();
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return TrainingBlogFragment.showLoader ? (posts.size() + 1) : posts.size(); return TrainingBlogFragment.showLoader ? (posts.size() + 1) : posts.size();
......
...@@ -184,6 +184,10 @@ public class Body implements CardInterface { ...@@ -184,6 +184,10 @@ public class Body implements CardInterface {
this.bodyRoles = bodyRoles; this.bodyRoles = bodyRoles;
} }
public long getId() {
return getBodyID().hashCode();
}
public String getTitle() { public String getTitle() {
return getBodyName(); return getBodyName();
} }
......
...@@ -243,6 +243,10 @@ public class Event implements CardInterface { ...@@ -243,6 +243,10 @@ public class Event implements CardInterface {
return Objects.hash(eventID); return Objects.hash(eventID);
} }
public long getId() {
return hashCode();
}
public String getTitle() { public String getTitle() {
return getEventName(); return getEventName();
} }
......
...@@ -79,6 +79,10 @@ public class Notification implements CardInterface { ...@@ -79,6 +79,10 @@ public class Notification implements CardInterface {
this.notificationActor = notificationActor; this.notificationActor = notificationActor;
} }
public long getId() {
return getNotificationId().hashCode();
}
public String getTitle() { public String getTitle() {
if (isEvent()) { if (isEvent()) {
return getEvent().getEventName(); return getEvent().getEventName();
......
...@@ -121,6 +121,10 @@ public class Role implements CardInterface { ...@@ -121,6 +121,10 @@ public class Role implements CardInterface {
this.roleUsersDetail = roleUsersDetail; this.roleUsersDetail = roleUsersDetail;
} }
public long getId() {
return getRoleID().hashCode();
}
public String getTitle() { public String getTitle() {
return getRoleBodyDetails().getBodyName(); return getRoleBodyDetails().getBodyName();
} }
......
...@@ -238,6 +238,10 @@ public class User implements CardInterface { ...@@ -238,6 +238,10 @@ public class User implements CardInterface {
return new Gson().toJson(this); return new Gson().toJson(this);
} }
public long getId() {
return getUserID().hashCode();
}
public String getTitle() { public String getTitle() {
return getUserName(); return getUserName();
} }
......
...@@ -74,10 +74,10 @@ public class FeedFragment extends BaseFragment { ...@@ -74,10 +74,10 @@ public class FeedFragment extends BaseFragment {
fab = getView().findViewById(R.id.fab); fab = getView().findViewById(R.id.fab);
// Initialize the feed // Initialize the feed
if (Utils.eventCache.getCache() == null || Utils.eventCache.getCache().size() == 0) { if (Utils.eventCache == null || Utils.eventCache.isEmpty()) {
updateFeed(); updateFeed();
} else { } else {
displayEvents(Utils.eventCache.getCache()); displayEvents(Utils.eventCache);
} }
} }
...@@ -107,8 +107,8 @@ public class FeedFragment extends BaseFragment { ...@@ -107,8 +107,8 @@ public class FeedFragment extends BaseFragment {
@Override @Override
public void onResponse(Call<NewsFeedResponse> call, Response<NewsFeedResponse> response) { public void onResponse(Call<NewsFeedResponse> call, Response<NewsFeedResponse> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
Utils.eventCache.setCache(response.body().getEvents()); Utils.eventCache.setList(response.body().getEvents());
displayEvents(Utils.eventCache.getCache()); displayEvents(Utils.eventCache);
} }
//Server Error //Server Error
feedSwipeRefreshLayout.setRefreshing(false); feedSwipeRefreshLayout.setRefreshing(false);
......
...@@ -37,6 +37,7 @@ import retrofit2.Response; ...@@ -37,6 +37,7 @@ import retrofit2.Response;
*/ */
public class MessMenuFragment extends BaseFragment { public class MessMenuFragment extends BaseFragment {
private MessMenuAdapter messMenuAdapter;
private RecyclerView messMenuRecyclerView; private RecyclerView messMenuRecyclerView;
private SwipeRefreshLayout messMenuSwipeRefreshLayout; private SwipeRefreshLayout messMenuSwipeRefreshLayout;
private Spinner hostelSpinner; private Spinner hostelSpinner;
...@@ -164,14 +165,19 @@ public class MessMenuFragment extends BaseFragment { ...@@ -164,14 +165,19 @@ public class MessMenuFragment extends BaseFragment {
} }
} }
final MessMenuAdapter messMenuAdapter = new MessMenuAdapter(sortedMenus);
getActivityBuffer().safely(new ActivityBuffer.IRunnable() { getActivityBuffer().safely(new ActivityBuffer.IRunnable() {
@Override @Override
public void run(Activity pActivity) { public void run(Activity pActivity) {
try { try {
if (messMenuAdapter == null) {
messMenuAdapter = new MessMenuAdapter(sortedMenus);
messMenuRecyclerView = getActivity().findViewById(R.id.mess_menu_recycler_view); messMenuRecyclerView = getActivity().findViewById(R.id.mess_menu_recycler_view);
messMenuRecyclerView.setAdapter(messMenuAdapter); messMenuRecyclerView.setAdapter(messMenuAdapter);
messMenuRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); messMenuRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
} else {
messMenuAdapter.setMenu(sortedMenus);
messMenuAdapter.notifyDataSetChanged();
}
} catch (NullPointerException e) { } catch (NullPointerException e) {
e.printStackTrace(); e.printStackTrace();
} }
......
...@@ -2,10 +2,10 @@ package app.insti.fragment; ...@@ -2,10 +2,10 @@ package app.insti.fragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.BottomSheetDialogFragment;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
...@@ -13,22 +13,22 @@ import android.view.ViewGroup; ...@@ -13,22 +13,22 @@ import android.view.ViewGroup;
import java.util.List; import java.util.List;
import app.insti.R; import app.insti.R;
import app.insti.UpdatableList;
import app.insti.Utils; import app.insti.Utils;
import app.insti.adapter.NotificationsAdapter; import app.insti.adapter.NotificationsAdapter;
import app.insti.api.EmptyCallback;
import app.insti.api.RetrofitInterface; import app.insti.api.RetrofitInterface;
import app.insti.api.model.Notification; import app.insti.api.model.Notification;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
/** /**
* A simple {@link Fragment} subclass. * A simple {@link Fragment} subclass.
*/ */
public class NotificationsFragment extends BaseFragment { public class NotificationsFragment extends BottomSheetDialogFragment {
RecyclerView notificationsRecyclerView; private RecyclerView notificationsRecyclerView;
private NotificationsAdapter notificationsAdapter = null;
List<Notification> notifications;
public NotificationsFragment() { public NotificationsFragment() {
// Required empty public constructor // Required empty public constructor
...@@ -46,35 +46,42 @@ public class NotificationsFragment extends BaseFragment { ...@@ -46,35 +46,42 @@ public class NotificationsFragment extends BaseFragment {
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
Toolbar toolbar = getActivity().findViewById(R.id.toolbar); /* Show cached notifications */
toolbar.setTitle("Notifications"); if (Utils.notificationCache != null) {
showNotifications(Utils.notificationCache);
} else {
Utils.notificationCache = new UpdatableList<>();
}
/* Update notifications */
RetrofitInterface retrofitInterface = Utils.getRetrofitInterface(); RetrofitInterface retrofitInterface = Utils.getRetrofitInterface();
retrofitInterface.getNotifications(Utils.getSessionIDHeader()).enqueue(new Callback<List<Notification>>() { retrofitInterface.getNotifications(Utils.getSessionIDHeader()).enqueue(new EmptyCallback<List<Notification>>() {
@Override @Override
public void onResponse(Call<List<Notification>> call, Response<List<Notification>> response) { public void onResponse(Call<List<Notification>> call, Response<List<Notification>> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
notifications = response.body(); Utils.notificationCache.setList(response.body());
showNotifications(notifications); showNotifications(Utils.notificationCache);
}
} }
@Override
public void onFailure(Call<List<Notification>> call, Throwable t) {
} }
}); });
} }
private void showNotifications(final List<Notification> notifications) { private void showNotifications(final List<Notification> notifications) {
/* Check if activity is done with */ /* Check if activity is done with */
if (getActivity() == null) return; if (getActivity() == null || getView() == null) return;
/* Hide loader */ /* Hide loader */
getActivity().findViewById(R.id.loadingPanel).setVisibility(View.GONE); getView().findViewById(R.id.loadingPanel).setVisibility(View.GONE);
NotificationsAdapter notificationsAdapter = new NotificationsAdapter(notifications, this); /* Initialize */
notificationsRecyclerView = (RecyclerView) getActivity().findViewById(R.id.notifications_recycler_view); if (notificationsAdapter == null) {
notificationsAdapter = new NotificationsAdapter(notifications, this);
notificationsRecyclerView = (RecyclerView) getView().findViewById(R.id.notifications_recycler_view);
notificationsRecyclerView.setAdapter(notificationsAdapter); notificationsRecyclerView.setAdapter(notificationsAdapter);
notificationsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); notificationsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
} else {
notificationsAdapter.setList(notifications);
notificationsAdapter.notifyDataSetChanged();
}
} }
} }
package app.insti.interfaces; package app.insti.interfaces;
public interface CardInterface { public interface CardInterface {
long getId();
String getTitle(); String getTitle();
String getSubtitle(); String getSubtitle();
String getAvatarUrl(); String getAvatarUrl();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment