package net.minecraft.client.gui.screens.reporting; import com.mojang.authlib.minecraft.report.AbuseReportLimits; import com.mojang.logging.LogUtils; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; import net.minecraft.Optionull; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Checkbox; import net.minecraft.client.gui.components.MultiLineEditBox; import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.layouts.FrameLayout; import net.minecraft.client.gui.layouts.Layout; import net.minecraft.client.gui.layouts.LinearLayout; import net.minecraft.client.gui.screens.GenericWaitingScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.multiplayer.WarningScreen; import net.minecraft.client.multiplayer.chat.report.ReportingContext; import net.minecraft.client.multiplayer.chat.report.Report.Builder; import net.minecraft.client.multiplayer.chat.report.Report.CannotBuildReason; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ThrowingComponent; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public abstract class AbstractReportScreen> extends Screen { private static final Component REPORT_SENT_MESSAGE = Component.translatable("gui.abuseReport.report_sent_msg"); private static final Component REPORT_SENDING_TITLE = Component.translatable("gui.abuseReport.sending.title").withStyle(ChatFormatting.BOLD); private static final Component REPORT_SENT_TITLE = Component.translatable("gui.abuseReport.sent.title").withStyle(ChatFormatting.BOLD); private static final Component REPORT_ERROR_TITLE = Component.translatable("gui.abuseReport.error.title").withStyle(ChatFormatting.BOLD); private static final Component REPORT_SEND_GENERIC_ERROR = Component.translatable("gui.abuseReport.send.generic_error"); protected static final Component SEND_REPORT = Component.translatable("gui.abuseReport.send"); protected static final Component OBSERVED_WHAT_LABEL = Component.translatable("gui.abuseReport.observed_what"); protected static final Component SELECT_REASON = Component.translatable("gui.abuseReport.select_reason"); private static final Component DESCRIBE_PLACEHOLDER = Component.translatable("gui.abuseReport.describe"); protected static final Component MORE_COMMENTS_LABEL = Component.translatable("gui.abuseReport.more_comments"); private static final Component MORE_COMMENTS_NARRATION = Component.translatable("gui.abuseReport.comments"); private static final Component ATTESTATION_CHECKBOX = Component.translatable("gui.abuseReport.attestation"); protected static final int BUTTON_WIDTH = 120; protected static final int MARGIN = 20; protected static final int SCREEN_WIDTH = 280; protected static final int SPACING = 8; private static final Logger LOGGER = LogUtils.getLogger(); protected final Screen lastScreen; protected final ReportingContext reportingContext; protected final LinearLayout layout = LinearLayout.vertical().spacing(8); protected B reportBuilder; private Checkbox attestation; protected Button sendButton; protected AbstractReportScreen(Component title, Screen lastScreen, ReportingContext reportingContext, B reportBuilder) { super(title); this.lastScreen = lastScreen; this.reportingContext = reportingContext; this.reportBuilder = reportBuilder; } protected MultiLineEditBox createCommentBox(int width, int height, Consumer valueListener) { AbuseReportLimits abuseReportLimits = this.reportingContext.sender().reportLimits(); MultiLineEditBox multiLineEditBox = new MultiLineEditBox(this.font, 0, 0, width, height, DESCRIBE_PLACEHOLDER, MORE_COMMENTS_NARRATION); multiLineEditBox.setValue(this.reportBuilder.comments()); multiLineEditBox.setCharacterLimit(abuseReportLimits.maxOpinionCommentsLength()); multiLineEditBox.setValueListener(valueListener); return multiLineEditBox; } @Override protected void init() { this.layout.defaultCellSetting().alignHorizontallyCenter(); this.createHeader(); this.addContent(); this.createFooter(); this.onReportChanged(); this.layout.visitWidgets(guiEventListener -> { AbstractWidget var10000 = this.addRenderableWidget(guiEventListener); }); this.repositionElements(); } protected void createHeader() { this.layout.addChild(new StringWidget(this.title, this.font)); } protected abstract void addContent(); protected void createFooter() { this.attestation = this.layout .addChild(Checkbox.builder(ATTESTATION_CHECKBOX, this.font).selected(this.reportBuilder.attested()).maxWidth(280).onValueChange((checkbox, bl) -> { this.reportBuilder.setAttested(bl); this.onReportChanged(); }).build()); LinearLayout linearLayout = this.layout.addChild(LinearLayout.horizontal().spacing(8)); linearLayout.addChild(Button.builder(CommonComponents.GUI_BACK, button -> this.onClose()).width(120).build()); this.sendButton = linearLayout.addChild(Button.builder(SEND_REPORT, button -> this.sendReport()).width(120).build()); } protected void onReportChanged() { CannotBuildReason cannotBuildReason = this.reportBuilder.checkBuildable(); this.sendButton.active = cannotBuildReason == null && this.attestation.selected(); this.sendButton.setTooltip(Optionull.map(cannotBuildReason, CannotBuildReason::tooltip)); } @Override protected void repositionElements() { this.layout.arrangeElements(); FrameLayout.centerInRectangle(this.layout, this.getRectangle()); } protected void sendReport() { this.reportBuilder.build(this.reportingContext).ifLeft(result -> { CompletableFuture completableFuture = this.reportingContext.sender().send(result.id(), result.reportType(), result.report()); this.minecraft.setScreen(GenericWaitingScreen.createWaiting(REPORT_SENDING_TITLE, CommonComponents.GUI_CANCEL, () -> { this.minecraft.setScreen(this); completableFuture.cancel(true); })); completableFuture.handleAsync((object, throwable) -> { if (throwable == null) { this.onReportSendSuccess(); } else { if (throwable instanceof CancellationException) { return null; } this.onReportSendError(throwable); } return null; }, this.minecraft); }).ifRight(cannotBuildReason -> this.displayReportSendError(cannotBuildReason.message())); } private void onReportSendSuccess() { this.clearDraft(); this.minecraft .setScreen(GenericWaitingScreen.createCompleted(REPORT_SENT_TITLE, REPORT_SENT_MESSAGE, CommonComponents.GUI_DONE, () -> this.minecraft.setScreen(null))); } private void onReportSendError(Throwable throwable) { LOGGER.error("Encountered error while sending abuse report", throwable); Component component; if (throwable.getCause() instanceof ThrowingComponent throwingComponent) { component = throwingComponent.getComponent(); } else { component = REPORT_SEND_GENERIC_ERROR; } this.displayReportSendError(component); } private void displayReportSendError(Component error) { Component component = error.copy().withStyle(ChatFormatting.RED); this.minecraft .setScreen(GenericWaitingScreen.createCompleted(REPORT_ERROR_TITLE, component, CommonComponents.GUI_BACK, () -> this.minecraft.setScreen(this))); } void saveDraft() { if (this.reportBuilder.hasContent()) { this.reportingContext.setReportDraft(this.reportBuilder.report().copy()); } } void clearDraft() { this.reportingContext.setReportDraft(null); } @Override public void onClose() { if (this.reportBuilder.hasContent()) { this.minecraft.setScreen(new AbstractReportScreen.DiscardReportWarningScreen()); } else { this.minecraft.setScreen(this.lastScreen); } } @Override public void removed() { this.saveDraft(); super.removed(); } @Environment(EnvType.CLIENT) class DiscardReportWarningScreen extends WarningScreen { private static final Component TITLE = Component.translatable("gui.abuseReport.discard.title").withStyle(ChatFormatting.BOLD); private static final Component MESSAGE = Component.translatable("gui.abuseReport.discard.content"); private static final Component RETURN = Component.translatable("gui.abuseReport.discard.return"); private static final Component DRAFT = Component.translatable("gui.abuseReport.discard.draft"); private static final Component DISCARD = Component.translatable("gui.abuseReport.discard.discard"); protected DiscardReportWarningScreen() { super(TITLE, MESSAGE, MESSAGE); } @Override protected Layout addFooterButtons() { LinearLayout linearLayout = LinearLayout.vertical().spacing(8); linearLayout.defaultCellSetting().alignHorizontallyCenter(); LinearLayout linearLayout2 = linearLayout.addChild(LinearLayout.horizontal().spacing(8)); linearLayout2.addChild(Button.builder(RETURN, button -> this.onClose()).build()); linearLayout2.addChild(Button.builder(DRAFT, button -> { AbstractReportScreen.this.saveDraft(); this.minecraft.setScreen(AbstractReportScreen.this.lastScreen); }).build()); linearLayout.addChild(Button.builder(DISCARD, button -> { AbstractReportScreen.this.clearDraft(); this.minecraft.setScreen(AbstractReportScreen.this.lastScreen); }).build()); return linearLayout; } @Override public void onClose() { this.minecraft.setScreen(AbstractReportScreen.this); } @Override public boolean shouldCloseOnEsc() { return false; } } }