Add Issue/Comment Reactions (#557)
Minor performance improvements. Merge branch 'master' of https://codeberg.org/gitnex/GitNex into issue-reactions Improving color of selected elements. First, fully working implementation of reactions. Merge branch 'master' of https://codeberg.org/gitnex/GitNex into issue-reactions Conflicts: app/src/main/res/layout/bottom_sheet_issue_comments.xml app/src/main/res/layout/list_issue_comments.xml (Hopefully) fixing merge issues. Merge branch 'master' of https://codeberg.org/gitnex/GitNex into issue-reactions Conflicts: app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java app/src/main/res/layout/activity_issue_detail.xml app/src/main/res/layout/bottom_sheet_issue_comments.xml app/src/main/res/layout/bottom_sheet_single_issue.xml app/src/main/res/values/colors.xml Moving reactions below time frame on comments. Merge branch 'master' into layout-reactions Add IssueReactions Merge remote-tracking branch 'origin/layout-reactions' into layout-reactions Merge branch 'master' of https://gitea.com/gitnex/GitNex into layout-reactions Merge branch 'master' into layout-reactions Applying to pulls and issues. Merge branch 'master' of https://gitea.com/gitnex/GitNex into layout-reactions Providing external layouts. Some improvements. Adding comment emote indications. Adding circle around emotes. Adding some padding. First tests. Co-authored-by: opyale <opyale@noreply.gitea.io> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: 6543 <6543@noreply.gitea.io> Reviewed-on: #557 Reviewed-by: M M Arif <mmarif@noreply.codeberg.org>
This commit is contained in:
parent f97f668363
commit ade7b797f1
33 changed files with 1000 additions and 101 deletions
15 .idea/codeStyles/Project.xml generated
15
.idea/codeStyles/Project.xml generated | @ -19,9 +19,18 @@ | |||
<JetCodeStyleSettings> | ||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS"> | ||||
<value> | ||||
<package name="java.util" withSubpackages="false" static="false" /> | ||||
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" /> | ||||
<package name="io.ktor" withSubpackages="true" static="false" /> | ||||
<package name="java.util" alias="false" withSubpackages="false" /> | ||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" /> | ||||
<package name="io.ktor" alias="false" withSubpackages="true" /> | ||||
</value> | ||||
</option> | ||||
<option name="PACKAGES_IMPORT_LAYOUT"> | ||||
<value> | ||||
<package name="" alias="false" withSubpackages="true" /> | ||||
<package name="java" alias="false" withSubpackages="true" /> | ||||
<package name="javax" alias="false" withSubpackages="true" /> | ||||
<package name="kotlin" alias="false" withSubpackages="true" /> | ||||
<package name="" alias="true" withSubpackages="true" /> | ||||
</value> | ||||
</option> | ||||
</JetCodeStyleSettings> | ||||
| |
| @ -89,5 +89,6 @@ Thanks to all the open source libraries, contributors and donators. | |||
- Ge0rg/memorizingTrustManager | ||||
- Dimezis/blurView | ||||
- Mikaelhg/urlbuilder | ||||
- emoji-java | ||||
| ||||
[Follow me on Fediverse - mastodon.social/@mmarif](https://mastodon.social/@mmarif) | ||||
| |
| @ -38,8 +38,10 @@ android { | |||
abortOnError false | ||||
} | ||||
compileOptions { | ||||
targetCompatibility = "8" | ||||
sourceCompatibility = "8" | ||||
coreLibraryDesugaringEnabled true | ||||
| ||||
sourceCompatibility JavaVersion.VERSION_1_8 | ||||
targetCompatibility JavaVersion.VERSION_1_8 | ||||
} | ||||
defaultConfig{ | ||||
vectorDrawables.useSupportLibrary = true | ||||
| @ -60,7 +62,7 @@ dependencies { | |||
implementation fileTree(include: ['*.jar'], dir: 'libs') | ||||
implementation 'androidx.appcompat:appcompat:1.3.0-alpha02' | ||||
implementation 'com.google.android.material:material:1.3.0-alpha03' | ||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.2' | ||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' | ||||
implementation "androidx.legacy:legacy-support-v4:1.0.0" | ||||
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" | ||||
testImplementation 'junit:junit:4.13.1' | ||||
| @ -108,5 +110,7 @@ dependencies { | |||
implementation "androidx.work:work-runtime:$work_version" | ||||
implementation "com.eightbitlab:blurview:1.6.4" | ||||
implementation "io.mikael:urlbuilder:2.0.9" | ||||
implementation 'com.vdurmont:emoji-java:5.1.1' | ||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.0' | ||||
| ||||
} | ||||
| |
| @ -59,6 +59,7 @@ import org.mian.gitnex.models.Labels; | |||
import org.mian.gitnex.models.UpdateIssueAssignees; | ||||
import org.mian.gitnex.models.WatchInfo; | ||||
import org.mian.gitnex.viewmodels.IssueCommentsViewModel; | ||||
import org.mian.gitnex.views.ReactionList; | ||||
import java.text.DateFormat; | ||||
import java.text.SimpleDateFormat; | ||||
import java.util.ArrayList; | ||||
| @ -208,6 +209,10 @@ public class IssueDetailActivity extends BaseActivity implements LabelsListAdapt | |||
| ||||
switch(text) { | ||||
| ||||
case "onResume": | ||||
onResume(); | ||||
break; | ||||
| ||||
case "showLabels": | ||||
showLabels(); | ||||
break; | ||||
| @ -519,7 +524,13 @@ public class IssueDetailActivity extends BaseActivity implements LabelsListAdapt | |||
viewBinding.divider.setVisibility(View.VISIBLE); | ||||
} | ||||
| ||||
adapter = new IssueCommentsAdapter(ctx, issueCommentsMain, getSupportFragmentManager(), this::onResume); | ||||
Bundle bundle = new Bundle(); | ||||
bundle.putString("repoOwner", repoOwner); | ||||
bundle.putString("repoName", repoName); | ||||
bundle.putInt("issueNumber", issueIndex); | ||||
| ||||
adapter = new IssueCommentsAdapter(ctx, bundle, issueCommentsMain, getSupportFragmentManager(), this::onResume); | ||||
| ||||
viewBinding.recyclerView.setAdapter(adapter); | ||||
| ||||
}); | ||||
| @ -718,6 +729,15 @@ public class IssueDetailActivity extends BaseActivity implements LabelsListAdapt | |||
.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getCreated_at()), ctx)); | ||||
} | ||||
| ||||
Bundle bundle = new Bundle(); | ||||
bundle.putString("repoOwner", repoOwner); | ||||
bundle.putString("repoName", repoName); | ||||
bundle.putInt("issueId", singleIssue.getNumber()); | ||||
| ||||
ReactionList reactionList = new ReactionList(ctx, bundle); | ||||
viewBinding.commentReactionBadges.removeAllViews(); | ||||
viewBinding.commentReactionBadges.addView(reactionList); | ||||
| ||||
if(singleIssue.getMilestone() != null) { | ||||
| ||||
viewBinding.issueMilestone.setVisibility(View.VISIBLE); | ||||
| |
| @ -15,8 +15,9 @@ import android.widget.TextView; | |||
import androidx.fragment.app.FragmentManager; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.fragments.BottomSheetReplyFragment; | ||||
import org.mian.gitnex.helpers.DiffTextView; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.models.FileDiffView; | ||||
import org.mian.gitnex.views.DiffTextView; | ||||
import java.util.List; | ||||
import java.util.Map; | ||||
import java.util.concurrent.ConcurrentSkipListMap; | ||||
| @ -48,11 +49,11 @@ public class FilesDiffAdapter extends BaseAdapter { | |||
| ||||
selectedViews = new ConcurrentSkipListMap<>(); | ||||
| ||||
COLOR_ADDED = getColorFromAttribute(R.attr.diffAddedColor); | ||||
COLOR_REMOVED = getColorFromAttribute(R.attr.diffRemovedColor); | ||||
COLOR_NORMAL = getColorFromAttribute(R.attr.primaryBackgroundColor); | ||||
COLOR_SELECTED = getColorFromAttribute(R.attr.diffSelectedColor); | ||||
COLOR_FONT = getColorFromAttribute(R.attr.inputTextColor); | ||||
COLOR_ADDED = AppUtil.getColorFromAttribute(context, R.attr.diffAddedColor); | ||||
COLOR_REMOVED = AppUtil.getColorFromAttribute(context, R.attr.diffRemovedColor); | ||||
COLOR_NORMAL = AppUtil.getColorFromAttribute(context, R.attr.primaryBackgroundColor); | ||||
COLOR_SELECTED = AppUtil.getColorFromAttribute(context, R.attr.diffSelectedColor); | ||||
COLOR_FONT = AppUtil.getColorFromAttribute(context, R.attr.inputTextColor); | ||||
| ||||
} | ||||
| ||||
| @ -256,13 +257,4 @@ public class FilesDiffAdapter extends BaseAdapter { | |||
| ||||
} | ||||
| ||||
private int getColorFromAttribute(int resid) { | ||||
| ||||
TypedValue typedValue = new TypedValue(); | ||||
context.getTheme().resolveAttribute(resid, typedValue, true); | ||||
| ||||
return typedValue.data; | ||||
| ||||
} | ||||
| ||||
} | ||||
| |
| @ -10,6 +10,7 @@ import android.view.LayoutInflater; | |||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import android.widget.ImageView; | ||||
import android.widget.LinearLayout; | ||||
import android.widget.TextView; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.fragment.app.FragmentManager; | ||||
| @ -29,6 +30,8 @@ import org.mian.gitnex.helpers.TimeHelper; | |||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.helpers.Toasty; | ||||
import org.mian.gitnex.models.IssueComments; | ||||
import org.mian.gitnex.views.ReactionList; | ||||
import org.mian.gitnex.views.ReactionSpinner; | ||||
import java.util.List; | ||||
import java.util.Locale; | ||||
import java.util.Objects; | ||||
| @ -42,17 +45,22 @@ import retrofit2.Callback; | |||
public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdapter.IssueCommentViewHolder> { | ||||
| ||||
private final Context ctx; | ||||
private final TinyDB tinyDB; | ||||
private final Bundle bundle; | ||||
private final List<IssueComments> issuesComments; | ||||
private final FragmentManager fragmentManager; | ||||
private final BottomSheetReplyFragment.OnInteractedListener onInteractedListener; | ||||
| ||||
public IssueCommentsAdapter(Context ctx, List<IssueComments> issuesCommentsMain, FragmentManager fragmentManager, BottomSheetReplyFragment.OnInteractedListener onInteractedListener) { | ||||
public IssueCommentsAdapter(Context ctx, Bundle bundle, List<IssueComments> issuesCommentsMain, FragmentManager fragmentManager, BottomSheetReplyFragment.OnInteractedListener onInteractedListener) { | ||||
| ||||
this.ctx = ctx; | ||||
this.bundle = bundle; | ||||
this.issuesComments = issuesCommentsMain; | ||||
this.fragmentManager = fragmentManager; | ||||
this.onInteractedListener = onInteractedListener; | ||||
| ||||
tinyDB = TinyDB.getInstance(ctx); | ||||
| ||||
} | ||||
| ||||
class IssueCommentViewHolder extends RecyclerView.ViewHolder { | ||||
| @ -63,6 +71,7 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap | |||
private final TextView author; | ||||
private final TextView information; | ||||
private final TextView comment; | ||||
private final LinearLayout commentReactionBadges; | ||||
| ||||
private IssueCommentViewHolder(View view) { | ||||
| ||||
| @ -73,12 +82,12 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap | |||
information = view.findViewById(R.id.information); | ||||
ImageView menu = view.findViewById(R.id.menu); | ||||
comment = view.findViewById(R.id.comment); | ||||
commentReactionBadges = view.findViewById(R.id.commentReactionBadges); | ||||
| ||||
menu.setOnClickListener(v -> { | ||||
| ||||
final Context ctx = v.getContext(); | ||||
final TinyDB tinyDb = TinyDB.getInstance(ctx); | ||||
final String loginUid = tinyDb.getString("loginUid"); | ||||
final String loginUid = tinyDB.getString("loginUid"); | ||||
| ||||
@SuppressLint("InflateParams") View vw = LayoutInflater.from(ctx).inflate(R.layout.bottom_sheet_issue_comments, null); | ||||
| ||||
| @ -102,6 +111,24 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap | |||
dialog.setContentView(vw); | ||||
dialog.show(); | ||||
| ||||
LinearLayout linearLayout = vw.findViewById(R.id.commentReactionButtons); | ||||
| ||||
Bundle bundle1 = new Bundle(); | ||||
bundle1.putAll(bundle); | ||||
bundle1.putInt("commentId", issueComment.getId()); | ||||
| ||||
ReactionSpinner reactionSpinner = new ReactionSpinner(ctx, bundle1); | ||||
reactionSpinner.setOnInteractedListener(() -> { | ||||
| ||||
tinyDB.putBoolean("commentEdited", true); | ||||
| ||||
onInteractedListener.onInteracted(); | ||||
dialog.dismiss(); | ||||
| ||||
}); | ||||
| ||||
linearLayout.addView(reactionSpinner); | ||||
| ||||
commentMenuEdit.setOnClickListener(v1 -> { | ||||
| ||||
Bundle bundle = new Bundle(); | ||||
| @ -125,7 +152,7 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap | |||
// share issue comment | ||||
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); | ||||
sharingIntent.setType("text/plain"); | ||||
String intentHeader = tinyDb.getString("issueNumber") + ctx.getResources().getString(R.string.hash) + "issuecomment-" + issueComment.getId() + " " + tinyDb.getString("issueTitle"); | ||||
String intentHeader = tinyDB.getString("issueNumber") + ctx.getResources().getString(R.string.hash) + "issuecomment-" + issueComment.getId() + " " + tinyDB.getString("issueTitle"); | ||||
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, intentHeader); | ||||
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, commentUrl); | ||||
ctx.startActivity(Intent.createChooser(sharingIntent, intentHeader)); | ||||
| @ -155,7 +182,7 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap | |||
StringBuilder stringBuilder = new StringBuilder(); | ||||
String commenterName = issueComment.getUser().getUsername(); | ||||
| ||||
if(!commenterName.equals(tinyDb.getString("userLogin"))) { | ||||
if(!commenterName.equals(tinyDB.getString("userLogin"))) { | ||||
| ||||
stringBuilder.append("@").append(commenterName).append("\n\n"); | ||||
} | ||||
| @ -183,7 +210,7 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap | |||
ClipboardManager clipboard = (ClipboardManager) Objects.requireNonNull(ctx).getSystemService(Context.CLIPBOARD_SERVICE); | ||||
assert clipboard != null; | ||||
| ||||
ClipData clip = ClipData.newPlainText("Comment on issue #" + tinyDb.getString("issueNumber"), issueComment.getBody()); | ||||
ClipData clip = ClipData.newPlainText("Comment on issue #" + tinyDB.getString("issueNumber"), issueComment.getBody()); | ||||
clipboard.setPrimaryClip(clip); | ||||
| ||||
dialog.dismiss(); | ||||
| @ -214,10 +241,9 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap | |||
| ||||
private void deleteIssueComment(final Context ctx, final int commentId, int position) { | ||||
| ||||
final TinyDB tinyDb = TinyDB.getInstance(ctx); | ||||
final String loginUid = tinyDb.getString("loginUid"); | ||||
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); | ||||
String[] repoFullName = tinyDb.getString("repoFullName").split("/"); | ||||
final String loginUid = tinyDB.getString("loginUid"); | ||||
final String instanceToken = "token " + tinyDB.getString(loginUid + "-token"); | ||||
String[] repoFullName = tinyDB.getString("repoFullName").split("/"); | ||||
| ||||
if (repoFullName.length != 2) { | ||||
return; | ||||
| @ -307,6 +333,13 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap | |||
| ||||
holder.information.setText(informationBuilder.toString()); | ||||
| ||||
Bundle bundle1 = new Bundle(); | ||||
bundle1.putAll(bundle); | ||||
bundle1.putInt("commentId", issueComment.getId()); | ||||
| ||||
ReactionList reactionList = new ReactionList(ctx, bundle1); | ||||
holder.commentReactionBadges.addView(reactionList); | ||||
| ||||
} | ||||
| ||||
@Override | ||||
| |
| @ -8,6 +8,7 @@ import android.os.Bundle; | |||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import android.widget.LinearLayout; | ||||
import android.widget.TextView; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
| @ -20,6 +21,7 @@ import org.mian.gitnex.activities.MergePullRequestActivity; | |||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.helpers.Toasty; | ||||
import org.mian.gitnex.helpers.Version; | ||||
import org.mian.gitnex.views.ReactionSpinner; | ||||
import java.util.Objects; | ||||
| ||||
/** | ||||
| @ -51,6 +53,29 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { | |||
TextView subscribeIssue = v.findViewById(R.id.subscribeIssue); | ||||
TextView unsubscribeIssue = v.findViewById(R.id.unsubscribeIssue); | ||||
| ||||
LinearLayout linearLayout = v.findViewById(R.id.commentReactionButtons); | ||||
| ||||
Bundle bundle1 = new Bundle(); | ||||
| ||||
String repoFullName = tinyDB.getString("repoFullName"); | ||||
String[] parts = repoFullName.split("/"); | ||||
| ||||
bundle1.putString("repoOwner", parts[0]); | ||||
bundle1.putString("repoName", parts[1]); | ||||
bundle1.putInt("issueId", Integer.parseInt(tinyDB.getString("issueNumber"))); | ||||
| ||||
ReactionSpinner reactionSpinner = new ReactionSpinner(ctx, bundle1); | ||||
reactionSpinner.setOnInteractedListener(() -> { | ||||
| ||||
tinyDB.putBoolean("singleIssueUpdate", true); | ||||
| ||||
bmListener.onButtonClicked("onResume"); | ||||
dismiss(); | ||||
| ||||
}); | ||||
| ||||
linearLayout.addView(reactionSpinner); | ||||
| ||||
if(tinyDB.getString("issueType").equalsIgnoreCase("Pull")) { | ||||
| ||||
editIssue.setText(R.string.editPrText); | ||||
| |
| @ -9,7 +9,9 @@ import android.net.ConnectivityManager; | |||
import android.net.NetworkInfo; | ||||
import android.util.Base64; | ||||
import android.util.DisplayMetrics; | ||||
import android.util.TypedValue; | ||||
import android.view.View; | ||||
import androidx.annotation.ColorInt; | ||||
import java.net.HttpURLConnection; | ||||
import java.net.URL; | ||||
import java.nio.charset.StandardCharsets; | ||||
| @ -131,6 +133,16 @@ public class AppUtil { | |||
| ||||
} | ||||
| ||||
@ColorInt | ||||
public static int getColorFromAttribute(Context context, int resid) { | ||||
| ||||
TypedValue typedValue = new TypedValue(); | ||||
context.getTheme().resolveAttribute(resid, typedValue, true); | ||||
| ||||
return typedValue.data; | ||||
| ||||
} | ||||
| ||||
public static String customDateFormat(String customDate) { | ||||
| ||||
String[] parts = customDate.split("-"); | ||||
| |
| @ -1,10 +1,5 @@ | |||
package org.mian.gitnex.helpers; | ||||
| ||||
import java.io.IOException; | ||||
import java.util.Objects; | ||||
import org.mian.gitnex.R; | ||||
import org.xmlpull.v1.XmlPullParser; | ||||
import org.xmlpull.v1.XmlPullParserException; | ||||
import android.app.Activity; | ||||
import android.content.pm.PackageManager; | ||||
import android.content.res.Resources; | ||||
| @ -12,6 +7,11 @@ import android.content.res.XmlResourceParser; | |||
import android.text.Html; | ||||
import android.util.Log; | ||||
import androidx.appcompat.app.AlertDialog; | ||||
import org.mian.gitnex.R; | ||||
import org.xmlpull.v1.XmlPullParser; | ||||
import org.xmlpull.v1.XmlPullParserException; | ||||
import java.io.IOException; | ||||
import java.util.Objects; | ||||
| ||||
/** | ||||
* Author M M Arif | ||||
| @ -90,13 +90,14 @@ public class ChangeLog { | |||
| ||||
String changelogMessage = getChangelog(resId, res); | ||||
| ||||
androidx.appcompat.app.AlertDialog.Builder builder = new AlertDialog.Builder(changelogActivity); | ||||
AlertDialog.Builder builder = new AlertDialog.Builder(changelogActivity); | ||||
| ||||
builder.setTitle(R.string.changelogTitle); | ||||
builder.setMessage(Html.fromHtml("<small>" + changelogMessage + "</small>")); | ||||
builder.setNeutralButton(R.string.close, null); | ||||
builder.setCancelable(false); | ||||
builder.create(); | ||||
builder.show(); | ||||
| ||||
builder.create().show(); | ||||
| ||||
} | ||||
| ||||
| |
| @ -1,13 +1,16 @@ | |||
package org.mian.gitnex.interfaces; | ||||
| ||||
import com.google.gson.JsonElement; | ||||
import org.mian.gitnex.models.APISettings; | ||||
import org.mian.gitnex.models.AddEmail; | ||||
import org.mian.gitnex.models.AttachmentSettings; | ||||
import org.mian.gitnex.models.Branches; | ||||
import org.mian.gitnex.models.Collaborators; | ||||
import org.mian.gitnex.models.Commits; | ||||
import org.mian.gitnex.models.CreateIssue; | ||||
import org.mian.gitnex.models.CreateLabel; | ||||
import org.mian.gitnex.models.CreatePullRequest; | ||||
import org.mian.gitnex.models.CreateStatusOption; | ||||
import org.mian.gitnex.models.DeleteFile; | ||||
import org.mian.gitnex.models.EditFile; | ||||
import org.mian.gitnex.models.Emails; | ||||
| @ -15,8 +18,10 @@ import org.mian.gitnex.models.ExploreRepositories; | |||
import org.mian.gitnex.models.Files; | ||||
import org.mian.gitnex.models.GiteaVersion; | ||||
import org.mian.gitnex.models.IssueComments; | ||||
import org.mian.gitnex.models.IssueReaction; | ||||
import org.mian.gitnex.models.Issues; | ||||
import org.mian.gitnex.models.Labels; | ||||
import org.mian.gitnex.models.MarkdownOption; | ||||
import org.mian.gitnex.models.MergePullRequest; | ||||
import org.mian.gitnex.models.Milestones; | ||||
import org.mian.gitnex.models.NewFile; | ||||
| @ -28,10 +33,14 @@ import org.mian.gitnex.models.OrganizationRepository; | |||
import org.mian.gitnex.models.Permission; | ||||
import org.mian.gitnex.models.PullRequests; | ||||
import org.mian.gitnex.models.Releases; | ||||
import org.mian.gitnex.models.RepositorySettings; | ||||
import org.mian.gitnex.models.RepositoryTransfer; | ||||
import org.mian.gitnex.models.Status; | ||||
import org.mian.gitnex.models.Teams; | ||||
import org.mian.gitnex.models.UISettings; | ||||
import org.mian.gitnex.models.UpdateIssueAssignees; | ||||
import org.mian.gitnex.models.UpdateIssueState; | ||||
import org.mian.gitnex.models.UserHeatmap; | ||||
import org.mian.gitnex.models.UserInfo; | ||||
import org.mian.gitnex.models.UserOrganizations; | ||||
import org.mian.gitnex.models.UserRepositories; | ||||
| @ -70,8 +79,26 @@ public interface ApiInterface { | |||
@GET("version") // gitea version API | ||||
Call<GiteaVersion> getGiteaVersionWithToken(@Header("Authorization") String token); | ||||
| ||||
@GET("user") // username, full name, email | ||||
Call<UserInfo> getUserInfo(@Header("Authorization") String token); | ||||
@POST("markdown") | ||||
Call<String> renderMarkdown(@Header("Authorization") String token, @Body MarkdownOption markdownOption); | ||||
| ||||
@POST("markdown/raw") | ||||
Call<String> renderRawMarkdown(@Header("Authorization") String token, @Body String body); | ||||
| ||||
@GET("signing-key.gpg") // Get default signing-key.gpg | ||||
Call<String> getSigningKey(@Header("Authorization") String token); | ||||
| ||||
@GET("settings/api") // Get instance's global settings for api | ||||
Call<APISettings> getAPISettings(@Header("Authorization") String token); | ||||
| ||||
@GET("settings/attachment") // Get instance's global settings for attachments | ||||
Call<AttachmentSettings> getAttachmentSettings(@Header("Authorization") String token); | ||||
| ||||
@GET("settings/repository") // Get instance's global settings for repositories | ||||
Call<RepositorySettings> getRepositorySettings(@Header("Authorization") String token); | ||||
| ||||
@GET("settings/ui") // Get instance's global settings for ui | ||||
Call<UISettings> getUISettings(@Header("Authorization") String token); | ||||
| ||||
@GET("users/{username}/tokens") // get user token | ||||
Call<List<UserTokens>> getUserTokens(@Header("Authorization") String authorization, @Path("username") String loginUid); | ||||
| @ -112,6 +139,9 @@ public interface ApiInterface { | |||
@PUT("repos/{owner}/{repo}/notifications") // Mark notification threads as read, pinned or unread on a specific repo | ||||
Call<ResponseBody> markRepoNotificationThreadsAsRead(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Query("all") Boolean all, @Query("status-types") String[] statusTypes, @Query("to-status") String toStatus, @Query("last_read_at") String last_read_at); | ||||
| ||||
@GET("user") // username, full name, email | ||||
Call<UserInfo> getUserInfo(@Header("Authorization") String token); | ||||
| ||||
@GET("user/orgs") // get user organizations | ||||
Call<List<UserOrganizations>> getUserOrgs(@Header("Authorization") String token); | ||||
| ||||
| @ -130,6 +160,24 @@ public interface ApiInterface { | |||
@POST("user/repos") // create new repository | ||||
Call<OrganizationRepository> createNewUserRepository(@Header("Authorization") String token, @Body OrganizationRepository jsonStr); | ||||
| ||||
@GET("user/followers") // get user followers | ||||
Call<List<UserInfo>> getFollowers(@Header("Authorization") String token); | ||||
| ||||
@GET("user/following") // get following | ||||
Call<List<UserInfo>> getFollowing(@Header("Authorization") String token); | ||||
| ||||
@POST("user/emails") // add new email | ||||
Call<JsonElement> addNewEmail(@Header("Authorization") String token, @Body AddEmail jsonStr); | ||||
| ||||
@GET("user/emails") // get user emails | ||||
Call<List<Emails>> getUserEmails(@Header("Authorization") String token); | ||||
| ||||
@GET("user/starred") // get user starred repositories | ||||
Call<List<UserRepositories>> getUserStarredRepos(@Header("Authorization") String token, @Query("page") int page, @Query("limit") int limit); | ||||
| ||||
@GET("users/{username}/heatmap") // Get a user's heatmap | ||||
Call<List<UserHeatmap>> getUserHeatmap(@Header("Authorization") String token, @Path("username") String username); | ||||
| ||||
@GET("repos/{owner}/{repo}") // get repo information | ||||
Call<UserRepositories> getUserRepository(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName); | ||||
| ||||
| @ -193,9 +241,6 @@ public interface ApiInterface { | |||
@PATCH("repos/{owner}/{repo}/labels/{index}") // update / patch a label | ||||
Call<CreateLabel> patchLabel(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int labelIndex, @Body CreateLabel jsonStr); | ||||
| ||||
@GET("user/starred") // get user starred repositories | ||||
Call<List<UserRepositories>> getUserStarredRepos(@Header("Authorization") String token, @Query("page") int page, @Query("limit") int limit); | ||||
| ||||
@GET("orgs/{orgName}/repos") // get repositories by org | ||||
Call<List<UserRepositories>> getReposByOrg(@Header("Authorization") String token, @Path("orgName") String orgName, @Query("page") int page, @Query("limit") int limit); | ||||
| ||||
| @ -226,17 +271,23 @@ public interface ApiInterface { | |||
@PATCH("repos/{owner}/{repo}/issues/comments/{commentId}") // edit a comment | ||||
Call<IssueComments> patchIssueComment(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("commentId") int commentId, @Body IssueComments jsonStr); | ||||
| ||||
@GET("user/followers") // get user followers | ||||
Call<List<UserInfo>> getFollowers(@Header("Authorization") String token); | ||||
@GET("repos/{owner}/{repo}/issues/comments/{commentId}/reactions") // get comment reactions | ||||
Call<List<IssueReaction>> getIssueCommentReactions(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("commentId") int commentId); | ||||
| ||||
@GET("user/following") // get following | ||||
Call<List<UserInfo>> getFollowing(@Header("Authorization") String token); | ||||
@POST("repos/{owner}/{repo}/issues/comments/{commentId}/reactions") // add reaction to a comment | ||||
Call<IssueReaction> setIssueCommentReaction(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("commentId") int commentId, @Body IssueReaction jsonStr); | ||||
| ||||
@POST("user/emails") // add new email | ||||
Call<JsonElement> addNewEmail(@Header("Authorization") String token, @Body AddEmail jsonStr); | ||||
@HTTP(method = "DELETE", path = "repos/{owner}/{repo}/issues/comments/{commentId}/reactions", hasBody = true) // delete a reaction of a comment | ||||
Call<ResponseBody> removeIssueCommentReaction(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("commentId") int commentId, @Body IssueReaction jsonStr); | ||||
| ||||
@GET("user/emails") // get user emails | ||||
Call<List<Emails>> getUserEmails(@Header("Authorization") String token); | ||||
@GET("repos/{owner}/{repo}/issues/{index}/reactions") // get issue reactions | ||||
Call<List<IssueReaction>> getIssueReactions(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int issueIndex); | ||||
| ||||
@POST("repos/{owner}/{repo}/issues/{index}/reactions") // add reaction to an issue | ||||
Call<IssueReaction> setIssueReaction(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int issueIndex, @Body IssueReaction jsonStr); | ||||
| ||||
@HTTP(method = "DELETE", path = "repos/{owner}/{repo}/issues/{index}/reactions", hasBody = true) // delete a reaction of an issue | ||||
Call<ResponseBody> removeIssueReaction(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int issueIndex, @Body IssueReaction jsonStr); | ||||
| ||||
@GET("repos/{owner}/{repo}/issues/{index}/labels") // get issue labels | ||||
Call<List<Labels>> getIssueLabels(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("index") int issueIndex); | ||||
| @ -363,4 +414,12 @@ public interface ApiInterface { | |||
| ||||
@GET("repos/{owner}/{repo}/forks") // get all repo forks | ||||
Call<List<UserRepositories>> getRepositoryForks(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Query("page") int page, @Query("limit") int limit); | ||||
| ||||
@POST("repos/{owner}/{repo}/statuses/{sha}") // Create a commit status | ||||
Call<Status> createCommitStatus(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Path("sha") String sha, @Body CreateStatusOption createStatusOption); | ||||
| ||||
@GET("repos/{owner}/{repo}/statuses/{sha}") // Get a commit's statuses | ||||
Call<List<Status>> getCommitStatuses(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Query("sort") String sort, @Query("state") String state, @Query("page") int page, @Query("limit") int limit); | ||||
| ||||
| ||||
} | ||||
| |
34 app/src/main/java/org/mian/gitnex/models/APISettings.java Normal file
34
app/src/main/java/org/mian/gitnex/models/APISettings.java Normal file | @ -0,0 +1,34 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class APISettings { | ||||
| ||||
private int default_git_trees_per_page; | ||||
private int default_max_blob_size; | ||||
private int default_paging_num; | ||||
private int max_response_items; | ||||
| ||||
public int getDefault_git_trees_per_page() { | ||||
| ||||
return default_git_trees_per_page; | ||||
} | ||||
| ||||
public int getDefault_max_blob_size() { | ||||
| ||||
return default_max_blob_size; | ||||
} | ||||
| ||||
public int getDefault_paging_num() { | ||||
| ||||
return default_paging_num; | ||||
} | ||||
| ||||
public int getMax_response_items() { | ||||
| ||||
return max_response_items; | ||||
} | ||||
| ||||
} |
| @ -0,0 +1,34 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class AttachmentSettings { | ||||
| ||||
private String allowed_types; | ||||
private boolean enabled; | ||||
private float max_files; | ||||
private float max_size; | ||||
| ||||
public String getAllowed_types() { | ||||
| ||||
return allowed_types; | ||||
} | ||||
| ||||
public boolean isEnabled() { | ||||
| ||||
return enabled; | ||||
} | ||||
| ||||
public float getMax_files() { | ||||
| ||||
return max_files; | ||||
} | ||||
| ||||
public float getMax_size() { | ||||
| ||||
return max_size; | ||||
} | ||||
| ||||
} |
| @ -0,0 +1,36 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class CreateStatusOption { | ||||
| ||||
private String context; | ||||
private String description; | ||||
private String statusState; | ||||
private String target_url; | ||||
| ||||
public CreateStatusOption(String context, String description, String statusState, String target_url) { | ||||
this.context = context; | ||||
this.description = description; | ||||
this.statusState = statusState; | ||||
this.target_url = target_url; | ||||
} | ||||
| ||||
public String getContext() { | ||||
return context; | ||||
} | ||||
| ||||
public String getDescription() { | ||||
return description; | ||||
} | ||||
| ||||
public String getStatusState() { | ||||
return statusState; | ||||
} | ||||
| ||||
public String getTarget_url() { | ||||
return target_url; | ||||
} | ||||
} |
71 app/src/main/java/org/mian/gitnex/models/IssueReaction.java Normal file
71
app/src/main/java/org/mian/gitnex/models/IssueReaction.java Normal file | @ -0,0 +1,71 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
import java.util.Date; | ||||
| ||||
/** | ||||
* Author 6543 | ||||
*/ | ||||
| ||||
public class IssueReaction { | ||||
| ||||
private String content; | ||||
private userObject user; | ||||
private Date created_at; | ||||
| ||||
public IssueReaction(String content) { | ||||
this.content = content; | ||||
} | ||||
| ||||
public static class userObject { | ||||
| ||||
private int id; | ||||
private String login; | ||||
private String full_name; | ||||
private String email; | ||||
private String avatar_url; | ||||
private String language; | ||||
private String username; | ||||
| ||||
public int getId() { | ||||
return id; | ||||
} | ||||
| ||||
public String getLogin() { | ||||
return login; | ||||
} | ||||
| ||||
public String getFull_name() { | ||||
return full_name; | ||||
} | ||||
| ||||
public String getEmail() { | ||||
return email; | ||||
} | ||||
| ||||
public String getAvatar_url() { | ||||
return avatar_url; | ||||
} | ||||
| ||||
public String getLanguage() { | ||||
return language; | ||||
} | ||||
| ||||
public String getUsername() { | ||||
return username; | ||||
} | ||||
| ||||
} | ||||
| ||||
public String getContent() { | ||||
return content; | ||||
} | ||||
| ||||
public userObject getUser() { | ||||
return user; | ||||
} | ||||
| ||||
public Date getCreated_at() { | ||||
return created_at; | ||||
} | ||||
| ||||
} |
42 app/src/main/java/org/mian/gitnex/models/MarkdownOption.java Normal file
42
app/src/main/java/org/mian/gitnex/models/MarkdownOption.java Normal file | @ -0,0 +1,42 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class MarkdownOption { | ||||
| ||||
private String Context; | ||||
private String Mode; | ||||
private String Text; | ||||
private boolean Wiki; | ||||
| ||||
public MarkdownOption(String context, String mode, String text, boolean wiki) { | ||||
| ||||
Context = context; | ||||
Mode = mode; | ||||
Text = text; | ||||
Wiki = wiki; | ||||
} | ||||
| ||||
public String getContext() { | ||||
| ||||
return Context; | ||||
} | ||||
| ||||
public String getMode() { | ||||
| ||||
return Mode; | ||||
} | ||||
| ||||
public String getText() { | ||||
| ||||
return Text; | ||||
} | ||||
| ||||
public boolean isWiki() { | ||||
| ||||
return Wiki; | ||||
} | ||||
| ||||
} |
| @ -1,42 +0,0 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
/** | ||||
* Author com.github.abumoallim, modified by M M Arif | ||||
*/ | ||||
| ||||
public class MultiSelectModel { | ||||
| ||||
private Integer id; | ||||
private String name; | ||||
private Boolean isSelected; | ||||
| ||||
public MultiSelectModel(Integer id, String name) { | ||||
this.id = id; | ||||
this.name = name; | ||||
} | ||||
| ||||
public int getId() { | ||||
return id; | ||||
} | ||||
| ||||
public void setId(Integer id) { | ||||
this.id = id; | ||||
} | ||||
| ||||
public String getName() { | ||||
return name; | ||||
} | ||||
| ||||
public void setName(String name) { | ||||
this.name = name; | ||||
} | ||||
| ||||
public Boolean getSelected() { | ||||
return isSelected; | ||||
} | ||||
| ||||
public void setSelected(Boolean selected) { | ||||
isSelected = selected; | ||||
} | ||||
| ||||
} |
| @ -0,0 +1,22 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class RepositorySettings { | ||||
| ||||
private boolean http_git_disabled; | ||||
private boolean mirrors_disabled; | ||||
| ||||
public boolean isHttp_git_disabled() { | ||||
| ||||
return http_git_disabled; | ||||
} | ||||
| ||||
public boolean isMirrors_disabled() { | ||||
| ||||
return mirrors_disabled; | ||||
} | ||||
| ||||
} |
56 app/src/main/java/org/mian/gitnex/models/Status.java Normal file
56
app/src/main/java/org/mian/gitnex/models/Status.java Normal file | @ -0,0 +1,56 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
import java.util.Date; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class Status { | ||||
| ||||
private String context; | ||||
private Date created_at; | ||||
private UserInfo creator; | ||||
private String description; | ||||
private int id; | ||||
private String status; | ||||
private String target_url; | ||||
private Date updated_at; | ||||
private String url; | ||||
| ||||
public String getContext() { | ||||
return context; | ||||
} | ||||
| ||||
public Date getCreated_at() { | ||||
return created_at; | ||||
} | ||||
| ||||
public UserInfo getCreator() { | ||||
return creator; | ||||
} | ||||
| ||||
public String getDescription() { | ||||
return description; | ||||
} | ||||
| ||||
public int getId() { | ||||
return id; | ||||
} | ||||
| ||||
public String getStatus() { | ||||
return status; | ||||
} | ||||
| ||||
public String getTarget_url() { | ||||
return target_url; | ||||
} | ||||
| ||||
public Date getUpdated_at() { | ||||
return updated_at; | ||||
} | ||||
| ||||
public String getUrl() { | ||||
return url; | ||||
} | ||||
} |
16 app/src/main/java/org/mian/gitnex/models/UISettings.java Normal file
16
app/src/main/java/org/mian/gitnex/models/UISettings.java Normal file | @ -0,0 +1,16 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class UISettings { | ||||
| ||||
private String[] allowed_reactions; | ||||
| ||||
public String[] getAllowed_reactions() { | ||||
| ||||
return allowed_reactions; | ||||
} | ||||
| ||||
} |
12 app/src/main/java/org/mian/gitnex/models/UserHeatmap.java Normal file
12
app/src/main/java/org/mian/gitnex/models/UserHeatmap.java Normal file | @ -0,0 +1,12 @@ | |||
package org.mian.gitnex.models; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class UserHeatmap { | ||||
| ||||
private long contributions; | ||||
private long timestamp; | ||||
| ||||
} |
| @ -1,4 +1,4 @@ | |||
package org.mian.gitnex.helpers; | ||||
package org.mian.gitnex.views; | ||||
| ||||
import android.content.Context; | ||||
import android.util.AttributeSet; |
140 app/src/main/java/org/mian/gitnex/views/ReactionList.java Normal file
140
app/src/main/java/org/mian/gitnex/views/ReactionList.java Normal file | @ -0,0 +1,140 @@ | |||
package org.mian.gitnex.views; | ||||
| ||||
import android.annotation.SuppressLint; | ||||
import android.content.Context; | ||||
import android.os.Bundle; | ||||
import android.view.Gravity; | ||||
import android.view.LayoutInflater; | ||||
import android.view.ViewGroup; | ||||
import android.widget.HorizontalScrollView; | ||||
import android.widget.LinearLayout; | ||||
import android.widget.TextView; | ||||
import androidx.cardview.widget.CardView; | ||||
import com.vdurmont.emoji.Emoji; | ||||
import com.vdurmont.emoji.EmojiManager; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.Authorization; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.models.IssueReaction; | ||||
import java.io.IOException; | ||||
import java.util.ArrayList; | ||||
import java.util.HashMap; | ||||
import java.util.List; | ||||
import java.util.Map; | ||||
import retrofit2.Response; | ||||
| ||||
/** | ||||
* @author opyale | ||||
*/ | ||||
| ||||
@SuppressLint("ViewConstructor") | ||||
public class ReactionList extends HorizontalScrollView { | ||||
| ||||
private enum ReactionType { COMMENT, ISSUE } | ||||
| ||||
@SuppressLint("SetTextI18n") | ||||
public ReactionList(Context context, Bundle bundle) { | ||||
| ||||
super(context); | ||||
| ||||
LinearLayout root = new LinearLayout(context); | ||||
| ||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); | ||||
| ||||
root.setOrientation(LinearLayout.HORIZONTAL); | ||||
root.setGravity(Gravity.START); | ||||
root.setLayoutParams(layoutParams); | ||||
| ||||
addView(root); | ||||
setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); | ||||
| ||||
TinyDB tinyDB = TinyDB.getInstance(context); | ||||
| ||||
String loginUid = tinyDB.getString("loginUid"); | ||||
String repoOwner = bundle.getString("repoOwner"); | ||||
String repoName = bundle.getString("repoName"); | ||||
| ||||
int id; | ||||
ReactionType reactionType; | ||||
| ||||
if(bundle.containsKey("commentId")) { | ||||
id = bundle.getInt("commentId"); | ||||
reactionType = ReactionType.COMMENT; | ||||
} else { | ||||
id = bundle.getInt("issueId"); | ||||
reactionType = ReactionType.ISSUE; | ||||
} | ||||
| ||||
new Thread(() -> { | ||||
| ||||
try { | ||||
| ||||
Response<List<IssueReaction>> response = null; | ||||
| ||||
switch(reactionType) { | ||||
| ||||
case ISSUE: | ||||
response = RetrofitClient | ||||
.getApiInterface(context) | ||||
.getIssueReactions(Authorization.get(context), repoOwner, repoName, id) | ||||
.execute(); | ||||
break; | ||||
| ||||
case COMMENT: | ||||
response = RetrofitClient | ||||
.getApiInterface(context) | ||||
.getIssueCommentReactions(Authorization.get(context), repoOwner, repoName, id) | ||||
.execute(); | ||||
break; | ||||
| ||||
} | ||||
| ||||
Map<String, List<IssueReaction>> sortedReactions = new HashMap<>(); | ||||
| ||||
if(response.isSuccessful() && response.body() != null) { | ||||
| ||||
for(IssueReaction issueReaction : response.body()) { | ||||
| ||||
if(sortedReactions.containsKey(issueReaction.getContent())) { | ||||
| ||||
sortedReactions.get(issueReaction.getContent()).add(issueReaction); | ||||
} else { | ||||
List<IssueReaction> issueReactions = new ArrayList<>(); | ||||
issueReactions.add(issueReaction); | ||||
| ||||
sortedReactions.put(issueReaction.getContent(), issueReactions); | ||||
} | ||||
} | ||||
} | ||||
| ||||
for(String content : sortedReactions.keySet()) { | ||||
| ||||
List<IssueReaction> issueReactions = sortedReactions.get(content); | ||||
| ||||
@SuppressLint("InflateParams") CardView reactionBadge = (CardView) LayoutInflater.from(context) | ||||
.inflate(R.layout.layout_reaction_badge, this, false); | ||||
| ||||
for(IssueReaction issueReaction : issueReactions) { | ||||
| ||||
if(issueReaction.getUser().getLogin().equals(loginUid)) { | ||||
reactionBadge.setCardBackgroundColor(AppUtil.getColorFromAttribute(context, R.attr.inputSelectedColor)); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
Emoji emoji = EmojiManager.getForAlias(content); | ||||
| ||||
((TextView) reactionBadge.findViewById(R.id.symbol)).setText(((emoji == null) ? content : emoji.getUnicode()) + " " + issueReactions.size()); | ||||
root.post(() -> root.addView(reactionBadge)); | ||||
| ||||
} | ||||
| ||||
} catch (IOException ignored) {} | ||||
| ||||
}).start(); | ||||
| ||||
} | ||||
| ||||
} |
238 app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java Normal file
238
app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java Normal file | @ -0,0 +1,238 @@ | |||
package org.mian.gitnex.views; | ||||
| ||||
import android.annotation.SuppressLint; | ||||
import android.content.Context; | ||||
import android.os.Bundle; | ||||
import android.view.Gravity; | ||||
import android.view.LayoutInflater; | ||||
import android.view.ViewGroup; | ||||
import android.widget.HorizontalScrollView; | ||||
import android.widget.LinearLayout; | ||||
import android.widget.TextView; | ||||
import androidx.cardview.widget.CardView; | ||||
import com.vdurmont.emoji.Emoji; | ||||
import com.vdurmont.emoji.EmojiManager; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.Authorization; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.models.IssueReaction; | ||||
import org.mian.gitnex.models.UISettings; | ||||
import java.io.IOException; | ||||
import java.util.ArrayList; | ||||
import java.util.Arrays; | ||||
import java.util.Collections; | ||||
import java.util.List; | ||||
import retrofit2.Response; | ||||
| ||||
/** | ||||
* @author opyale | ||||
*/ | ||||
| ||||
@SuppressLint("ViewConstructor") | ||||
public class ReactionSpinner extends HorizontalScrollView { | ||||
| ||||
private enum ReactionType { COMMENT, ISSUE } | ||||
private enum ReactionAction { REMOVE, ADD } | ||||
| ||||
private OnInteractedListener onInteractedListener; | ||||
| ||||
public ReactionSpinner(Context context, Bundle bundle) { | ||||
| ||||
super(context); | ||||
| ||||
LinearLayout root = new LinearLayout(context); | ||||
| ||||
int dens = AppUtil.getPixelsFromDensity(context, 10); | ||||
| ||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); | ||||
| ||||
root.setOrientation(LinearLayout.HORIZONTAL); | ||||
root.setPadding(dens, 0, dens, 0); | ||||
root.setGravity(Gravity.START); | ||||
root.setLayoutParams(layoutParams); | ||||
| ||||
TinyDB tinyDB = TinyDB.getInstance(context); | ||||
| ||||
String loginUid = tinyDB.getString("loginUid"); | ||||
String repoOwner = bundle.getString("repoOwner"); | ||||
String repoName = bundle.getString("repoName"); | ||||
| ||||
int id; | ||||
ReactionType reactionType; | ||||
| ||||
if(bundle.containsKey("commentId")) { | ||||
id = bundle.getInt("commentId"); | ||||
reactionType = ReactionType.COMMENT; | ||||
} else { | ||||
id = bundle.getInt("issueId"); | ||||
reactionType = ReactionType.ISSUE; | ||||
} | ||||
| ||||
new Thread(() -> { | ||||
| ||||
try { | ||||
| ||||
List<IssueReaction> allReactions = getReactions(repoOwner, repoName, reactionType, id); | ||||
| ||||
for(String allowedReaction : getAllowedReactions()) { | ||||
| ||||
@SuppressLint("InflateParams") CardView reactionButton = (CardView) LayoutInflater.from(context) | ||||
.inflate(R.layout.layout_reaction_button, root, false); | ||||
| ||||
IssueReaction myReaction = null; | ||||
| ||||
for(IssueReaction issueReaction : allReactions) { | ||||
| ||||
if(issueReaction.getContent().equals(allowedReaction) && issueReaction.getUser().getLogin().equals(loginUid)) { | ||||
myReaction = issueReaction; | ||||
break; | ||||
} | ||||
} | ||||
| ||||
ReactionAction reactionAction; | ||||
| ||||
if(myReaction != null) { | ||||
| ||||
reactionButton.setCardBackgroundColor(AppUtil.getColorFromAttribute(context, R.attr.inputSelectedColor)); | ||||
reactionAction = ReactionAction.REMOVE; | ||||
} else { | ||||
reactionAction = ReactionAction.ADD; | ||||
} | ||||
| ||||
reactionButton.setOnClickListener(v -> new Thread(() -> { | ||||
| ||||
try { | ||||
if(react(repoOwner, repoName, reactionType, reactionAction, new IssueReaction(allowedReaction), id)) { | ||||
v.post(() -> onInteractedListener.onInteracted()); | ||||
} | ||||
} catch(IOException ignored) {} | ||||
| ||||
}).start()); | ||||
| ||||
Emoji emoji = EmojiManager.getForAlias(allowedReaction); | ||||
| ||||
((TextView) reactionButton.findViewById(R.id.symbol)).setText((emoji == null) ? allowedReaction : emoji.getUnicode()); | ||||
root.post(() -> root.addView(reactionButton)); | ||||
| ||||
} | ||||
| ||||
} catch(IOException ignored) {} | ||||
| ||||
}).start(); | ||||
| ||||
setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); | ||||
addView(root); | ||||
| ||||
} | ||||
| ||||
private boolean react(String repoOwner, String repoName, ReactionType reactionType, ReactionAction reactionAction, IssueReaction issueReaction, int id) throws IOException { | ||||
| ||||
Response<?> response = null; | ||||
| ||||
switch(reactionType) { | ||||
| ||||
case ISSUE: | ||||
switch(reactionAction) { | ||||
| ||||
case ADD: | ||||
response = RetrofitClient | ||||
.getApiInterface(getContext()) | ||||
.setIssueReaction(Authorization.get(getContext()), repoOwner, repoName, id, issueReaction) | ||||
.execute(); | ||||
break; | ||||
| ||||
| ||||
case REMOVE: | ||||
response = RetrofitClient | ||||
.getApiInterface(getContext()) | ||||
.removeIssueReaction(Authorization.get(getContext()), repoOwner, repoName, id, issueReaction) | ||||
.execute(); | ||||
break; | ||||
| ||||
} | ||||
break; | ||||
| ||||
case COMMENT: | ||||
switch(reactionAction) { | ||||
| ||||
case ADD: | ||||
response = RetrofitClient | ||||
.getApiInterface(getContext()) | ||||
.setIssueCommentReaction(Authorization.get(getContext()), repoOwner, repoName, id, issueReaction) | ||||
.execute(); | ||||
break; | ||||
| ||||
| ||||
case REMOVE: | ||||
response = RetrofitClient | ||||
.getApiInterface(getContext()) | ||||
.removeIssueCommentReaction(Authorization.get(getContext()), repoOwner, repoName, id, issueReaction) | ||||
.execute(); | ||||
break; | ||||
| ||||
} | ||||
break; | ||||
| ||||
} | ||||
| ||||
return response.isSuccessful(); | ||||
| ||||
} | ||||
| ||||
private List<IssueReaction> getReactions(String repoOwner, String repoName, ReactionType reactionType, int id) throws IOException { | ||||
| ||||
Response<List<IssueReaction>> response = null; | ||||
| ||||
switch(reactionType) { | ||||
| ||||
case ISSUE: | ||||
response = RetrofitClient | ||||
.getApiInterface(getContext()) | ||||
.getIssueReactions(Authorization.get(getContext()), repoOwner, repoName, id) | ||||
.execute(); | ||||
break; | ||||
| ||||
case COMMENT: | ||||
response = RetrofitClient | ||||
.getApiInterface(getContext()) | ||||
.getIssueCommentReactions(Authorization.get(getContext()), repoOwner, repoName, id) | ||||
.execute(); | ||||
break; | ||||
| ||||
} | ||||
| ||||
if(response.isSuccessful() && response.body() != null) | ||||
return response.body(); | ||||
else | ||||
return Collections.emptyList(); | ||||
| ||||
} | ||||
| ||||
private List<String> getAllowedReactions() throws IOException { | ||||
| ||||
List<String> allowedReactions = new ArrayList<>(); | ||||
| ||||
Response<UISettings> response = RetrofitClient | ||||
.getApiInterface(getContext()) | ||||
.getUISettings(Authorization.get(getContext())) | ||||
.execute(); | ||||
| ||||
if(response.isSuccessful() && response.body() != null) { | ||||
allowedReactions.addAll(Arrays.asList(response.body().getAllowed_reactions())); | ||||
} else { | ||||
allowedReactions.addAll(Arrays.asList("+1", "-1", "laugh", "hooray", "confused", "heart", "rocket", "eyes")); | ||||
} | ||||
| ||||
return allowedReactions; | ||||
| ||||
} | ||||
| ||||
public void setOnInteractedListener(OnInteractedListener onInteractedListener) { | ||||
this.onInteractedListener = onInteractedListener; | ||||
} | ||||
| ||||
public interface OnInteractedListener { void onInteracted(); } | ||||
| ||||
} |
| @ -225,6 +225,14 @@ | |||
| ||||
</RelativeLayout> | ||||
| ||||
<LinearLayout | ||||
android:id="@+id/commentReactionBadges" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_below="@+id/issueTimeFrame" | ||||
android:layout_marginTop="10dp" | ||||
android:orientation="horizontal" /> | ||||
| ||||
</RelativeLayout> | ||||
| ||||
<View | ||||
| |
| @ -4,10 +4,10 @@ | |||
xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:background="?attr/primaryBackgroundColor" | ||||
android:orientation="vertical" | ||||
android:paddingTop="6dp" | ||||
android:paddingBottom="12dp" | ||||
android:background="?attr/primaryBackgroundColor"> | ||||
android:paddingBottom="12dp"> | ||||
| ||||
<androidx.core.widget.NestedScrollView | ||||
android:layout_width="match_parent" | ||||
| @ -15,8 +15,15 @@ | |||
| ||||
<LinearLayout | ||||
android:layout_width="match_parent" | ||||
android:orientation="vertical" | ||||
android:layout_height="wrap_content"> | ||||
android:layout_height="wrap_content" | ||||
android:orientation="vertical"> | ||||
| ||||
<LinearLayout | ||||
android:id="@+id/commentReactionButtons" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginBottom="10dp" | ||||
android:orientation="horizontal" /> | ||||
| ||||
<TextView | ||||
android:id="@+id/commentMenuEdit" | ||||
| |
| @ -17,6 +17,13 @@ | |||
android:orientation="vertical" | ||||
android:layout_height="wrap_content"> | ||||
| ||||
<LinearLayout | ||||
android:id="@+id/commentReactionButtons" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginBottom="10dp" | ||||
android:orientation="horizontal" /> | ||||
| ||||
<TextView | ||||
android:id="@+id/openFilesDiff" | ||||
android:layout_width="match_parent" | ||||
| |
24 app/src/main/res/layout/layout_reaction_badge.xml Normal file
24
app/src/main/res/layout/layout_reaction_badge.xml Normal file | @ -0,0 +1,24 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | ||||
<androidx.cardview.widget.CardView 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:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginEnd="5dp" | ||||
app:cardBackgroundColor="?attr/inputBackgroundColor" | ||||
app:cardCornerRadius="10dp" | ||||
app:cardElevation="0dp"> | ||||
| ||||
<TextView | ||||
android:id="@+id/symbol" | ||||
android:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:paddingLeft="8dp" | ||||
android:paddingTop="3dp" | ||||
android:paddingRight="8dp" | ||||
android:paddingBottom="3dp" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="15sp" | ||||
tools:text="👍" /> | ||||
| ||||
</androidx.cardview.widget.CardView> |
22 app/src/main/res/layout/layout_reaction_button.xml Normal file
22
app/src/main/res/layout/layout_reaction_button.xml Normal file | @ -0,0 +1,22 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | ||||
<androidx.cardview.widget.CardView 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:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginEnd="5dp" | ||||
app:cardBackgroundColor="?attr/inputBackgroundColor" | ||||
app:cardCornerRadius="25sp" | ||||
app:cardElevation="0dp" | ||||
app:contentPadding="10dp"> | ||||
| ||||
<TextView | ||||
android:id="@+id/symbol" | ||||
android:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_gravity="center" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="20sp" | ||||
tools:text="👍" /> | ||||
| ||||
</androidx.cardview.widget.CardView> |
| @ -71,4 +71,11 @@ | |||
android:textIsSelectable="true" | ||||
android:textSize="14sp" /> | ||||
| ||||
<LinearLayout | ||||
android:id="@+id/commentReactionBadges" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginTop="15dp" | ||||
android:orientation="horizontal" /> | ||||
| ||||
</LinearLayout> | ||||
| |
| @ -17,6 +17,7 @@ | |||
<item name="primaryTextColor">@color/lightThemeTextColor</item> | ||||
<item name="primaryBackgroundColor">@color/lightThemeBackground</item> | ||||
<item name="inputBackgroundColor">@color/lightThemeInputBackground</item> | ||||
<item name="inputSelectedColor">@color/lightThemInputSelected</item> | ||||
<item name="inputTextColor">@color/lightThemeInputTextColor</item> | ||||
<item name="checkboxStyle">@style/AppThemeLightCheckBoxStyle</item> | ||||
<item name="selectedTextColor">@color/darkGreen</item> | ||||
| @ -57,6 +58,7 @@ | |||
<item name="primaryTextColor">@color/retroThemeTextColor</item> | ||||
<item name="primaryBackgroundColor">@color/retroThemeBackground</item> | ||||
<item name="inputBackgroundColor">@color/retroThemeInputBackground</item> | ||||
<item name="inputSelectedColor">@color/retroThemeInputSelected</item> | ||||
<item name="inputTextColor">@color/retroThemeInputTextColor</item> | ||||
<item name="checkboxStyle">@style/AppThemeRetroCheckBoxStyle</item> | ||||
<item name="selectedTextColor">@color/retroThemeColorPrimary</item> | ||||
| |
| @ -6,6 +6,7 @@ | |||
<attr name="primaryTextColor" format="reference"/> | ||||
<attr name="primaryBackgroundColor" format="reference" /> | ||||
<attr name="inputBackgroundColor" format="reference" /> | ||||
<attr name="inputSelectedColor" format="reference" /> | ||||
<attr name="hintColor" format="reference" /> | ||||
<attr name="inputTextColor" format="reference" /> | ||||
<attr name="selectedTextColor" format="reference" /> | ||||
| |
| @ -12,6 +12,7 @@ | |||
<color name="btnBackground">#009486</color> | ||||
<color name="btnTextColor">#ffffff</color> | ||||
<color name="inputBackground">#1d1d1d</color> | ||||
<color name="inputSelected">#3A3A3A</color> | ||||
<color name="toastBackground">#1d1d1d</color> | ||||
<color name="releasePre">#f2711c</color> | ||||
<color name="releaseStable">@color/btnBackground</color> | ||||
| @ -35,7 +36,8 @@ | |||
<color name="lightThemeDiffSelectedColor">#e0e0e0</color> | ||||
<color name="lightThemeTextColor">#646565</color> | ||||
<color name="lightThemeBackground">#f9f9f9</color> | ||||
<color name="lightThemeInputBackground">#f2f2f2</color> | ||||
<color name="lightThemeInputBackground">#EFEFEF</color> | ||||
<color name="lightThemInputSelected">#C9CCCC</color> | ||||
<color name="lightThemeInputTextColor">#212121</color> | ||||
<color name="lightThemeDividerColor">#dbdbdb</color> | ||||
| ||||
| @ -45,6 +47,7 @@ | |||
<color name="retroThemeTextColor">#212f3c</color> | ||||
<color name="retroThemeBackground">#fcfcfc</color> | ||||
<color name="retroThemeInputBackground">#f2f2f2</color> | ||||
<color name="retroThemeInputSelected">#D3D3D3</color> | ||||
<color name="retroThemeInputTextColor">#6200EE</color> | ||||
<color name="retroThemeDividerColor">#dbdbdb</color> | ||||
<color name="retroThemeColorPrimary">#6200EE</color> | ||||
| |
| @ -17,6 +17,7 @@ | |||
<item name="primaryTextColor">@color/colorWhite</item> | ||||
<item name="primaryBackgroundColor">@color/colorPrimary</item> | ||||
<item name="inputBackgroundColor">@color/inputBackground</item> | ||||
<item name="inputSelectedColor">@color/inputSelected</item> | ||||
<item name="inputTextColor">@color/colorWhite</item> | ||||
<item name="checkboxStyle">@style/AppThemeCheckBoxStyle</item> | ||||
<item name="selectedTextColor">@color/darkGreen</item> | ||||
| @ -56,6 +57,7 @@ | |||
<item name="primaryTextColor">@color/lightThemeTextColor</item> | ||||
<item name="primaryBackgroundColor">@color/lightThemeBackground</item> | ||||
<item name="inputBackgroundColor">@color/lightThemeInputBackground</item> | ||||
<item name="inputSelectedColor">@color/lightThemInputSelected</item> | ||||
<item name="inputTextColor">@color/lightThemeInputTextColor</item> | ||||
<item name="checkboxStyle">@style/AppThemeLightCheckBoxStyle</item> | ||||
<item name="selectedTextColor">@color/darkGreen</item> | ||||
| @ -95,6 +97,7 @@ | |||
<item name="primaryTextColor">@color/retroThemeTextColor</item> | ||||
<item name="primaryBackgroundColor">@color/retroThemeBackground</item> | ||||
<item name="inputBackgroundColor">@color/retroThemeInputBackground</item> | ||||
<item name="inputSelectedColor">@color/retroThemeInputSelected</item> | ||||
<item name="inputTextColor">@color/retroThemeInputTextColor</item> | ||||
<item name="checkboxStyle">@style/AppThemeRetroCheckBoxStyle</item> | ||||
<item name="selectedTextColor">@color/retroThemeColorPrimary</item> | ||||
| |
Loading…
Add table
Add a link
Reference in a new issue