Pinned issues, d and bash lang support, pin and unpin an issue (#1401)

Closes #1395

Closes #1398

Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1401
Co-authored-by: M M Arif <mmarif@swatian.com>
Co-committed-by: M M Arif <mmarif@swatian.com>
This commit is contained in:
M M Arif 2024-11-14 18:42:50 +00:00 committed by M M Arif
parent 9673def07c
commit 4493917b39
25 changed files with 853 additions and 34 deletions

View File

@ -4,6 +4,7 @@ steps:
commands:
- ./gradlew assembleFreeRelease
when:
event: [ push ]
path: [ app/**, build.gradle ]
sign:

View File

@ -5,13 +5,22 @@ steps:
pattern: "*.java"
regex: " \\\\* \\\\@author [\\\\S\\\\s]+"
must_contain: true
when:
event: [ push ]
path: [ app/**, build.gradle ]
style:
image: alvrme/alpine-android:android-32-jdk17
commands:
- ./gradlew :app:spotlessCheck
when:
event: [ push ]
path: [ app/**, build.gradle ]
test:
image: alvrme/alpine-android:android-32-jdk17
commands:
- ./gradlew test
when:
event: [ push ]
path: [ app/**, build.gradle ]

View File

@ -25,6 +25,7 @@ android {
}
buildFeatures {
viewBinding true
buildConfig true
}
buildTypes {
release {
@ -58,12 +59,12 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.compose.material3:material3:1.3.0'
implementation 'androidx.compose.material3:material3-window-size-class:1.3.0'
implementation 'androidx.compose.material3:material3:1.3.1'
implementation 'androidx.compose.material3:material3-window-size-class:1.3.1'
implementation 'androidx.viewpager2:viewpager2:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:2.7.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:2.8.7"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'

View File

@ -254,6 +254,97 @@ public class IssueActions {
});
}
public static void pinIssue(final Context ctx, IssueContext issue) {
Call<Void> call;
call =
RetrofitClient.getApiInterface(ctx)
.pinIssue(
issue.getRepository().getOwner(),
issue.getRepository().getName(),
(long) issue.getIssueIndex());
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<Void> call, @NonNull retrofit2.Response<Void> response) {
if (response.isSuccessful()) {
if (response.code() == 204) {
Toasty.success(ctx, ctx.getString(R.string.issue_pinned));
IssuesFragment.resumeIssues = true;
issue.setPinned(true);
}
} else if (response.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx);
} else {
Toasty.error(ctx, ctx.getString(R.string.pinning_failed));
}
}
@Override
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
Toasty.error(
ctx,
ctx.getResources().getString(R.string.genericServerResponseError));
}
});
}
public static void unpinIssue(final Context ctx, IssueContext issue) {
Call<Void> call;
call =
RetrofitClient.getApiInterface(ctx)
.unpinIssue(
issue.getRepository().getOwner(),
issue.getRepository().getName(),
(long) issue.getIssueIndex());
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<Void> call, @NonNull retrofit2.Response<Void> response) {
if (response.isSuccessful()) {
if (response.code() == 204) {
Toasty.success(ctx, ctx.getString(R.string.issue_unpinned));
IssuesFragment.resumeIssues = true;
issue.setPinned(false);
issue.getIssue().setPinOrder(0L);
}
} else if (response.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx);
} else {
Toasty.error(ctx, ctx.getString(R.string.unpinning_failed));
}
}
@Override
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
Toasty.error(
ctx,
ctx.getResources().getString(R.string.genericServerResponseError));
}
});
}
public static ActionResult<ActionResult.None> reply(
Context context, String comment, IssueContext issue) {

View File

@ -45,7 +45,7 @@ public class LabelsActions {
assert issueLabelsList != null;
if (issueLabelsList.size() > 0) {
if (!issueLabelsList.isEmpty()) {
for (Label label : issueLabelsList) {

View File

@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.google.android.material.card.MaterialCardView;
import com.vdurmont.emoji.EmojiParser;
import java.util.List;
import java.util.Locale;
@ -49,18 +50,24 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
private List<Issue> issuesList;
private Runnable loadMoreListener;
private boolean isLoading = false, isMoreDataAvailable = true;
private final String type;
public IssuesAdapter(Context ctx, List<Issue> issuesListMain) {
public IssuesAdapter(Context ctx, List<Issue> issuesListMain, String type) {
this.context = ctx;
this.issuesList = issuesListMain;
tinyDb = TinyDB.getInstance(context);
this.type = type;
}
@NonNull @Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
return new IssuesHolder(inflater.inflate(R.layout.list_issues, parent, false));
if (type.equalsIgnoreCase("pinned")) {
return new IssuesHolder(inflater.inflate(R.layout.list_issues_pinned, parent, false));
} else {
return new IssuesHolder(inflater.inflate(R.layout.list_issues, parent, false));
}
}
@Override
@ -118,6 +125,7 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
private final HorizontalScrollView labelsScrollViewDots;
private final LinearLayout frameLabelsDots;
private final ImageView commentIcon;
private final MaterialCardView cardView;
private Issue issueObject;
IssuesHolder(View itemView) {
@ -132,6 +140,7 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
labelsScrollViewDots = itemView.findViewById(R.id.labelsScrollViewDots);
frameLabelsDots = itemView.findViewById(R.id.frameLabelsDots);
commentIcon = itemView.findViewById(R.id.comment_icon);
cardView = itemView.findViewById(R.id.card_view);
new Handler()
.postDelayed(
@ -291,6 +300,14 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
context.getResources().getColor(R.color.releasePre, null));
}
if (issue.getState().equalsIgnoreCase("open") && type.equalsIgnoreCase("pinned")) {
cardView.setStrokeColor(context.getResources().getColor(R.color.darkGreen, null));
}
if (issue.getState().equalsIgnoreCase("closed") && type.equalsIgnoreCase("pinned")) {
cardView.setStrokeColor(
context.getResources().getColor(R.color.iconIssuePrClosedColor, null));
}
this.issueCreatedTime.setText(TimeHelper.formatTime(issue.getCreatedAt(), locale));
this.issueCreatedTime.setOnClickListener(
new ClickListener(

View File

@ -39,6 +39,14 @@ public class MainGrammarLocator {
case "csx":
return "csharp";
case "bash":
case "sh":
case "bsh":
return "sh";
case "d":
return "d";
case "groovy":
case "gradle":
case "gvy":

View File

@ -34,6 +34,7 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
private final IssueContext issue;
private final String issueCreator;
private BottomSheetListener bmListener;
private boolean issuePinStatus = false;
public BottomSheetSingleIssueFragment(IssueContext issue, String username) {
this.issue = issue;
@ -288,6 +289,36 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
binding.editLabels.setVisibility(View.GONE);
}
binding.pinIssue.setOnClickListener(
pinIssue -> {
IssueActions.pinIssue(ctx, issue);
dismiss();
});
binding.unpinIssue.setOnClickListener(
unpinIssue -> {
IssueActions.unpinIssue(ctx, issue);
dismiss();
});
if (issue.getIssue().getPinOrder() > 0) {
binding.pinIssue.setVisibility(View.GONE);
binding.unpinIssue.setVisibility(View.VISIBLE);
} else {
binding.pinIssue.setVisibility(View.VISIBLE);
binding.unpinIssue.setVisibility(View.GONE);
if (issue.isPinned()) {
binding.pinIssue.setVisibility(View.GONE);
binding.unpinIssue.setVisibility(View.VISIBLE);
}
}
if (!isRepoAdmin) {
binding.pinIssue.setVisibility(View.GONE);
binding.unpinIssue.setVisibility(View.GONE);
}
return binding.getRoot();
}

View File

@ -43,7 +43,9 @@ public class IssuesFragment extends Fragment {
private Context context;
private Menu menu;
private List<Issue> issuesList;
private List<Issue> pinnedIssuesList;
private IssuesAdapter adapter;
private IssuesAdapter adapterPinned;
private int pageSize = Constants.issuesPageInit;
private int resultLimit;
private RepositoryContext repository;
@ -71,6 +73,7 @@ public class IssuesFragment extends Fragment {
resultLimit = Constants.getCurrentResultLimit(context);
issuesList = new ArrayList<>();
pinnedIssuesList = new ArrayList<>();
fragmentIssuesBinding.pullToRefresh.setOnRefreshListener(
() ->
@ -91,7 +94,7 @@ public class IssuesFragment extends Fragment {
},
200));
adapter = new IssuesAdapter(context, issuesList);
adapter = new IssuesAdapter(context, issuesList, "");
adapter.setLoadMoreListener(
() ->
fragmentIssuesBinding.recyclerView.post(
@ -114,6 +117,12 @@ public class IssuesFragment extends Fragment {
fragmentIssuesBinding.recyclerView.setLayoutManager(new LinearLayoutManager(context));
fragmentIssuesBinding.recyclerView.setAdapter(adapter);
adapterPinned = new IssuesAdapter(context, pinnedIssuesList, "pinned");
LinearLayoutManager horizontalLayoutManager =
new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
fragmentIssuesBinding.rvPinnedIssues.setLayoutManager(horizontalLayoutManager);
fragmentIssuesBinding.rvPinnedIssues.setAdapter(adapterPinned);
((RepoDetailActivity) requireActivity())
.setFragmentRefreshListener(
issueState -> {
@ -125,7 +134,7 @@ public class IssuesFragment extends Fragment {
issuesList.clear();
adapter = new IssuesAdapter(context, issuesList);
adapter = new IssuesAdapter(context, issuesList, "");
adapter.setLoadMoreListener(
() ->
fragmentIssuesBinding.recyclerView.post(
@ -169,7 +178,7 @@ public class IssuesFragment extends Fragment {
filterIssueByMilestone -> {
issuesList.clear();
adapter = new IssuesAdapter(context, issuesList);
adapter = new IssuesAdapter(context, issuesList, "");
adapter.setLoadMoreListener(
() ->
fragmentIssuesBinding.recyclerView.post(
@ -217,6 +226,8 @@ public class IssuesFragment extends Fragment {
repository.getIssueMilestoneFilterName(),
null);
getPinnedIssues(repository.getOwner(), repository.getName());
if (archived) {
fragmentIssuesBinding.createNewIssue.setVisibility(View.GONE);
}
@ -251,10 +262,53 @@ public class IssuesFragment extends Fragment {
repository.getIssueState().toString(),
repository.getIssueMilestoneFilterName(),
null);
getPinnedIssues(repository.getOwner(), repository.getName());
resumeIssues = false;
}
}
private void getPinnedIssues(String repoOwner, String repoName) {
Call<List<Issue>> call =
RetrofitClient.getApiInterface(context).repoListPinnedIssues(repoOwner, repoName);
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<List<Issue>> call,
@NonNull Response<List<Issue>> response) {
if (response.code() == 200) {
assert response.body() != null;
if (!response.body().isEmpty()) {
fragmentIssuesBinding.pinnedIssuesFrame.setVisibility(View.VISIBLE);
pinnedIssuesList.clear();
pinnedIssuesList.addAll(response.body());
adapterPinned.notifyDataChanged();
fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE);
} else {
pinnedIssuesList.clear();
adapterPinned.notifyDataChanged();
fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE);
}
fragmentIssuesBinding.progressBar.setVisibility(View.GONE);
} else if (response.code() == 404) {
fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE);
fragmentIssuesBinding.progressBar.setVisibility(View.GONE);
} else {
Toasty.error(context, getString(R.string.genericError));
}
}
@Override
public void onFailure(@NonNull Call<List<Issue>> call, @NonNull Throwable t) {
Toasty.error(context, getString(R.string.genericServerResponseError));
}
});
}
private void loadInitial(
String repoOwner,
String repoName,
@ -296,10 +350,12 @@ public class IssuesFragment extends Fragment {
issuesList.clear();
issuesList.addAll(response.body());
adapter.notifyDataChanged();
adapterPinned.notifyDataChanged();
fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE);
} else {
issuesList.clear();
adapter.notifyDataChanged();
adapterPinned.notifyDataChanged();
fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE);
}
fragmentIssuesBinding.progressBar.setVisibility(View.GONE);
@ -366,8 +422,10 @@ public class IssuesFragment extends Fragment {
fragmentIssuesBinding.getRoot(),
getString(R.string.noMoreData));
adapter.setMoreDataAvailable(false);
adapterPinned.setMoreDataAvailable(false);
}
adapter.notifyDataChanged();
adapterPinned.notifyDataChanged();
fragmentIssuesBinding.progressBar.setVisibility(View.GONE);
} else {
Toasty.error(context, getString(R.string.genericError));

View File

@ -94,6 +94,7 @@ public class AppUtil {
"c",
"cc",
"cpp",
"d",
"h",
"cxx",
"cyc",

View File

@ -0,0 +1,154 @@
package org.mian.gitnex.helpers.codeeditor.languages;
import com.amrdeveloper.codeview.Code;
import com.amrdeveloper.codeview.Keyword;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* @author M M Arif
*/
public class BashLanguage extends Language {
private static final Pattern PATTERN_BUILTINS = Pattern.compile("[,:;[->]{}()]");
private static final Pattern PATTERN_SINGLE_LINE_COMMENT = Pattern.compile("//[^\\n]*");
private static final Pattern PATTERN_MULTI_LINE_COMMENT =
Pattern.compile("/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/");
private static final Pattern PATTERN_ATTRIBUTE = Pattern.compile("\\.[a-zA-Z0-9_]+");
private static final Pattern PATTERN_OPERATION =
Pattern.compile(
":|==|>|<|!=|>=|<=|->|=|>|<|%|-|-=|%=|\\+|\\-|\\-=|\\+=|\\^|\\&|\\|::|\\?|\\*");
private static final Pattern PATTERN_GENERIC = Pattern.compile("<[a-zA-Z0-9,<>]+>");
private static final Pattern PATTERN_TODO_COMMENT =
Pattern.compile("//\\s?(TODO|todo)\\s[^\n]*");
private static final Pattern PATTERN_NUMBERS = Pattern.compile("\\b(\\d*[.]?\\d+)\\b");
private static final Pattern PATTERN_CHAR = Pattern.compile("['](.*?)[']");
private static final Pattern PATTERN_STRING = Pattern.compile("[\"](.*?)[\"]");
private static final Pattern PATTERN_HEX = Pattern.compile("0x[0-9a-fA-F]+");
public static String getCommentStart() {
return "//";
}
public static String getCommentEnd() {
return "";
}
@Override
public Pattern getPattern(LanguageElement element) {
switch (element) {
case KEYWORD:
return Pattern.compile("\\b(" + String.join("|", getKeywords()) + ")\\b");
case BUILTIN:
return PATTERN_BUILTINS;
case NUMBER:
return PATTERN_NUMBERS;
case CHAR:
return PATTERN_CHAR;
case STRING:
return PATTERN_STRING;
case HEX:
return PATTERN_HEX;
case SINGLE_LINE_COMMENT:
return PATTERN_SINGLE_LINE_COMMENT;
case MULTI_LINE_COMMENT:
return PATTERN_MULTI_LINE_COMMENT;
case ATTRIBUTE:
return PATTERN_ATTRIBUTE;
case OPERATION:
return PATTERN_OPERATION;
case TODO_COMMENT:
return PATTERN_TODO_COMMENT;
case GENERIC:
return PATTERN_GENERIC;
case ANNOTATION:
default:
return null;
}
}
@Override
public String[] getKeywords() {
return new String[] {
"BASH_VERSION",
"BASH",
"PWD",
"OSTYPE",
"HOME",
"LANG",
"HOSTNAME",
"PATH",
"COLUMNS",
"USER",
"then",
"set",
"env",
"printenv",
"for",
"register",
"typedef",
"class",
"return",
"union",
"const",
"goto",
"short",
"unsigned",
"continue",
"if",
"fi",
"signed",
"virtual",
"default",
"inline",
"sizeof",
"delete",
"int",
"static",
"do",
"long",
"while",
"echo",
"alias",
"ps",
"ax",
"grep",
"do",
"done",
"exit",
"read"
};
}
@Override
public List<Code> getCodeList() {
List<Code> codeList = new ArrayList<>();
String[] keywords = getKeywords();
for (String keyword : keywords) {
codeList.add(new Keyword(keyword));
}
return codeList;
}
@Override
public String getName() {
return "sh";
}
@Override
public Set<Character> getIndentationStarts() {
Set<Character> characterSet = new HashSet<>();
characterSet.add('{');
return characterSet;
}
@Override
public Set<Character> getIndentationEnds() {
Set<Character> characterSet = new HashSet<>();
characterSet.add('}');
return characterSet;
}
}

View File

@ -0,0 +1,162 @@
package org.mian.gitnex.helpers.codeeditor.languages;
import com.amrdeveloper.codeview.Code;
import com.amrdeveloper.codeview.Keyword;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* @author M M Arif
*/
public class DLanguage extends Language {
private static final Pattern PATTERN_BUILTINS = Pattern.compile("[,:;[->]{}()]");
private static final Pattern PATTERN_SINGLE_LINE_COMMENT = Pattern.compile("//[^\\n]*");
private static final Pattern PATTERN_MULTI_LINE_COMMENT =
Pattern.compile("/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/");
private static final Pattern PATTERN_ATTRIBUTE = Pattern.compile("\\.[a-zA-Z0-9_]+");
private static final Pattern PATTERN_OPERATION =
Pattern.compile(
":|==|>|<|!=|>=|<=|->|=|>|<|%|-|-=|%=|\\+|\\-|\\-=|\\+=|\\^|\\&|\\|::|\\?|\\*");
private static final Pattern PATTERN_GENERIC = Pattern.compile("<[a-zA-Z0-9,<>]+>");
private static final Pattern PATTERN_TODO_COMMENT =
Pattern.compile("//\\s?(TODO|todo)\\s[^\n]*");
private static final Pattern PATTERN_NUMBERS = Pattern.compile("\\b(\\d*[.]?\\d+)\\b");
private static final Pattern PATTERN_CHAR = Pattern.compile("['](.*?)[']");
private static final Pattern PATTERN_STRING = Pattern.compile("[\"](.*?)[\"]");
private static final Pattern PATTERN_HEX = Pattern.compile("0x[0-9a-fA-F]+");
public static String getCommentStart() {
return "//";
}
public static String getCommentEnd() {
return "";
}
@Override
public Pattern getPattern(LanguageElement element) {
switch (element) {
case KEYWORD:
return Pattern.compile("\\b(" + String.join("|", getKeywords()) + ")\\b");
case BUILTIN:
return PATTERN_BUILTINS;
case NUMBER:
return PATTERN_NUMBERS;
case CHAR:
return PATTERN_CHAR;
case STRING:
return PATTERN_STRING;
case HEX:
return PATTERN_HEX;
case SINGLE_LINE_COMMENT:
return PATTERN_SINGLE_LINE_COMMENT;
case MULTI_LINE_COMMENT:
return PATTERN_MULTI_LINE_COMMENT;
case ATTRIBUTE:
return PATTERN_ATTRIBUTE;
case OPERATION:
return PATTERN_OPERATION;
case TODO_COMMENT:
return PATTERN_TODO_COMMENT;
case GENERIC:
return PATTERN_GENERIC;
case ANNOTATION:
default:
return null;
}
}
@Override
public String[] getKeywords() {
return new String[] {
"new",
"switch",
"auto",
"else",
"operator",
"template",
"break",
"enum",
"private",
"this",
"case",
"extern",
"protected",
"throw",
"catch",
"float",
"public",
"try",
"char",
"for",
"register",
"typedef",
"class",
"friend",
"return",
"union",
"const",
"goto",
"short",
"unsigned",
"continue",
"if",
"signed",
"virtual",
"default",
"inline",
"sizeof",
"void",
"delete",
"int",
"static",
"volatile",
"do",
"long",
"struct",
"while",
"property",
"Log",
"alias",
"import",
"module",
"Singleton",
"string",
"bool",
"deprecated"
};
}
@Override
public List<Code> getCodeList() {
List<Code> codeList = new ArrayList<>();
String[] keywords = getKeywords();
for (String keyword : keywords) {
codeList.add(new Keyword(keyword));
}
return codeList;
}
@Override
public String getName() {
return "d";
}
@Override
public Set<Character> getIndentationStarts() {
Set<Character> characterSet = new HashSet<>();
characterSet.add('{');
return characterSet;
}
@Override
public Set<Character> getIndentationEnds() {
Set<Character> characterSet = new HashSet<>();
characterSet.add('}');
return characterSet;
}
}

View File

@ -35,7 +35,9 @@ public abstract class Language {
new JsonLanguage(),
new CppLanguage(),
new CLanguage(),
new LispLanguage()
new LispLanguage(),
new DLanguage(),
new BashLanguage()
};
for (Language l : languagesArray) {
languages.put(l.getName().toUpperCase(), l);

View File

@ -19,6 +19,7 @@ public class IssueContext implements Serializable {
private Issue issue;
private PullRequest pullRequest;
private boolean isSubscribed;
private boolean isPinned;
private int issueIndex = 0;
private String issueType;
@ -133,6 +134,16 @@ public class IssueContext implements Serializable {
isSubscribed = subscribed;
}
public boolean isPinned() {
return isPinned;
}
public void setPinned(boolean pinned) {
isPinned = pinned;
}
public String getIssueType() {
return issueType;

View File

@ -1,17 +1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,17L12,22"
android:pathData="M15,4.5l-4,4l-4,1.5l-1.5,1.5l7,7l1.5,-1.5l1.5,-4l4,-4"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M5,17h14v-1.76a2,2 0,0 0,-1.11 -1.79l-1.78,-0.9A2,2 0,0 1,15 10.76V6h1a2,2 0,0 0,0 -4H8a2,2 0,0 0,0 4h1v4.76a2,2 0,0 1,-1.11 1.79l-1.78,0.9A2,2 0,0 0,5 15.24Z"
android:pathData="M9,15l-4.5,4.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M14.5,4l5.5,5.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"

View File

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,3l18,18"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M15,4.5l-3.249,3.249m-2.57,1.433l-2.181,0.818l-1.5,1.5l7,7l1.5,-1.5l0.82,-2.186m1.43,-2.563l3.25,-3.251"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M9,15l-4.5,4.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M14.5,4l5.5,5.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
</vector>

View File

@ -163,6 +163,33 @@
app:drawableTopCompat="@drawable/ic_tag"
app:layout_alignSelf="flex_start" />
<TextView
android:id="@+id/pin_issue"
android:layout_width="@dimen/dimen132dp"
android:layout_height="@dimen/dimen100dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:padding="@dimen/dimen4dp"
android:text="@string/pin"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen14sp"
app:drawableTopCompat="@drawable/ic_pin"
app:layout_alignSelf="flex_start" />
<TextView
android:id="@+id/unpin_issue"
android:layout_width="@dimen/dimen132dp"
android:layout_height="@dimen/dimen100dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:padding="@dimen/dimen4dp"
android:text="@string/unpin"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen14sp"
android:visibility="gone"
app:drawableTopCompat="@drawable/ic_unpin"
app:layout_alignSelf="flex_start" />
<TextView
android:id="@+id/subscribeIssue"
android:layout_width="@dimen/dimen132dp"

View File

@ -7,27 +7,53 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".activities.RepoDetailActivity">
<FrameLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
android:padding="@dimen/dimen8dp">
android:layout_height="wrap_content">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/pullToRefresh"
<LinearLayout
android:id="@+id/pinnedIssuesFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:paddingStart="@dimen/dimen8dp"
android:paddingEnd="@dimen/dimen8dp"
android:paddingTop="@dimen/dimen8dp"
android:visibility="gone"
android:background="?attr/primaryBackgroundColor">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:id="@+id/rvPinnedIssues"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="@dimen/dimen72dp" />
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryBackgroundColor"
android:layout_below="@id/pinnedIssuesFrame"
android:paddingStart="@dimen/dimen8dp"
android:paddingEnd="@dimen/dimen8dp"
android:paddingTop="@dimen/dimen8dp">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/pullToRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="@dimen/dimen72dp" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout>
</RelativeLayout>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progressBar"

View File

@ -10,6 +10,7 @@
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?attr/materialCardViewElevatedStyle"

View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relativeLayoutFrameIssuesList"
android:layout_width="@dimen/dimen320dp"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dimen4dp"
android:paddingBottom="@dimen/dimen4dp"
android:layout_marginEnd="@dimen/dimen12dp"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?attr/materialCardViewElevatedStyle"
app:strokeWidth="@dimen/dimen1dp"
app:cardElevation="@dimen/dimen0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
android:background="?attr/materialCardBackgroundColor"
android:padding="@dimen/dimen12dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/infoSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<com.google.android.material.card.MaterialCardView
android:layout_width="@dimen/dimen32dp"
android:layout_height="@dimen/dimen32dp"
style="?attr/materialCardViewElevatedStyle"
android:backgroundTint="@android:color/transparent"
app:cardElevation="@dimen/dimen0dp"
android:layout_marginEnd="@dimen/dimen12dp"
app:cardCornerRadius="@dimen/dimen8dp">
<ImageView
android:id="@+id/assigneeAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/generalImgContentText"
tools:src="@tools:sample/avatars"/>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/titleLabelsSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/issueTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_vertical"
android:text="@string/newIssueTitle"
android:textAlignment="gravity"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen16sp"
android:maxLines="2"
android:minLines="2" />
<HorizontalScrollView
android:id="@+id/labelsScrollViewDots"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dimen8dp"
android:fillViewport="true"
android:foregroundGravity="right"
android:scrollbarThumbHorizontal="@android:color/transparent"
android:visibility="gone">
<LinearLayout
android:id="@+id/frameLabelsDots"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen10dp"
android:orientation="horizontal" />
</HorizontalScrollView>
</LinearLayout>
</LinearLayout>
<HorizontalScrollView
android:id="@+id/labelsScrollViewWithText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dimen8dp"
android:fillViewport="true"
android:foregroundGravity="right"
android:scrollbarThumbHorizontal="@android:color/transparent"
android:visibility="gone">
<LinearLayout
android:id="@+id/frameLabels"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen10dp"
android:orientation="horizontal" />
</HorizontalScrollView>
<LinearLayout
android:id="@+id/issueInfoFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/issueCreatedTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:textColor="?attr/hintColor"
android:textSize="@dimen/dimen12sp"
tools:text="10.01.2020" />
<LinearLayout
android:id="@+id/frameCommentsCount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginStart="@dimen/dimen10dp"
android:layout_marginEnd="@dimen/dimen6dp"
android:layout_weight="1"
android:gravity="end"
android:orientation="horizontal"
android:paddingLeft="@dimen/dimen4dp"
android:paddingRight="@dimen/dimen4dp"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/comment_icon"
android:layout_width="@dimen/dimen16dp"
android:layout_height="@dimen/dimen16dp"
android:layout_marginStart="@dimen/dimen2dp"
android:layout_marginEnd="@dimen/dimen4dp"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_comment"
app:tint="?attr/iconsColor" />
<TextView
android:id="@+id/issueCommentsCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="@string/repoStars"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen14sp"
tools:text="50" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>

View File

@ -2,6 +2,6 @@
<resources>
<string name="versionLow" translatable="false">1.21</string>
<string name="versionHigh" translatable="false">1.24</string>
<string name="versionHigh" translatable="false">10.0.0</string>
</resources>

View File

@ -556,6 +556,8 @@
<string name="title">Title</string>
<string name="description">Description</string>
<string name="bioText">Bio</string>
<string name="pin">Pin</string>
<string name="unpin">Unpin</string>
<!-- generic copy -->
<string name="exploreUsers">Explore users</string>
@ -643,6 +645,11 @@
<string name="alreadyUnsubscribed">You have already Unsubscribed</string>
<string name="unSubscriptionError">Un-Subscription failed</string>
<string name="issue_pinned">Issue pinned successfully</string>
<string name="issue_unpinned">Issue unpinned successfully</string>
<string name="pinning_failed">Pinning issue failed</string>
<string name="unpinning_failed">Unpinning issue failed</string>
<string name="closeMilestone">Close Milestone</string>
<string name="openMilestone">Open Milestone</string>
<string name="milestoneStatusUpdate">Milestone status updated successfully</string>

View File

@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.2'
classpath 'com.android.tools.build:gradle:8.7.2'
}
}

View File

@ -6,7 +6,6 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.defaults.buildfeatures.buildconfig=true
android.enableJetifier=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false

View File

@ -1,6 +1,6 @@
#Tue Jul 11 23:34:25 PKT 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists