ReportView improvements

This commit is contained in:
f43nd1r 2019-04-23 02:32:45 +02:00
parent 232612a97f
commit 0649a6f402
9 changed files with 125 additions and 60 deletions

View file

@ -18,14 +18,14 @@ plugins {
id 'idea'
id 'war'
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'com.devsoap.vaadin-flow' version '1.0.0.RC8'
id 'com.devsoap.vaadin-flow' version '1.1'
id 'io.spring.dependency-management' version '1.0.7.RELEASE'
id 'cn.bestwu.propdeps' version '0.0.10'
id 'net.researchgate.release' version '2.8.0'
}
vaadin {
version '13.0.0'
version '13.0.4'
submitStatistics false
}
@ -96,7 +96,7 @@ dependencies {
implementation 'org.codeartisans:org.json:20161124'
implementation 'org.apache.commons:commons-text:1.6'
implementation 'org.xbib:time:1.0.0'
implementation 'ch.acra:acra-javacore:5.3.0-rc01'
implementation 'ch.acra:acra-javacore:5.3.0'
implementation 'com.faendir.proguard:retrace:1.3'
implementation 'javax.xml.bind:jaxb-api:2.3.1'
implementation 'com.github.ziplet:ziplet:2.3.0'
@ -113,7 +113,7 @@ dependencies {
compileJava.dependsOn(processResources)
war {
archiveName = 'acra.war'
archiveFileName = 'acra.war'
version = version
enabled = true
}

View file

@ -42,7 +42,11 @@ public class AvatarService {
//@Cacheable(cacheNames = "avatars", key = "#report.installationId")
public Component getAvatar(@NonNull Report report) {
byte[] bytes = avatar.createAsPngBytes(report.getInstallationId().hashCode());
return new Image(new StreamResource("", () -> new ByteArrayInputStream(bytes)), "");
return new Image(getAvatar(report.getInstallationId()), "");
}
public StreamResource getAvatar(@NonNull String installationId) {
byte[] bytes = avatar.createAsPngBytes(installationId.hashCode());
return new StreamResource("", () -> new ByteArrayInputStream(bytes));
}
}

View file

@ -21,9 +21,9 @@ import com.faendir.acra.model.User;
import com.faendir.acra.rest.RestReportInterface;
import com.faendir.acra.ui.component.Label;
import com.faendir.acra.ui.view.Overview;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.i18n.LocaleChangeEvent;
import com.vaadin.flow.i18n.LocaleChangeObserver;
import com.vaadin.flow.router.RouteConfiguration;
import org.springframework.lang.NonNull;
/**
@ -40,6 +40,6 @@ public class ConfigurationLabel extends Label implements LocaleChangeObserver {
@Override
public void localeChange(LocaleChangeEvent event) {
getElement().setProperty("innerHTML", getTranslation(Messages.CONFIGURATION_LABEL, UI.getCurrent().getRouter().getUrl(Overview.class), RestReportInterface.REPORT_PATH, user.getUsername(), user.getPlainTextPassword()));
getElement().setProperty("innerHTML", getTranslation(Messages.CONFIGURATION_LABEL, RouteConfiguration.forSessionScope().getUrl(Overview.class), RestReportInterface.REPORT_PATH, user.getUsername(), user.getPlainTextPassword()));
}
}

View file

@ -31,6 +31,7 @@ import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.i18n.LocaleChangeEvent;
import com.vaadin.flow.i18n.LocaleChangeObserver;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.RouteConfiguration;
import com.vaadin.flow.shared.Registration;
import org.springframework.data.util.Pair;
import org.springframework.lang.NonNull;
@ -102,7 +103,7 @@ public class MyGrid<T> extends Composite<Grid<T>> implements LocaleChangeObserve
}
public <C extends Component & HasUrlParameter<R>, R> void addOnClickNavigation(Class<C> target, Function<T, R> parameterTransformer) {
getContent().addItemClickListener(e -> getUI().ifPresent(e.getButton() == 1 ? (ui -> ui.getPage().executeJavaScript("window.open(\"" + ui.getRouter().getUrl(target, parameterTransformer.apply(e.getItem())) + "\", \"blank\", \"\");")) : (ui -> ui.navigate(target, parameterTransformer.apply(e.getItem())))));
getContent().addItemClickListener(e -> getUI().ifPresent(e.getButton() == 1 ? (ui -> ui.getPage().executeJavaScript("window.open(\"" + RouteConfiguration.forSessionScope().getUrl(target, parameterTransformer.apply(e.getItem())) + "\", \"blank\", \"\");")) : (ui -> ui.navigate(target, parameterTransformer.apply(e.getItem())))));
}
public Registration addSelectionListener(SelectionListener<Grid<T>, T> listener) {

View file

@ -0,0 +1,45 @@
/*
* (C) Copyright 2019 Lukas Morawietz (https://github.com/F43nd1r)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.faendir.acra.ui.component;
import com.faendir.acra.service.AvatarService;
import com.vaadin.flow.component.html.Image;
import org.springframework.lang.NonNull;
/**
* @author lukas
* @since 23.04.19
*/
public class InstallationView extends FlexLayout {
private final Image image;
private final Label label;
private final AvatarService avatarService;
public InstallationView(@NonNull AvatarService avatarService) {
this.avatarService = avatarService;
image = new Image();
label = new Label();
add(image, label);
label.setPaddingLeft(5, Unit.PIXEL);
setAlignItems(Alignment.CENTER);
}
public void setInstallationId(String installationId) {
image.setSrc(avatarService.getAvatar(installationId));
label.setText(installationId);
}
}

View file

@ -20,7 +20,7 @@ package com.faendir.acra.ui.component;
* @author lukas
* @since 19.11.18
*/
public class Label extends com.vaadin.flow.component.html.Label {
public class Label extends com.vaadin.flow.component.html.Label implements HasStyle {
public Label() {
}

View file

@ -25,20 +25,17 @@ import com.faendir.acra.ui.base.HasSecureStringParameter;
import com.faendir.acra.ui.base.Path;
import com.faendir.acra.ui.component.Card;
import com.faendir.acra.ui.component.CssGrid;
import com.faendir.acra.ui.component.FlexLayout;
import com.faendir.acra.ui.component.HasSize;
import com.faendir.acra.ui.component.InstallationView;
import com.faendir.acra.ui.component.Label;
import com.faendir.acra.ui.component.Translatable;
import com.faendir.acra.ui.view.MainView;
import com.faendir.acra.ui.view.bug.tabs.ReportTab;
import com.faendir.acra.util.Utils;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Composite;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.InputStreamFactory;
@ -47,11 +44,13 @@ import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.annotation.UIScope;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.xbib.time.pretty.PrettyTime;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@ -64,14 +63,46 @@ import java.util.Optional;
@Route(value = "report", layout = MainView.class)
public class ReportView extends Composite<Div> implements HasSecureStringParameter, HasRoute {
private final DataService dataService;
private final AvatarService avatarService;
private final Label version;
private final Label date;
private final InstallationView installation;
private final Label email;
private final Label comment;
private final Translatable<Label> mappedStacktraceLabel;
private final Translatable<Label> unmappedStacktraceLabel;
private final Label stacktrace;
private final Div attachments;
private final Card details;
private Report report;
private PrettyTime prettyTime;
@Autowired
public ReportView(@NonNull DataService dataService, @NonNull AvatarService avatarService) {
this.dataService = dataService;
this.avatarService = avatarService;
prettyTime = new PrettyTime(Locale.US);
getElement().getStyle().set("overflow", "auto");
CssGrid summaryLayout = new CssGrid();
summaryLayout.setTemplateColumns("auto auto");
summaryLayout.setColumnGap(1, HasSize.Unit.EM);
summaryLayout.setJustifyItems(CssGrid.JustifyMode.START);
summaryLayout.setAlignItems(CssGrid.AlignMode.FIRST_BASELINE);
Translatable<Label> installationLabel = Translatable.createLabel(Messages.USER).with(Label::secondary);
version = new Label();
date = new Label();
installation = new InstallationView(avatarService);
email = new Label();
comment = new Label();
mappedStacktraceLabel = Translatable.createLabel(Messages.DE_OBFUSCATED_STACKTRACE).with(Label::secondary);
unmappedStacktraceLabel = Translatable.createLabel(Messages.NO_MAPPING_STACKTRACE).with(Label::secondary);
stacktrace = new Label().honorWhitespaces();
attachments = new Div();
summaryLayout.add(Translatable.createLabel(Messages.VERSION).with(Label::secondary), version, Translatable.createLabel(Messages.DATE).with(Label::secondary), date, installationLabel, installation, Translatable.createLabel(Messages.EMAIL).with(Label::secondary), email, Translatable.createLabel(Messages.COMMENT).with(Label::secondary), comment, mappedStacktraceLabel, unmappedStacktraceLabel, stacktrace, Translatable.createLabel(Messages.ATTACHMENTS).with(Label::secondary));
summaryLayout.alignItems(CssGrid.AlignMode.CENTER, installationLabel);
Card summary = new Card(summaryLayout);
summary.setHeader(Translatable.createText(Messages.SUMMARY));
details = new Card();
details.setHeader(Translatable.createText(Messages.DETAILS));
getContent().add(summary, details);
}
@Override
@ -79,52 +110,34 @@ public class ReportView extends Composite<Div> implements HasSecureStringParamet
Optional<Report> r = dataService.findReport(parameter);
if (r.isPresent()) {
report = r.get();
version.setText(report.getStacktrace().getVersion().getName());
date.setText(prettyTime.formatUnrounded(report.getDate().toLocalDateTime()));
installation.setInstallationId(report.getInstallationId());
email.setText(report.getUserEmail());
comment.setText(report.getUserComment());
Optional<String> mapping = Optional.ofNullable(report.getStacktrace().getVersion().getMappings());
stacktrace.setText(mapping.map(m -> Utils.retrace(report.getStacktrace().getStacktrace(), m)).orElse(report.getStacktrace().getStacktrace()));
mappedStacktraceLabel.getStyle().set("display", mapping.isPresent() ? null : "none");
unmappedStacktraceLabel.getStyle().set("display", mapping.isPresent() ? "none" : null);
attachments.removeAll();
attachments.add(dataService.findAttachments(report).stream().map(attachment -> {
Anchor anchor = new Anchor(new StreamResource(attachment.getFilename(), (InputStreamFactory) () -> {
try {
return attachment.getContent().getBinaryStream();
} catch (SQLException e) {
throw new RuntimeException(e); //TODO
}
}), attachment.getFilename());
anchor.getElement().setAttribute("download", true);
return anchor;
}).toArray(Component[]::new));
details.removeAll();
details.add(getLayoutForMap(report.getJsonObject().toMap()));
} else {
event.rerouteToError(IllegalArgumentException.class);
}
}
@Override
protected void onAttach(AttachEvent attachEvent) {
super.onAttach(attachEvent);
getContent().removeAll();
CssGrid summaryLayout = new CssGrid();
summaryLayout.setTemplateColumns("auto auto");
summaryLayout.setColumnGap(1, HasSize.Unit.EM);
summaryLayout.setJustifyItems(CssGrid.JustifyMode.START);
summaryLayout.setAlignItems(CssGrid.AlignMode.FIRST_BASELINE);
summaryLayout.add(Translatable.createLabel(Messages.VERSION).with(Label::secondary), new Label(report.getStacktrace().getVersion().getName()));
FlexLayout userLayout = new FlexLayout(avatarService.getAvatar(report), new Text(report.getInstallationId()));
userLayout.setAlignItems(FlexComponent.Alignment.CENTER);
Translatable<Label> userLabel = Translatable.createLabel(Messages.USER).with(Label::secondary);
summaryLayout.alignItems(CssGrid.AlignMode.CENTER, userLabel);
summaryLayout.add(userLabel, userLayout);
summaryLayout.add(Translatable.createLabel(Messages.EMAIL).with(Label::secondary), new Label(report.getUserEmail()));
summaryLayout.add(Translatable.createLabel(Messages.COMMENT).with(Label::secondary), new Label(report.getUserComment()));
Optional<String> mapping = Optional.ofNullable(report.getStacktrace().getVersion().getMappings());
Label stacktrace = new Label(mapping.map(m -> Utils.retrace(report.getStacktrace().getStacktrace(), m)).orElse(report.getStacktrace().getStacktrace()));
stacktrace.honorWhitespaces();
summaryLayout.add(Translatable.createLabel(mapping.isPresent() ? Messages.DE_OBFUSCATED_STACKTRACE : Messages.NO_MAPPING_STACKTRACE).with(Label::secondary), stacktrace);
summaryLayout.add(Translatable.createLabel(Messages.ATTACHMENTS).with(Label::secondary), new Div(dataService.findAttachments(report).stream().map(attachment -> {
Anchor anchor = new Anchor(new StreamResource(attachment.getFilename(), (InputStreamFactory) () -> {
try {
return attachment.getContent().getBinaryStream();
} catch (SQLException e) {
throw new RuntimeException(e); //TODO
}
}), attachment.getFilename());
anchor.getElement().setAttribute("download", true);
return anchor;
}).toArray(Component[]::new)));
Card summaryCard = new Card(summaryLayout);
summaryCard.setHeader(Translatable.createText(Messages.SUMMARY));
getContent().add(summaryCard);
Card detailCard = new Card(getLayoutForMap(report.getJsonObject().toMap()));
detailCard.setHeader(Translatable.createText(Messages.DETAILS));
getContent().add(detailCard);
}
@NonNull
private Div getLayoutForMap(@NonNull Map<String, ?> map) {
CssGrid layout = new CssGrid();
@ -159,7 +172,7 @@ public class ReportView extends Composite<Div> implements HasSecureStringParamet
@NonNull
@Override
public Path.Element<?> getPathElement() {
return new Path.ParametrizedTextElement<>(getClass(), report.getId(), Messages.ONE_ARG, report.getId());
return new Path.ParametrizedTextElement<>(getClass(), report.getId(), Messages.REPORT_FROM, prettyTime.formatUnrounded(report.getDate().toLocalDateTime()));
}
@Override

View file

@ -157,4 +157,5 @@ passwordRequired=Ein Passwort muss gesetzt werden
welcome=Willkommen zu
createAdmin=Zuerst müssen Sie einen Adminstrator anlegen:
notSolved=Nicht gelöst
versionName=Versionsname
versionName=Versionsname
reportFrom=Bericht vom %s

View file

@ -157,4 +157,5 @@ passwordRequired=A password is required
welcome=Welcome to
createAdmin=First, you have to create an administrator:
notSolved=Not solved
versionName=Version name
versionName=Version name
reportFrom=Report from %s