package com.example.application.navigation;
|
|
|
|
import com.example.application.data.entity.Match;
|
|
import com.example.application.data.entity.Matchday;
|
|
import com.example.application.data.entity.Season;
|
|
import com.example.application.data.service.MatchService;
|
|
import com.example.application.data.service.MatchdayService;
|
|
import com.example.application.data.service.SeasonService;
|
|
import com.vaadin.flow.component.*;
|
|
import com.vaadin.flow.component.button.Button;
|
|
import com.vaadin.flow.component.html.Label;
|
|
import com.vaadin.flow.component.icon.Icon;
|
|
import com.vaadin.flow.component.icon.VaadinIcon;
|
|
import com.vaadin.flow.component.select.Select;
|
|
import com.vaadin.flow.router.BeforeEvent;
|
|
import com.vaadin.flow.router.HasUrlParameter;
|
|
import com.vaadin.flow.router.WildcardParameter;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.lang.NonNull;
|
|
import org.springframework.lang.Nullable;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
|
|
public class Navigation implements HasUrlParameter<String> {
|
|
// TODO: show dropdown menus also for invalid URLs (with content that fits the situation)
|
|
|
|
private final String route;
|
|
private final boolean onlyMatchdaysWithActivity;
|
|
private final List<Runnable> runnablesToBeRunAfterSelection = new ArrayList<>();
|
|
|
|
private final SeasonService seasonService;
|
|
private final MatchdayService matchdayService;
|
|
private final MatchService matchService;
|
|
|
|
private final List<Season> seasonList = new ArrayList<>();
|
|
private final List<Matchday> matchdayList = new ArrayList<>();
|
|
private final List<Match> matchList = new ArrayList<>();
|
|
|
|
private final Select<Season> seasonSelect = new Select<>();
|
|
private final Select<Matchday> matchdaySelect = new Select<>();
|
|
private final Select<Match> matchSelect = new Select<>();
|
|
|
|
private String seasonParam;
|
|
private String matchdayParam;
|
|
private String matchParam;
|
|
|
|
private boolean autoselectSeason = false;
|
|
private boolean autoselectMatchday = false;
|
|
private boolean autoselectMatch = false;
|
|
|
|
private final Label invalidUrlLabel = new Label();
|
|
private final Button prevMatchdayButton = new Button(new Icon(VaadinIcon.ARROW_LEFT));
|
|
private final Button nextMatchdayButton = new Button(new Icon(VaadinIcon.ARROW_RIGHT));
|
|
|
|
public Navigation(String route,
|
|
@Autowired SeasonService seasonService,
|
|
@Autowired MatchdayService matchdayService,
|
|
@Autowired MatchService matchService) {
|
|
this(route, seasonService, matchdayService, matchService, false);
|
|
}
|
|
|
|
public Navigation(String route,
|
|
@Autowired SeasonService seasonService,
|
|
@Autowired MatchdayService matchdayService,
|
|
@Autowired MatchService matchService,
|
|
boolean onlyMatchdaysWithActivity) {
|
|
this.route = route;
|
|
this.seasonService = seasonService;
|
|
this.matchdayService = matchdayService;
|
|
this.matchService = matchService;
|
|
this.onlyMatchdaysWithActivity = onlyMatchdaysWithActivity;
|
|
|
|
fillSeasonSelectWithData();
|
|
seasonSelect.addValueChangeListener(seasonSelectValueChangeListener());
|
|
matchdaySelect.addValueChangeListener(matchdaySelectValueChangeListener());
|
|
matchSelect.addValueChangeListener(matchSelectValueChangeListener());
|
|
}
|
|
|
|
public void setAutoselectSeason(boolean autoselectSeason) {
|
|
this.autoselectSeason = autoselectSeason;
|
|
}
|
|
|
|
public void setAutoselectMatchday(boolean autoselectMatchday) {
|
|
this.autoselectMatchday = autoselectMatchday;
|
|
}
|
|
|
|
public void setAutoselectMatch(boolean autoselectMatch) {
|
|
this.autoselectMatch = autoselectMatch;
|
|
}
|
|
|
|
// TODO: run the runnables after each selection (anyhow), then push history state (UI.getCurrent().getPage().getHistory().pushState(null, "http://host.com/person?action=edit&id=1");).
|
|
// Navigate as little as possible.
|
|
private void updateUrl() {
|
|
String params = NavigationUtils.getWildcardParam(seasonParam, matchdayParam, matchParam);
|
|
UI.getCurrent().getPage().getHistory().pushState(null, String.format("%s/%s", route, params));
|
|
}
|
|
|
|
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Matchday>, Matchday>> matchdaySelectValueChangeListener() {
|
|
return matchdayChangeEvent -> {
|
|
Matchday matchday = matchdayChangeEvent.getValue();
|
|
if (matchday != null) {
|
|
matchParam = matchday.toString();
|
|
updateUrl();
|
|
runnablesToBeRunAfterSelection.forEach(Runnable::run);
|
|
}
|
|
};
|
|
}
|
|
|
|
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Season>, Season>> seasonSelectValueChangeListener() {
|
|
return seasonChangeEvent -> {
|
|
Season newSeason = seasonChangeEvent.getValue();
|
|
if (newSeason != null) {
|
|
String seasonParam = newSeason.toString();
|
|
String matchdayParam = null;
|
|
|
|
Matchday matchdayInNewSeason = null;
|
|
Matchday matchdayInOldSeason = matchdaySelect.getValue();
|
|
if (matchdayInOldSeason != null) {
|
|
matchdayParam = matchdayInOldSeason.toString();
|
|
matchdayInNewSeason = getMatchdayFromParam(matchdayParam, newSeason);
|
|
}
|
|
matchdayParam = matchdayInNewSeason == null ? "1" : matchdayParam;
|
|
|
|
this.seasonParam = seasonParam;
|
|
this.matchdayParam = matchdayParam;
|
|
this.matchParam = null;
|
|
updateUrl();
|
|
runnablesToBeRunAfterSelection.forEach(Runnable::run);
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Match>, Match>> matchSelectValueChangeListener() {
|
|
return matchChangeEvent -> {
|
|
Match match = matchChangeEvent.getValue();
|
|
if (match != null) {
|
|
matchParam = match.toString();
|
|
updateUrl();
|
|
runnablesToBeRunAfterSelection.forEach(Runnable::run); // TODO: offer different lists for season, matchday, match
|
|
}
|
|
};
|
|
}
|
|
|
|
private void fillSeasonSelectWithData() {
|
|
seasonList.clear();
|
|
seasonList.addAll(seasonService.getAllSeasonsSorted());
|
|
seasonSelect.setItems(seasonList);
|
|
}
|
|
|
|
private void fillMatchdaySelectWithData(Season season) {
|
|
matchdayList.clear();
|
|
List<Matchday> matchdaysToAdd = onlyMatchdaysWithActivity ?
|
|
matchdayService.getMatchdaysWithActivitySorted(season) : matchdayService.getMatchdaysSorted(season);
|
|
matchdayList.addAll(matchdaysToAdd);
|
|
matchdaySelect.setItems(matchdayList);
|
|
}
|
|
|
|
private void fillMatchSelectWithData(Matchday matchday) {
|
|
matchList.clear();
|
|
matchList.addAll(matchService.getMatches(matchday));
|
|
matchSelect.setItems(matchList);
|
|
}
|
|
|
|
private boolean isMatchDayParamValid(@NonNull String matchdayParam) {
|
|
return matchdayList.stream().anyMatch(matchday -> matchdayParam.equals(matchday.toString()));
|
|
}
|
|
|
|
private void navigate(String seasonParam, String matchdayParam, String matchParam) { // TODO: change this to String... -> see where you need which parameters
|
|
UI.getCurrent().navigate(String.format("%s/%s", route, NavigationUtils.getWildcardParam(seasonParam, matchdayParam, matchParam)));
|
|
}
|
|
|
|
@Override
|
|
public void setParameter(BeforeEvent event, @WildcardParameter String param) {
|
|
Map<UrlParameterType, String> map = NavigationUtils.getParameterMap(param);
|
|
setParameter(map.get(UrlParameterType.SEASON), map.get(UrlParameterType.MATCHDAY), map.get(UrlParameterType.MATCH));
|
|
}
|
|
|
|
private boolean paramInvalid(@Nullable String param) {
|
|
return param == null || param.equals("");
|
|
}
|
|
|
|
private void noMatchFound(Season season, Matchday matchday) {
|
|
invalidUrlLabel.setText(String.format("No Match found in Matchday %s in Season %s!", matchday.toString(), season.toString()));
|
|
}
|
|
|
|
private void matchFound(Season season, Matchday matchday, Match match) {
|
|
matchSelect.setValue(match);
|
|
|
|
navigate(season.toString(), matchday.toString(), match.toString());
|
|
}
|
|
|
|
private void noMatchdayFound(Season season) {
|
|
invalidUrlLabel.setText(String.format("No Matchday found in Season %s!", season.toString()));
|
|
}
|
|
|
|
private void autoselectMatch(Season season, Matchday matchday) {
|
|
Optional<Match> firstMatch = matchService.getFirstMatch(matchday);
|
|
firstMatch.ifPresentOrElse(
|
|
match -> matchFound(season, matchday, match),
|
|
() -> noMatchFound(season, matchday));
|
|
}
|
|
|
|
private void matchdayFound(Season season, Matchday matchday, String matchParam) {
|
|
matchdaySelect.setValue(matchday);
|
|
fillMatchSelectWithData(matchday);
|
|
|
|
if (paramInvalid(matchParam) && autoselectMatch) {
|
|
autoselectMatch(season, matchday);
|
|
return;
|
|
}
|
|
navigate(season.toString(), matchday.toString(), matchParam);
|
|
}
|
|
|
|
private void noSeasonFound() {
|
|
invalidUrlLabel.setText("No Season found!");
|
|
}
|
|
|
|
private void autoselectMatchday(Season season, String matchParam) {
|
|
Optional<Matchday> latestMatchday = matchdayService.getLastMatchdayWithActivityOrElseFirstMatchday(season);
|
|
latestMatchday.ifPresentOrElse(
|
|
matchday -> matchdayFound(season, matchday, matchParam),
|
|
() -> noMatchdayFound(season));
|
|
}
|
|
|
|
private void seasonFound(Season season, String matchdayParam, String matchParam) {
|
|
seasonSelect.setValue(season);
|
|
fillMatchdaySelectWithData(season);
|
|
|
|
if (paramInvalid(matchdayParam) && autoselectMatchday) {
|
|
autoselectMatchday(season, matchParam);
|
|
return;
|
|
}
|
|
navigate(season.toString(), matchdayParam, matchParam);
|
|
}
|
|
|
|
private void autoselectSeason(String matchdayParam, String matchParam) {
|
|
Optional<Season> latestSeason = seasonService.getLatestSeason();
|
|
latestSeason.ifPresentOrElse(
|
|
season -> seasonFound(season, matchdayParam, matchParam),
|
|
this::noSeasonFound);
|
|
}
|
|
|
|
private boolean autoselectIfNecessary(String seasonParam, String matchdayParam, String matchParam) {
|
|
if (paramInvalid(seasonParam) && autoselectSeason) {
|
|
autoselectSeason(matchdayParam, matchParam);
|
|
return true;
|
|
}
|
|
if (paramInvalid(matchdayParam) && autoselectMatchday) {
|
|
autoselectMatchday(seasonSelect.getValue(), matchParam);
|
|
return true;
|
|
}
|
|
if (paramInvalid(matchParam) && autoselectMatch) {
|
|
autoselectMatch(seasonSelect.getValue(), matchdaySelect.getValue());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void setParameter(String seasonParam, String matchdayParam, String matchParam) {
|
|
if (autoselectIfNecessary(seasonParam, matchdayParam, matchParam)) {
|
|
for (Runnable runnable : runnablesToBeRunAfterSelection) {
|
|
runnable.run();
|
|
}
|
|
return;
|
|
}
|
|
|
|
Season season = getSeasonFromParam(seasonParam);
|
|
if (season != null) {
|
|
seasonSelect.setValue(season);
|
|
this.seasonParam = seasonParam;
|
|
fillMatchdaySelectWithData(season);
|
|
} else if (autoselectSeason) {
|
|
invalidUrlLabel.setText(String.format("Invalid URL! Season \"%s\" does not exist in the database!", seasonParam));
|
|
return;
|
|
}
|
|
|
|
Matchday matchday = getMatchdayFromParam(matchdayParam);
|
|
if (matchday != null) {
|
|
matchdaySelect.setValue(matchday);
|
|
this.matchdayParam = matchdayParam;
|
|
fillMatchSelectWithData(matchday);
|
|
configureButtons();
|
|
} else if (autoselectMatchday) {
|
|
String messageExtra = onlyMatchdaysWithActivity ? " or has no games played yet" : "";
|
|
invalidUrlLabel.setText(String.format("Invalid URL! Matchday \"%s\" in Season \"%s\" does not exist in the database%s!", matchdayParam, seasonParam, messageExtra));
|
|
return;
|
|
}
|
|
|
|
Match match = getMatchFromParam(matchParam);
|
|
if (match != null) {
|
|
matchSelect.setValue(match);
|
|
this.matchParam = matchParam;
|
|
} else if (autoselectMatch) {
|
|
invalidUrlLabel.setText(String.format("Invalid URL: Match \"%s\" in Matchday \"%s\" in Season \"%s\" does not exist in the database!", matchParam, matchdayParam, seasonParam));
|
|
}
|
|
|
|
for (Runnable runnable : runnablesToBeRunAfterSelection) {
|
|
runnable.run();
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private Season getSeasonFromParam(@Nullable String seasonParam) {
|
|
if (seasonParam == null) {
|
|
return null;
|
|
}
|
|
for (Season season : seasonList) {
|
|
if (seasonParam.equals(season.toString())) {
|
|
return season;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Nullable
|
|
private Matchday getMatchdayFromParam(@Nullable String matchdayParam) {
|
|
return getMatchdayFromParam(matchdayParam, null);
|
|
}
|
|
|
|
@Nullable
|
|
private Matchday getMatchdayFromParam(@Nullable String matchdayParam, @Nullable Season season) {
|
|
if (matchdayParam == null) {
|
|
return null;
|
|
}
|
|
List<Matchday> matchdayList = season == null ? this.matchdayList : matchdayService.getMatchdaysSorted(season);
|
|
for (Matchday matchday : matchdayList) {
|
|
if (matchdayParam.equals(matchday.toString())) {
|
|
return matchday;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Nullable
|
|
private Match getMatchFromParam(@Nullable String matchParam) {
|
|
if (matchParam == null) {
|
|
return null;
|
|
}
|
|
for (Match match : matchList) {
|
|
if (matchParam.equals(match.toString())) {
|
|
return match;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void addRunnableToBeRunAfterSelection(Runnable runnable) {
|
|
runnablesToBeRunAfterSelection.add(runnable);
|
|
}
|
|
|
|
private void configureButtons() {
|
|
prevMatchdayButton.setEnabled(isMatchDayParamValid(getPrevMatchdayParam()));
|
|
prevMatchdayButton.addClickListener(getButtonClickListener(getPrevMatchdayParam()));
|
|
|
|
nextMatchdayButton.setEnabled(isMatchDayParamValid(getNextMatchdayParam()));
|
|
nextMatchdayButton.addClickListener(getButtonClickListener(getNextMatchdayParam()));
|
|
}
|
|
|
|
private ComponentEventListener<ClickEvent<Button>> getButtonClickListener(String matchdayParam) {
|
|
return buttonClickEvent -> navigate(seasonParam, matchdayParam, matchParam);
|
|
}
|
|
|
|
private String getPrevMatchdayParam() {
|
|
try {
|
|
return String.valueOf(Integer.parseInt(matchdayParam) - 1);
|
|
} catch (NumberFormatException e) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
private String getNextMatchdayParam() {
|
|
try {
|
|
return String.valueOf(Integer.parseInt(matchdayParam) + 1);
|
|
} catch (NumberFormatException e) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
public Optional<Matchday> getSelectedMatchday() {
|
|
return matchdaySelect.getOptionalValue();
|
|
}
|
|
|
|
public Optional<Season> getSelectedSeason() {
|
|
return seasonSelect.getOptionalValue();
|
|
}
|
|
|
|
public Optional<Match> getSelectedMatch() {
|
|
return matchSelect.getOptionalValue();
|
|
}
|
|
|
|
public Button getPrevMatchdayButton() {
|
|
return prevMatchdayButton;
|
|
}
|
|
|
|
public Button getNextMatchdayButton() {
|
|
return nextMatchdayButton;
|
|
}
|
|
|
|
public Label getInvalidUrlLabel() {
|
|
return invalidUrlLabel;
|
|
}
|
|
|
|
public Select<Season> getSeasonSelect() {
|
|
return seasonSelect;
|
|
}
|
|
|
|
public Select<Matchday> getMatchdaySelect() {
|
|
return matchdaySelect;
|
|
}
|
|
|
|
public Select<Match> getMatchSelect() {
|
|
return matchSelect;
|
|
}
|
|
|
|
public void selectMatch(Match match) {
|
|
navigate(seasonParam, matchdayParam, match.toString());
|
|
}
|
|
}
|