From 546346ff48136e976d8c8a641d20f870eb0e30e5 Mon Sep 17 00:00:00 2001 From: opyale Date: Mon, 1 Jun 2020 16:43:58 +0000 Subject: [PATCH] Improving milestones. (#525) Removing unused strings. Adding a little bit more space between title and progress bar. Swapping icons. Merge remote-tracking branch 'remotes/main/master' into improve-milestones # Conflicts: # app/src/main/res/layout/list_milestones.xml Minor improvements. Merge branch 'improve-layouts' of https://gitea.com/opyale/GitNex into improve-milestones Merge branch 'improve-layouts' of https://gitea.com/gitnex/GitNex into improve-milestones Moving items. Merge branch 'master' into improve-layouts Merge branch 'master' into improve-layouts Translation and additional formatting. Enhancing editorconfig Formatting and removing unused imports. Removing milestone state. Improving milestones. branches and milestones layout update. Fix milestone infinite pagination loop for lower versions layout updates for issues, pr. Fix pr nullable objects for lower versions improve files layout improve org info and list orgs improve teams list layout by org Fix repo layouts Co-authored-by: M M Arif Reviewed-on: https://gitea.com/gitnex/GitNex/pulls/525 Reviewed-by: M M Arif --- .editorconfig | 1 + .../gitnex/adapters/MilestonesAdapter.java | 557 +++++++++--------- app/src/main/res/drawable/progress_bar.xml | 8 +- app/src/main/res/layout/list_milestones.xml | 183 +++--- app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/strings.xml | 6 +- 6 files changed, 384 insertions(+), 373 deletions(-) diff --git a/.editorconfig b/.editorconfig index b795216e..a91945a3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,6 +4,7 @@ end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true +trim_trailing_whitespace = true max_line_length = 150 [*.java] diff --git a/app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java index 9807250e..f47ea10a 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java @@ -2,7 +2,6 @@ package org.mian.gitnex.adapters; import android.annotation.SuppressLint; import android.content.Context; -import android.graphics.Color; import android.graphics.drawable.Drawable; import android.net.Uri; import android.text.Spanned; @@ -15,7 +14,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; -import com.amulyakhare.textdrawable.TextDrawable; import com.google.android.material.bottomsheet.BottomSheetDialog; import com.vdurmont.emoji.EmojiParser; import org.mian.gitnex.R; @@ -56,369 +54,346 @@ import io.noties.markwon.linkify.LinkifyPlugin; public class MilestonesAdapter extends RecyclerView.Adapter { - private Context context; - private final int TYPE_LOAD = 0; - private List dataList; - private OnLoadMoreListener loadMoreListener; - private boolean isLoading = false; - private boolean isMoreDataAvailable = true; - private String TAG = StaticGlobalVariables.tagMilestonesAdapter; + private Context context; + private final int TYPE_LOAD = 0; + private List dataList; + private OnLoadMoreListener loadMoreListener; + private boolean isLoading = false; + private boolean isMoreDataAvailable = true; + private String TAG = StaticGlobalVariables.tagMilestonesAdapter; - public MilestonesAdapter(Context context, List dataListMain) { + public MilestonesAdapter(Context context, List dataListMain) { - this.context = context; - this.dataList = dataListMain; + this.context = context; + this.dataList = dataListMain; - } + } - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - LayoutInflater inflater = LayoutInflater.from(context); + LayoutInflater inflater = LayoutInflater.from(context); - if(viewType == TYPE_LOAD) { - return new MilestonesAdapter.DataHolder(inflater.inflate(R.layout.list_milestones, parent, false)); - } - else { - return new MilestonesAdapter.LoadHolder(inflater.inflate(R.layout.row_load, parent, false)); - } + if(viewType == TYPE_LOAD) { + return new MilestonesAdapter.DataHolder(inflater.inflate(R.layout.list_milestones, parent, false)); + } + else { + return new MilestonesAdapter.LoadHolder(inflater.inflate(R.layout.row_load, parent, false)); + } - } + } - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { - if(position >= getItemCount() - 1 && isMoreDataAvailable && !isLoading && loadMoreListener != null) { + if(position >= getItemCount() - 1 && isMoreDataAvailable && !isLoading && loadMoreListener != null) { - isLoading = true; - loadMoreListener.onLoadMore(); + isLoading = true; + loadMoreListener.onLoadMore(); - } + } - if(getItemViewType(position) == TYPE_LOAD) { + if(getItemViewType(position) == TYPE_LOAD) { - ((MilestonesAdapter.DataHolder) holder).bindData(dataList.get(position)); + ((MilestonesAdapter.DataHolder) holder).bindData(dataList.get(position)); - } + } - } + } - class DataHolder extends RecyclerView.ViewHolder { + class DataHolder extends RecyclerView.ViewHolder { - private TextView milestoneId; - private TextView msTitle; - private TextView msDescription; - private TextView msOpenIssues; - private TextView msClosedIssues; - private TextView msDueDate; - private ImageView msStatus; - private ProgressBar msProgress; - private TextView milestoneStatus; + private TextView milestoneId; + private TextView msTitle; + private TextView msDescription; + private TextView msOpenIssues; + private TextView msClosedIssues; + private TextView msDueDate; + private ProgressBar msProgress; + private TextView milestoneStatus; - DataHolder(View itemView) { + DataHolder(View itemView) { - super(itemView); + super(itemView); - milestoneId = itemView.findViewById(R.id.milestoneId); - msTitle = itemView.findViewById(R.id.milestoneTitle); - msStatus = itemView.findViewById(R.id.milestoneState); - msDescription = itemView.findViewById(R.id.milestoneDescription); - msOpenIssues = itemView.findViewById(R.id.milestoneIssuesOpen); - msClosedIssues = itemView.findViewById(R.id.milestoneIssuesClosed); - msDueDate = itemView.findViewById(R.id.milestoneDueDate); - msProgress = itemView.findViewById(R.id.milestoneProgress); - ImageView milestonesMenu = itemView.findViewById(R.id.milestonesMenu); - milestoneStatus = itemView.findViewById(R.id.milestoneStatus); + milestoneId = itemView.findViewById(R.id.milestoneId); + msTitle = itemView.findViewById(R.id.milestoneTitle); + msDescription = itemView.findViewById(R.id.milestoneDescription); + msOpenIssues = itemView.findViewById(R.id.milestoneIssuesOpen); + msClosedIssues = itemView.findViewById(R.id.milestoneIssuesClosed); + msDueDate = itemView.findViewById(R.id.milestoneDueDate); + msProgress = itemView.findViewById(R.id.milestoneProgress); + ImageView milestonesMenu = itemView.findViewById(R.id.milestonesMenu); + milestoneStatus = itemView.findViewById(R.id.milestoneStatus); - milestonesMenu.setOnClickListener(v -> { + milestonesMenu.setOnClickListener(v -> { - Context ctx = v.getContext(); - int milestoneId_ = Integer.parseInt(milestoneId.getText().toString()); + Context ctx = v.getContext(); + int milestoneId_ = Integer.parseInt(milestoneId.getText().toString()); - @SuppressLint("InflateParams") View view = LayoutInflater.from(ctx).inflate(R.layout.bottom_sheet_milestones_in_list, null); + @SuppressLint("InflateParams") View view = LayoutInflater.from(ctx).inflate(R.layout.bottom_sheet_milestones_in_list, null); - TextView closeMilestone = view.findViewById(R.id.closeMilestone); - TextView openMilestone = view.findViewById(R.id.openMilestone); + TextView closeMilestone = view.findViewById(R.id.closeMilestone); + TextView openMilestone = view.findViewById(R.id.openMilestone); - BottomSheetDialog dialog = new BottomSheetDialog(ctx); - dialog.setContentView(view); - dialog.show(); + BottomSheetDialog dialog = new BottomSheetDialog(ctx); + dialog.setContentView(view); + dialog.show(); - if(milestoneStatus.getText().toString().equals("open")) { + if(milestoneStatus.getText().toString().equals("open")) { - closeMilestone.setVisibility(View.VISIBLE); - openMilestone.setVisibility(View.GONE); + closeMilestone.setVisibility(View.VISIBLE); + openMilestone.setVisibility(View.GONE); - } - else { + } + else { - closeMilestone.setVisibility(View.GONE); - openMilestone.setVisibility(View.VISIBLE); + closeMilestone.setVisibility(View.GONE); + openMilestone.setVisibility(View.VISIBLE); - } + } - closeMilestone.setOnClickListener(v12 -> { + closeMilestone.setOnClickListener(v12 -> { - MilestoneActions.closeMilestone(ctx, milestoneId_); - dialog.dismiss(); - updateAdapter(getAdapterPosition()); + MilestoneActions.closeMilestone(ctx, milestoneId_); + dialog.dismiss(); + updateAdapter(getAdapterPosition()); - }); + }); - openMilestone.setOnClickListener(v12 -> { + openMilestone.setOnClickListener(v12 -> { - MilestoneActions.openMilestone(ctx, milestoneId_); - dialog.dismiss(); - updateAdapter(getAdapterPosition()); + MilestoneActions.openMilestone(ctx, milestoneId_); + dialog.dismiss(); + updateAdapter(getAdapterPosition()); - }); + }); - }); + }); - } - - @SuppressLint("SetTextI18n") - void bindData(Milestones dataModel) { - - final TinyDB tinyDb = new TinyDB(context); - final String locale = tinyDb.getString("locale"); - final String timeFormat = tinyDb.getString("dateFormat"); + } - milestoneId.setText(String.valueOf(dataModel.getId())); - milestoneStatus.setText(dataModel.getState()); + @SuppressLint("SetTextI18n") + void bindData(Milestones dataModel) { - final Markwon markwon = Markwon.builder(Objects.requireNonNull(context)) - .usePlugin(CorePlugin.create()) - .usePlugin(ImagesPlugin.create(plugin -> { - plugin.addSchemeHandler(new SchemeHandler() { - @NonNull - @Override - public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { - - final int resourceId = context.getResources().getIdentifier( - raw.substring("drawable://".length()), - "drawable", - context.getPackageName()); - - final Drawable drawable = context.getDrawable(resourceId); - - assert drawable != null; - return ImageItem.withResult(drawable); - } - - @NonNull - @Override - public Collection supportedSchemes() { - return Collections.singleton("drawable"); - } - }); - plugin.placeholderProvider(drawable -> null); - plugin.addMediaDecoder(GifMediaDecoder.create(false)); - plugin.addMediaDecoder(SvgMediaDecoder.create(context.getResources())); - plugin.addMediaDecoder(SvgMediaDecoder.create()); - plugin.defaultMediaDecoder(DefaultMediaDecoder.create(context.getResources())); - plugin.defaultMediaDecoder(DefaultMediaDecoder.create()); - })) - .usePlugin(new AbstractMarkwonPlugin() { - @Override - public void configureTheme(@NonNull MarkwonTheme.Builder builder) { - builder - .codeTextColor(tinyDb.getInt("codeBlockColor")) - .codeBackgroundColor(tinyDb.getInt("codeBlockBackground")) - .linkColor(context.getResources().getColor(R.color.lightBlue)); - } - }) - .usePlugin(TablePlugin.create(context)) - .usePlugin(TaskListPlugin.create(context)) - .usePlugin(HtmlPlugin.create()) - .usePlugin(StrikethroughPlugin.create()) - .usePlugin(LinkifyPlugin.create()) - .build(); - - Spanned msTitle_ = markwon.toMarkdown(dataModel.getTitle()); - markwon.setParsedMarkdown(msTitle, msTitle_); - - if(dataModel.getState().equals("open")) { - - @SuppressLint("ResourceType") int color = Color.parseColor(context.getResources().getString(R.color.releaseStable)); - TextDrawable drawable = TextDrawable.builder() - .beginConfig() - //.useFont(Typeface.DEFAULT) - .textColor(context.getResources().getColor(R.color.white)) - .fontSize(30) - .toUpperCase() - .width(120) - .height(60) - .endConfig() - .buildRoundRect("open", color, 8); - - msStatus.setImageDrawable(drawable); - - } - else if(dataModel.getState().equals("closed")) { - - @SuppressLint("ResourceType") int color = Color.parseColor(context.getResources().getString(R.color.colorRed)); - TextDrawable drawable = TextDrawable.builder() - .beginConfig() - //.useFont(Typeface.DEFAULT) - .textColor(context.getResources().getColor(R.color.white)) - .fontSize(30) - .toUpperCase() - .width(140) - .height(60) - .endConfig() - .buildRoundRect("closed", color, 8); - - msStatus.setImageDrawable(drawable); - - } - - if (!dataModel.getDescription().equals("")) { - final CharSequence bodyWithMD = markwon.toMarkdown(EmojiParser.parseToUnicode(dataModel.getDescription())); - msDescription.setText(bodyWithMD); - } - else { - msDescription.setText(""); - } - - msOpenIssues.setText(String.valueOf(dataModel.getOpen_issues())); - msOpenIssues.setOnClickListener(new ClickListener(context.getResources().getString(R.string.milestoneOpenIssues, dataModel.getOpen_issues()), context)); - - msClosedIssues.setText(String.valueOf(dataModel.getClosed_issues())); - msClosedIssues.setOnClickListener(new ClickListener(context.getResources().getString(R.string.milestoneClosedIssues, dataModel.getClosed_issues()), context)); - - if ((dataModel.getOpen_issues() + dataModel.getClosed_issues()) > 0) { - - if (dataModel.getOpen_issues() == 0) { - msProgress.setProgress(100); - msProgress.setOnClickListener(new ClickListener(context.getResources().getString(R.string.milestoneCompletion, 100), context)); - } - else { - int msCompletion = 100 * dataModel.getClosed_issues() / (dataModel.getOpen_issues() + dataModel.getClosed_issues()); - msProgress.setOnClickListener(new ClickListener(context.getResources().getString(R.string.milestoneCompletion, msCompletion), context)); - msProgress.setProgress(msCompletion); - } - - } - else { - msProgress.setProgress(0); - msProgress.setOnClickListener(new ClickListener(context.getResources().getString(R.string.milestoneCompletion, 0), context)); - } - - if(dataModel.getDue_on() != null) { - - if (timeFormat.equals("normal") || timeFormat.equals("pretty")) { - - DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", new Locale(locale)); - Date date = null; - try { - date = formatter.parse(dataModel.getDue_on()); - } - catch (ParseException e) { - Log.e(TAG, e.toString()); - } - assert date != null; - String dueDate = formatter.format(date); + final TinyDB tinyDb = new TinyDB(context); + final String locale = tinyDb.getString("locale"); + final String timeFormat = tinyDb.getString("dateFormat"); - if(date.before(new Date())) { - msDueDate.setTextColor(context.getResources().getColor(R.color.darkRed)); - } + milestoneId.setText(String.valueOf(dataModel.getId())); + milestoneStatus.setText(dataModel.getState()); - msDueDate.setText(dueDate); - msDueDate.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToast(dataModel.getDue_on()), context)); + Markwon markwon = Markwon.builder(Objects.requireNonNull(context)).usePlugin(CorePlugin.create()).usePlugin(ImagesPlugin.create(plugin -> { + plugin.addSchemeHandler(new SchemeHandler() { - } - else if (timeFormat.equals("normal1")) { + @NonNull + @Override + public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { - SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", new Locale(locale)); - Date date1 = null; - try { - date1 = formatter.parse(dataModel.getDue_on()); - } - catch (ParseException e) { - Log.e(TAG, e.toString()); - } - assert date1 != null; - String dueDate = formatter.format(date1); - msDueDate.setText(dueDate); + final int resourceId = context.getResources().getIdentifier( + raw.substring("drawable://".length()), + "drawable", + context.getPackageName()); - } + final Drawable drawable = context.getDrawable(resourceId); - } - else { - msDueDate.setText(""); - } + assert drawable != null; + return ImageItem.withResult(drawable); + } - } + @NonNull + @Override + public Collection supportedSchemes() { + return Collections.singleton("drawable"); + } + }); - } + plugin.placeholderProvider(drawable -> null); + plugin.addMediaDecoder(GifMediaDecoder.create(false)); + plugin.addMediaDecoder(SvgMediaDecoder.create(context.getResources())); + plugin.addMediaDecoder(SvgMediaDecoder.create()); + plugin.defaultMediaDecoder(DefaultMediaDecoder.create(context.getResources())); + plugin.defaultMediaDecoder(DefaultMediaDecoder.create()); - private void updateAdapter(int position) { + })) - dataList.remove(position); - notifyItemRemoved(position); - notifyItemRangeChanged(position, dataList.size()); + .usePlugin(new AbstractMarkwonPlugin() { + @Override + public void configureTheme(@NonNull MarkwonTheme.Builder builder) { + builder + .codeTextColor(tinyDb.getInt("codeBlockColor")) + .codeBackgroundColor(tinyDb.getInt("codeBlockBackground")) + .linkColor(context.getResources().getColor(R.color.lightBlue)); + } + }) + .usePlugin(TablePlugin.create(context)) + .usePlugin(TaskListPlugin.create(context)) + .usePlugin(HtmlPlugin.create()) + .usePlugin(StrikethroughPlugin.create()) + .usePlugin(LinkifyPlugin.create()) + .build(); - } + Spanned msTitle_ = markwon.toMarkdown(dataModel.getTitle()); + markwon.setParsedMarkdown(msTitle, msTitle_); - @Override - public int getItemViewType(int position) { + if(!dataModel.getDescription().equals("")) { - if(dataList.get(position).getTitle() != null) { - return TYPE_LOAD; - } - else { - return 1; - } + CharSequence bodyWithMD = markwon.toMarkdown(EmojiParser.parseToUnicode(dataModel.getDescription())); + msDescription.setText(bodyWithMD); - } + } + else { - @Override - public int getItemCount() { + msDescription.setText(context.getString(R.string.milestoneNoDescription)); + } - return dataList.size(); + msOpenIssues.setText(context.getString(R.string.milestoneIssueStatusOpen, dataModel.getOpen_issues())); + msClosedIssues.setText(context.getString(R.string.milestoneIssueStatusClosed, dataModel.getClosed_issues())); - } + if((dataModel.getOpen_issues() + dataModel.getClosed_issues()) > 0) { - static class LoadHolder extends RecyclerView.ViewHolder { + if(dataModel.getOpen_issues() == 0) { - LoadHolder(View itemView) { + msProgress.setProgress(100); + msProgress.setOnClickListener(new ClickListener(context.getResources().getString(R.string.milestoneCompletion, 100), context)); - super(itemView); - } + } + else { - } + int msCompletion = 100 * dataModel.getClosed_issues() / (dataModel.getOpen_issues() + dataModel.getClosed_issues()); + msProgress.setOnClickListener(new ClickListener(context.getResources().getString(R.string.milestoneCompletion, msCompletion), context)); + msProgress.setProgress(msCompletion); - public void setMoreDataAvailable(boolean moreDataAvailable) { + } - isMoreDataAvailable = moreDataAvailable; + } + else { - } + msProgress.setProgress(0); + msProgress.setOnClickListener(new ClickListener(context.getResources().getString(R.string.milestoneCompletion, 0), context)); - public void notifyDataChanged() { + } - notifyDataSetChanged(); - isLoading = false; + if(dataModel.getDue_on() != null) { - } + if(timeFormat.equals("normal") || timeFormat.equals("pretty")) { - public interface OnLoadMoreListener { + DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", new Locale(locale)); + Date date = null; - void onLoadMore(); + try { + date = formatter.parse(dataModel.getDue_on()); + } + catch(ParseException e) { + Log.e(TAG, e.toString()); + } - } + assert date != null; + String dueDate = formatter.format(date); - public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) { + if(date.before(new Date())) { + msDueDate.setTextColor(context.getResources().getColor(R.color.darkRed)); + } - this.loadMoreListener = loadMoreListener; + msDueDate.setText(dueDate); + msDueDate.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToast(dataModel.getDue_on()), context)); - } + } + else if(timeFormat.equals("normal1")) { - public void updateList(List list) { + SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", new Locale(locale)); - dataList = list; - notifyDataSetChanged(); - } + Date date1 = null; + + try { + date1 = formatter.parse(dataModel.getDue_on()); + } + catch(ParseException e) { + Log.e(TAG, e.toString()); + } + + assert date1 != null; + String dueDate = formatter.format(date1); + msDueDate.setText(dueDate); + + } + + } + else { + + msDueDate.setText(context.getString(R.string.milestoneNoDueDate)); + } + + } + + } + + private void updateAdapter(int position) { + + dataList.remove(position); + notifyItemRemoved(position); + notifyItemRangeChanged(position, dataList.size()); + + } + + @Override + public int getItemViewType(int position) { + + if(dataList.get(position).getTitle() != null) { + return TYPE_LOAD; + } + else { + return 1; + } + + } + + @Override + public int getItemCount() { + + return dataList.size(); + + } + + static class LoadHolder extends RecyclerView.ViewHolder { + + LoadHolder(View itemView) { + + super(itemView); + } + + } + + public void setMoreDataAvailable(boolean moreDataAvailable) { + + isMoreDataAvailable = moreDataAvailable; + + } + + public void notifyDataChanged() { + + notifyDataSetChanged(); + isLoading = false; + + } + + public interface OnLoadMoreListener { + + void onLoadMore(); + + } + + public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) { + + this.loadMoreListener = loadMoreListener; + + } + + public void updateList(List list) { + + dataList = list; + notifyDataSetChanged(); + } } diff --git a/app/src/main/res/drawable/progress_bar.xml b/app/src/main/res/drawable/progress_bar.xml index 935176f9..b8b362e3 100644 --- a/app/src/main/res/drawable/progress_bar.xml +++ b/app/src/main/res/drawable/progress_bar.xml @@ -9,8 +9,8 @@ android:right="1dp" android:left="1dp"> - - + + @@ -23,9 +23,9 @@ - + - \ No newline at end of file + diff --git a/app/src/main/res/layout/list_milestones.xml b/app/src/main/res/layout/list_milestones.xml index e1c256ac..f6b5ebab 100644 --- a/app/src/main/res/layout/list_milestones.xml +++ b/app/src/main/res/layout/list_milestones.xml @@ -1,6 +1,8 @@ + tools:visibility="visible" /> - - - - - - - - - - - - + android:progressTint="@color/btnBackground" /> - + + + + android:textSize="14sp" /> + + + + + + + + + + + + + + + + + + + + + + + + android:src="@drawable/ic_dotted_menu_horizontal" /> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5bd6a624..a691ee7d 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -18,7 +18,7 @@ #1d1d1d #eb7121 #219214 - #eb3718 + #CC361C #49da39 #009486 #e74c3c diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c5f98a00..775bab8c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -232,8 +232,10 @@ Milestone created successfully Something went wrong, please try again Please choose due date - Open issues : %1$d - Closed issues : %1$d + No due date + No description + %1$d Open + %1$d Closed Select Assignees Select Labels