To make death I removed RxJava
This commit is contained in:
@@ -47,7 +47,6 @@ import org.jackhuang.hellominecraft.lookandfeel.HelloMinecraftLookAndFeel;
|
||||
import org.jackhuang.hellominecraft.utils.MathUtils;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.VersionNumber;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -146,8 +145,7 @@ public final class Main implements Runnable {
|
||||
}
|
||||
|
||||
Settings.UPDATE_CHECKER.outdated.register(IUpgrader.NOW_UPGRADER);
|
||||
Settings.UPDATE_CHECKER.process(false).subscribeOn(Schedulers.newThread()).subscribe(t
|
||||
-> Main.invokeUpdate());
|
||||
Settings.UPDATE_CHECKER.process(false).reg(t -> Main.invokeUpdate()).execute();
|
||||
|
||||
if (StrUtils.isNotBlank(Settings.getInstance().getProxyHost()) && StrUtils.isNotBlank(Settings.getInstance().getProxyPort()) && MathUtils.canParseInt(Settings.getInstance().getProxyPort())) {
|
||||
HMCLog.log("Initializing customized proxy");
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.jackhuang.hellominecraft.launcher.core.assets;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
@@ -30,9 +29,8 @@ import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.IDownloadProvider;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.VersionNumber;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -45,61 +43,42 @@ public class AssetsMojangLoader extends IAssetsHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<String[]> getList(MinecraftVersion mv, IMinecraftAssetService mp) {
|
||||
return Observable.<String[]>createWithEmptySubscription((Observer<String[]> t1) -> {
|
||||
if (mv == null) {
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
String assetsId = mv.assets == null ? "legacy" : mv.assets;
|
||||
File assets = mp.getAssets();
|
||||
HMCLog.log("Get index: " + assetsId);
|
||||
File f = IOUtils.tryGetCanonicalFile(new File(assets, "indexes/" + assetsId + ".json"));
|
||||
if (!f.exists() && !mp.downloadMinecraftAssetsIndex(assetsId)) {
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
public OverridableSwingWorker<String[]> getList(MinecraftVersion mv, IMinecraftAssetService mp) {
|
||||
return new OverridableSwingWorker<String[]>() {
|
||||
@Override
|
||||
protected void work() throws Exception {
|
||||
if (mv == null)
|
||||
throw new IllegalArgumentException("AssetsMojangLoader: null argument: MinecraftVersion");
|
||||
String assetsId = mv.assets == null ? "legacy" : mv.assets;
|
||||
File assets = mp.getAssets();
|
||||
HMCLog.log("Gathering asset index: " + assetsId);
|
||||
File f = IOUtils.tryGetCanonicalFile(new File(assets, "indexes/" + assetsId + ".json"));
|
||||
if (!f.exists() && !mp.downloadMinecraftAssetsIndex(assetsId))
|
||||
throw new IllegalStateException("Failed to get index json");
|
||||
|
||||
String result;
|
||||
try {
|
||||
result = FileUtils.readFileToString(f);
|
||||
} catch (IOException ex) {
|
||||
HMCLog.warn("Failed to read index json: " + f, ex);
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
if (StrUtils.isBlank(result)) {
|
||||
HMCLog.err("Index json is empty, please redownload it!");
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
AssetsIndex o;
|
||||
try {
|
||||
o = C.gson.fromJson(result, AssetsIndex.class);
|
||||
} catch (Exception e) {
|
||||
HMCLog.err("Failed to parse index json, please redownload it!", e);
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
assetsDownloadURLs = new ArrayList<>();
|
||||
assetsLocalNames = new ArrayList<>();
|
||||
ArrayList<String> al = new ArrayList<>();
|
||||
contents = new ArrayList<>();
|
||||
if (o != null && o.getFileMap() != null)
|
||||
for (Map.Entry<String, AssetsObject> e : o.getFileMap().entrySet()) {
|
||||
Contents c = new Contents();
|
||||
c.eTag = e.getValue().getHash();
|
||||
c.key = c.eTag.substring(0, 2) + "/" + e.getValue().getHash();
|
||||
c.size = e.getValue().getSize();
|
||||
contents.add(c);
|
||||
assetsDownloadURLs.add(c.key);
|
||||
assetsLocalNames.add(new File(assets, "objects" + File.separator + c.key.replace("/", File.separator)));
|
||||
al.add(e.getKey());
|
||||
}
|
||||
String result = FileUtils.readFileToString(f);
|
||||
if (StrUtils.isBlank(result))
|
||||
throw new IllegalStateException("Index json is empty, please redownload it!");
|
||||
AssetsIndex o = C.gson.fromJson(result, AssetsIndex.class);
|
||||
assetsDownloadURLs = new ArrayList<>();
|
||||
assetsLocalNames = new ArrayList<>();
|
||||
ArrayList<String> al = new ArrayList<>();
|
||||
contents = new ArrayList<>();
|
||||
if (o != null && o.getFileMap() != null)
|
||||
for (Map.Entry<String, AssetsObject> e : o.getFileMap().entrySet()) {
|
||||
Contents c = new Contents();
|
||||
c.eTag = e.getValue().getHash();
|
||||
c.key = c.eTag.substring(0, 2) + "/" + e.getValue().getHash();
|
||||
c.size = e.getValue().getSize();
|
||||
contents.add(c);
|
||||
assetsDownloadURLs.add(c.key);
|
||||
assetsLocalNames.add(new File(assets, "objects" + File.separator + c.key.replace("/", File.separator)));
|
||||
al.add(e.getKey());
|
||||
}
|
||||
|
||||
t1.onNext(al.toArray(new String[1]));
|
||||
t1.onCompleted();
|
||||
});
|
||||
publish(al.toArray(new String[1]));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.code.DigestUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.NetUtils;
|
||||
import rx.Observable;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
|
||||
/**
|
||||
* Assets
|
||||
@@ -73,7 +73,7 @@ public abstract class IAssetsHandler {
|
||||
* @param mp Asset Service
|
||||
* @param x finished event
|
||||
*/
|
||||
public abstract Observable<String[]> getList(MinecraftVersion mv, IMinecraftAssetService mp);
|
||||
public abstract OverridableSwingWorker<String[]> getList(MinecraftVersion mv, IMinecraftAssetService mp);
|
||||
|
||||
/**
|
||||
* Will be invoked when the user invoked "Download all assets".
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -49,9 +48,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
|
||||
public void executeTask() throws Throwable {
|
||||
IAssetsHandler type = IAssetsHandler.ASSETS_HANDLER;
|
||||
type.getList(service.version().getVersionById(mcVersion), service.asset())
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.observeOn(Schedulers.eventQueue())
|
||||
.subscribe((t) -> TaskWindow.getInstance().addTask(type.getDownloadTask(service.getDownloadType().getProvider())).start());
|
||||
.reg((t) -> TaskWindow.getInstance().addTask(type.getDownloadTask(service.getDownloadType().getProvider())).start()).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,12 +31,12 @@ import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.NetUtils;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftRemoteVersion;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftRemoteVersions;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -151,10 +151,14 @@ public class MinecraftDownloadService extends IMinecraftDownloadService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<MinecraftRemoteVersion> getRemoteVersions() {
|
||||
return NetUtils.getRx(service.getDownloadType().getProvider().getVersionsListDownloadURL())
|
||||
.map(r -> C.gson.fromJson(r, MinecraftRemoteVersions.class))
|
||||
.filter(r -> r != null && r.versions != null)
|
||||
.flatMap(r -> Observable.from(r.versions));
|
||||
public OverridableSwingWorker<MinecraftRemoteVersion> getRemoteVersions() {
|
||||
return new OverridableSwingWorker<MinecraftRemoteVersion>() {
|
||||
@Override
|
||||
protected void work() throws Exception {
|
||||
MinecraftRemoteVersions r = C.gson.fromJson(NetUtils.get(service.getDownloadType().getProvider().getVersionsListDownloadURL()), MinecraftRemoteVersions.class);
|
||||
if (r != null && r.versions != null)
|
||||
publish(r.versions.toArray(new MinecraftRemoteVersion[0]));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,7 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
|
||||
|
||||
if (!checkAssetsExist())
|
||||
if (MessageBox.Show(C.i18n("assets.no_assets"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION) {
|
||||
IAssetsHandler.ASSETS_HANDLER.getList(version, service.asset()).subscribe(a -> {
|
||||
});
|
||||
IAssetsHandler.ASSETS_HANDLER.getList(version, service.asset()).execute();
|
||||
TaskWindow.getInstance().addTask(IAssetsHandler.ASSETS_HANDLER.getDownloadTask(service.getDownloadType().getProvider())).start();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ import java.util.List;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.DownloadLibraryJob;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftRemoteVersion;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -51,6 +51,6 @@ public abstract class IMinecraftDownloadService extends IMinecraftBasicService {
|
||||
*/
|
||||
public abstract List<DownloadLibraryJob> getDownloadLibraries(MinecraftVersion mv) throws GameException;
|
||||
|
||||
public abstract Observable<MinecraftRemoteVersion> getRemoteVersions();
|
||||
public abstract OverridableSwingWorker<MinecraftRemoteVersion> getRemoteVersions();
|
||||
|
||||
}
|
||||
|
||||
@@ -100,4 +100,8 @@ public class DefaultMinecraftService extends IMinecraftService {
|
||||
return new MinecraftLoader(options, this, p);
|
||||
}
|
||||
|
||||
public Profile getProfile() {
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.jackhuang.hellominecraft.utils.VersionNumber;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.OS;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -91,7 +90,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
|
||||
@Override
|
||||
public boolean call(Object sender, VersionNumber number) {
|
||||
((UpdateChecker) sender).requestDownloadLink().subscribeOn(Schedulers.newThread()).observeOn(Schedulers.eventQueue()).subscribe(map -> {
|
||||
((UpdateChecker) sender).requestDownloadLink().reg(map -> {
|
||||
if (map != null && map.containsKey("pack"))
|
||||
try {
|
||||
if (TaskWindow.getInstance().addTask(new AppDataUpgraderTask(map.get("pack"), number.version)).start()) {
|
||||
@@ -120,7 +119,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
MessageBox.Show(C.i18n("update.no_browser"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}).execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -108,13 +107,13 @@ public class GameDownloadPanel extends AnimatedPanel implements Selectable {
|
||||
public void refreshDownloads() {
|
||||
DefaultTableModel model = SwingUtils.clearDefaultTable(lstDownloads);
|
||||
gsp.getProfile().service().download().getRemoteVersions()
|
||||
.observeOn(Schedulers.eventQueue()).subscribeOn(Schedulers.newThread())
|
||||
.subscribe((ver) -> model.addRow(new Object[] { ver.id, ver.time,
|
||||
StrUtils.equalsOne(ver.type, "old_beta", "old_alpha", "release", "snapshot") ? C.i18n("versions." + ver.type) : ver.type }),
|
||||
(e) -> {
|
||||
.reg((ver) -> model.addRow(new Object[] { ver.id, ver.time,
|
||||
StrUtils.equalsOne(ver.type, "old_beta", "old_alpha", "release", "snapshot") ? C.i18n("versions." + ver.type) : ver.type }))
|
||||
.regDone(lstDownloads::updateUI).execute();
|
||||
/*(e) -> {
|
||||
MessageBox.Show("Failed to refresh download: " + e.getLocalizedMessage());
|
||||
HMCLog.err("Failed to refresh download.", e);
|
||||
}, lstDownloads::updateUI);
|
||||
}, );*/
|
||||
}
|
||||
|
||||
void downloadMinecraft() {
|
||||
|
||||
@@ -32,9 +32,7 @@ import java.awt.event.ItemEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
@@ -46,7 +44,6 @@ import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.LauncherVisibility;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.Profile;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.Settings;
|
||||
@@ -56,13 +53,12 @@ import org.jackhuang.hellominecraft.launcher.core.installers.InstallerType;
|
||||
import org.jackhuang.hellominecraft.launcher.core.mod.ModpackManager;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.GameDirType;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.launcher.views.modpack.ModpackInitializationPanel;
|
||||
import org.jackhuang.hellominecraft.launcher.views.modpack.ModpackWizard;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskRunnable;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.Event;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftVersionRequest;
|
||||
import org.jackhuang.hellominecraft.utils.system.OS;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
@@ -70,8 +66,6 @@ import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.Java;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.WizardDisplayer;
|
||||
import rx.Observable;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -1300,14 +1294,16 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
|
||||
return;
|
||||
reloadingMods = true;
|
||||
DefaultTableModel model = SwingUtils.clearDefaultTable(lstExternalMods);
|
||||
Observable.<List<ModInfo>>createWithEmptySubscription(
|
||||
t -> t.onNext(getProfile().service().mod().recacheMods(getProfile().getSelectedVersion())))
|
||||
.subscribeOn(Schedulers.newThread()).observeOn(Schedulers.eventQueue())
|
||||
.subscribe(t -> {
|
||||
for (ModInfo x : t)
|
||||
model.addRow(new Object[] { x.isActive(), x, x.version });
|
||||
reloadingMods = false;
|
||||
});
|
||||
new OverridableSwingWorker<List<ModInfo>>() {
|
||||
@Override
|
||||
protected void work() throws Exception {
|
||||
publish(getProfile().service().mod().recacheMods(getProfile().getSelectedVersion()));
|
||||
}
|
||||
}.reg(t -> {
|
||||
for (ModInfo x : t)
|
||||
model.addRow(new Object[] { x.isActive(), x, x.version });
|
||||
reloadingMods = false;
|
||||
}).execute();
|
||||
}
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jackhuang.hellominecraft.launcher.core.download.DownloadType;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -43,8 +42,10 @@ public class LauncherSettingsPanel extends AnimatedPanel {
|
||||
public LauncherSettingsPanel() {
|
||||
initComponents();
|
||||
|
||||
Observable.from(DownloadType.values()).map(t -> t.getName()).toList()
|
||||
.subscribe(t -> cboDownloadSource.setModel(new DefaultComboBoxModel(t.toArray(new String[0]))));
|
||||
DefaultComboBoxModel d = new DefaultComboBoxModel();
|
||||
for (DownloadType type : DownloadType.values())
|
||||
d.addElement(type.getName());
|
||||
cboDownloadSource.setModel(d);
|
||||
|
||||
txtBackgroundPath.setText(Settings.getInstance().getBgpath());
|
||||
txtProxyHost.setText(Settings.getInstance().getProxyHost());
|
||||
@@ -362,7 +363,7 @@ public class LauncherSettingsPanel extends AnimatedPanel {
|
||||
private void chkDecoratedFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_chkDecoratedFocusLost
|
||||
Settings.getInstance().setDecorated(chkDecorated.isSelected());
|
||||
}//GEN-LAST:event_chkDecoratedFocusLost
|
||||
|
||||
|
||||
private void jLabel9MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel9MouseClicked
|
||||
SwingUtils.openLink("http://blog.163.com/huanghongxun2008@126/blog/static/7738046920160323812771/");
|
||||
}//GEN-LAST:event_jLabel9MouseClicked
|
||||
|
||||
@@ -20,7 +20,6 @@ package org.jackhuang.hellominecraft.launcher.views.modpack;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -31,7 +30,6 @@ import org.jackhuang.hellominecraft.launcher.core.mod.ModpackManager;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.views.checktree.CheckBoxTreeNode;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.DeferredWizardResult;
|
||||
|
||||
Reference in New Issue
Block a user