Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6133da808 | ||
|
|
59fb11dbaa | ||
|
|
3de88254f6 | ||
|
|
306338992e | ||
|
|
370ee53bde | ||
|
|
ccc61ebb92 | ||
|
|
522cac34b0 | ||
|
|
282c2e3ed7 |
@@ -81,6 +81,7 @@ public final class DialogActionsHelper {
|
||||
CollectionExt.a(arrayList, DialogAction.NOTIFICATIONS_OFF, dialog.b(b2));
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,11 @@ import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class DNRInjector {
|
||||
public static void inject(Dialog dialog, List<DialogAction> list) {
|
||||
int peerId = dialog.getId();
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
}
|
||||
|
||||
list.add(DialogAction.pinmsg);
|
||||
list.add(DialogAction.unpinmsg);
|
||||
@@ -82,8 +86,8 @@ public class DNRInjector {
|
||||
hashMap.put(DialogAction.ENCRYPT, getIdentifier("encryption", "string"));
|
||||
hashMap.put(DialogAction.ENCRYPT_SETT, getIdentifier("encryption_sett", "string"));
|
||||
|
||||
hashMap.put(DialogAction.pinmsg, getIdentifier("pinmsg", "string"));
|
||||
hashMap.put(DialogAction.unpinmsg, getIdentifier("unpinmsg", "string"));
|
||||
// hashMap.put(DialogAction.pinmsg, getIdentifier("pinmsg", "string"));
|
||||
// hashMap.put(DialogAction.unpinmsg, getIdentifier("unpinmsg", "string"));
|
||||
return hashMap;
|
||||
}
|
||||
|
||||
@@ -111,8 +115,12 @@ public class DNRInjector {
|
||||
ImEngine1.a().a(new OnDialogUpdateEvent(null, map));
|
||||
}
|
||||
|
||||
public static List<DialogAction> injectToListAccess(List<DialogAction> actions, Dialog dialog, ProfilesSimpleInfo profilesSimpleInfo) {
|
||||
var peerId = dialog.getId();
|
||||
public static List<DialogAction> injectToListAccess(List<DialogAction> actions, Dialog dialog) {
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
}
|
||||
|
||||
if (isDnrEnabledFor(peerId)) {
|
||||
actions.add(DialogAction.DNR_OFF);
|
||||
@@ -137,7 +145,11 @@ public class DNRInjector {
|
||||
}
|
||||
|
||||
public static boolean onClickHeader(DialogAction action, Dialog dialog) {
|
||||
var id = dialog.getId();
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
}
|
||||
|
||||
if (action == DialogAction.MARK_AS_READ) {
|
||||
DNRModule.hookRead(dialog);
|
||||
@@ -165,10 +177,10 @@ public class DNRInjector {
|
||||
}
|
||||
|
||||
if (action == DialogAction.ENCRYPT_SETT) {
|
||||
CryptImHook.hookPref(id);
|
||||
CryptImHook.hookPref(peerId);
|
||||
return true;
|
||||
} else if (action == DialogAction.ENCRYPT) {
|
||||
CryptImHook.hook(id, dialog);
|
||||
CryptImHook.hook(peerId, dialog);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -176,7 +188,11 @@ public class DNRInjector {
|
||||
}
|
||||
|
||||
public static boolean onClick(Dialog dialog, DialogAction action) {
|
||||
var id = dialog.getId();
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
}
|
||||
|
||||
if (action == DialogAction.MARK_AS_READ) {
|
||||
DNRModule.hookRead(dialog);
|
||||
@@ -199,18 +215,18 @@ public class DNRInjector {
|
||||
}
|
||||
|
||||
if (action == DialogAction.pinmsg) {
|
||||
pinnedMsg(id, true);
|
||||
pinnedMsg(peerId, true);
|
||||
return true;
|
||||
} else if (action == DialogAction.unpinmsg) {
|
||||
pinnedMsg(id, false);
|
||||
pinnedMsg(peerId, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.ENCRYPT_SETT) {
|
||||
CryptImHook.hookPref(id);
|
||||
CryptImHook.hookPref(peerId);
|
||||
return true;
|
||||
} else if (action == DialogAction.ENCRYPT) {
|
||||
CryptImHook.hook(id, dialog);
|
||||
CryptImHook.hook(peerId, dialog);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -282,15 +298,27 @@ public class DNRInjector {
|
||||
|
||||
thread.start();
|
||||
|
||||
sendToast(AndroidUtils.getString("pin_dialog") + (needToBePinned ? AndroidUtils.getString("dialog_pinned") : AndroidUtils.getString("dialog_unpinned")));
|
||||
sendToast(AndroidUtils.getString("pin_dialog") + " " + (needToBePinned ? AndroidUtils.getString("dialog_pinned") : AndroidUtils.getString("dialog_unpinned")));
|
||||
}
|
||||
|
||||
public static void setDnr(Dialog dialog, boolean value) {
|
||||
hookDNR(dialog.getId());
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
}
|
||||
|
||||
hookDNR(peerId);
|
||||
}
|
||||
|
||||
public static void setDnt(Dialog dialog, boolean value) {
|
||||
hookDNT(dialog.getId());
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
}
|
||||
|
||||
hookDNT(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.MD5;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Random;
|
||||
@@ -13,27 +19,30 @@ public class DeviceInfoHook{
|
||||
}
|
||||
|
||||
public static String getDeviceId(String input) {
|
||||
Log.d("DeviceInfoHook", "getDeviceId: " + input);
|
||||
Log.d("DeviceInfoHook", "getDeviceId shuffled: " + shuffle(input));
|
||||
if (getBoolValue("hideDeviceInfo", true)) return shuffle(input);
|
||||
return input;
|
||||
return findDeviceId() + ":" + getDeviceId();
|
||||
}
|
||||
|
||||
public static String shuffle(String string) {
|
||||
StringBuilder sb = new StringBuilder(string.length());
|
||||
double rnd;
|
||||
for (char c: string.toCharArray()) {
|
||||
rnd = Math.random();
|
||||
if (rnd < 0.34)
|
||||
sb.append(c);
|
||||
else if (rnd < 0.67)
|
||||
sb.insert(sb.length() / 2, c);
|
||||
else
|
||||
sb.insert(0, c);
|
||||
public static String getDeviceId() {
|
||||
String sb2 = Build.PRODUCT +
|
||||
Build.BOARD +
|
||||
Build.BOOTLOADER +
|
||||
Build.BRAND +
|
||||
Build.DEVICE +
|
||||
Build.DISPLAY +
|
||||
Build.FINGERPRINT +
|
||||
Build.HARDWARE +
|
||||
Build.HOST +
|
||||
Build.ID +
|
||||
Build.MANUFACTURER +
|
||||
Build.MODEL +
|
||||
Build.PRODUCT +
|
||||
Build.TAGS;
|
||||
|
||||
sb.append(new Random().nextInt(10));
|
||||
sb.deleteCharAt(0);
|
||||
}
|
||||
return sb.toString();
|
||||
return MD5(sb2);
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
private static String findDeviceId() {
|
||||
return Settings.Secure.getString(getGlobalContext().getContentResolver(), "android_id");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,15 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.vk.core.dialogs.actionspopup.ActionsPopup;
|
||||
import com.vk.core.drawable.RecoloredDrawable;
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vk.dto.common.AttachmentWithMedia;
|
||||
import com.vk.dto.common.ImageSize;
|
||||
import com.vtosters.lite.R;
|
||||
import com.vtosters.lite.attachments.DocumentAttachment;
|
||||
import com.vtosters.lite.attachments.PhotoAttachment;
|
||||
|
||||
import org.json.JSONException;
|
||||
@@ -26,6 +31,7 @@ import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
@@ -36,18 +42,17 @@ import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.ThemesUtils;
|
||||
|
||||
public class PhotoViewer {
|
||||
|
||||
static OkHttpClient client = new OkHttpClient();
|
||||
|
||||
public static boolean interceptClick(AttachmentWithMedia attachment, MenuItem item, View view) {
|
||||
if (item.getItemId() == AndroidUtils.getIdentifier("search_photo", "id")) {
|
||||
searchPhoto(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
searchPhoto(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
return true;
|
||||
} else if (item.getItemId() == AndroidUtils.getIdentifier("copy_photo_url", "id")) {
|
||||
copyPhotoUrl(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
return true;
|
||||
} else if (item.getItemId() == AndroidUtils.getIdentifier("open_original_photo", "id")) {
|
||||
openUrl(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
@@ -61,7 +66,7 @@ public class PhotoViewer {
|
||||
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_menu_search_outline_28), ThemesUtils.getAccentColor()),
|
||||
false,
|
||||
() -> {
|
||||
searchPhoto(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
searchPhoto(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
return null;
|
||||
}
|
||||
);
|
||||
@@ -71,7 +76,7 @@ public class PhotoViewer {
|
||||
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_copy_outline_28), Color.WHITE),
|
||||
false,
|
||||
() -> {
|
||||
copyPhotoUrl(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
return null;
|
||||
}
|
||||
);
|
||||
@@ -82,14 +87,32 @@ public class PhotoViewer {
|
||||
ThemesUtils.getAccentColor()),
|
||||
false,
|
||||
() -> {
|
||||
openUrl(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
if (attachment instanceof PhotoAttachment) {
|
||||
openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
} else if (attachment instanceof DocumentAttachment) {
|
||||
var documentAttachment = (DocumentAttachment) attachment;
|
||||
if (documentAttachment.J != null)
|
||||
openUrl(getUrlFromDocumentAttachment(documentAttachment));
|
||||
else
|
||||
ToastUtils.a("Не удалось получить ссылку на фото");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static String getUrlFromAttachment(PhotoAttachment attachment) {
|
||||
private static String getUrlFromDocumentAttachment(DocumentAttachment attachment) {
|
||||
var imageSizes = attachment.J.t1();
|
||||
return getUrlFromImageSizeList(imageSizes);
|
||||
}
|
||||
|
||||
private static String getUrlFromPhotoAttachment(PhotoAttachment attachment) {
|
||||
var imageSizes = attachment.D.Q.t1();
|
||||
return getUrlFromImageSizeList(imageSizes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getUrlFromImageSizeList(List<ImageSize> imageSizes) {
|
||||
if (imageSizes.isEmpty())
|
||||
return null;
|
||||
var max = imageSizes.get(0);
|
||||
|
||||
@@ -67,7 +67,7 @@ public class APKInstallActivity extends AppCompatActivity {
|
||||
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, AndroidUtils.getString("apk_install_error") + e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(this, AndroidUtils.getString("apk_install_error") + " " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
finish();
|
||||
|
||||
@@ -55,7 +55,7 @@ public class BackupManager {
|
||||
FileWriter out = new FileWriter(file);
|
||||
out.write(getPrefContent("onlines.xml"));
|
||||
out.close();
|
||||
Toast.makeText(getGlobalContext(), AndroidUtils.getString("backup_saved_in") + file.getAbsolutePath(), LENGTH_LONG).show();
|
||||
Toast.makeText(getGlobalContext(), AndroidUtils.getString("backup_saved_in") + " " + file.getAbsolutePath(), LENGTH_LONG).show();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getGlobalContext(), AndroidUtils.getString("backup_save_error"), LENGTH_SHORT).show();
|
||||
@@ -71,7 +71,7 @@ public class BackupManager {
|
||||
FileWriter out = new FileWriter(file);
|
||||
out.write(getPrefContent("com.vtosters.lite_preferences.xml"));
|
||||
out.close();
|
||||
Toast.makeText(getGlobalContext(), AndroidUtils.getString("backup_saved_in") + file.getAbsolutePath(), LENGTH_LONG).show();
|
||||
Toast.makeText(getGlobalContext(), AndroidUtils.getString("backup_saved_in") + " " + file.getAbsolutePath(), LENGTH_LONG).show();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getGlobalContext(), AndroidUtils.getString("backup_save_error"), LENGTH_SHORT).show();
|
||||
|
||||
@@ -117,7 +117,7 @@ public class IconsFragment extends MaterialPreferenceToolbarFragment {
|
||||
|
||||
if (!hasVerification() && !getBoolValue("dialogrecomm", false)) {
|
||||
addPreference(this, "", AndroidUtils.getString("icons_warning"), AndroidUtils.getString("icons_warning_info"), "ic_about_outline_28", preference -> {
|
||||
getContext().startActivity(new Intent("android.intent.action.VIEW").setData(Uri.parse("https://vtosters.app")));
|
||||
getContext().startActivity(new Intent("android.intent.action.VIEW").setData(Uri.parse("https://vtosters.app/donate/")));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,8 +68,6 @@ public class MediaFragment extends MaterialPreferenceToolbarFragment {
|
||||
var prefs = getPreferences();
|
||||
var editor = prefs.edit();
|
||||
var actualPath = FileUriUtils.getFullPathFromTreeUri(path, getContext());
|
||||
Log.d("MediaFragment", "onActivityResult: " + actualPath);
|
||||
Log.d("MediaFragment", "onActivityResult: " + requestCode);
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_SET_DOWNLOAD_DIRECTORY:
|
||||
editor.putString("downloads_directory", actualPath);
|
||||
@@ -158,7 +156,7 @@ public class MediaFragment extends MaterialPreferenceToolbarFragment {
|
||||
});
|
||||
|
||||
if (isLoggedIn()) {
|
||||
findPreference("lastfm_auth").setSummary(AndroidUtils.getString("lastfm_authorized_as") + Scrobbler.getUserName());
|
||||
findPreference("lastfm_auth").setSummary(AndroidUtils.getString("lastfm_authorized_as") + " " + Scrobbler.getUserName());
|
||||
} else {
|
||||
findPreference("lastfm_enabled").setEnabled(false);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class WallpaperMenuFragment extends MaterialPreferenceToolbarFragment {
|
||||
|
||||
if (!hasVerification() && !getBoolValue("dialogrecomm", false)) {
|
||||
PreferencesUtil.addPreference(this, "", AndroidUtils.getString("filters_warning"), AndroidUtils.getString("icons_warning_info"), null, preference -> {
|
||||
getContext().startActivity(new Intent("android.intent.action.VIEW").setData(Uri.parse("https://vtosters.app")));
|
||||
getContext().startActivity(new Intent("android.intent.action.VIEW").setData(Uri.parse("https://vtosters.app/donate/")));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5978,7 +5978,7 @@
|
||||
<string name="telegram_stickers_error_update">: ошибка при обновлении</string>
|
||||
<string name="telegram_stickers_error_download">: ошибка при загрузке</string>
|
||||
|
||||
<string name="lastfm_authorized_as">Вы авторизованы как </string>
|
||||
<string name="lastfm_authorized_as">Вы авторизованы как</string>
|
||||
<string name="lastfm_login">Логин</string>
|
||||
<string name="lastfm_password">Пароль</string>
|
||||
<string name="lastfm_enter_credentials">Введите логин и пароль</string>
|
||||
@@ -6019,7 +6019,7 @@
|
||||
<string name="translator_reply">Реплай:</string>
|
||||
<string name="translator_no_text">Нет текста для перевода</string>
|
||||
|
||||
<string name="pin_dialog">Диалог успешно </string>
|
||||
<string name="pin_dialog">Диалог успешно</string>
|
||||
<string name="dialog_pinned">закреплен</string>
|
||||
<string name="dialog_unpinned">откреплен</string>
|
||||
|
||||
@@ -6074,7 +6074,7 @@
|
||||
<string name="proxy_warning">Zaborona никак не связана с ВТостерс и мы не можем гарантировать её работу и безопасность</string>
|
||||
<string name="proxy_settings">Настройки прокси</string>
|
||||
|
||||
<string name="apk_install_error">Ошибка установки обновления </string>
|
||||
<string name="apk_install_error">Ошибка установки обновления</string>
|
||||
|
||||
<string name="message_self_destruct_15sec">15 секунд</string>
|
||||
<string name="message_self_destruct_1min">1 минута</string>
|
||||
@@ -6088,7 +6088,7 @@
|
||||
|
||||
<string name="update_vk_admin_token">Обновите токен VK Admin в настройках в разделе Другое</string>
|
||||
|
||||
<string name="backup_saved_in">Сохранено в файл </string>
|
||||
<string name="backup_saved_in">Сохранено в файл</string>
|
||||
<string name="backup_save_error">Не удалось сохранить файл</string>
|
||||
|
||||
<string name="newsfeed_list_update_feed">Обновите ленту для получения списка доступных пунктов</string>
|
||||
|
||||
@@ -6116,7 +6116,7 @@ May look like a fake leaving the online"</string>
|
||||
<string name="telegram_stickers_error_update">: update failed</string>
|
||||
<string name="telegram_stickers_error_download">: download failed</string>
|
||||
|
||||
<string name="lastfm_authorized_as">You are authorized as </string>
|
||||
<string name="lastfm_authorized_as">You are authorized as</string>
|
||||
<string name="lastfm_login">Login</string>
|
||||
<string name="lastfm_password">Password</string>
|
||||
<string name="lastfm_enter_credentials">Enter your username and password</string>
|
||||
@@ -6212,7 +6212,7 @@ May look like a fake leaving the online"</string>
|
||||
<string name="proxy_warning">Zaborona is not associated with VTosters in any way and we cannot guarantee its work and security</string>
|
||||
<string name="proxy_settings">Proxy settings</string>
|
||||
|
||||
<string name="apk_install_error">Error installing update </string>
|
||||
<string name="apk_install_error">Error installing update</string>
|
||||
|
||||
<string name="message_self_destruct_15sec">15 seconds</string>
|
||||
<string name="message_self_destruct_1min">1 minute</string>
|
||||
@@ -6226,7 +6226,7 @@ May look like a fake leaving the online"</string>
|
||||
|
||||
<string name="update_vk_admin_token">Update VK Admin token in Other settings</string>
|
||||
|
||||
<string name="backup_saved_in">Saved to file </string>
|
||||
<string name="backup_saved_in">Saved to file</string>
|
||||
<string name="backup_save_error">Failed to save file</string>
|
||||
|
||||
<string name="newsfeed_list_update_feed">Update the feed to get a list of available items</string>
|
||||
|
||||
+1
-11
@@ -440,17 +440,7 @@
|
||||
|
||||
move-result-object v3
|
||||
|
||||
iget-object v2, p0, Lcom/vk/im/ui/components/dialog_header/info/DialogHeaderInfoComponent;->G:Lcom/vk/im/ui/components/dialog_header/info/State;
|
||||
|
||||
invoke-virtual {v2}, Lcom/vk/im/ui/components/dialog_header/info/State;->h()Lcom/vk/im/engine/models/ProfilesInfo;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
invoke-virtual {v2}, Lcom/vk/im/engine/models/ProfilesInfo;->A1()Lcom/vk/im/engine/models/ProfilesSimpleInfo;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
invoke-static {v0, v3, v2}, Lru/vtosters/lite/dnr/DNRInjector;->injectToListAccess(Ljava/util/List;Lcom/vk/im/engine/models/dialogs/Dialog;Lcom/vk/im/engine/models/ProfilesSimpleInfo;)Ljava/util/List;
|
||||
invoke-static {v0, v3}, Lru/vtosters/lite/dnr/DNRInjector;->injectToListAccess(Ljava/util/List;Lcom/vk/im/engine/models/dialogs/Dialog;)Ljava/util/List;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
|
||||
+1
-1
@@ -1284,7 +1284,7 @@
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :cond_0
|
||||
if-nez v0, :cond_0
|
||||
|
||||
.line 15
|
||||
sget-object v0, Lcom/vk/im/ui/reporters/DialogActionReporter;->b:Lcom/vk/im/ui/reporters/DialogActionReporter;
|
||||
|
||||
Reference in New Issue
Block a user