|
|
@ -0,0 +1,422 @@ |
|
|
|
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()); |
|
|
|
} |
|
|
|
} |