feat: copy link when web browser cannot be opened.

This commit is contained in:
huanghongxun 2021-09-09 23:16:44 +08:00
parent 3327899cf6
commit 98c6651793
12 changed files with 75 additions and 21 deletions

View File

@ -41,6 +41,8 @@ public final class MicrosoftAuthenticationServer extends NanoHTTPD implements Mi
private final int port; private final int port;
private final CompletableFuture<String> future = new CompletableFuture<>(); private final CompletableFuture<String> future = new CompletableFuture<>();
public static String lastlyOpenedURL;
private MicrosoftAuthenticationServer(int port) { private MicrosoftAuthenticationServer(int port) {
super(port); super(port);
@ -107,7 +109,7 @@ public final class MicrosoftAuthenticationServer extends NanoHTTPD implements Mi
@Override @Override
public void openBrowser(String url) throws IOException { public void openBrowser(String url) throws IOException {
// TODO: error! lastlyOpenedURL = url;
FXUtils.openLink(url); FXUtils.openLink(url);
} }

View File

@ -272,6 +272,10 @@ public final class Controllers {
decorator.getNavigator().navigate(node, ContainerAnimations.FADE.getAnimationProducer()); decorator.getNavigator().navigate(node, ContainerAnimations.FADE.getAnimationProducer());
} }
public static void showToast(String content) {
decorator.showToast(content);
}
public static boolean isStopped() { public static boolean isStopped() {
return decorator == null; return decorator == null;
} }

View File

@ -37,9 +37,7 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter; import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode; import javafx.scene.input.*;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle; import javafx.scene.shape.Rectangle;
@ -70,6 +68,7 @@ import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Lang.thread; import static org.jackhuang.hmcl.util.Lang.thread;
import static org.jackhuang.hmcl.util.Lang.tryCast; import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class FXUtils { public final class FXUtils {
private FXUtils() { private FXUtils() {
@ -613,4 +612,12 @@ public final class FXUtils {
} }
return wr; return wr;
} }
public static void copyText(String text) {
ClipboardContent content = new ClipboardContent();
content.putString(text);
Clipboard.getSystemClipboard().setContent(content);
Controllers.showToast(i18n("message.copied"));
}
} }

View File

