feat: notification of dev/nightly version.
This commit is contained in:
@@ -43,6 +43,8 @@ public final class Metadata {
|
|||||||
public static final String PUBLISH_URL = "http://www.mcbbs.net/thread-142335-1-1.html";
|
public static final String PUBLISH_URL = "http://www.mcbbs.net/thread-142335-1-1.html";
|
||||||
public static final String EULA_URL = "https://hmcl.huangyuhui.net/eula";
|
public static final String EULA_URL = "https://hmcl.huangyuhui.net/eula";
|
||||||
|
|
||||||
|
public static final String BUILD_CHANNEL = JarUtils.thisJar().flatMap(JarUtils::getManifest).map(manifest -> manifest.getMainAttributes().getValue("Build-Channel")).orElse("nightly");
|
||||||
|
|
||||||
public static final Path MINECRAFT_DIRECTORY = OperatingSystem.getWorkingDirectory("minecraft");
|
public static final Path MINECRAFT_DIRECTORY = OperatingSystem.getWorkingDirectory("minecraft");
|
||||||
public static final Path HMCL_DIRECTORY = getHMCLDirectory();
|
public static final Path HMCL_DIRECTORY = getHMCLDirectory();
|
||||||
|
|
||||||
@@ -54,4 +56,16 @@ public final class Metadata {
|
|||||||
}
|
}
|
||||||
return OperatingSystem.getWorkingDirectory("hmcl");
|
return OperatingSystem.getWorkingDirectory("hmcl");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isStable() {
|
||||||
|
return "stable".equals(BUILD_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDev() {
|
||||||
|
return "dev".equals(BUILD_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNightly() {
|
||||||
|
return !isStable() && !isDev();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,10 +44,13 @@ import javafx.scene.layout.Priority;
|
|||||||
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;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
||||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||||
@@ -55,9 +58,17 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
|||||||
import org.jackhuang.hmcl.util.javafx.ExtendedProperties;
|
import org.jackhuang.hmcl.util.javafx.ExtendedProperties;
|
||||||
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
|
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
|
||||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.HyperlinkEvent;
|
import javax.swing.event.HyperlinkEvent;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -65,6 +76,7 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
@@ -687,4 +699,36 @@ public final class FXUtils {
|
|||||||
|
|
||||||
Controllers.showToast(i18n("message.copied"));
|
Controllers.showToast(i18n("message.copied"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TextFlow segmentToTextFlow(final String segment, Consumer<String> hyperlinkAction) throws ParserConfigurationException, IOException, SAXException {
|
||||||
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
|
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||||
|
Document doc = builder.parse(new InputSource(new StringReader("<body>" + segment + "</body>")));
|
||||||
|
Element r = doc.getDocumentElement();
|
||||||
|
|
||||||
|
NodeList children = r.getChildNodes();
|
||||||
|
List<javafx.scene.Node> texts = new ArrayList<>();
|
||||||
|
for (int i = 0; i < children.getLength(); i++) {
|
||||||
|
org.w3c.dom.Node node = children.item(i);
|
||||||
|
|
||||||
|
if (node instanceof Element) {
|
||||||
|
Element element = (Element) node;
|
||||||
|
if ("a".equals(element.getTagName())) {
|
||||||
|
String href = element.getAttribute("href");
|
||||||
|
JFXHyperlink hyperlink = new JFXHyperlink(element.getTextContent());
|
||||||
|
hyperlink.setOnAction(e -> hyperlinkAction.accept(href));
|
||||||
|
texts.add(hyperlink);
|
||||||
|
} else if ("br".equals(element.getTagName())) {
|
||||||
|
texts.add(new Text("\n"));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("unsupported tag " + element.getTagName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
texts.add(new Text(node.getTextContent()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final TextFlow tf = new TextFlow(texts.toArray(new javafx.scene.Node[0]));
|
||||||
|
return tf;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.ui.construct;
|
||||||
|
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||||
|
|
||||||
|
public class AnnouncementCard extends VBox {
|
||||||
|
|
||||||
|
public AnnouncementCard(String title, String content) {
|
||||||
|
TextFlow tf;
|
||||||
|
try {
|
||||||
|
tf = FXUtils.segmentToTextFlow(content, AnnouncementCard::onAction);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.log(Level.WARNING, "Failed to parse announcement content", e);
|
||||||
|
tf = new TextFlow();
|
||||||
|
tf.getChildren().setAll(new Text(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
Label label = new Label(title);
|
||||||
|
label.getStyleClass().add("title");
|
||||||
|
getChildren().setAll(label, tf);
|
||||||
|
setSpacing(14);
|
||||||
|
getStyleClass().addAll("card", "announcement");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void onAction(String href) {
|
||||||
|
if (href.startsWith("hmcl://")) {
|
||||||
|
if ("hmcl://settings/feedback".equals(href)) {
|
||||||
|
Controllers.getSettingsPage().showFeedback();
|
||||||
|
Controllers.navigate(Controllers.getSettingsPage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FXUtils.openLink(href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -130,6 +130,10 @@ public class LauncherSettingsPage extends BorderPane implements DecoratorPage {
|
|||||||
tab.getSelectionModel().select(gameTab);
|
tab.getSelectionModel().select(gameTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showFeedback() {
|
||||||
|
tab.getSelectionModel().select(feedbackTab);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReadOnlyObjectProperty<State> stateProperty() {
|
public ReadOnlyObjectProperty<State> stateProperty() {
|
||||||
return state.getReadOnlyProperty();
|
return state.getReadOnlyProperty();
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import org.jackhuang.hmcl.setting.Profiles;
|
|||||||
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.ui.construct.AnnouncementCard;
|
||||||
import org.jackhuang.hmcl.ui.construct.PopupMenu;
|
import org.jackhuang.hmcl.ui.construct.PopupMenu;
|
||||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||||
@@ -73,8 +74,9 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
|||||||
private final ObservableList<Node> versionNodes;
|
private final ObservableList<Node> versionNodes;
|
||||||
private Profile profile;
|
private Profile profile;
|
||||||
|
|
||||||
private StackPane updatePane;
|
private final VBox announcementPane;
|
||||||
private JFXButton menuButton;
|
private final StackPane updatePane;
|
||||||
|
private final JFXButton menuButton;
|
||||||
|
|
||||||
{
|
{
|
||||||
HBox titleNode = new HBox(8);
|
HBox titleNode = new HBox(8);
|
||||||
@@ -92,6 +94,8 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
|||||||
|
|
||||||
setPadding(new Insets(20));
|
setPadding(new Insets(20));
|
||||||
|
|
||||||
|
announcementPane = new VBox(16);
|
||||||
|
|
||||||
updatePane = new StackPane();
|
updatePane = new StackPane();
|
||||||
updatePane.setVisible(false);
|
updatePane.setVisible(false);
|
||||||
updatePane.getStyleClass().add("bubble");
|
updatePane.getStyleClass().add("bubble");
|
||||||
@@ -198,7 +202,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
|||||||
launchPane.getChildren().setAll(launchButton, separator, menuButton);
|
launchPane.getChildren().setAll(launchButton, separator, menuButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren().setAll(updatePane, launchPane);
|
getChildren().setAll(announcementPane, updatePane, launchPane);
|
||||||
|
|
||||||
menu.setMaxHeight(365);
|
menu.setMaxHeight(365);
|
||||||
menu.setMaxWidth(545);
|
menu.setMaxWidth(545);
|
||||||
@@ -212,6 +216,14 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
|||||||
Bindings.bindContent(menu.getContent(), versionNodes);
|
Bindings.bindContent(menu.getContent(), versionNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MainPage() {
|
||||||
|
if (Metadata.isNightly()) {
|
||||||
|
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint")));
|
||||||
|
} else if (Metadata.isDev()) {
|
||||||
|
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void doAnimation(boolean show) {
|
private void doAnimation(boolean show) {
|
||||||
Duration duration = Duration.millis(320);
|
Duration duration = Duration.millis(320);
|
||||||
Timeline nowAnimation = new Timeline();
|
Timeline nowAnimation = new Timeline();
|
||||||
|
|||||||
@@ -120,6 +120,19 @@
|
|||||||
-fx-text-fill: rgba(0.0, 0.0, 0.0, 0.87);
|
-fx-text-fill: rgba(0.0, 0.0, 0.0, 0.87);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.announcement {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement .title {
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement JFXHyperlink, .announcement Text {
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.rippler-container {
|
.rippler-container {
|
||||||
-jfx-rippler-fill: #a2a2a2;
|
-jfx-rippler-fill: #a2a2a2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -839,8 +839,20 @@ system.operating_system=OS
|
|||||||
update=Update
|
update=Update
|
||||||
update.accept=Update
|
update.accept=Update
|
||||||
update.changelog=Changes
|
update.changelog=Changes
|
||||||
update.channel.dev=Update to beta version
|
update.channel.dev=Beta
|
||||||
update.channel.stable=Update to stable version
|
update.channel.dev.hint=You are using the beta version, which may include some extra functionalities compared to the release version, only used for testing.\n\
|
||||||
|
Beta version should be unstable!\n\
|
||||||
|
If you met some problems, you can go to <a href="hmcl://settings/feedback">the feedback page</a> to report them, or join chat <a href="https://discord.gg/jVvC7HfM6U">Discord</a> or <a href="https://kaihei.co/Kx7n3t">KaiHeiLa</a> to report problems.\n\n\
|
||||||
|
To hide this hint, you need to <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donate</a> or make impressive contribution in feedback.
|
||||||
|
update.channel.dev.title=Hints for beta version
|
||||||
|
update.channel.dev.update=Update to beta version
|
||||||
|
update.channel.nightly=Alpha
|
||||||
|
update.channel.nightly.hint=You are using the alpha version, which may include some extra functionalities compared to the beta and release version, only used for testing.\n\
|
||||||
|
Alpha version should be unstable!\n\
|
||||||
|
If you met some problems, you can go to <a href="hmcl://settings/feedback">the feedback page</a> to report them, or join chat <a href="https://discord.gg/jVvC7HfM6U">Discord</a> or <a href="https://kaihei.co/Kx7n3t">KaiHeiLa</a> to report problems.\n\n\
|
||||||
|
To hide this hint, you need to <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donate</a> or make impressive contribution in feedback.
|
||||||
|
update.channel.nightly.title=Hints for alpha version
|
||||||
|
update.channel.stable.update=Update to release version
|
||||||
update.checking=Checking for updates
|
update.checking=Checking for updates
|
||||||
update.failed=Failed to perform update
|
update.failed=Failed to perform update
|
||||||
update.found=Update Available!
|
update.found=Update Available!
|
||||||
|
|||||||
@@ -838,8 +838,20 @@ system.operating_system=操作系統
|
|||||||
update=啟動器更新
|
update=啟動器更新
|
||||||
update.accept=更新
|
update.accept=更新
|
||||||
update.changelog=更新日誌
|
update.changelog=更新日誌
|
||||||
update.channel.dev=更新到開發版
|
update.channel.dev=測試版
|
||||||
update.channel.stable=更新到建議版本
|
update.channel.dev.hint=你正在使用測試版。測試版包含一些未在正式版中包含的測試性功能,僅用於體驗新功能。\n\
|
||||||
|
測試版功能未受充分驗證,使用起來可能不穩定!\n\
|
||||||
|
如果你遇到了使用問題,可以在設置的 <a href="hmcl://settings/feedback">回饋頁面</a> 中進行回饋,或加入回饋頁面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kaihei.co/Kx7n3t">開黑啦</a>群以回饋問題。\n\n\
|
||||||
|
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">贊助</a> 或在回饋過程中作出貢獻以隱藏本提示。
|
||||||
|
update.channel.dev.title=測試版提示
|
||||||
|
update.channel.dev.update=更新到開發版
|
||||||
|
update.channel.nightly=預覽版
|
||||||
|
update.channel.nightly.hint=你正在使用預覽版。預覽版可能會每天更新一次,包含一些未在正式版和測試版中包含的測試性功能,僅用於體驗新功能\n\
|
||||||
|
測試版功能未受充分驗證,使用起來可能不穩定!\n\
|
||||||
|
如果你遇到了使用問題,可以在設置的 <a href="hmcl://settings/feedback">回饋頁面</a> 中進行回饋,或加入回饋頁面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kaihei.co/Kx7n3t">開黑啦</a>群以回饋問題。\n\n\
|
||||||
|
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">贊助</a> 或在回饋過程中作出貢獻以隱藏本提示。
|
||||||
|
update.channel.nightly.title=預覽版提示
|
||||||
|
update.channel.stable.update=更新到建議版本
|
||||||
update.checking=正在檢查更新
|
update.checking=正在檢查更新
|
||||||
update.failed=更新失敗
|
update.failed=更新失敗
|
||||||
update.found=發現到更新
|
update.found=發現到更新
|
||||||
|
|||||||
@@ -838,8 +838,20 @@ system.operating_system=操作系统
|
|||||||
update=启动器更新
|
update=启动器更新
|
||||||
update.accept=更新
|
update.accept=更新
|
||||||
update.changelog=更新日志
|
update.changelog=更新日志
|
||||||
update.channel.dev=更新到开发版
|
update.channel.dev=测试版
|
||||||
update.channel.stable=更新到推荐版本
|
update.channel.dev.hint=你正在使用测试版。测试版包含一些未在正式版中包含的测试性功能,仅用于体验新功能。\n\
|
||||||
|
测试版功能未受充分验证,使用起来可能不稳定!\n\
|
||||||
|
如果你遇到了使用问题,可以在设置的 <a href="hmcl://settings/feedback">反馈页面</a> 中进行反馈,或加入反馈页面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kaihei.co/Kx7n3t">开黑啦</a>群以反馈问题。\n\n\
|
||||||
|
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">赞助</a> 或在反馈过程中作出贡献以隐藏本提示。
|
||||||
|
update.channel.dev.title=测试版提示
|
||||||
|
update.channel.dev.update=更新到开发版
|
||||||
|
update.channel.nightly=预览版
|
||||||
|
update.channel.nightly.hint=你正在使用预览版。预览版可能会每天更新一次,包含一些未在正式版和测试版中包含的测试性功能,仅用于体验新功能。\n\
|
||||||
|
测试版功能未受充分验证,使用起来可能不稳定!\n\
|
||||||
|
如果你遇到了使用问题,可以在设置的 <a href="hmcl://settings/feedback">反馈页面</a> 中进行反馈,或加入反馈页面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kaihei.co/Kx7n3t">开黑啦</a>群以反馈问题。\n\n\
|
||||||
|
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">赞助</a> 或在反馈过程中作出贡献以隐藏本提示。
|
||||||
|
update.channel.nightly.title=预览版提示
|
||||||
|
update.channel.stable.update=更新到推荐版本
|
||||||
update.checking=正在检查更新
|
update.checking=正在检查更新
|
||||||
update.failed=更新失败
|
update.failed=更新失败
|
||||||
update.found=发现更新
|
update.found=发现更新
|
||||||
|
|||||||
Reference in New Issue
Block a user