mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-10 22:30:56 +00:00
Worked on the regattaoverview settings dialog
This commit is contained in:
1 parent
b29027fd75
commit
2f5af22dd3
9 files changed
+225
-35
No files matched your search
+6
@@ -638,4 +638,10 @@ public interface StringMessages extends Messages {
|
||||
|
||||
String courseAreaOverview();
|
||||
String courseDesignOverview();
|
||||
String regattaOverview();
|
||||
String additionalSettings();
|
||||
String showFollowingCourseAreas();
|
||||
String showFollowingRegattas();
|
||||
String showOnlyRacesOfSameDay();
|
||||
String showOnlyCurrentlyRunningRaces();
|
||||
}
|
||||
+6
@@ -632,3 +632,9 @@ freemode=Keep Bearings
|
||||
whatIfCourse=What-If Course
|
||||
courseAreaOverview=Area overview
|
||||
courseDesignOverview=Course design overview
|
||||
regattaOverview=Regatta overview
|
||||
additionalSettings=Additional settings
|
||||
showFollowingCourseAreas=Show following course areas
|
||||
showFollowingRegattas=Show following regattas
|
||||
showOnlyRacesOfSameDay=Show only races of same day
|
||||
showOnlyCurrentlyRunningRaces=Show currently running races
|
||||
+6
@@ -631,3 +631,9 @@ freemode=Winkel erhalten
|
||||
whatIfCourse=What-If Course
|
||||
courseAreaOverview=Rennbahnübersicht
|
||||
courseDesignOverview=Kursübersicht
|
||||
regattaOverview=Rennbahnenübersicht
|
||||
additionalSettings=Zusätzliche Einstellungen
|
||||
showFollowingCourseAreas=Zeige folgende Rennbahnen
|
||||
showFollowingRegattas=Zeige folgende Regatten
|
||||
showOnlyRacesOfSameDay=Zeige nur Rennen desselben Tags
|
||||
showOnlyCurrentlyRunningRaces=Zeige nur gerade laufende Rennen
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.sap.sailing.gwt.ui.regattaoverview;
|
||||
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
|
||||
public interface EventProvider {
|
||||
|
||||
EventDTO getEvent();
|
||||
|
||||
}
|
||||
+1
-1
@@ -16,7 +16,7 @@ public class RegattaOverviewEntryPoint extends AbstractEntryPoint {
|
||||
boolean embedded = Window.Location.getParameter("embedded") != null
|
||||
&& Window.Location.getParameter("embedded").equalsIgnoreCase("true");
|
||||
if (!embedded) {
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel("Regatta Overview", stringMessages, this);
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages.regattaOverview(), stringMessages, this);
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
rootPanel.add(logoAndTitlePanel);
|
||||
} else {
|
||||
|
||||
+34
-7
@@ -27,7 +27,7 @@ import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialog;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO;
|
||||
|
||||
public class RegattaOverviewPanel extends SimplePanel implements RegattaOverviewRaceSelectionChangeListener {
|
||||
public class RegattaOverviewPanel extends SimplePanel implements RegattaOverviewRaceSelectionChangeListener, EventProvider {
|
||||
|
||||
private final long serverUpdateRateInMs = 10000;
|
||||
private final long uiUpdateRateInMs = 1000;
|
||||
@@ -39,6 +39,7 @@ public class RegattaOverviewPanel extends SimplePanel implements RegattaOverview
|
||||
protected final StringMessages stringMessages;
|
||||
|
||||
private final String eventIdAsString;
|
||||
private EventDTO eventDTO;
|
||||
|
||||
private RegattaOverviewRaceSelectionModel raceSelectionProvider;
|
||||
|
||||
@@ -69,6 +70,9 @@ public class RegattaOverviewPanel extends SimplePanel implements RegattaOverview
|
||||
this.stringMessages = stringMessages;
|
||||
this.eventIdAsString = eventIdAsString;
|
||||
|
||||
this.eventDTO = null;
|
||||
retrieveEvent();
|
||||
|
||||
VerticalPanel mainPanel = new VerticalPanel();
|
||||
setWidget(mainPanel);
|
||||
mainPanel.setWidth("100%");
|
||||
@@ -89,7 +93,8 @@ public class RegattaOverviewPanel extends SimplePanel implements RegattaOverview
|
||||
raceSelectionProvider = new RegattaOverviewRaceSelectionModel(false);
|
||||
raceSelectionProvider.addRegattaOverviewRaceSelectionChangeListener(this);
|
||||
|
||||
regattaRaceStatesComponent = new RegattaRaceStatesComponent(sailingService, errorReporter, stringMessages, eventIdAsString, raceSelectionProvider);
|
||||
regattaRaceStatesComponent = new RegattaRaceStatesComponent(sailingService, errorReporter, stringMessages, eventIdAsString,
|
||||
raceSelectionProvider, this);
|
||||
grid.setWidget(1, 0, regattaRaceStatesComponent);
|
||||
|
||||
raceCourseComposite = new RaceCourseComposite(sailingService, errorReporter, stringMessages);
|
||||
@@ -130,6 +135,7 @@ public class RegattaOverviewPanel extends SimplePanel implements RegattaOverview
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
//new RegattaRaceStatesSettingsDialogComponent(regattaRaceStatesComponent, stringMessages, null).show();
|
||||
new SettingsDialog<RegattaRaceStatesSettings>(regattaRaceStatesComponent, stringMessages).show();
|
||||
}
|
||||
});
|
||||
@@ -204,7 +210,6 @@ public class RegattaOverviewPanel extends SimplePanel implements RegattaOverview
|
||||
mainPanel.add(flexTable);
|
||||
mainPanel.add(grid);
|
||||
|
||||
fillEventAndVenueName();
|
||||
onUpdateUI(new Date());
|
||||
}
|
||||
|
||||
@@ -244,22 +249,44 @@ public class RegattaOverviewPanel extends SimplePanel implements RegattaOverview
|
||||
}
|
||||
}
|
||||
|
||||
private void fillEventAndVenueName() {
|
||||
private void retrieveEvent() {
|
||||
sailingService.getEventByIdAsString(eventIdAsString, new MarkedAsyncCallback<EventDTO>() {
|
||||
|
||||
@Override
|
||||
protected void handleFailure(Throwable cause) {
|
||||
|
||||
//Show a (friendly) error message
|
||||
settingsButton.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleSuccess(EventDTO result) {
|
||||
if (result != null) {
|
||||
eventNameLabel.setText(result.name);
|
||||
venueNameLabel.setText(result.venue.name);
|
||||
setEvent(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void fillEventAndVenueName() {
|
||||
eventNameLabel.setText(eventDTO.name);
|
||||
venueNameLabel.setText(eventDTO.venue.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventDTO getEvent() {
|
||||
return eventDTO;
|
||||
}
|
||||
|
||||
protected void setEvent(EventDTO event) {
|
||||
eventDTO = event;
|
||||
onEventUpdated();
|
||||
}
|
||||
|
||||
private void onEventUpdated() {
|
||||
fillEventAndVenueName();
|
||||
settingsButton.setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+20
-5
@@ -50,6 +50,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
private final StringMessages stringMessages;
|
||||
private final String eventIdAsString;
|
||||
private final RegattaOverviewRaceSelectionProvider raceSelectionProvider;
|
||||
private final EventProvider eventProvider;
|
||||
|
||||
private final FlagImageResolver flagImageResolver;
|
||||
|
||||
@@ -58,14 +59,16 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
private static RegattaRaceStatesTableResources tableRes = GWT.create(RegattaRaceStatesTableResources.class);
|
||||
|
||||
public RegattaRaceStatesComponent(final SailingServiceAsync sailingService, ErrorReporter errorReporter,
|
||||
final StringMessages stringMessages, final String eventIdAsString, final RegattaOverviewRaceSelectionProvider raceSelectionProvider) {
|
||||
final StringMessages stringMessages, final String eventIdAsString, final RegattaOverviewRaceSelectionProvider raceSelectionProvider,
|
||||
final EventProvider eventProvider) {
|
||||
this.sailingService = sailingService;
|
||||
this.stringMessages = stringMessages;
|
||||
this.eventIdAsString = eventIdAsString;
|
||||
this.raceSelectionProvider = raceSelectionProvider;
|
||||
this.eventProvider = eventProvider;
|
||||
this.flagImageResolver = new FlagImageResolver();
|
||||
|
||||
settings = new RegattaRaceStatesSettings();
|
||||
settings = new RegattaRaceStatesSettings();//TODO initialize settings properly
|
||||
|
||||
mainPanel = new VerticalPanel();
|
||||
setWidth("100%");
|
||||
@@ -388,7 +391,17 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
public List<RegattaOverviewEntryDTO> getAllRaces() {
|
||||
return allEntries;
|
||||
}
|
||||
|
||||
|
||||
public List<String> getRegattaNames() {
|
||||
List<String> regattaNames = new ArrayList<String>();
|
||||
for (RegattaOverviewEntryDTO entry : allEntries) {
|
||||
if (!regattaNames.contains(entry.regattaName)) {
|
||||
regattaNames.add(entry.regattaName);
|
||||
}
|
||||
}
|
||||
return regattaNames;
|
||||
}
|
||||
|
||||
private void showSelectedRaces() {
|
||||
List<RegattaOverviewEntryDTO> selectedRaces = getSelectedRaces();
|
||||
RegattaRaceStatesComponent.this.raceSelectionProvider.setSelection(selectedRaces);
|
||||
@@ -433,7 +446,9 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
|
||||
@Override
|
||||
public SettingsDialogComponent<RegattaRaceStatesSettings> getSettingsDialogComponent() {
|
||||
return new RegattaRaceStatesSettingsDialogComponent(settings, stringMessages);
|
||||
return new RegattaRaceStatesSettingsDialogComponent(settings, stringMessages,
|
||||
Collections.unmodifiableList(eventProvider.getEvent().venue.getCourseAreas()),
|
||||
Collections.unmodifiableList(getRegattaNames()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -444,7 +459,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
|
||||
@Override
|
||||
public String getLocalizedShortName() {
|
||||
return "Regatta Overview";
|
||||
return stringMessages.regattaOverview();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-11
@@ -3,32 +3,26 @@ package com.sap.sailing.gwt.ui.regattaoverview;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
|
||||
|
||||
public class RegattaRaceStatesSettings {
|
||||
private List<RaceLogRaceStatus> visibleRaceStates;
|
||||
private List<String> visibleCourseAreas;
|
||||
private List<String> visibleRegattas;
|
||||
|
||||
private boolean showOnlyRacesOfSameDay;
|
||||
private boolean showOnlyCurrentlyRunningRaces;
|
||||
|
||||
public RegattaRaceStatesSettings() {
|
||||
this.visibleRaceStates = new ArrayList<RaceLogRaceStatus>();
|
||||
this.visibleCourseAreas = new ArrayList<String>();
|
||||
this.visibleRegattas = new ArrayList<String>();
|
||||
this.showOnlyRacesOfSameDay = false;
|
||||
this.showOnlyCurrentlyRunningRaces = false;
|
||||
}
|
||||
|
||||
public RegattaRaceStatesSettings(List<RaceLogRaceStatus> visibleRaceStates, List<String> visibleCourseAreas,
|
||||
List<String> visibleRegattas, boolean showOnlyRacesOfSameDay) {
|
||||
this.visibleRaceStates = visibleRaceStates;
|
||||
public RegattaRaceStatesSettings(List<String> visibleCourseAreas, List<String> visibleRegattas,
|
||||
boolean showOnlyRacesOfSameDay, boolean showOnlyCurrentlyRunningRaces) {
|
||||
this.visibleCourseAreas = visibleCourseAreas;
|
||||
this.visibleRegattas = visibleRegattas;
|
||||
this.showOnlyRacesOfSameDay = showOnlyRacesOfSameDay;
|
||||
}
|
||||
|
||||
public List<RaceLogRaceStatus> getVisibleRaceStates() {
|
||||
return visibleRaceStates;
|
||||
this.showOnlyCurrentlyRunningRaces = showOnlyCurrentlyRunningRaces;
|
||||
}
|
||||
|
||||
public List<String> getVisibleCourseAreas() {
|
||||
@@ -42,4 +36,8 @@ public class RegattaRaceStatesSettings {
|
||||
public boolean isShowOnlyRacesOfSameDay() {
|
||||
return showOnlyRacesOfSameDay;
|
||||
}
|
||||
|
||||
public boolean isShowOnlyCurrentlyRunningRaces() {
|
||||
return showOnlyCurrentlyRunningRaces;
|
||||
}
|
||||
}
|
||||
+134
-11
@@ -1,44 +1,167 @@
|
||||
package com.sap.sailing.gwt.ui.regattaoverview;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
|
||||
import com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialogComponent;
|
||||
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
|
||||
|
||||
public class RegattaRaceStatesSettingsDialogComponent implements SettingsDialogComponent<RegattaRaceStatesSettings> {
|
||||
|
||||
private final StringMessages stringMessages;
|
||||
private final RegattaRaceStatesSettings initialSettings;
|
||||
private final List<CourseAreaDTO> courseAreas;
|
||||
private final List<String> regattaNames;
|
||||
|
||||
private TextBox test;
|
||||
private CheckBox showOnlyRacesOfSameDayCheckBox;
|
||||
private CheckBox showOnlyCurrentlyRunningRacesCheckBox;
|
||||
private final Map<String, CheckBox> courseAreaCheckBoxMap;
|
||||
private final Map<String, CheckBox> regattaCheckBoxMap;
|
||||
|
||||
public RegattaRaceStatesSettingsDialogComponent(RegattaRaceStatesSettings settings, StringMessages stringMessages) {
|
||||
public RegattaRaceStatesSettingsDialogComponent(RegattaRaceStatesSettings settings, StringMessages stringMessages,
|
||||
List<CourseAreaDTO> courseAreas, List<String> regattaNames) {
|
||||
this.stringMessages = stringMessages;
|
||||
this.initialSettings = settings;
|
||||
this.courseAreas = courseAreas;
|
||||
this.regattaNames = regattaNames;
|
||||
this.courseAreaCheckBoxMap = new HashMap<String, CheckBox>();
|
||||
this.regattaCheckBoxMap = new HashMap<String, CheckBox>();
|
||||
}
|
||||
|
||||
private FlowPanel fillCourseAreaWidget(DataEntryDialog<?> dialog) {
|
||||
FlowPanel flowPanel = new FlowPanel();
|
||||
flowPanel.addStyleName("SettingsDialogComponent");
|
||||
flowPanel.add(dialog.createHeadline(stringMessages.showFollowingCourseAreas(), true));
|
||||
FlowPanel courseAreaPanel = new FlowPanel();
|
||||
flowPanel.add(courseAreaPanel);
|
||||
|
||||
int maxCourseAreasPerRow = 4;
|
||||
int numberOfCourseAreas = courseAreas.size();
|
||||
int numberOfRequiredRows = numberOfCourseAreas / maxCourseAreasPerRow;
|
||||
if (numberOfCourseAreas % maxCourseAreasPerRow != 0) {
|
||||
numberOfRequiredRows++;
|
||||
}
|
||||
int rowIndex = 0;
|
||||
int columnIndex = 0;
|
||||
|
||||
Grid courseAreaGrid = new Grid(numberOfRequiredRows, maxCourseAreasPerRow);
|
||||
courseAreaPanel.add(courseAreaGrid);
|
||||
|
||||
for (CourseAreaDTO courseAreaDTO : courseAreas) {
|
||||
CheckBox checkBox = dialog.createCheckbox(courseAreaDTO.name);
|
||||
checkBox.setValue(Util.contains(initialSettings.getVisibleCourseAreas(), courseAreaDTO.name));
|
||||
courseAreaCheckBoxMap.put(courseAreaDTO.id, checkBox);
|
||||
|
||||
courseAreaGrid.setWidget(rowIndex, columnIndex++, checkBox);
|
||||
if(columnIndex == maxCourseAreasPerRow) {
|
||||
rowIndex++;
|
||||
columnIndex = 0;
|
||||
}
|
||||
}
|
||||
return flowPanel;
|
||||
}
|
||||
|
||||
private FlowPanel fillRegattaNamesWidget(DataEntryDialog<?> dialog) {
|
||||
FlowPanel flowPanel = new FlowPanel();
|
||||
flowPanel.addStyleName("SettingsDialogComponent");
|
||||
flowPanel.add(dialog.createHeadline(stringMessages.showFollowingRegattas(), true));
|
||||
FlowPanel regattaNamesPanel = new FlowPanel();
|
||||
flowPanel.add(regattaNamesPanel);
|
||||
|
||||
int maxRegattasPerRow = 4;
|
||||
int numberOfRegattas = regattaNames.size();
|
||||
int numberOfRequiredRows = numberOfRegattas / maxRegattasPerRow;
|
||||
if (numberOfRegattas % maxRegattasPerRow != 0) {
|
||||
numberOfRequiredRows++;
|
||||
}
|
||||
int rowIndex = 0;
|
||||
int columnIndex = 0;
|
||||
|
||||
Grid regattaGrid = new Grid(numberOfRequiredRows, maxRegattasPerRow);
|
||||
regattaNamesPanel.add(regattaGrid);
|
||||
|
||||
for (String regattaName : regattaNames) {
|
||||
CheckBox checkBox = dialog.createCheckbox(regattaName);
|
||||
checkBox.setValue(Util.contains(initialSettings.getVisibleRegattas(), regattaName));
|
||||
regattaCheckBoxMap.put(regattaName, checkBox);
|
||||
|
||||
regattaGrid.setWidget(rowIndex, columnIndex++, checkBox);
|
||||
if(columnIndex == maxRegattasPerRow) {
|
||||
rowIndex++;
|
||||
columnIndex = 0;
|
||||
}
|
||||
}
|
||||
return flowPanel;
|
||||
}
|
||||
|
||||
private CheckBox getShowOnlyRacesOfSameDayWidget(DataEntryDialog<?> dialog) {
|
||||
showOnlyRacesOfSameDayCheckBox = dialog.createCheckbox(stringMessages.showOnlyRacesOfSameDay());
|
||||
showOnlyRacesOfSameDayCheckBox.setValue(initialSettings.isShowOnlyRacesOfSameDay());
|
||||
return showOnlyRacesOfSameDayCheckBox;
|
||||
}
|
||||
|
||||
private CheckBox getShowOnlyCurrentlyRunningRacesWidget(DataEntryDialog<?> dialog) {
|
||||
showOnlyCurrentlyRunningRacesCheckBox = dialog.createCheckbox(stringMessages.showOnlyCurrentlyRunningRaces());
|
||||
showOnlyCurrentlyRunningRacesCheckBox.setValue(initialSettings.isShowOnlyCurrentlyRunningRaces());
|
||||
return showOnlyCurrentlyRunningRacesCheckBox;
|
||||
}
|
||||
|
||||
private Widget getAdditionalSettingsWidget(DataEntryDialog<?> dialog) {
|
||||
FlowPanel flowPanel = new FlowPanel();
|
||||
flowPanel.addStyleName("SettingsDialogComponent");
|
||||
flowPanel.add(dialog.createHeadline(stringMessages.additionalSettings(), true));
|
||||
FlowPanel additionalSettingsPanel = new FlowPanel();
|
||||
flowPanel.add(additionalSettingsPanel);
|
||||
|
||||
additionalSettingsPanel.add(getShowOnlyRacesOfSameDayWidget(dialog));
|
||||
additionalSettingsPanel.add(getShowOnlyCurrentlyRunningRacesWidget(dialog));
|
||||
|
||||
return flowPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Widget getAdditionalWidget(DataEntryDialog<?> dialog) {
|
||||
VerticalPanel vp = new VerticalPanel();
|
||||
vp.add(new Label("Hallo Settings"));
|
||||
|
||||
test = dialog.createTextBox("???");
|
||||
vp.add(test);
|
||||
vp.add(fillCourseAreaWidget(dialog));
|
||||
vp.add(fillRegattaNamesWidget(dialog));
|
||||
vp.add(getAdditionalSettingsWidget(dialog));
|
||||
|
||||
return vp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegattaRaceStatesSettings getResult() {
|
||||
RegattaRaceStatesSettings result = new RegattaRaceStatesSettings();
|
||||
List<String> selectedCourseAreas = new ArrayList<String>();
|
||||
for (Entry<String, CheckBox> entry : courseAreaCheckBoxMap.entrySet()) {
|
||||
if (entry.getValue().getValue()) {
|
||||
selectedCourseAreas.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: set the values of the settings from the UI widgets here
|
||||
return result;
|
||||
List<String> selectedRegattas = new ArrayList<String>();
|
||||
for (Entry<String, CheckBox> entry : regattaCheckBoxMap.entrySet()) {
|
||||
if (entry.getValue().getValue()) {
|
||||
selectedRegattas.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
boolean isShowOnlyRacesOfSameDay = showOnlyRacesOfSameDayCheckBox.getValue();
|
||||
boolean isShowOnlyCurrentlyRunningRaces = showOnlyCurrentlyRunningRacesCheckBox.getValue();
|
||||
return new RegattaRaceStatesSettings(selectedCourseAreas, selectedRegattas, isShowOnlyRacesOfSameDay, isShowOnlyCurrentlyRunningRaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +177,7 @@ public class RegattaRaceStatesSettingsDialogComponent implements SettingsDialogC
|
||||
|
||||
@Override
|
||||
public FocusWidget getFocusWidget() {
|
||||
return test;
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user