Create and edit wiki page

This commit is contained in:
M M Arif
2022-06-21 12:57:14 +05:00
parent 0fb637d6b3
commit c9b8403df4
8 changed files with 322 additions and 28 deletions

View File

@@ -56,8 +56,8 @@ dependencies {
def acra = '5.8.4'
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.legacy:legacy-support-v4:1.0.0"

View File

@@ -347,7 +347,10 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe
break;
case "createWiki":
startActivity(repository.getIntent(ctx, CodeEditorActivity.class));
Intent intent = new Intent(ctx, WikiActivity.class);
intent.putExtra("action", "add");
intent.putExtra(RepositoryContext.INTENT_EXTRA, ((RepoDetailActivity) ctx).repository);
ctx.startActivity(intent);
break;
}
}

View File

@@ -8,15 +8,18 @@ import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import com.vdurmont.emoji.EmojiParser;
import org.gitnex.tea4j.v2.models.CreateWikiPageOptions;
import org.gitnex.tea4j.v2.models.WikiPage;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.databinding.ActivityWikiBinding;
import org.mian.gitnex.fragments.WikiFragment;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.AppUtil;
import org.mian.gitnex.helpers.Markdown;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.helpers.contexts.RepositoryContext;
import java.util.Objects;
import retrofit2.Call;
import retrofit2.Callback;
@@ -28,6 +31,7 @@ public class WikiActivity extends BaseActivity {
private ActivityWikiBinding binding;
private String pageName;
private String action;
private RepositoryContext repository;
private boolean renderMd = true;
@@ -42,13 +46,152 @@ public class WikiActivity extends BaseActivity {
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
if(getIntent().getStringExtra("action") != null) {
action = getIntent().getStringExtra("action");
}
else {
action = "";
}
pageName = getIntent().getStringExtra("pageName");
binding.close.setOnClickListener(view -> finish());
binding.toolbarTitle.setMovementMethod(new ScrollingMovementMethod());
binding.toolbarTitle.setText(pageName);
getWikiPageContents();
if(action.equalsIgnoreCase("edit")) {
getWikiPageContents();
binding.renderWiki.setVisibility(View.GONE);
binding.wikiTitle.setText(pageName);
}
else if(action.equalsIgnoreCase("add")) {
binding.renderWiki.setVisibility(View.GONE);
binding.progressBar.setVisibility(View.GONE);
binding.toolbarTitle.setText(R.string.createWikiPage);
binding.createWiki.setVisibility(View.VISIBLE);
}
else {
getWikiPageContents();
binding.renderWiki.setVisibility(View.VISIBLE);
binding.createWiki.setVisibility(View.GONE);
}
}
private void processWiki() {
String wikiTitle = binding.wikiTitle.getText() != null ? binding.wikiTitle.getText().toString() : "";
String wikiContent = binding.wikiContent.getText() != null ? binding.wikiContent.getText().toString() : "";
if(wikiTitle.isEmpty() || wikiContent.isEmpty()) {
Toasty.error(ctx, getString(R.string.wikiPageNameAndContentError));
return;
}
if(action.equalsIgnoreCase("edit")) {
patchWiki(wikiTitle, wikiContent);
}
else if(action.equalsIgnoreCase("add")) {
addWiki(wikiTitle, wikiContent);
}
}
private void addWiki(String wikiTitle, String wikiContent) {
CreateWikiPageOptions createWikiPageOptions = new CreateWikiPageOptions();
createWikiPageOptions.setTitle(wikiTitle);
createWikiPageOptions.setContentBase64(AppUtil.encodeBase64(wikiContent));
Call<WikiPage> call = RetrofitClient
.getApiInterface(ctx)
.repoCreateWikiPage(repository.getOwner(), repository.getName(), createWikiPageOptions);
call.enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<WikiPage> call, @NonNull retrofit2.Response<WikiPage> response) {
if(response.isSuccessful()) {
if(response.code() == 201) {
Toasty.success(ctx, getString(R.string.wikiCreated));
WikiFragment.resumeWiki = true;
finish();
}
else {
switch(response.code()) {
case 401:
runOnUiThread(() -> AlertDialogs.authorizationTokenRevokedDialog(ctx));
break;
case 403:
runOnUiThread(() -> Toasty.error(ctx, ctx.getString(R.string.authorizeError)));
break;
case 404:
runOnUiThread(() -> Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)));
break;
default:
runOnUiThread(() -> Toasty.error(ctx, getString(R.string.genericError)));
}
}
}
}
@Override
public void onFailure(@NonNull Call<WikiPage> call, @NonNull Throwable t) {
Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError));
}
});
}
private void patchWiki(String wikiTitle, String wikiContent) {
CreateWikiPageOptions createWikiPageOptions = new CreateWikiPageOptions();
createWikiPageOptions.setTitle(wikiTitle);
createWikiPageOptions.setContentBase64(AppUtil.encodeBase64(wikiContent));
Call<WikiPage> call = RetrofitClient
.getApiInterface(ctx)
.repoEditWikiPage(repository.getOwner(), repository.getName(), pageName, createWikiPageOptions);
call.enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<WikiPage> call, @NonNull retrofit2.Response<WikiPage> response) {
if(response.isSuccessful()) {
if(response.code() == 200) {
Toasty.success(ctx, getString(R.string.wikiUpdated));
WikiFragment.resumeWiki = true;
finish();
}
else {
switch(response.code()) {
case 401:
runOnUiThread(() -> AlertDialogs.authorizationTokenRevokedDialog(ctx));
break;
case 403:
runOnUiThread(() -> Toasty.error(ctx, ctx.getString(R.string.authorizeError)));
break;
case 404:
runOnUiThread(() -> Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)));
break;
default:
runOnUiThread(() -> Toasty.error(ctx, getString(R.string.genericError)));
}
}
}
}
@Override
public void onFailure(@NonNull Call<WikiPage> call, @NonNull Throwable t) {
Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError));
}
});
}
private void getWikiPageContents() {
@@ -83,6 +226,10 @@ public class WikiActivity extends BaseActivity {
binding.markdownFrame.setVisibility(View.GONE);
binding.contents.setVisibility(View.VISIBLE);
}
if(action.equalsIgnoreCase("edit")) {
binding.wikiContent.setText(pageContents);
}
});
}
else {
@@ -120,6 +267,9 @@ public class WikiActivity extends BaseActivity {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.files_view_menu, menu);
if(action.equalsIgnoreCase("edit") || action.equalsIgnoreCase("add")) {
inflater.inflate(R.menu.save, menu);
}
return true;
}
@@ -134,28 +284,47 @@ public class WikiActivity extends BaseActivity {
finish();
return true;
}
else if(id == R.id.edit) {
else if(id == R.id.save) {
processWiki();
return true;
}
else if(id == R.id.markdown) {
if(!renderMd) {
if(action.equalsIgnoreCase("edit") || action.equalsIgnoreCase("add")) {
if(renderMd) {
Markdown.render(ctx, EmojiParser.parseToUnicode(String.valueOf(
Objects.requireNonNull(binding.contentLayout.getEditText()).getText())), binding.markdownPreview, repository);
if(binding.markdown.getAdapter() == null) {
Markdown.render(ctx, EmojiParser.parseToUnicode(binding.contents.getContent()), binding.markdown, repository);
binding.markdownPreview.setVisibility(View.VISIBLE);
binding.contentLayout.setVisibility(View.GONE);
renderMd = false;
}
else {
binding.markdownPreview.setVisibility(View.GONE);
binding.contentLayout.setVisibility(View.VISIBLE);
renderMd = true;
}
binding.contents.setVisibility(View.GONE);
binding.markdownFrame.setVisibility(View.VISIBLE);
renderMd = true;
}
else {
binding.markdownFrame.setVisibility(View.GONE);
binding.contents.setVisibility(View.VISIBLE);
renderMd = false;
if(!renderMd) {
if(binding.markdown.getAdapter() == null) {
Markdown.render(ctx, EmojiParser.parseToUnicode(binding.contents.getContent()), binding.markdown, repository);
}
binding.contents.setVisibility(View.GONE);
binding.markdownFrame.setVisibility(View.VISIBLE);
renderMd = true;
}
else {
binding.markdownFrame.setVisibility(View.GONE);
binding.contents.setVisibility(View.VISIBLE);
renderMd = false;
}
}
return true;

View File

@@ -123,6 +123,11 @@ public class WikiListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
editWiki.setOnClickListener(v12 -> {
Intent intent = new Intent(ctx, WikiActivity.class);
intent.putExtra("pageName", wikiPageMeta.getTitle());
intent.putExtra("action", "edit");
intent.putExtra(RepositoryContext.INTENT_EXTRA, ((RepoDetailActivity) itemView.getContext()).repository);
ctx.startActivity(intent);
dialog.dismiss();
});
@@ -152,6 +157,10 @@ public class WikiListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
TextDrawable drawable = TextDrawable.builder().beginConfig().useFont(Typeface.DEFAULT).fontSize(18).toUpperCase().width(28).height(28).endConfig().buildRoundRect(firstCharacter, color, 3);
avatar.setImageDrawable(drawable);
if(!((RepoDetailActivity) ctx).repository.getPermissions().isPush()) {
wikiMenu.setVisibility(View.GONE);
}
}
}

View File

@@ -26,6 +26,8 @@ import org.mian.gitnex.viewmodels.WikiViewModel;
public class WikiFragment extends Fragment {
public static boolean resumeWiki = false;
private WikiViewModel wikiViewModel;
private FragmentWikiBinding fragmentWikiBinding;
private WikiListAdapter adapter;
@@ -74,6 +76,16 @@ public class WikiFragment extends Fragment {
return fragmentWikiBinding.getRoot();
};
@Override
public void onResume() {
super.onResume();
if(resumeWiki) {
page = 1;
fetchDataAsync(repository.getOwner(), repository.getName());
resumeWiki = false;
}
}
private void fetchDataAsync(String owner, String repo) {
wikiViewModel.getWiki(owner, repo, page, resultLimit, getContext(), fragmentWikiBinding).observe(getViewLifecycleOwner(), wikiListMain -> {

View File

@@ -69,34 +69,118 @@
android:orientation="vertical">
<LinearLayout
android:id="@+id/markdown_frame"
android:id="@+id/render_wiki"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:orientation="vertical">
<LinearLayout
android:id="@+id/markdown_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryBackgroundColor"
android:orientation="vertical"
android:paddingTop="0dp"
android:paddingBottom="16dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:visibility="gone">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/markdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/primaryTextColor"
android:textIsSelectable="true"
android:textSize="14sp" />
</LinearLayout>
<org.mian.gitnex.views.SyntaxHighlightedArea
android:id="@+id/contents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<LinearLayout
android:id="@+id/create_wiki"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryBackgroundColor"
android:orientation="vertical"
android:paddingTop="0dp"
android:paddingBottom="16dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:visibility="gone">
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxBackgroundColor="?attr/inputBackgroundColor"
android:textColorHint="?attr/hintColor"
app:hintTextColor="?attr/hintColor"
app:boxStrokeErrorColor="@color/darkRed"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:endIconMode="clear_text"
app:endIconTint="?attr/iconsColor"
android:hint="@string/newIssueTitle">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/wiki_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:inputType="textCapSentences"
android:singleLine="true"
android:imeOptions="actionNext"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/content_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxBackgroundColor="?attr/inputBackgroundColor"
android:textColorHint="?attr/hintColor"
app:hintTextColor="?attr/hintColor"
app:boxStrokeErrorColor="@color/darkRed"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/wiki_content"
android:layout_width="match_parent"
android:layout_height="480dp"
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:gravity="top|start"
android:singleLine="false"
android:imeOptions="actionNext"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/markdown"
android:id="@+id/markdown_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/primaryTextColor"
android:textIsSelectable="true"
android:layout_marginTop="@dimen/dimen8dp"
android:layout_marginBottom="@dimen/dimen8dp"
android:textSize="14sp" />
</LinearLayout>
<org.mian.gitnex.views.SyntaxHighlightedArea
android:id="@+id/contents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/save"
android:icon="@drawable/ic_save"
android:title="@string/saveButton"
android:orderInCategory="1"
app:showAsAction="ifRoom" />
</menu>

View File

@@ -772,6 +772,10 @@
<string name="wikiAuthor"><![CDATA[<b>%1$s</b> updated %2$s]]></string>
<string name="deleteWikiPageMessage">Do you really want to delete %s?</string>
<string name="wikiPageDeleted">Wiki page deleted successfully</string>
<string name="wikiPageNameAndContentError">Page name and page content can\'t be empty</string>
<string name="createWikiPage">Create Wiki Page</string>
<string name="wikiUpdated">Wiki page updated successfully</string>
<string name="wikiCreated">Wiki page created successfully</string>
<!-- code editor -->
<string name="sourcePosition" translatable="false">%1$d:%2$d</string>