feat(multiplayer): join request should be cancelled when user operation time out.
This commit is contained in:
@@ -227,7 +227,7 @@ public final class Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void dialog(String text, String title, MessageType type, Runnable ok) {
|
public static void dialog(String text, String title, MessageType type, Runnable ok) {
|
||||||
dialog(MessageDialogPane.ok(text, title, type, ok));
|
dialog(new MessageDialogPane.Builder(text, title, type).ok(ok).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void confirm(String text, String title, Runnable yes, Runnable no) {
|
public static void confirm(String text, String title, Runnable yes, Runnable no) {
|
||||||
@@ -235,15 +235,15 @@ public final class Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void confirm(String text, String title, MessageType type, Runnable yes, Runnable no) {
|
public static void confirm(String text, String title, MessageType type, Runnable yes, Runnable no) {
|
||||||
dialog(MessageDialogPane.yesOrNo(text, title, type, yes, no));
|
dialog(new MessageDialogPane.Builder(text, title, type).yesOrNo(yes, no).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void confirmAction(String text, String title, MessageType type, ButtonBase actionButton) {
|
public static void confirmAction(String text, String title, MessageType type, ButtonBase actionButton) {
|
||||||
dialog(MessageDialogPane.actionOrCancel(text, title, type, actionButton, null));
|
dialog(new MessageDialogPane.Builder(text, title, type).actionOrCancel(actionButton, null).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void confirmAction(String text, String title, MessageType type, ButtonBase actionButton, Runnable cancel) {
|
public static void confirmAction(String text, String title, MessageType type, ButtonBase actionButton, Runnable cancel) {
|
||||||
dialog(MessageDialogPane.actionOrCancel(text, title, type, actionButton, cancel));
|
dialog(new MessageDialogPane.Builder(text, title, type).actionOrCancel(actionButton, cancel).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<String> prompt(String title, FutureCallback<String> onResult) {
|
public static CompletableFuture<String> prompt(String title, FutureCallback<String> onResult) {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package org.jackhuang.hmcl.ui.construct;
|
package org.jackhuang.hmcl.ui.construct;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.ButtonBase;
|
import javafx.scene.control.ButtonBase;
|
||||||
@@ -28,12 +27,16 @@ import javafx.scene.layout.StackPane;
|
|||||||
import org.jackhuang.hmcl.setting.Theme;
|
import org.jackhuang.hmcl.setting.Theme;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.SVG;
|
import org.jackhuang.hmcl.ui.SVG;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||||
|
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public final class MessageDialogPane extends StackPane {
|
public final class MessageDialogPane extends StackPane {
|
||||||
@@ -105,53 +108,89 @@ public final class MessageDialogPane extends StackPane {
|
|||||||
cancelButton = btn;
|
cancelButton = btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MessageDialogPane ok(String text, String title, MessageType type, Runnable ok) {
|
public ButtonBase getCancelButton() {
|
||||||
MessageDialogPane dialog = new MessageDialogPane(text, title, type);
|
return cancelButton;
|
||||||
|
|
||||||
JFXButton btnOk = new JFXButton(i18n("button.ok"));
|
|
||||||
btnOk.getStyleClass().add("dialog-accept");
|
|
||||||
if (ok != null) {
|
|
||||||
btnOk.setOnAction(e -> ok.run());
|
|
||||||
}
|
|
||||||
dialog.addButton(btnOk);
|
|
||||||
dialog.setCancelButton(btnOk);
|
|
||||||
|
|
||||||
return dialog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MessageDialogPane yesOrNo(String text, String title, MessageType type, Runnable yes, Runnable no) {
|
public static class Builder {
|
||||||
MessageDialogPane dialog = new MessageDialogPane(text, title, type);
|
private final MessageDialogPane dialog;
|
||||||
|
|
||||||
JFXButton btnYes = new JFXButton(i18n("button.yes"));
|
public Builder(String text, String title, MessageType type) {
|
||||||
btnYes.getStyleClass().add("dialog-accept");
|
this.dialog = new MessageDialogPane(text, title, type);
|
||||||
if (yes != null) {
|
|
||||||
btnYes.setOnAction(e -> yes.run());
|
|
||||||
}
|
}
|
||||||
dialog.addButton(btnYes);
|
|
||||||
|
|
||||||
JFXButton btnNo = new JFXButton(i18n("button.no"));
|
public Builder ok(Runnable ok) {
|
||||||
btnNo.getStyleClass().add("dialog-cancel");
|
JFXButton btnOk = new JFXButton(i18n("button.ok"));
|
||||||
if (no != null) {
|
btnOk.getStyleClass().add("dialog-accept");
|
||||||
btnNo.setOnAction(e -> no.run());
|
if (ok != null) {
|
||||||
|
btnOk.setOnAction(e -> ok.run());
|
||||||
|
}
|
||||||
|
dialog.addButton(btnOk);
|
||||||
|
dialog.setCancelButton(btnOk);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
dialog.addButton(btnNo);
|
|
||||||
dialog.setCancelButton(btnNo);
|
|
||||||
|
|
||||||
return dialog;
|
public Builder yesOrNo(Runnable yes, Runnable no) {
|
||||||
}
|
JFXButton btnYes = new JFXButton(i18n("button.yes"));
|
||||||
|
btnYes.getStyleClass().add("dialog-accept");
|
||||||
|
if (yes != null) {
|
||||||
|
btnYes.setOnAction(e -> yes.run());
|
||||||
|
}
|
||||||
|
dialog.addButton(btnYes);
|
||||||
|
|
||||||
public static MessageDialogPane actionOrCancel(String text, String title, MessageType type, ButtonBase actionButton, Runnable cancel) {
|
JFXButton btnNo = new JFXButton(i18n("button.no"));
|
||||||
MessageDialogPane dialog = new MessageDialogPane(text, title, type);
|
btnNo.getStyleClass().add("dialog-cancel");
|
||||||
dialog.addButton(actionButton);
|
if (no != null) {
|
||||||
|
btnNo.setOnAction(e -> no.run());
|
||||||
JFXButton btnCancel = new JFXButton(i18n("button.cancel"));
|
}
|
||||||
btnCancel.getStyleClass().add("dialog-cancel");
|
dialog.addButton(btnNo);
|
||||||
if (cancel != null) {
|
dialog.setCancelButton(btnNo);
|
||||||
btnCancel.setOnAction(e -> cancel.run());
|
return this;
|
||||||
}
|
}
|
||||||
dialog.addButton(btnCancel);
|
|
||||||
dialog.setCancelButton(btnCancel);
|
|
||||||
|
|
||||||
return dialog;
|
public Builder actionOrCancel(ButtonBase actionButton, Runnable cancel) {
|
||||||
|
dialog.addButton(actionButton);
|
||||||
|
|
||||||
|
JFXButton btnCancel = new JFXButton(i18n("button.cancel"));
|
||||||
|
btnCancel.getStyleClass().add("dialog-cancel");
|
||||||
|
if (cancel != null) {
|
||||||
|
btnCancel.setOnAction(e -> cancel.run());
|
||||||
|
}
|
||||||
|
dialog.addButton(btnCancel);
|
||||||
|
dialog.setCancelButton(btnCancel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder cancelOnTimeout(long timeoutMs) {
|
||||||
|
if (dialog.getCancelButton() == null) {
|
||||||
|
throw new IllegalStateException("Call ok/yesOrNo/actionOrCancel before calling cancelOnTimeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonBase cancelButton = dialog.getCancelButton();
|
||||||
|
String originalText = cancelButton.getText();
|
||||||
|
|
||||||
|
Timer timer = Lang.getTimer();
|
||||||
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
long timeout = timeoutMs;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (timeout < 0) {
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timeout -= 1000;
|
||||||
|
long currentTimeout = timeout;
|
||||||
|
runInFX(() -> cancelButton.setText(originalText + " (" + (currentTimeout / 1000) + ")"));
|
||||||
|
}
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageDialogPane build() {
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,8 +215,10 @@ public class MultiplayerPage extends DecoratorAnimatedPage implements DecoratorP
|
|||||||
MultiplayerManager.CatoSession session = MultiplayerManager.createSession(globalConfig().getMultiplayerToken(), result.getServer().getMotd(), gamePort, result.isAllowAllJoinRequests());
|
MultiplayerManager.CatoSession session = MultiplayerManager.createSession(globalConfig().getMultiplayerToken(), result.getServer().getMotd(), gamePort, result.isAllowAllJoinRequests());
|
||||||
session.getServer().setOnClientAdding((client, resolveClient, rejectClient) -> {
|
session.getServer().setOnClientAdding((client, resolveClient, rejectClient) -> {
|
||||||
runInFX(() -> {
|
runInFX(() -> {
|
||||||
Controllers.confirm(i18n("multiplayer.session.create.join.prompt", client.getUsername()), i18n("multiplayer.session.create.join"), MessageDialogPane.MessageType.INFO,
|
Controllers.dialog(new MessageDialogPane.Builder(i18n("multiplayer.session.create.join.prompt", client.getUsername()), i18n("multiplayer.session.create.join"), MessageDialogPane.MessageType.INFO)
|
||||||
resolveClient, () -> rejectClient.accept(""));
|
.yesOrNo(resolveClient, () -> rejectClient.accept(""))
|
||||||
|
.cancelOnTimeout(30 * 1000)
|
||||||
|
.build());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
session.getServer().onClientAdded().register(event -> {
|
session.getServer().onClientAdded().register(event -> {
|
||||||
|
|||||||
@@ -359,17 +359,21 @@ public final class Lang {
|
|||||||
|
|
||||||
private static Timer timer;
|
private static Timer timer;
|
||||||
|
|
||||||
public static synchronized TimerTask setTimeout(Runnable runnable, long delayMs) {
|
public static synchronized Timer getTimer() {
|
||||||
if (timer == null) {
|
if (timer == null) {
|
||||||
timer = new Timer();
|
timer = new Timer();
|
||||||
}
|
}
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized TimerTask setTimeout(Runnable runnable, long delayMs) {
|
||||||
TimerTask task = new TimerTask() {
|
TimerTask task = new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
timer.schedule(task, delayMs);
|
getTimer().schedule(task, delayMs);
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user