You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

422 lines
16 KiB

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());
}
}