Include previous Wikimedia hackathon (2018) task, peer review, to codebase (#2602)

* Add new activity to manifest

* Create review activity layout base

* Add a new menu item to drawer for peer review

* Add a top menu with randomizer icon to review activity

* Add strings for review button

* Add activity to ActivityBuilderModule for injection

* Add a new drawer item to start review acitivty

* Create base of the Review Activity

* Add fragment pager

* Add new fragment for injection

* Create a fragment pager layout

* Wikimedia hackathon 2018 (#1533)

* First draft of fn to get random recent image

* Use log entries for requests to beta, try to connect refresh button

FIXME: runs http request on main thread, breaks

* Tweak button connection

* Add ReviewController class

* Fix fragments

* Wmhack2018 (#1534)

* tiny fixes

* Load pictures into activities

* Re-use same class for all review fragments (#1537)

And try to add pager indicator

* [WIP] category check

* [WIP] add on-click actions to ReviewActivity

* [WIP] add SendThankTask

* Make it beautiful

* Add some category stuff back in to review (#1538)

* Use standalone category extraction code in MediaDataExtractor

* Add categories to category review page

* Change category question text sizes

* Call randomizer whenever the activity is ready

* Add progressbar

* [WIP] add DeleteTask.askReasonAndExecute

* Fix refresh button string

* Typo: "nominate *for* deletion"

* Add formatting to categories and put them in the same textView

* Pass context and adapters as parameters to controller

* Add actions to controller

* Make everyting work

* Add another fragment to thank

* Fix npe

* Add missing execute method

* Some codes

* Add a funy text

* More random recent image selection (#1542)

time-based randomness is biased - if someone uploaded 100 images in
hour, one week ago, and I select a random point in time, their last
image is way more likely to come up than anything else.

With this, there is still bias towards choosing one of the last N
in any burst of uploads (where N is the number of recent changes
fetched) but it's a bit better than before.

* Create Revision class

* Add meaningluf strings

* Error handling for review image/category fetch (#1543)

* Add information layout for username and filename

* Use Single to get firstRevision

* try to add username and filename

* Ensure caption is shown on every review fragment

* Fix build

* Fixes missing import

* Change button text,show current category, add skip image button

* Modify texts, fix night mode issues

* Positive Wording

* fix landscape issue

* Add checkbox popup,rewording

* Spelling Correction

* Fix merge

* Remove commented out code, use lambda

* Simplify toolbar include
This commit is contained in:
Silky Priya 2019-03-21 17:35:23 +05:30 committed by neslihanturan
parent a1a65d0832
commit a32ba452ec
33 changed files with 1594 additions and 33 deletions

View file

@ -0,0 +1,129 @@
package fr.free.nrw.commons.review;
import android.app.NotificationManager;
import android.content.Context;
import android.os.AsyncTask;
import android.view.Gravity;
import android.widget.Toast;
import javax.inject.Inject;
import androidx.core.app.NotificationCompat;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
// Example code:
// CheckCategoryTask deleteTask = new CheckCategoryTask(getActivity(), media);
// TODO: refactor; see DeleteTask and SendThankTask
public class CheckCategoryTask extends AsyncTask<Void, Integer, Boolean> {
@Inject
MediaWikiApi mwApi;
@Inject
SessionManager sessionManager;
public static final int NOTIFICATION_CHECK_CATEGORY = 0x101;
private NotificationManager notificationManager;
private NotificationCompat.Builder notificationBuilder;
private Context context;
private Media media;
public CheckCategoryTask(Context context, Media media){
this.context = context;
this.media = media;
}
@Override
protected void onPreExecute(){
ApplicationlessInjection
.getInstance(context.getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(context);
Toast toast = new Toast(context);
toast.setGravity(Gravity.CENTER,0,0);
toast = Toast.makeText(context, context.getString(R.string.check_category_toast, media.getDisplayTitle()), Toast.LENGTH_SHORT);
toast.show();
}
@Override
protected Boolean doInBackground(Void ...voids) {
publishProgress(0);
String editToken;
String authCookie;
String summary = context.getString(R.string.check_category_edit_summary);
authCookie = sessionManager.getAuthCookie();
mwApi.setAuthCookie(authCookie);
try {
editToken = mwApi.getEditToken();
if (editToken.equals("+\\")) {
return false;
}
publishProgress(1);
mwApi.appendEdit(editToken, "\n{{subst:chc}}\n", media.getFilename(), summary);
publishProgress(2);
}
catch (Exception e) {
Timber.d(e.getMessage());
return false;
}
return true;
}
@Override
protected void onProgressUpdate (Integer... values){
super.onProgressUpdate(values);
int[] messages = new int[]{R.string.getting_edit_token, R.string.check_category_adding_template};
String message = "";
if (0 < values[0] && values[0] < messages.length) {
message = context.getString(messages[values[0]]);
}
notificationBuilder.setContentTitle(context.getString(R.string.check_category_notification_title, media.getDisplayTitle()))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(message))
.setSmallIcon(R.drawable.ic_launcher)
.setProgress(messages.length, values[0], false)
.setOngoing(true);
notificationManager.notify(NOTIFICATION_CHECK_CATEGORY, notificationBuilder.build());
}
@Override
protected void onPostExecute(Boolean result) {
String message = "";
String title = "";
if (result){
title = context.getString(R.string.check_category_success_title);
message = context.getString(R.string.check_category_success_message, media.getDisplayTitle());
}
else {
title = context.getString(R.string.check_category_failure_title);
message = context.getString(R.string.check_category_failure_message, media.getDisplayTitle());
}
notificationBuilder.setDefaults(NotificationCompat.DEFAULT_ALL)
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(message))
.setSmallIcon(R.drawable.ic_launcher)
.setProgress(0,0,false)
.setOngoing(false)
.setPriority(NotificationCompat.PRIORITY_HIGH);
notificationManager.notify(NOTIFICATION_CHECK_CATEGORY, notificationBuilder.build());
}
}

View file

@ -0,0 +1,166 @@
package fr.free.nrw.commons.review;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import com.google.android.material.navigation.NavigationView;
import com.viewpagerindicator.CirclePageIndicator;
import java.io.IOException;
import java.util.ArrayList;
import javax.inject.Inject;
import androidx.appcompat.widget.Toolbar;
import androidx.drawerlayout.widget.DrawerLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.AuthenticatedActivity;
import fr.free.nrw.commons.mwapi.MediaResult;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
public class ReviewActivity extends AuthenticatedActivity {
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.navigation_view)
NavigationView navigationView;
@BindView(R.id.drawer_layout)
DrawerLayout drawerLayout;
@BindView(R.id.reviewPager)
ReviewViewPager reviewPager;
@BindView(R.id.skip_image)
Button skip_image_button;
@Inject MediaWikiApi mwApi;
public ReviewPagerAdapter reviewPagerAdapter;
public ReviewController reviewController;
@BindView(R.id.reviewPagerIndicator)
public CirclePageIndicator pagerIndicator;
@Override
protected void onAuthCookieAcquired(String authCookie) {
}
@Override
protected void onAuthFailure() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_review);
ButterKnife.bind(this);
initDrawer();
reviewController = new ReviewController(this);
reviewPagerAdapter = new ReviewPagerAdapter(getSupportFragmentManager());
reviewPager.setAdapter(reviewPagerAdapter);
reviewPagerAdapter.getItem(0);
pagerIndicator.setViewPager(reviewPager);
runRandomizer(); //Run randomizer whenever everything is ready so that a first random image will be added
skip_image_button.setOnClickListener(view -> runRandomizer());
}
public boolean runRandomizer() {
ProgressBar progressBar = reviewPagerAdapter.reviewImageFragments[reviewPager.getCurrentItem()].progressBar;
if (progressBar != null) {
progressBar.setVisibility(View.VISIBLE);
}
reviewPager.setCurrentItem(0);
Observable.fromCallable(() -> {
String result = "";
try {
Media media = mwApi.getRecentRandomImage();
if (media != null) {
result = media.getFilename();
}
} catch (IOException e) {
Timber.e("Error fetching recent random image: " + e.toString());
}
return result;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::updateImage);
return true;
}
private void updateImage(String fileName) {
if (fileName.length() == 0) {
ViewUtil.showShortSnackbar(drawerLayout, R.string.error_review);
return;
}
reviewController.onImageRefreshed(fileName); //file name is updated
mwApi.firstRevisionOfFile("File:" + fileName)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(revision -> {
reviewController.firstRevision = revision;
reviewPagerAdapter.updateFileInformation(fileName, revision);
});
reviewPager.setCurrentItem(0);
Observable.fromCallable(() -> {
MediaResult media = mwApi.fetchMediaByFilename("File:" + fileName);
return MediaDataExtractorUtil.extractCategories(media.getWikiSource());
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::updateCategories, this::categoryFetchError);
}
private void categoryFetchError(Throwable throwable) {
Timber.e(throwable, "Error fetching categories");
ViewUtil.showShortSnackbar(drawerLayout, R.string.error_review_categories);
}
private void updateCategories(ArrayList<String> categories) {
reviewController.onCategoriesRefreshed(categories);
reviewPagerAdapter.updateCategories();
}
/**
* References ReviewPagerAdapter to null before the activity is destroyed
*/
@Override
public void onDestroy() {
super.onDestroy();
}
/**
* Consumers should be simply using this method to use this activity.
* @param context
* @param title Page title
*/
public static void startYourself(Context context, String title) {
Intent reviewActivity = new Intent(context, ReviewActivity.class);
context.startActivity(reviewActivity);
}
}

View file

@ -0,0 +1,71 @@
package fr.free.nrw.commons.review;
import android.content.Context;
import java.util.ArrayList;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.delete.DeleteTask;
import fr.free.nrw.commons.mwapi.Revision;
public class ReviewController {
private String fileName;
@Nullable
public Revision firstRevision; // TODO: maybe we can expand this class to include fileName
protected static ArrayList<String> categories;
private ReviewPagerAdapter reviewPagerAdapter;
private ViewPager viewPager;
private ReviewActivity reviewActivity;
ReviewController(Context context) {
reviewActivity = (ReviewActivity)context;
reviewPagerAdapter = reviewActivity.reviewPagerAdapter;
viewPager = ((ReviewActivity)context).reviewPager;
}
public void onImageRefreshed(String fileName) {
this.fileName = fileName;
ReviewController.categories = new ArrayList<>();
}
public void onCategoriesRefreshed(ArrayList<String> categories) {
ReviewController.categories = categories;
}
public void swipeToNext() {
int nextPos = viewPager.getCurrentItem()+1;
if (nextPos <= 3) {
viewPager.setCurrentItem(nextPos);
} else {
reviewActivity.runRandomizer();
}
}
public void reportSpam() {
DeleteTask.askReasonAndExecute(new Media("File:"+fileName),
reviewActivity,
reviewActivity.getResources().getString(R.string.review_spam_report_question),
reviewActivity.getResources().getString(R.string.review_spam_report_problem));
}
public void reportPossibleCopyRightViolation() {
DeleteTask.askReasonAndExecute(new Media("File:"+fileName),
reviewActivity,
reviewActivity.getResources().getString(R.string.review_c_violation_report_question),
reviewActivity.getResources().getString(R.string.review_c_violation_report_problem));
}
public void reportWrongCategory() {
new CheckCategoryTask(reviewActivity, new Media("File:"+fileName)).execute();
swipeToNext();
}
public void sendThanks() {
new SendThankTask(reviewActivity, new Media("File:"+fileName), firstRevision).execute();
swipeToNext();
}
}

View file

@ -0,0 +1,164 @@
package fr.free.nrw.commons.review;
import android.graphics.Color;
import android.os.Bundle;
import android.text.Html;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.Revision;
public class ReviewImageFragment extends CommonsDaggerSupportFragment {
public static final int SPAM = 0;
public static final int COPYRIGHT = 1;
public static final int CATEGORY = 2;
public static final int THANKS = 3;
private int position;
private String fileName;
private String catString;
private View textViewQuestionContext;
private View imageCaption;
private View textViewQuestion;
private SimpleDraweeView simpleDraweeView;
private Button yesButton;
private Button noButton;
public ProgressBar progressBar;
private Revision revision;
public void update(int position, String fileName, Revision revision) {
this.position = position;
this.fileName = fileName;
this.revision = revision;
fillImageCaption();
if (simpleDraweeView != null) {
simpleDraweeView.setImageURI(Utils.makeThumbBaseUrl(fileName));
progressBar.setVisibility(View.GONE);
}
}
public void updateCategories(Iterable<String> categories) {
if (categories != null && isAdded()) {
catString = TextUtils.join(", ", categories);
if (catString != null && !catString.equals("") && textViewQuestionContext != null) {
catString = "<b>" + catString + "</b>";
String stringToConvertHtml = String.format(getResources().getString(R.string.review_category_explanation), catString);
((TextView) textViewQuestionContext).setText(Html.fromHtml(stringToConvertHtml));
} else if (textViewQuestionContext != null) {
((TextView) textViewQuestionContext).setText(getResources().getString(R.string.review_no_category));
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
position = getArguments().getInt("position");
View layoutView = inflater.inflate(R.layout.fragment_review_image, container,
false);
progressBar = layoutView.findViewById(R.id.progressBar);
textViewQuestion = layoutView.findViewById(R.id.reviewQuestion);
textViewQuestionContext = layoutView.findViewById(R.id.reviewQuestionContext);
imageCaption = layoutView.findViewById(R.id.imageCaption);
yesButton = layoutView.findViewById(R.id.yesButton);
noButton = layoutView.findViewById(R.id.noButton);
fillImageCaption();
String question, explanation, yesButtonText, noButtonText;
switch (position) {
case COPYRIGHT:
question = getString(R.string.review_copyright);
explanation = getString(R.string.review_copyright_explanation);
yesButtonText = getString(R.string.review_copyright_yes_button_text);
noButtonText = getString(R.string.review_copyright_no_button_text);
yesButton.setOnClickListener(view -> {
((ReviewActivity) getActivity()).reviewController.reportPossibleCopyRightViolation();
});
break;
case CATEGORY:
question = getString(R.string.review_category);
explanation = getString(R.string.review_no_category);
yesButtonText = getString(R.string.review_category_yes_button_text);
noButtonText = getString(R.string.review_category_no_button_text);
yesButton.setOnClickListener(view -> {
((ReviewActivity) getActivity()).reviewController.reportWrongCategory();
});
break;
case SPAM:
question = getString(R.string.review_spam);
explanation = getString(R.string.review_spam_explanation);
yesButtonText = getString(R.string.review_spam_yes_button_text);
noButtonText = getString(R.string.review_spam_no_button_text);
yesButton.setOnClickListener(view -> {
((ReviewActivity) getActivity()).reviewController.reportSpam();
});
break;
case THANKS:
question = getString(R.string.review_thanks);
explanation = getString(R.string.review_thanks_explanation, ((ReviewActivity) getActivity()).reviewController.firstRevision.username);
yesButtonText = getString(R.string.review_thanks_yes_button_text);
noButtonText = getString(R.string.review_thanks_no_button_text);
yesButton.setTextColor(Color.parseColor("#228b22"));
noButton.setTextColor(Color.parseColor("#116aaa"));
yesButton.setOnClickListener(view -> {
((ReviewActivity) getActivity()).reviewController.sendThanks();
});
break;
default :
question = "How did we get here?";
explanation = "No idea.";
yesButtonText = "yes";
noButtonText = "no";
}
noButton.setOnClickListener(view -> {
((ReviewActivity) getActivity()).reviewController.swipeToNext();
});
((TextView) textViewQuestion).setText(question);
((TextView) textViewQuestionContext).setText(explanation);
yesButton.setText(yesButtonText);
noButton.setText(noButtonText);
if(position==CATEGORY){
updateCategories(ReviewController.categories);
}
simpleDraweeView = layoutView.findViewById(R.id.imageView);
if (fileName != null) {
simpleDraweeView.setImageURI(Utils.makeThumbBaseUrl(fileName));
progressBar.setVisibility(View.GONE);
}
return layoutView;
}
private void fillImageCaption() {
if (imageCaption != null && fileName != null && revision != null) {
((TextView) imageCaption).setText(fileName + " is uploaded by: " + revision.username);
}
}
}

View file

@ -0,0 +1,49 @@
package fr.free.nrw.commons.review;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import fr.free.nrw.commons.mwapi.Revision;
public class ReviewPagerAdapter extends FragmentStatePagerAdapter {
ReviewImageFragment[] reviewImageFragments;
public ReviewPagerAdapter(FragmentManager fm) {
super(fm);
reviewImageFragments = new ReviewImageFragment[] {
new ReviewImageFragment(),
new ReviewImageFragment(),
new ReviewImageFragment(),
new ReviewImageFragment()
};
}
@Override
public int getCount() {
return reviewImageFragments.length;
}
public void updateFileInformation(String fileName, Revision revision) {
for (int i = 0; i < getCount(); i++) {
ReviewImageFragment fragment = reviewImageFragments[i];
fragment.update(i, fileName, revision);
}
}
public void updateCategories() {
ReviewImageFragment categoryFragment = reviewImageFragments[ReviewImageFragment.CATEGORY];
categoryFragment.updateCategories(ReviewController.categories);
}
@Override
public Fragment getItem(int position) {
Bundle bundle = new Bundle();
bundle.putInt("position", position);
reviewImageFragments[position].setArguments(bundle);
return reviewImageFragments[position];
}
}

View file

@ -0,0 +1,30 @@
package fr.free.nrw.commons.review;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.viewpager.widget.ViewPager;
public class ReviewViewPager extends ViewPager {
public ReviewViewPager(Context context) {
super(context);
}
public ReviewViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// Never allow swiping to switch between pages
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Never allow swiping to switch between pages
return false;
}
}

View file

@ -0,0 +1,138 @@
package fr.free.nrw.commons.review;
import android.app.NotificationManager;
import android.content.Context;
import android.os.AsyncTask;
import android.view.Gravity;
import android.widget.Toast;
import javax.inject.Inject;
import androidx.core.app.NotificationCompat;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.mwapi.Revision;
import timber.log.Timber;
// example code:
//
// media = new Media("File:Iru.png");
// Observable.fromCallable(() -> mwApi.firstRevisionOfFile(media.getFilename()))
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(revision -> {
// SendThankTask task = new SendThankTask(getActivity(), media, revision);
// task.execute();
// });
public class SendThankTask extends AsyncTask<Void, Integer, Boolean> {
@Inject
MediaWikiApi mwApi;
@Inject
SessionManager sessionManager;
public static final int NOTIFICATION_SEND_THANK = 0x102;
private NotificationManager notificationManager;
private NotificationCompat.Builder notificationBuilder;
private Context context;
private Media media;
private Revision revision;
public SendThankTask(Context context, Media media, Revision revision){
this.context = context;
this.media = media;
this.revision = revision;
}
@Override
protected void onPreExecute(){
ApplicationlessInjection
.getInstance(context.getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(context);
Toast toast = new Toast(context);
toast.setGravity(Gravity.CENTER,0,0);
toast = Toast.makeText(context, context.getString(R.string.send_thank_toast, media.getDisplayTitle()), Toast.LENGTH_SHORT);
toast.show();
}
@Override
protected Boolean doInBackground(Void ...voids) {
publishProgress(0);
String editToken;
String authCookie;
authCookie = sessionManager.getAuthCookie();
mwApi.setAuthCookie(authCookie);
try {
editToken = mwApi.getEditToken();
if (editToken.equals("+\\")) {
return false;
}
publishProgress(1);
mwApi.thank(editToken, revision.revisionId);
publishProgress(2);
}
catch (Exception e) {
Timber.d(e.getMessage());
return false;
}
return true;
}
@Override
protected void onProgressUpdate (Integer... values){
super.onProgressUpdate(values);
int[] messages = new int[]{R.string.getting_edit_token, R.string.send_thank_send};
String message = "";
if (0 < values[0] && values[0] < messages.length) {
message = context.getString(messages[values[0]]);
}
notificationBuilder.setContentTitle(context.getString(R.string.send_thank_notification_title))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(message))
.setSmallIcon(R.drawable.ic_launcher)
.setProgress(messages.length, values[0], false)
.setOngoing(true);
notificationManager.notify(NOTIFICATION_SEND_THANK, notificationBuilder.build());
}
@Override
protected void onPostExecute(Boolean result) {
String message = "";
String title = "";
if (result){
title = context.getString(R.string.send_thank_success_title);
message = context.getString(R.string.send_thank_success_message, media.getDisplayTitle());
}
else {
title = context.getString(R.string.send_thank_failure_title);
message = context.getString(R.string.send_thank_failure_message, media.getDisplayTitle());
}
notificationBuilder.setDefaults(NotificationCompat.DEFAULT_ALL)
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(message))
.setSmallIcon(R.drawable.ic_launcher)
.setProgress(0,0,false)
.setOngoing(false)
.setPriority(NotificationCompat.PRIORITY_HIGH);
notificationManager.notify(NOTIFICATION_SEND_THANK, notificationBuilder.build());
}
}