@ -22,6 +22,8 @@ import com.jfoenix.validation.base.ValidatorBase;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.NamedArg; import javafx.beans.NamedArg;
import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.HPos; import javafx.geometry.HPos;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
@ -36,10 +38,12 @@ import org.jackhuang.hmcl.auth.CharacterSelector;
import org.jackhuang.hmcl.auth.NoSelectedCharacterException; import org.jackhuang.hmcl.auth.NoSelectedCharacterException;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccountFactory; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccountFactory;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.auth.microsoft.MicrosoftAccountFactory;
import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory; import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile; import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
import org.jackhuang.hmcl.game.MicrosoftAuthenticationServer;
import org.jackhuang.hmcl.game.TexturesLoader; import org.jackhuang.hmcl.game.TexturesLoader;
import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.setting.Theme;
@ -82,6 +86,8 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
private Node detailsPane; // AccountDetailsInputPane for Offline / Mojang / authlib-injector, Label for Microsoft private Node detailsPane; // AccountDetailsInputPane for Offline / Mojang / authlib-injector, Label for Microsoft
private final Pane detailsContainer; private final Pane detailsContainer;
private final BooleanProperty logging = new SimpleBooleanProperty();
private TaskExecutor loginTask; private TaskExecutor loginTask;
public CreateAccountPane() { public CreateAccountPane() {
@ -185,7 +191,10 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
private void onAccept() { private void onAccept() {
spinner.showSpinner(); spinner.showSpinner();
lblErrorMessage.setText(""); lblErrorMessage.setText("");
body.setDisable(true);
if (!(factory instanceof MicrosoftAccountFactory)) {
body.setDisable(true);
}
String username; String username;
String password; String password;
@ -201,6 +210,8 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
additionalData = null; additionalData = null;
} }
logging.set(true);
loginTask = Task.supplyAsync(() -> factory.create(new DialogCharacterSelector(), username, password, null, additionalData)) loginTask = Task.supplyAsync(() -> factory.create(new DialogCharacterSelector(), username, password, null, additionalData))
.whenComplete(Schedulers.javafx(), account -> { .whenComplete(Schedulers.javafx(), account -> {
int oldIndex = Accounts.getAccounts().indexOf(account); int oldIndex = Accounts.getAccounts().indexOf(account);
@ -243,9 +254,17 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
lblErrorMessage.setText(""); lblErrorMessage.setText("");
} }
if (factory == Accounts.FACTORY_MICROSOFT) { if (factory == Accounts.FACTORY_MICROSOFT) {
Label lblTip = new Label(i18n("account.methods.microsoft.manual")); // TODO HintPane hintPane = new HintPane(MessageDialogPane.MessageType.INFORMATION);
lblTip.setWrapText(true); hintPane.textProperty().bind(BindingMapping.of(logging).map(logging ->
detailsPane = lblTip; logging
? i18n("account.methods.microsoft.manual")
: i18n("account.methods.microsoft.hint")));
hintPane.setOnMouseClicked(e -> {
if (logging.get() && MicrosoftAuthenticationServer.lastlyOpenedURL != null) {
FXUtils.copyText(MicrosoftAuthenticationServer.lastlyOpenedURL);
}
});
detailsPane = hintPane;
btnAccept.setDisable(false); btnAccept.setDisable(false);
} else { } else {
detailsPane = new AccountDetailsInputPane(factory, btnAccept::fire); detailsPane = new AccountDetailsInputPane(factory, btnAccept::fire);

View File

@ -33,7 +33,6 @@ 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;
public class MenuUpDownButton extends Control { public class MenuUpDownButton extends Control {
private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected"); private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected");

View File

@ -17,6 +17,7 @@
*/ */
package org.jackhuang.hmcl.ui.decorator; package org.jackhuang.hmcl.ui.decorator;
import com.jfoenix.controls.JFXSnackbar;
import javafx.beans.property.*; import javafx.beans.property.*;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@ -54,6 +55,7 @@ public class Decorator extends Control {
private final Stage primaryStage; private final Stage primaryStage;
private Navigation.NavigationDirection navigationDirection = Navigation.NavigationDirection.START; private Navigation.NavigationDirection navigationDirection = Navigation.NavigationDirection.START;
private StackPane drawerWrapper; private StackPane drawerWrapper;
private final JFXSnackbar snackbar = new JFXSnackbar();
private final ReadOnlyBooleanWrapper allowMove = new ReadOnlyBooleanWrapper(); private final ReadOnlyBooleanWrapper allowMove = new ReadOnlyBooleanWrapper();
private final ReadOnlyBooleanWrapper dragging = new ReadOnlyBooleanWrapper(); private final ReadOnlyBooleanWrapper dragging = new ReadOnlyBooleanWrapper();
@ -214,6 +216,10 @@ public class Decorator extends Control {
return onRefreshNavButtonAction; return onRefreshNavButtonAction;
} }
public JFXSnackbar getSnackbar() {
return snackbar;
}
@Override @Override
protected Skin<?> createDefaultSkin() { protected Skin<?> createDefaultSkin() {
return new DecoratorSkin(this); return new DecoratorSkin(this);

View File

@ -18,6 +18,7 @@
package org.jackhuang.hmcl.ui.decorator; package org.jackhuang.hmcl.ui.decorator;
import com.jfoenix.controls.JFXDialog; import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXSnackbar;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
@ -363,6 +364,12 @@ public class DecoratorController {
} }
} }
// ==== Toast ====
public void showToast(String content) {
decorator.getSnackbar().fireEvent(new JFXSnackbar.SnackbarEvent(content, null, 2000L, false, null));
}
// ==== Wizard ==== // ==== Wizard ====
public void startWizard(WizardProvider wizardProvider) { public void startWizard(WizardProvider wizardProvider) {

View File

@ -89,6 +89,8 @@ public class DecoratorSkin extends SkinBase<Decorator> {
clip.setArcHeight(8); clip.setArcHeight(8);
parent.setClip(clip); parent.setClip(clip);
skinnable.getSnackbar().registerSnackbarContainer(parent);
root.addEventFilter(MouseEvent.MOUSE_RELEASED, this::onMouseReleased); root.addEventFilter(MouseEvent.MOUSE_RELEASED, this::onMouseReleased);
root.addEventFilter(MouseEvent.MOUSE_DRAGGED, this::onMouseDragged); root.addEventFilter(MouseEvent.MOUSE_DRAGGED, this::onMouseDragged);
root.addEventFilter(MouseEvent.MOUSE_MOVED, this::onMouseMoved); root.addEventFilter(MouseEvent.MOUSE_MOVED, this::onMouseMoved);

View File

@ -21,9 +21,6 @@ import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.game.Artifact; import org.jackhuang.hmcl.game.Artifact;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jackhuang.hmcl.util.platform.ManagedProcess; import org.jackhuang.hmcl.util.platform.ManagedProcess;
@ -33,16 +30,17 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
/** /**
* Cato Management. * Cato Management.
*/ */
public class MultiplayerManager { public final class MultiplayerManager {
private static final String CATO_DOWNLOAD_URL = "https://hmcl.huangyuhui.net/maven/"; private static final String CATO_DOWNLOAD_URL = "https://hmcl.huangyuhui.net/maven/";
private static final String CATO_VERSION = "2021-09-01"; private static final String CATO_VERSION = "2021-09-01";
private static final Artifact CATO_ARTIFACT = new Artifact("cato", "cato", CATO_VERSION); private static final Artifact CATO_ARTIFACT = new Artifact("cato", "cato", CATO_VERSION);
private MultiplayerManager() {
}
public static void fetchIdAndToken() { public static void fetchIdAndToken() {
// TODO // TODO
} }

View File

@ -81,7 +81,8 @@ account.methods.microsoft.error.missing_xbox_account=Your Microsoft account is n
account.methods.microsoft.error.no_character=Account is missing a Minecraft Java profile. While the Microsoft account is valid, it does not own the game. account.methods.microsoft.error.no_character=Account is missing a Minecraft Java profile. While the Microsoft account is valid, it does not own the game.
account.methods.microsoft.error.unknown=Failed to log in. Microsoft respond with error code %d. account.methods.microsoft.error.unknown=Failed to log in. Microsoft respond with error code %d.
account.methods.microsoft.logging_in=Logging in... account.methods.microsoft.logging_in=Logging in...
account.methods.microsoft.manual=You should finish authorization in the newly opened browser window. If the browser window failed to show, you can click here to copy the URL, and manually open it in your browser. account.methods.microsoft.hint=You should click "login" button and continue login process in newly opened browser window.
account.methods.microsoft.manual=After clicking "login" button, you should finish authorization in the newly opened browser window. If the browser window failed to show, you can click here to copy the URL, and manually open it in your browser.
account.methods.microsoft.waiting_browser=Waiting for authorization in opened browser window... account.methods.microsoft.waiting_browser=Waiting for authorization in opened browser window...
account.methods.offline=Offline account.methods.offline=Offline
account.methods.offline.uuid=UUID account.methods.offline.uuid=UUID
@ -394,6 +395,7 @@ logwindow.export_game_crash_logs=Export game crash info
main_page=Home main_page=Home
message.confirm=Confirm message.confirm=Confirm
message.copied=Copied to clipboard
message.doing=Please wait message.doing=Please wait
message.downloading=Downloading... message.downloading=Downloading...
message.error=Error message.error=Error
@ -489,6 +491,7 @@ mods.not_modded=You should install a modloader first (Fabric, Forge or LiteLoade
mods.url=Official Page mods.url=Official Page
multiplayer=Multiplayer multiplayer=Multiplayer
multiplayer.download=Downloading dependencies for multiplayer
multiplayer.nat=Network Type Detection multiplayer.nat=Network Type Detection
multiplayer.nat.hint=Network type detection will make it clear whether your network fulfills our requirement for multiplayer mode. multiplayer.nat.hint=Network type detection will make it clear whether your network fulfills our requirement for multiplayer mode.
multiplayer.nat.latency=Latency multiplayer.nat.latency=Latency
@ -503,7 +506,8 @@ multiplayer.nat.type.port_restricted_cone=Medium (Port Restricted Cone)
multiplayer.nat.type.restricted_cone=Medium (Restricted Cone) multiplayer.nat.type.restricted_cone=Medium (Restricted Cone)
multiplayer.nat.type.symmetric=Bad (Symmetric) multiplayer.nat.type.symmetric=Bad (Symmetric)
multiplayer.nat.type.symmetric_udp_firewall=Bad (Symmetric with UDP Firewall) multiplayer.nat.type.symmetric_udp_firewall=Bad (Symmetric with UDP Firewall)
multiplayer.nat.type.unknown=Unknownn multiplayer.nat.type.unknown=Unknown
multiplayer.powered_by=Powered by cato
multiplayer.room=Room multiplayer.room=Room
multiplayer.room.name.format=%1$s's Room multiplayer.room.name.format=%1$s's Room
multiplayer.room.close=Close Room multiplayer.room.close=Close Room

View File

@ -74,8 +74,8 @@ account.methods.microsoft.error.missing_xbox_account=你的微軟帳號尚未關
account.methods.microsoft.error.no_character=該帳號沒有包含 Minecraft Java 版購買記錄 account.methods.microsoft.error.no_character=該帳號沒有包含 Minecraft Java 版購買記錄
account.methods.microsoft.error.unknown=登錄失敗,錯誤碼:%d account.methods.microsoft.error.unknown=登錄失敗,錯誤碼:%d
account.methods.microsoft.logging_in=登錄中... account.methods.microsoft.logging_in=登錄中...
account.methods.microsoft.hint=點擊確定以登錄 account.methods.microsoft.hint=您需要點擊登錄按鈕,並在新打開的瀏覽器窗口中完成登錄。
account.methods.microsoft.manual=您需要在新打開的瀏覽器窗口中完成登錄。若頁面未能打開,您可以點擊此處複製連結,並手動在瀏覽器中打開網頁。 account.methods.microsoft.manual=若登錄頁面未能打開,您可以點擊此處複製連結,並手動在瀏覽器中打開網頁。
account.methods.microsoft.waiting_browser=等待在新打開的瀏覽器窗口中完成登錄... account.methods.microsoft.waiting_browser=等待在新打開的瀏覽器窗口中完成登錄...
account.methods.offline=離線模式 account.methods.offline=離線模式
account.methods.offline.uuid=UUID account.methods.offline.uuid=UUID
@ -380,6 +380,7 @@ logwindow.export_game_crash_logs=導出遊戲崩潰訊息
main_page=首頁 main_page=首頁
message.confirm=提示 message.confirm=提示
message.copied=已複製到剪貼板
message.doing=請耐心等待 message.doing=請耐心等待
message.downloading=正在下載… message.downloading=正在下載…
message.error=錯誤 message.error=錯誤
@ -464,6 +465,7 @@ mods.name=名稱
mods.not_modded=你需要先在自動安裝頁面安裝 Fabric、Forge 或 LiteLoader 才能進行模組管理。 mods.not_modded=你需要先在自動安裝頁面安裝 Fabric、Forge 或 LiteLoader 才能進行模組管理。
multiplayer=聯機 multiplayer=聯機
multiplayer.download=正在下載依賴
multiplayer.nat=網路檢測 multiplayer.nat=網路檢測
multiplayer.nat.hint=執行網路檢測可以讓你更清楚裡的網路狀況是否符合聯機功能的需求。不符合聯機功能運行條件的網路狀況將可能導致聯機失敗。 multiplayer.nat.hint=執行網路檢測可以讓你更清楚裡的網路狀況是否符合聯機功能的需求。不符合聯機功能運行條件的網路狀況將可能導致聯機失敗。
multiplayer.nat.latency=延遲 multiplayer.nat.latency=延遲
@ -479,6 +481,7 @@ multiplayer.nat.type.restricted_cone=中(受限圓錐型)
multiplayer.nat.type.symmetric=差(對稱型) multiplayer.nat.type.symmetric=差(對稱型)
multiplayer.nat.type.symmetric_udp_firewall=差(對稱型+防火牆) multiplayer.nat.type.symmetric_udp_firewall=差(對稱型+防火牆)
multiplayer.nat.type.unknown=未知 multiplayer.nat.type.unknown=未知
multiplayer.powered_by=由 cato 提供技術支持
multiplayer.room=房間 multiplayer.room=房間
multiplayer.room.name.format=%1$s 的房間 multiplayer.room.name.format=%1$s 的房間
multiplayer.room.close=關閉房間 multiplayer.room.close=關閉房間

View File

@ -80,8 +80,8 @@ account.methods.microsoft.error.missing_xbox_account=你的微软账号尚未关
account.methods.microsoft.error.no_character=该账号没有包含 Minecraft Java 版购买记录 account.methods.microsoft.error.no_character=该账号没有包含 Minecraft Java 版购买记录
account.methods.microsoft.error.unknown=登录失败,错误码:%d account.methods.microsoft.error.unknown=登录失败,错误码:%d
account.methods.microsoft.logging_in=登录中... account.methods.microsoft.logging_in=登录中...
account.methods.microsoft.hint=点击确定以登录 account.methods.microsoft.hint=您需要点击登录按钮,并在新打开的浏览器窗口中完成登录。
account.methods.microsoft.manual=您需要在新打开的浏览器窗口中完成登录。若页面未能打开,您可以点击此处复制链接,并手动在浏览器中打开网页。 account.methods.microsoft.manual=若登录页面未能打开,您可以点击此处复制链接,并手动在浏览器中打开网页。
account.methods.microsoft.waiting_browser=等待在新打开的浏览器窗口中完成登录... account.methods.microsoft.waiting_browser=等待在新打开的浏览器窗口中完成登录...
account.methods.offline=离线模式 account.methods.offline=离线模式
account.methods.offline.uuid=UUID account.methods.offline.uuid=UUID
@ -400,6 +400,7 @@ logwindow.export_game_crash_logs=导出游戏崩溃信息
main_page=主页 main_page=主页
message.confirm=提示 message.confirm=提示
message.copied=已复制到剪贴板
message.doing=请耐心等待 message.doing=请耐心等待
message.downloading=正在下载 message.downloading=正在下载
message.error=错误 message.error=错误
@ -496,6 +497,7 @@ mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteL
mods.url=官方页面 mods.url=官方页面
multiplayer=联机 multiplayer=联机
multiplayer.download=正在下载依赖
multiplayer.nat=网络检测 multiplayer.nat=网络检测
multiplayer.nat.hint=执行网络检测可以让你更清楚里的网络状况是否符合联机功能的需求。不符合联机功能运行条件的网络状况将可能导致联机失败。 multiplayer.nat.hint=执行网络检测可以让你更清楚里的网络状况是否符合联机功能的需求。不符合联机功能运行条件的网络状况将可能导致联机失败。
multiplayer.nat.latency=延迟 multiplayer.nat.latency=延迟
@ -511,6 +513,7 @@ multiplayer.nat.type.restricted_cone=中(受限圆锥型)
multiplayer.nat.type.symmetric=差(对称型) multiplayer.nat.type.symmetric=差(对称型)
multiplayer.nat.type.symmetric_udp_firewall=差(对称型+防火墙) multiplayer.nat.type.symmetric_udp_firewall=差(对称型+防火墙)
multiplayer.nat.type.unknown=未知 multiplayer.nat.type.unknown=未知
multiplayer.powered_by=由 cato 提供技术支持
multiplayer.room=房间 multiplayer.room=房间
multiplayer.room.name.format=%1$s 的房间 multiplayer.room.name.format=%1$s 的房间
multiplayer.room.close=关闭房间 multiplayer.room.close=关闭房间