From 3ac4ecd41cb335c63bed4109c49b8f7696fcbd4f Mon Sep 17 00:00:00 2001 From: Alessandro Schwaiger Date: Mon, 15 Apr 2024 23:54:39 +0200 Subject: [PATCH 1/9] Fix issues with Tracking in 1.6.x. Mostly related to Suwayomi tracking. It is now mostly an own implementation until I can take the time to properly get the Suwayomi stuff working. Also removed unused methods and variables --- schemas/schema.graphql | 49 ++- .../dialog/tracking/TrackingDialog.java | 287 ++++++++++++++---- .../tracking/TrackingMangaChoiceDialog.java | 16 +- .../tracking/provider/AniListProvider.java | 24 ++ .../provider/Suwayomi/MALTrackerProvider.java | 89 ++++++ .../{ => Suwayomi}/SuwayomiProvider.java | 7 +- .../tracking/provider/TrackerProvider.java | 15 +- .../listbox/chapter/ChapterRenderer.java | 2 +- .../component/reader/MangaReader.java | 36 +-- .../data/tachidesk/TrackRecord.java | 14 + .../data/tachidesk/TrackerType.java | 45 +++ .../data/tracking/Tracker.java | 4 + .../statistics/AniListMangaStatistics.java | 80 +++++ .../statistics/MALMangaStatistics.java | 82 +++++ .../MangaStatistics.java} | 11 +- .../services/MangaService.java | 48 +-- .../suwayomi/SuwayomiTrackingClient.java | 89 +++++- .../services/tracker/AniListAPIService.java | 58 +--- .../tracker/MyAnimeListAPIService.java | 77 ++++- .../tracker/SuwayomiTrackingService.java | 75 +++-- .../utils/TachideskUtils.java | 4 +- .../tachideskvaadinui/view/MangaView.java | 9 +- .../tachideskvaadinui/view/ReadingView.java | 14 +- 23 files changed, 879 insertions(+), 256 deletions(-) create mode 100644 src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java rename src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/{ => Suwayomi}/SuwayomiProvider.java (83%) create mode 100644 src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java create mode 100644 src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackerType.java create mode 100644 src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java create mode 100644 src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java rename src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/{anilist/responses/AniListMangaStatistics.java => statistics/MangaStatistics.java} (50%) diff --git a/schemas/schema.graphql b/schemas/schema.graphql index 560fc012..4963ae82 100644 --- a/schemas/schema.graphql +++ b/schemas/schema.graphql @@ -615,6 +615,16 @@ enum FetchSourceMangaType { LATEST } +input FetchTrackInput { + clientMutationId: String + recordId: Int! +} + +type FetchTrackPayload { + clientMutationId: String + trackRecord: TrackRecordType! +} + union Filter = CheckBoxFilter | GroupFilter | HeaderFilter | SelectFilter | SeparatorFilter | SortFilter | TextFilter | TriStateFilter input FilterChangeInput { @@ -884,10 +894,12 @@ type MangaType { updateStrategy: UpdateStrategy! url: String! age: LongString + bookmarkCount: Int! categories: CategoryNodeList! chapters: ChapterNodeList! chaptersAge: LongString downloadCount: Int! + firstUnreadChapter: ChapterType lastReadChapter: ChapterType latestFetchedChapter: ChapterType latestReadChapter: ChapterType @@ -988,9 +1000,12 @@ type Mutation { setSourceMeta(input: SetSourceMetaInput!): SetSourceMetaPayload! updateSourcePreference(input: UpdateSourcePreferenceInput!): UpdateSourcePreferencePayload! bindTrack(input: BindTrackInput!): BindTrackPayload! + fetchTrack(input: FetchTrackInput!): FetchTrackPayload! loginTrackerCredentials(input: LoginTrackerCredentialsInput!): LoginTrackerCredentialsPayload! loginTrackerOAuth(input: LoginTrackerOAuthInput!): LoginTrackerOAuthPayload! logoutTracker(input: LogoutTrackerInput!): LogoutTrackerPayload! + trackProgress(input: TrackProgressInput!): TrackProgressPayload! + unbindTrack(input: UnbindTrackInput!): UnbindTrackPayload! updateTrack(input: UpdateTrackInput!): UpdateTrackPayload! updateCategoryManga(input: UpdateCategoryMangaInput!): UpdateCategoryMangaPayload! updateLibraryManga(input: UpdateLibraryMangaInput!): UpdateLibraryMangaPayload! @@ -1031,6 +1046,7 @@ type PageInfo { type PartialSettingsType implements Settings { autoDownloadAheadLimit: Int @deprecated(reason: "Replaced with autoDownloadNewChaptersLimit, replace with autoDownloadNewChaptersLimit") + autoDownloadIgnoreReUploads: Boolean autoDownloadNewChapters: Boolean autoDownloadNewChaptersLimit: Int backupInterval: Int @@ -1076,6 +1092,7 @@ type PartialSettingsType implements Settings { } input PartialSettingsTypeInput { + autoDownloadIgnoreReUploads: Boolean autoDownloadNewChapters: Boolean autoDownloadNewChaptersLimit: Int backupInterval: Int @@ -1265,6 +1282,7 @@ type SetSourceMetaPayload { interface Settings { autoDownloadAheadLimit: Int @deprecated(reason: "Replaced with autoDownloadNewChaptersLimit, replace with autoDownloadNewChaptersLimit") + autoDownloadIgnoreReUploads: Boolean autoDownloadNewChapters: Boolean autoDownloadNewChaptersLimit: Int backupInterval: Int @@ -1311,6 +1329,7 @@ interface Settings { type SettingsType implements Settings { autoDownloadAheadLimit: Int! @deprecated(reason: "Replaced with autoDownloadNewChaptersLimit, replace with autoDownloadNewChaptersLimit") + autoDownloadIgnoreReUploads: Boolean autoDownloadNewChapters: Boolean! autoDownloadNewChaptersLimit: Int! backupInterval: Int! @@ -1560,12 +1579,23 @@ type TrackerType { id: Int! isLoggedIn: Boolean! name: String! + supportsTrackDeletion: Boolean isTokenExpired: Boolean! scores: [String!]! statuses: [TrackStatusType!]! trackRecords: TrackRecordNodeList! } +input TrackProgressInput { + clientMutationId: String + mangaId: Int! +} + +type TrackProgressPayload { + clientMutationId: String + trackRecords: [TrackRecordType!]! +} + input TrackRecordConditionInput { finishDate: LongString id: Int @@ -1676,6 +1706,21 @@ type TriStateFilter { name: String! } +input UnbindTrackInput { + clientMutationId: String + + """ + This will only work if the tracker of the track record supports deleting tracks + """ + deleteRemoteTrack: Boolean + recordId: Int! +} + +type UnbindTrackPayload { + clientMutationId: String + trackRecord: TrackRecordType +} + input UpdateCategoriesInput { clientMutationId: String ids: [Int!]! @@ -1904,7 +1949,6 @@ input UpdateTrackInput { scoreString: String startDate: LongString status: Int - unbind: Boolean } type UpdateTrackPayload { @@ -1969,4 +2013,5 @@ type WebUIUpdateStatus { info: WebUIUpdateInfo! progress: Int! state: UpdateState! -} \ No newline at end of file +} + diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java index b14ce093..d1e972f1 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java @@ -17,20 +17,25 @@ import com.vaadin.flow.component.notification.NotificationVariant; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.theme.lumo.LumoIcon; +import dev.katsute.mal4j.manga.property.MangaStatus; import java.time.LocalDate; import java.util.Objects; import lombok.extern.slf4j.Slf4j; import online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider.AniListProvider; -import online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider.SuwayomiProvider; +import online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider.Suwayomi.MALTrackerProvider; import online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider.TrackerProvider; import online.hatsunemiku.tachideskvaadinui.data.tachidesk.Manga; +import online.hatsunemiku.tachideskvaadinui.data.tachidesk.TrackerType; import online.hatsunemiku.tachideskvaadinui.data.tracking.Tracker; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListScoreFormat; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListStatus; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; -import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.responses.AniListMangaStatistics; +import online.hatsunemiku.tachideskvaadinui.data.tracking.statistics.AniListMangaStatistics; +import online.hatsunemiku.tachideskvaadinui.data.tracking.statistics.MALMangaStatistics; +import online.hatsunemiku.tachideskvaadinui.data.tracking.statistics.MangaStatistics; import online.hatsunemiku.tachideskvaadinui.services.TrackingDataService; import online.hatsunemiku.tachideskvaadinui.services.tracker.AniListAPIService; +import online.hatsunemiku.tachideskvaadinui.services.tracker.MyAnimeListAPIService; import online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService; import org.jetbrains.annotations.NotNull; import org.springframework.web.reactive.function.client.WebClientRequestException; @@ -49,45 +54,65 @@ public class TrackingDialog extends Dialog { private final AniListAPIService aniListAPI; private final SuwayomiTrackingService suwayomiTrackingService; private final TrackingDataService dataService; + private final MyAnimeListAPIService malAPI; /** * Constructs a {@link TrackingDialog} with the given parameters. * - * @param dataService The {@link TrackingDataService} used for storing tracking data. - * @param manga the {@link Manga} to track with the dialog. - * @param aniListAPIService the {@link AniListAPIService} used for making requests to the AniList - * API. + * @param dataService The {@link TrackingDataService} used for storing tracking data. + * @param manga the {@link Manga} to track with the dialog. + * @param aniListAPIService the {@link AniListAPIService} used for making requests to the + * AniList API. * @param suwayomiTrackingService the {@link SuwayomiTrackingService} used for making requests to - * the Suwayomi API. + * the Suwayomi API. + * @param malAPI the {@link MyAnimeListAPIService} used for making requests to + * the MyAnimeList API. */ public TrackingDialog( TrackingDataService dataService, Manga manga, AniListAPIService aniListAPIService, - SuwayomiTrackingService suwayomiTrackingService) { + SuwayomiTrackingService suwayomiTrackingService, + MyAnimeListAPIService malAPI) { super(); this.dataService = dataService; this.aniListAPI = aniListAPIService; this.suwayomiTrackingService = suwayomiTrackingService; + this.malAPI = malAPI; Tracker tracker = dataService.getTracker(manga.getId()); - if (!tracker.hasAniListId()) { + if (tracker.hasAniListId()) { + if (!suwayomiTrackingService.isMangaTrackedOnAniList(manga.getId())) { + tracker.removeAniListId(); + } + } + + if (tracker.hasMalId()) { + if (!suwayomiTrackingService.isMangaTrackedOnMAL(manga.getId())) { + tracker.removeMalId(); + } + } + + if (!tracker.hasAniListId() && !tracker.hasMalId()) { addTrackingButtons(manga, aniListAPIService, tracker); } else { - Div statistics; + try { - statistics = getTrackingStatistics(tracker); + TrackerProvider provider; + if (tracker.hasAniListId()) { + provider = new AniListProvider(aniListAPIService, suwayomiTrackingService); + } else if (tracker.hasMalId()) { + provider = new MALTrackerProvider(suwayomiTrackingService, malAPI); + } else { + throw new IllegalStateException("Tracker has no tracking IDs"); + } + + statistics = getTrackingStatistics(tracker, provider); add(statistics); } catch (RuntimeException e) { addTrackingButtons(manga, aniListAPIService, tracker); - } catch (Exception e) { - log.error("Error retrieving tracking statistics from Tracker", e); - Notification notification = new Notification(); - notification.addThemeVariants(NotificationVariant.LUMO_ERROR); - notification.setText("Error retrieving tracking statistics"); - notification.open(); } } } @@ -95,10 +120,10 @@ public TrackingDialog( /** * Adds the tracking buttons to the dialog. * - * @param manga the {@link Manga} to track + * @param manga the {@link Manga} to track * @param aniListAPIService the {@link AniListAPIService} to communicate with AniList with - * @param tracker the {@link Tracker} instance to update the button states via {@link - * #updateButtons(Button, Button, Tracker)} + * @param tracker the {@link Tracker} instance to update the button states via + * {@link #updateButtons(Button, Button, Tracker)} */ private void addTrackingButtons( Manga manga, AniListAPIService aniListAPIService, Tracker tracker) { @@ -134,7 +159,7 @@ private void addTrackingButtons( try { displaySearch(manga.getTitle(), manga.getId(), provider); } catch (WebClientResponseException.InternalServerError - | WebClientRequestException error) { + | WebClientRequestException error) { log.error("Invalid response from AniList", error); Notification notification = new Notification(); notification.addThemeVariants(NotificationVariant.LUMO_ERROR); @@ -152,12 +177,18 @@ private void addTrackingButtons( return; } - TrackerProvider provider = new SuwayomiProvider(suwayomiTrackingService); + if (!malAPI.hasMalToken()) { + String url = malAPI.getAuthUrl(); + getUI().ifPresent(ui -> ui.getPage().open(url)); + return; + } + + TrackerProvider provider = new MALTrackerProvider(suwayomiTrackingService, malAPI); try { displaySearch(manga.getTitle(), manga.getId(), provider); } catch (WebClientResponseException.InternalServerError - | WebClientRequestException error) { + | WebClientRequestException error) { log.error("Invalid response from MyAnimeList", error); Notification notification = new Notification(); notification.addThemeVariants(NotificationVariant.LUMO_ERROR); @@ -189,10 +220,13 @@ private void addTrackingButtons( * @throws RuntimeException if the manga is not found on AniList. */ @NotNull - private Div getTrackingStatistics(Tracker tracker) throws RuntimeException { - AniListMangaStatistics mangaStats; + private Div getTrackingStatistics(Tracker tracker, TrackerProvider provider) + throws RuntimeException { + + //get manga stats via provider + MangaStatistics mangaStats; try { - mangaStats = aniListAPI.getMangaFromList(tracker.getAniListId()); + mangaStats = provider.getStatistics(tracker); } catch (WebClientResponseException.NotFound e) { log.debug("Manga not found on AniList", e); Notification notification = new Notification(); @@ -207,23 +241,35 @@ private Div getTrackingStatistics(Tracker tracker) throws RuntimeException { notification.open(); tracker.removeAniListId(); throw new RuntimeException("Manga not found on AniList"); + } catch (Exception e) { + log.error("Error getting manga stats", e); + Notification notification = new Notification(); + notification.addThemeVariants(NotificationVariant.LUMO_ERROR); + notification.setText("Error getting manga stats: " + e.getMessage()); + notification.open(); + throw new RuntimeException("Error getting manga stats"); } Div content = new Div(); - ComboBox status = getTrackingStatusField(tracker, mangaStats); + ComboBox status = getTrackingStatusField(tracker, mangaStats); - var maxChapters = aniListAPI.getChapterCount(tracker.getAniListId()).orElse(null); + var maxChapters = provider.getMaxChapter(tracker); SuperIntegerField chapter = getTrackingChapterField(tracker, mangaStats, maxChapters); - SuperIntegerField score = getTrackingScoreField(tracker, mangaStats); + SuperIntegerField score = getTrackingScoreField(tracker, mangaStats, provider); SuperDatePicker endDate = new SuperDatePicker(); SuperDatePicker startDate = getTrackingStartDateField(tracker, mangaStats, endDate); Checkbox privateCheckbox = getPrivateCheckboxField(tracker); + if (!provider.canSetPrivate()) { + privateCheckbox.setVisible(false); + privateCheckbox.setEnabled(false); + } + configureTrackingEndDateField(tracker, endDate, mangaStats, startDate); Div statistics = new Div(); @@ -231,17 +277,46 @@ private Div getTrackingStatistics(Tracker tracker) throws RuntimeException { statistics.add(status, chapter, score, startDate, endDate, privateCheckbox); - Button trackingDeleteBtn = new Button("Remove AniList Tracking", VaadinIcon.TRASH.create()); + String trackerName; + TrackerType type; + + if (provider instanceof AniListProvider) { + trackerName = "AniList"; + type = TrackerType.ANILIST; + } else if (provider instanceof MALTrackerProvider) { + trackerName = "MAL"; + type = TrackerType.MAL; + } else { + throw new IllegalArgumentException("Unknown TrackerProvider type"); + } + + String trashBtnText = "Remove %s tracking".formatted(trackerName); + Button trackingDeleteBtn = new Button(trashBtnText, VaadinIcon.TRASH.create()); trackingDeleteBtn.addClickListener( e -> { - tracker.removeAniListId(); + + if (type == TrackerType.ANILIST) { + tracker.removeAniListId(); + } else { + tracker.removeMalId(); + } + close(); }); - var nukeBtn = new Button("Remove from AniList and stop Tracking", VaadinIcon.BOMB.create()); + String nukeBtnText = "Remove from %s and stop Tracking".formatted(trackerName); + var nukeBtn = new Button(nukeBtnText, VaadinIcon.BOMB.create()); nukeBtn.addClickListener( e -> { - aniListAPI.removeMangaFromList(tracker.getAniListId()); + + if (type == TrackerType.ANILIST) { + aniListAPI.removeMangaFromList(tracker.getAniListId()); + tracker.removeAniListId(); + } else { + malAPI.removeMangaFromList(tracker.getMalId()); + tracker.removeMalId(); + } + tracker.removeAniListId(); close(); }); @@ -270,7 +345,7 @@ private Checkbox getPrivateCheckboxField(Tracker tracker) { private void configureTrackingEndDateField( Tracker tracker, SuperDatePicker endDate, - AniListMangaStatistics mangaStats, + MangaStatistics mangaStats, SuperDatePicker startDate) { endDate.setPlaceholder("End date"); endDate.setClearButtonVisible(true); @@ -286,27 +361,47 @@ private void configureTrackingEndDateField( e -> { if (e.getValue() == null) { MediaDate date = new MediaDate(null, null, null); - aniListAPI.updateMangaEndDate(tracker.getAniListId(), date); + + if (mangaStats instanceof AniListMangaStatistics) { + aniListAPI.updateMangaEndDate(tracker.getAniListId(), date); + } else if (mangaStats instanceof MALMangaStatistics) { + log.warn("End date is null, but MAL doesn't support null end dates"); + + Notification notification = new Notification(); + notification.addThemeVariants(NotificationVariant.LUMO_ERROR); + notification.setText("MyAnimeList doesn't support removing the end date"); + notification.open(); + } else { + throw new IllegalArgumentException("Unknown MangaStatistics type"); + } + return; } if (startDate.getValue() == null) { startDate.setValue(e.getValue()); } else { - if (!startDate.getValue().isAfter(e.getValue())) { + if (startDate.getValue().isAfter(e.getValue())) { endDate.setValue(e.getOldValue()); return; } } MediaDate date = new MediaDate(e.getValue()); - aniListAPI.updateMangaEndDate(tracker.getAniListId(), date); + + if (mangaStats instanceof AniListMangaStatistics) { + aniListAPI.updateMangaEndDate(tracker.getAniListId(), date); + } else if (mangaStats instanceof MALMangaStatistics) { + malAPI.updateMangaListEndDate(tracker.getMalId(), date); + } else { + throw new IllegalArgumentException("Unknown MangaStatistics type"); + } }); } @NotNull private SuperDatePicker getTrackingStartDateField( - Tracker tracker, AniListMangaStatistics mangaStats, SuperDatePicker endDate) { + Tracker tracker, MangaStatistics mangaStats, SuperDatePicker endDate) { SuperDatePicker startDate = new SuperDatePicker(); startDate.setPlaceholder("Start date"); startDate.setClearButtonVisible(true); @@ -325,11 +420,19 @@ private SuperDatePicker getTrackingStartDateField( e -> { if (e.getValue() == null) { MediaDate date = new MediaDate(null, null, null); - aniListAPI.updateMangaStartDate(tracker.getAniListId(), date); + + if (mangaStats instanceof AniListMangaStatistics) { + aniListAPI.updateMangaStartDate(tracker.getAniListId(), date); + } else if (mangaStats instanceof MALMangaStatistics) { + malAPI.updateMangaListStartDate(tracker.getMalId(), date); + } else { + throw new IllegalArgumentException("Unknown MangaStatistics type"); + } + return; } - if (e.getValue().isAfter(endDate.getValue())) { + if (endDate.getValue() != null && e.getValue().isAfter(endDate.getValue())) { startDate.setValue(e.getOldValue()); Notification notification = new Notification(); @@ -340,31 +443,44 @@ private SuperDatePicker getTrackingStartDateField( } MediaDate date = new MediaDate(e.getValue()); - aniListAPI.updateMangaStartDate(tracker.getAniListId(), date); + + if (mangaStats instanceof AniListMangaStatistics) { + aniListAPI.updateMangaStartDate(tracker.getAniListId(), date); + } else if (mangaStats instanceof MALMangaStatistics) { + malAPI.updateMangaListStartDate(tracker.getMalId(), date); + } else { + throw new IllegalArgumentException("Unknown MangaStatistics type"); + } }); return startDate; } @NotNull - private SuperIntegerField getTrackingScoreField( - Tracker tracker, AniListMangaStatistics mangaStats) { - AniListScoreFormat format = aniListAPI.getScoreFormat(); + private SuperIntegerField getTrackingScoreField(Tracker tracker, MangaStatistics mangaStats, + TrackerProvider provider) { + AniListScoreFormat format = provider.getScoreFormat(); SuperIntegerField score = new SuperIntegerField(); score.setNullValueAllowed(true); score.setPreventingInvalidInput(true); score.setPlaceholder("Score"); score.addClassName("two-span"); + if (mangaStats.score() != 0) { score.setValue(mangaStats.score()); } else { score.setValue(null); } + score.addValueChangeListener( e -> { if (e.getValue() == null || e.getValue() == 0) { score.setValue(null); - aniListAPI.updateMangaScore(tracker.getAniListId(), 0); + if (mangaStats instanceof AniListMangaStatistics) { + aniListAPI.updateMangaScore(tracker.getAniListId(), 0); + } else if (mangaStats instanceof MALMangaStatistics) { + malAPI.updateMangaListScore(tracker.getMalId(), 0); + } return; } @@ -386,33 +502,66 @@ private SuperIntegerField getTrackingScoreField( return; } - aniListAPI.updateMangaScore(tracker.getAniListId(), e.getValue()); + if (mangaStats instanceof AniListMangaStatistics) { + aniListAPI.updateMangaScore(tracker.getAniListId(), e.getValue()); + } else if (mangaStats instanceof MALMangaStatistics) { + malAPI.updateMangaListScore(tracker.getMalId(), e.getValue()); + } }); return score; } @NotNull - private ComboBox getTrackingStatusField( - Tracker tracker, AniListMangaStatistics mangaStats) { + private ComboBox getTrackingStatusField(Tracker tracker, MangaStatistics mangaStats) { + ComboBox status; + if (mangaStats instanceof AniListMangaStatistics) { + status = configureStatusComboBoxAniList(tracker, (AniListMangaStatistics) mangaStats); + } else if (mangaStats instanceof MALMangaStatistics) { + status = configureStatusComboBoxMAL(tracker, (MALMangaStatistics) mangaStats); + } else { + throw new IllegalArgumentException("Unknown MangaStatistics type"); + } + + status.setPlaceholder("Status"); + status.addClassName("two-span"); + return status; + } + + private ComboBox configureStatusComboBoxAniList(Tracker tracker, + AniListMangaStatistics mangaStats) { ComboBox status = new ComboBox<>(); status.setItems(AniListStatus.values()); - status.setPlaceholder("Status"); status.setValue(mangaStats.status()); - status.addClassName("two-span"); - status.addValueChangeListener( - e -> { - if (e.getValue() == null) { - status.setValue(e.getOldValue()); - } else { - aniListAPI.updateMangaStatus(tracker.getAniListId(), e.getValue()); - } - }); + status.addValueChangeListener(e -> { + if (e.getValue() == null) { + status.setValue(e.getOldValue()); + } else { + aniListAPI.updateMangaStatus(tracker.getAniListId(), e.getValue()); + } + }); + + return status; + } + + private ComboBox configureStatusComboBoxMAL(Tracker tracker, + MALMangaStatistics mangaStats) { + ComboBox status = new ComboBox<>(); + status.setItems(MangaStatus.values()); + status.setValue(mangaStats.status()); + status.addValueChangeListener(e -> { + if (e.getValue() == null) { + status.setValue(e.getOldValue()); + } else { + malAPI.updateMangaListStatus(tracker.getMalId(), e.getValue()); + } + }); + return status; } @NotNull private SuperIntegerField getTrackingChapterField( - Tracker tracker, AniListMangaStatistics mangaStats, Integer maxChapters) { + Tracker tracker, MangaStatistics mangaStats, Integer maxChapters) { SuperIntegerField chapter = new SuperIntegerField(); chapter.setPreventingInvalidInput(true); chapter.setValue(mangaStats.progress()); @@ -439,12 +588,20 @@ private SuperIntegerField getTrackingChapterField( return; } - aniListAPI.updateMangaProgress(tracker.getAniListId(), e.getValue()); + if (mangaStats instanceof AniListMangaStatistics) { + aniListAPI.updateMangaProgress(tracker.getAniListId(), e.getValue()); + } else if (mangaStats instanceof MALMangaStatistics) { + malAPI.updateMangaListProgress(tracker.getMalId(), e.getValue()); + } else { + throw new IllegalArgumentException("Unknown MangaStatistics type"); + + } + }); return chapter; } - private void displaySearch(String mangaName, long mangaId, TrackerProvider trackerProvider) { + private void displaySearch(String mangaName, int mangaId, TrackerProvider trackerProvider) { var dialog = new TrackingMangaChoiceDialog(mangaName, mangaId, trackerProvider, dataService); dialog.open(); @@ -452,9 +609,9 @@ private void displaySearch(String mangaName, long mangaId, TrackerProvider track e -> { if (!e.isOpened()) { Tracker tracker = dataService.getTracker(mangaId); - if (tracker.hasAniListId()) { + if (tracker.hasAniListId() || tracker.hasMalId()) { removeAll(); - add(getTrackingStatistics(tracker)); + add(getTrackingStatistics(tracker, trackerProvider)); } } }); @@ -464,8 +621,8 @@ private void displaySearch(String mangaName, long mangaId, TrackerProvider track * Updates the tracking buttons with the current tracking status. * * @param aniListBtn the AniList tracking button - * @param malBtn the MyAnimeList tracking button - * @param tracker the tracker to check the status of + * @param malBtn the MyAnimeList tracking button + * @param tracker the tracker to check the status of */ private void updateButtons(Button aniListBtn, Button malBtn, Tracker tracker) { if (tracker.hasAniListId()) { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingMangaChoiceDialog.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingMangaChoiceDialog.java index 68e8c1b9..572d32e9 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingMangaChoiceDialog.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingMangaChoiceDialog.java @@ -28,7 +28,6 @@ /** Represents a dialog for choosing and tracking a manga. */ public class TrackingMangaChoiceDialog extends Dialog { - /** * Constructs a {@link TrackingMangaChoiceDialog}. * @@ -39,7 +38,7 @@ public class TrackingMangaChoiceDialog extends Dialog { */ public TrackingMangaChoiceDialog( String mangaName, - long mangaId, + int mangaId, TrackerProvider trackerProvider, TrackingDataService dataService) { @@ -118,14 +117,19 @@ public TrackingMangaChoiceDialog( Notification.show("Please select a manga to save"); return; } - int aniListId = manga.getRemoteId(); + int remoteId = manga.getRemoteId(); boolean isPrivate = trackerProvider.canSetPrivate() && privateCheckbox.getValue(); - trackerProvider.submitToTracker(isPrivate, manga.getId(), manga.getRemoteId()); + trackerProvider.submitToTracker(isPrivate, mangaId, manga.getRemoteId()); + + switch (trackerProvider.getTrackerType()) { + case MAL -> dataService.getTracker(mangaId).setMalId(remoteId); + case ANILIST -> dataService.getTracker(mangaId).setAniListId(remoteId); + default -> throw new IllegalArgumentException("Invalid tracker type"); + } - dataService.getTracker(mangaId).setAniListId(aniListId); - dataService.getTracker(mangaId).setPrivate(privateCheckbox.getValue()); + dataService.getTracker(mangaId).setPrivate(isPrivate); close(); }); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/AniListProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/AniListProvider.java index b3076272..cabac79b 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/AniListProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/AniListProvider.java @@ -8,7 +8,11 @@ import java.util.List; import lombok.AllArgsConstructor; +import online.hatsunemiku.tachideskvaadinui.data.tachidesk.TrackerType; +import online.hatsunemiku.tachideskvaadinui.data.tracking.Tracker; +import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListScoreFormat; import online.hatsunemiku.tachideskvaadinui.data.tracking.search.TrackerSearchResult; +import online.hatsunemiku.tachideskvaadinui.data.tracking.statistics.AniListMangaStatistics; import online.hatsunemiku.tachideskvaadinui.services.tracker.AniListAPIService; import online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService; @@ -40,4 +44,24 @@ public void submitToTracker(boolean isPrivate, int mangaId, int externalId) { suwayomiAPI.trackOnAniList(mangaId, externalId); } + + @Override + public TrackerType getTrackerType() { + return TrackerType.ANILIST; + } + + @Override + public AniListMangaStatistics getStatistics(Tracker tracker) { + return aniListAPI.getMangaFromList(tracker.getAniListId()); + } + + @Override + public Integer getMaxChapter(Tracker tracker) { + return aniListAPI.getChapterCount(tracker.getAniListId()).orElse(null); + } + + @Override + public AniListScoreFormat getScoreFormat() { + return aniListAPI.getScoreFormat(); + } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java new file mode 100644 index 00000000..8a3b9c9b --- /dev/null +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java @@ -0,0 +1,89 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider.Suwayomi; + +import dev.katsute.mal4j.manga.Manga; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import online.hatsunemiku.tachideskvaadinui.data.tachidesk.TrackerType; +import online.hatsunemiku.tachideskvaadinui.data.tracking.Tracker; +import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListScoreFormat; +import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; +import online.hatsunemiku.tachideskvaadinui.data.tracking.statistics.MALMangaStatistics; +import online.hatsunemiku.tachideskvaadinui.services.tracker.MyAnimeListAPIService; +import online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService; + +public class MALTrackerProvider extends SuwayomiProvider { + + private final MyAnimeListAPIService malAPI; + + public MALTrackerProvider(SuwayomiTrackingService suwayomiAPI, MyAnimeListAPIService malAPI) { + super(suwayomiAPI); + this.malAPI = malAPI; + } + + @Override + public TrackerType getTrackerType() { + return TrackerType.MAL; + } + + public MALMangaStatistics getStatistics(Tracker tracker) { + int id = tracker.getMalId(); + + Manga manga = malAPI.getManga(id); + var trackedManga = manga.getListStatus(); + + var status = trackedManga.getStatus(); + var score = trackedManga.getScore(); + var progress = trackedManga.getChaptersRead(); + var started = trackedManga.getStartDate().toInstant(); + + Date finishDate = trackedManga.getFinishDate(); + + Instant finished; + if (finishDate == null) { + finished = null; + } else { + finished = finishDate.toInstant(); + } + + MediaDate startedAt = null; + if (started != null) { + LocalDateTime dt = LocalDateTime.ofInstant(started, ZoneId.systemDefault()); + + startedAt = new MediaDate(dt.getYear(), dt.getMonthValue(), dt.getDayOfMonth()); + } + + MediaDate finishedAt = null; + if (finished != null) { + LocalDateTime dt = LocalDateTime.ofInstant(finished, ZoneId.systemDefault()); + + finishedAt = new MediaDate(dt.getYear(), dt.getMonthValue(), dt.getDayOfMonth()); + } + + return new MALMangaStatistics(status, progress, score, startedAt, finishedAt); + } + + @Override + public Integer getMaxChapter(Tracker tracker) { + int id = tracker.getMalId(); + int max = malAPI.getManga(id).getChapters(); + + if (max == 0) { + return null; + } + + return max; + } + + @Override + public AniListScoreFormat getScoreFormat() { + return AniListScoreFormat.POINT_10; + } +} diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/SuwayomiProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/SuwayomiProvider.java similarity index 83% rename from src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/SuwayomiProvider.java rename to src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/SuwayomiProvider.java index 16f0b150..3ca0a658 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/SuwayomiProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/SuwayomiProvider.java @@ -4,10 +4,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider; +package online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider.Suwayomi; import java.util.List; import lombok.AllArgsConstructor; +import online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider.TrackerProvider; import online.hatsunemiku.tachideskvaadinui.data.tracking.search.TrackerSearchResult; import online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService; @@ -17,9 +18,9 @@ * It implements the {@link TrackerProvider} interface. */ @AllArgsConstructor -public class SuwayomiProvider implements TrackerProvider { +public abstract class SuwayomiProvider implements TrackerProvider { - private SuwayomiTrackingService suwayomiAPI; + protected SuwayomiTrackingService suwayomiAPI; @Override public boolean canSetPrivate() { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java index d894326b..cd202f99 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java @@ -7,7 +7,11 @@ package online.hatsunemiku.tachideskvaadinui.component.dialog.tracking.provider; import java.util.List; +import online.hatsunemiku.tachideskvaadinui.data.tachidesk.TrackerType; +import online.hatsunemiku.tachideskvaadinui.data.tracking.Tracker; +import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListScoreFormat; import online.hatsunemiku.tachideskvaadinui.data.tracking.search.TrackerSearchResult; +import online.hatsunemiku.tachideskvaadinui.data.tracking.statistics.MangaStatistics; /** * Represents a provider for a tracking service. It contains methods for searching for manga on the @@ -41,8 +45,11 @@ public interface TrackerProvider { */ void submitToTracker(boolean isPrivate, int mangaId, int externalId); - /** Equivalent to calling `submitToTracker(false, mangaId, externalId)` */ - default void submitToTracker(int mangaId, int externalId) { - submitToTracker(false, mangaId, externalId); - } + TrackerType getTrackerType(); + + MangaStatistics getStatistics(Tracker tracker); + + Integer getMaxChapter(Tracker tracker); + + AniListScoreFormat getScoreFormat(); } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/listbox/chapter/ChapterRenderer.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/listbox/chapter/ChapterRenderer.java index 5b805bfc..58e39a2d 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/listbox/chapter/ChapterRenderer.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/listbox/chapter/ChapterRenderer.java @@ -207,7 +207,7 @@ private static Button getReadButton(Chapter chapter, MangaService mangaService, Button readButton = new Button(VaadinIcon.EYE.create()); readButton.addClickListener( e -> { - if (!mangaService.setChapterRead(chapter.getId())) { + if (!mangaService.setChapterRead(chapter.getId(), chapter.getMangaId())) { log.error("Failed to set chapter read"); Notification notification = new Notification("Failed to set chapter read", 5000); notification.setPosition(Notification.Position.MIDDLE); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/reader/MangaReader.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/reader/MangaReader.java index 78cbf704..15daa33f 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/reader/MangaReader.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/reader/MangaReader.java @@ -6,7 +6,11 @@ package online.hatsunemiku.tachideskvaadinui.component.reader; -import com.vaadin.flow.component.*; +import com.vaadin.flow.component.ComponentEventListener; +import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.Key; +import com.vaadin.flow.component.Text; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.dependency.CssImport; @@ -19,8 +23,6 @@ import com.vaadin.flow.shared.Registration; import java.util.List; import java.util.Objects; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import lombok.extern.slf4j.Slf4j; import online.hatsunemiku.tachideskvaadinui.component.reader.paged.PagedReader; @@ -31,8 +33,6 @@ import online.hatsunemiku.tachideskvaadinui.data.tachidesk.Chapter; import online.hatsunemiku.tachideskvaadinui.services.MangaService; import online.hatsunemiku.tachideskvaadinui.services.SettingsService; -import online.hatsunemiku.tachideskvaadinui.services.TrackingCommunicationService; -import online.hatsunemiku.tachideskvaadinui.services.TrackingDataService; import online.hatsunemiku.tachideskvaadinui.utils.NavigationUtils; import online.hatsunemiku.tachideskvaadinui.view.RootView; import org.jetbrains.annotations.NotNull; @@ -48,29 +48,21 @@ public class MangaReader extends Div { private final SettingsService settingsService; private final MangaService mangaService; - private final TrackingDataService tds; - private final TrackingCommunicationService tcs; private final int chapterIndex; private final List chapters; - private final ExecutorService trackerExecutor = Executors.newSingleThreadExecutor(); /** * Constructs a {@link MangaReader} object. * * @param chapter The Chapter object representing the chapter being read. * @param settingsService The SettingsService object used for managing reader settings. - * @param tds The TrackingDataService object used for tracking chapter progress. - * @param tcs The TrackingCommunicationService object used for communication with tracking - * service. * @param mangaService The MangaService object used for manga-related operations. * @param chapters The list of chapters in the manga */ public MangaReader( Chapter chapter, SettingsService settingsService, - TrackingDataService tds, MangaService mangaService, - TrackingCommunicationService tcs, List chapters) { addClassName("manga-reader"); @@ -78,8 +70,6 @@ public MangaReader( this.mangaService = mangaService; this.chapterIndex = chapters.stream().map(Chapter::getId).toList().indexOf(chapter.getId()); this.chapters = List.copyOf(chapters); - this.tds = tds; - this.tcs = tcs; Settings settings = settingsService.getSettings(); var readerSettings = settings.getReaderSettings(chapter.getMangaId()); @@ -157,7 +147,7 @@ private void replaceReader(ReaderDirection direction, Chapter chapter) { reader.addReaderReachEndListener( e -> { - if (mangaService.setChapterRead(chapter.getId())) { + if (mangaService.setChapterRead(chapter.getId(), chapter.getMangaId())) { log.info("Set chapter {} to read", chapter.getName()); } else { log.warn("Couldn't set chapter {} to read", chapter.getName()); @@ -166,20 +156,6 @@ private void replaceReader(ReaderDirection direction, Chapter chapter) { e.unregisterListener(); }); - int mangaId = chapter.getMangaId(); - - var tracker = tds.getTracker(mangaId); - - if (tracker.hasAniListId()) { - reader.addReaderReachEndListener( - e -> { - log.debug("Setting chapter {} to read on AniList", chapter.getName()); - trackerExecutor.submit( - () -> tcs.setChapterProgress(mangaId, chapter.getChapterNumber(), true)); - e.unregisterListener(); - }); - } - add(sidebar, reader, controls); } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java new file mode 100644 index 00000000..2e23bc47 --- /dev/null +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java @@ -0,0 +1,14 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package online.hatsunemiku.tachideskvaadinui.data.tachidesk; + +import lombok.Data; + +@Data +public class TrackRecord { + private int trackerId; +} diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackerType.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackerType.java new file mode 100644 index 00000000..68e199f3 --- /dev/null +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackerType.java @@ -0,0 +1,45 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package online.hatsunemiku.tachideskvaadinui.data.tachidesk; + +import java.util.Arrays; +import javax.annotation.Nullable; + +/** + * An enumeration representing different types of trackers. + */ +public enum TrackerType { + MAL(1), + ANILIST(2); + + public final int id; + + /** + * Instantiates a TrackerType object with the specified id. + * + * @param id the id of the Tracker on the Suwayomi Server + */ + TrackerType(int id) { + this.id = id; + } + + /** + * Retrieves the {@link TrackerType} object based on the provided id. + * + * @param id the id of the TrackerType + * @return the corresponding {@link TrackerType} object, or null if no match is found + */ + @Nullable + public static TrackerType fromId(int id) { + var match = + Arrays.stream(TrackerType.values()) + .filter(trackerType -> trackerType.id == id) + .findFirst(); + + return match.orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java index 374d4819..df3c884e 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java @@ -47,4 +47,8 @@ public boolean hasMalId() { public void removeAniListId() { aniListId = 0; } + + public void removeMalId() { + malId = 0; + } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java new file mode 100644 index 00000000..791f3de4 --- /dev/null +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java @@ -0,0 +1,80 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package online.hatsunemiku.tachideskvaadinui.data.tracking.statistics; + +import java.util.Objects; +import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListStatus; +import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; + +public class AniListMangaStatistics implements MangaStatistics { + + private final AniListStatus status; + private final int progress; + private final int score; + private final MediaDate startedAt; + private final MediaDate completedAt; + + public AniListMangaStatistics( + AniListStatus status, int progress, int score, MediaDate startedAt, MediaDate completedAt) { + this.status = status; + this.progress = progress; + this.score = score; + this.startedAt = startedAt; + this.completedAt = completedAt; + } + + public AniListStatus status() { + return status; + } + + public int progress() { + return progress; + } + + public int score() { + return score; + } + + public MediaDate startedAt() { + return startedAt; + } + + public MediaDate completedAt() { + return completedAt; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + var that = (AniListMangaStatistics) obj; + return Objects.equals(this.status, that.status) && + this.progress == that.progress && + this.score == that.score && + Objects.equals(this.startedAt, that.startedAt) && + Objects.equals(this.completedAt, that.completedAt); + } + + @Override + public int hashCode() { + return Objects.hash(status, progress, score, startedAt, completedAt); + } + + @Override + public String toString() { + return "AniListMangaStatistics[" + + "status=" + status + ", " + + "progress=" + progress + ", " + + "score=" + score + ", " + + "startedAt=" + startedAt + ", " + + "completedAt=" + completedAt + ']'; + } +} diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java new file mode 100644 index 00000000..57d18d8a --- /dev/null +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java @@ -0,0 +1,82 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package online.hatsunemiku.tachideskvaadinui.data.tracking.statistics; + +import dev.katsute.mal4j.manga.property.MangaStatus; +import java.util.Objects; +import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; + +public class MALMangaStatistics implements MangaStatistics { + + private final MangaStatus status; + private final int progress; + private final int score; + private final MediaDate startedAt; + private final MediaDate completedAt; + + public MALMangaStatistics(MangaStatus status, int progress, int score, MediaDate startedAt, + MediaDate completedAt) { + this.status = status; + this.progress = progress; + this.score = score; + this.startedAt = startedAt; + this.completedAt = completedAt; + } + + public MangaStatus status() { + return status; + } + + public int progress() { + return progress; + } + + public int score() { + return score; + } + + public MediaDate startedAt() { + return startedAt; + } + + public MediaDate completedAt() { + return completedAt; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + var that = (MALMangaStatistics) obj; + return Objects.equals(this.status, that.status) && + this.progress == that.progress && + this.score == that.score && + Objects.equals(this.startedAt, that.startedAt) && + Objects.equals(this.completedAt, that.completedAt); + } + + @Override + public int hashCode() { + return Objects.hash(status, progress, score, startedAt, completedAt); + } + + @Override + public String toString() { + return "MALMangaStatistics[" + + "status=" + status + ", " + + "progress=" + progress + ", " + + "score=" + score + ", " + + "startedAt=" + startedAt + ", " + + "completedAt=" + completedAt + ']'; + } + + +} diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/anilist/responses/AniListMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java similarity index 50% rename from src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/anilist/responses/AniListMangaStatistics.java rename to src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java index 7b10d4f3..c4bf2040 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/anilist/responses/AniListMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java @@ -4,10 +4,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.responses; +package online.hatsunemiku.tachideskvaadinui.data.tracking.statistics; -import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListStatus; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; -public record AniListMangaStatistics( - AniListStatus status, int progress, int score, MediaDate startedAt, MediaDate completedAt) {} +public interface MangaStatistics { + int progress(); + int score(); + MediaDate startedAt(); + MediaDate completedAt(); +} diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java index beeef5ce..e049cac3 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java @@ -13,6 +13,7 @@ import online.hatsunemiku.tachideskvaadinui.services.client.DownloadClient; import online.hatsunemiku.tachideskvaadinui.services.client.DownloadClient.DownloadChangeEvent; import online.hatsunemiku.tachideskvaadinui.services.client.MangaClient; +import online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -26,12 +27,15 @@ public class MangaService { private final MangaClient mangaClient; private final DownloadClient downloadClient; private final Flux> downloadChangeEventTracker; + private final SuwayomiTrackingService suwayomiTrackingService; @Autowired - public MangaService(MangaClient mangaClient, DownloadClient downloadCLient) { + public MangaService(MangaClient mangaClient, DownloadClient downloadCLient, + SuwayomiTrackingService suwayomiTrackingService) { this.mangaClient = mangaClient; this.downloadClient = downloadCLient; this.downloadChangeEventTracker = downloadCLient.trackDownloads(); + this.suwayomiTrackingService = suwayomiTrackingService; } /** @@ -89,9 +93,19 @@ public Chapter getChapter(int chapterId) { * @param chapterId the ID of the chapter to be set as read * @return {@code true} if the chapter was successfully set as read, {@code false} otherwise */ - public boolean setChapterRead(int chapterId) { + public boolean setChapterRead(int chapterId, int mangaId) { try { - return mangaClient.setChapterRead(chapterId); + + boolean updated = mangaClient.setChapterRead(chapterId); + + if (!updated) { + return false; + } + + suwayomiTrackingService.trackProgress(mangaId); + + return true; + } catch (Exception e) { return false; } @@ -201,21 +215,19 @@ public void addDownloadTrackListener(int chapterId, Runnable callback) { var subscription = downloadChangeEventTracker.subscribe( - events -> { - events.forEach( - event -> { - if (event.chapter().id() != chapterId) { - return; - } - - if (event.progress() != 1) { - return; - } - - callback.run(); - cancellation.dispose(); - }); - }); + events -> events.forEach( + event -> { + if (event.chapter().id() != chapterId) { + return; + } + + if (event.progress() != 1) { + return; + } + + callback.run(); + cancellation.dispose(); + })); cancellation.add(subscription); } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java index e1b993b3..1432ac6c 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java @@ -10,6 +10,7 @@ import java.time.Duration; import java.util.List; import java.util.Map; +import online.hatsunemiku.tachideskvaadinui.data.tachidesk.TrackRecord; import online.hatsunemiku.tachideskvaadinui.data.tracking.search.TrackerSearchResult; import online.hatsunemiku.tachideskvaadinui.services.WebClientService; import org.intellij.lang.annotations.Language; @@ -105,7 +106,7 @@ query GetTrackerAuthUrl($id: Int!) { * Logs in to a tracker using the provided redirect URL and tracker ID. * * @param url the redirect URL to log in to the tracker - * @param id the ID of the tracker to log in to + * @param id the ID of the tracker to log in to */ public void loginTracker(String url, int id) { @Language("graphql") @@ -137,7 +138,7 @@ mutation LoginTracker($url: String!, $trackerId: Int!) { * Searches for a manga on a tracker using the provided query and tracker ID. * * @param query the search query for the manga - * @param id the ID of the tracker to search on + * @param id the ID of the tracker to search on * @return a list of {@link TrackerSearchResult} objects representing the search results * @see online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService.TrackerType */ @@ -180,7 +181,8 @@ query searchTracker($query: String!, $id: Int!) { throw new RuntimeException(errorText); } - TypeRef> typeRef = new TypeRef<>() {}; + TypeRef> typeRef = new TypeRef<>() { + }; return response.extractValueAsObject("searchTracker.trackSearches", typeRef); } @@ -188,27 +190,31 @@ query searchTracker($query: String!, $id: Int!) { /** * Tracks a manga on a tracker using the provided manga ID, external ID, and tracker ID. * - * @param mangaId the Suwayomi ID of the manga to be tracked + * @param mangaId the Suwayomi ID of the manga to be tracked * @param externalId the external ID of the manga on the tracker. This is the ID of the manga on - * the tracker's website. - * @param trackerId the ID of the tracker to track the manga on. + * the tracker's website. + * @param trackerId the ID of the tracker to track the manga on. * @see online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService.TrackerType */ @SuppressWarnings("JavadocReference") - public void trackMangaOnTracker(int mangaId, int externalId, int trackerId) { + public void trackMangaOnTracker(int mangaId, long externalId, int trackerId) { @Language("graphql") var query = """ - mutation TrackManga($mangaId: Int!, $remoteId: Int!, $trackerId: Int!) { + mutation TrackManga($mangaId: Int!, $remoteId: LongString!, $trackerId: Int!) { bindTrack(input: {mangaId: $mangaId, remoteId: $remoteId, trackerId: $trackerId}) { trackRecord { id + trackerId + mangaId } } } """; - var variables = Map.of("mangaId", mangaId, "externalId", externalId, "trackerId", trackerId); + String remoteId = String.valueOf(externalId); + + var variables = Map.of("mangaId", mangaId, "remoteId", remoteId, "trackerId", trackerId); var graphClient = clientService.getDgsGraphQlClient(); @@ -219,5 +225,70 @@ mutation TrackManga($mangaId: Int!, $remoteId: Int!, $trackerId: Int!) { throw new RuntimeException( "Didn't receive a response from the server after trying to track the manga"); } + + if (optional.get().hasErrors()) { + throw new RuntimeException("Error while tracking manga: " + optional.get().getErrors()); + } + } + + public void trackProgress(int mangaId) { + @Language("graphql") + var query = + """ + mutation TrackProgressOnTrackers($mangaId: Int!) { + trackProgress(input: {mangaId: $mangaId}) { + trackRecords { + id + } + } + } + """; + + var graphClient = clientService.getDgsGraphQlClient(); + + var variables = Map.of("mangaId", mangaId); + + Duration timeout = Duration.ofSeconds(10); + var optional = graphClient.reactiveExecuteQuery(query, variables).blockOptional(timeout); + + if (optional.isEmpty()) { + throw new RuntimeException( + "Didn't receive a response from the server after trying to track the manga"); + } + + log.info("Tracked progress on trackers"); + } + + public boolean isMangaTracked(int mangaId, int trackerId) { + @Language("graphql") + var query = """ + query IsMangaTracked($mangaId: Int!) { + manga(id: $mangaId) { + trackRecords { + nodes { + trackerId + } + } + } + } + """; + + var variables = Map.of("mangaId", mangaId, "trackerId", trackerId); + + var graphClient = clientService.getDgsGraphQlClient(); + + var response = graphClient.reactiveExecuteQuery(query, variables).block(); + + if (response == null) { + throw new RuntimeException("Error while checking if manga is tracked"); + } + + + TypeRef> typeRef = new TypeRef<>() { + }; + + var trackRecords = response.extractValueAsObject("manga.trackRecords.nodes", typeRef); + + return trackRecords.stream().anyMatch(record -> record.getTrackerId() == trackerId); } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/AniListAPIService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/AniListAPIService.java index 49a2dc50..6e7e6e31 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/AniListAPIService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/AniListAPIService.java @@ -21,7 +21,6 @@ import lombok.extern.slf4j.Slf4j; import online.hatsunemiku.tachideskvaadinui.data.tracking.OAuthData; import online.hatsunemiku.tachideskvaadinui.data.tracking.TrackerTokens; -import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListMangaListResponse; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListMedia; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListScoreFormat; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListStatus; @@ -31,12 +30,11 @@ import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.responses.AniListAddMangaResponse; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.responses.AniListChangePrivacyStatusResponse; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.responses.AniListChangeStatusResponse; -import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.responses.AniListMangaStatistics; +import online.hatsunemiku.tachideskvaadinui.data.tracking.statistics.AniListMangaStatistics; import online.hatsunemiku.tachideskvaadinui.services.TrackingDataService; import org.jetbrains.annotations.Nullable; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; -import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; @@ -137,60 +135,6 @@ public String getAniListAuthUrl() { return String.format(OAUTH_CODE_PATTERN, OAUTH_CLIENT_ID); } - /** - * Searches for manga with the given name on AniList. Returns a response object containing a list - * of manga, which are possible matches for the given name. - * - * @param name The name of the manga to search for - * @return An {@link AniListMangaListResponse} object representing the manga search results - */ - public AniListMangaListResponse searchManga(String name) { - String query = - """ - query Search($search: String) { - Page(perPage: 50) { - media(search: $search, type: MANGA, format_not_in: [NOVEL]) { - id - title { - romaji - english - native - userPreferred - } - coverImage { - large - } - format - status - chapters - description - startDate { - year - month - day - } - } - } - } - """; - - String variables = """ - { - "search": "%s" - } - """.formatted(name); - - GraphQLRequest request = new GraphQLRequest(query, variables); - - return webClient - .post() - .contentType(MediaType.APPLICATION_JSON) - .body(BodyInserters.fromValue(request)) - .retrieve() - .bodyToMono(AniListMangaListResponse.class) - .block(); - } - /** * Returns the current user's ID. * diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java index 91bf8422..51e08ba6 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java @@ -20,12 +20,16 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import online.hatsunemiku.tachideskvaadinui.data.tracking.OAuthData; +import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; import online.hatsunemiku.tachideskvaadinui.services.TrackingDataService; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -49,12 +53,13 @@ public class MyAnimeListAPIService { private final TrackingDataService tds; private final WebClient webClient; private final Cache pkceCache; - @Nullable private MyAnimeList mal; + @Nullable + private MyAnimeList mal; /** * Initializes an instance of the MyAnimeListAPIService class. * - * @param tds The {@link TrackingDataService} used for storing tokens. + * @param tds The {@link TrackingDataService} used for storing tokens. * @param webClient The {@link WebClient} used for making requests to the MAL API. */ public MyAnimeListAPIService(TrackingDataService tds, WebClient webClient) { @@ -127,7 +132,7 @@ public boolean hasMalToken() { * Exchanges the authorization code for an access and refresh token. Verifies the PKCE ID before * exchanging the code for tokens. * - * @param code The authorization code to exchange for tokens. + * @param code The authorization code to exchange for tokens. * @param pkceId The PKCE ID used for generating the code challenge. */ public void exchangeCodeForTokens(String code, String pkceId) { @@ -199,4 +204,70 @@ public List getMangaWithStatus(MangaStatus status) { return list.stream().map(MangaListStatus::getManga).toList(); } + + public Manga getManga(int id) { + if (mal == null) { + throw new IllegalStateException("Not authenticated with MAL"); + } + + return mal.getManga(id); + } + + public void updateMangaListStatus(int id, MangaStatus status) { + if (mal == null) { + throw new IllegalStateException("Not authenticated with MAL"); + } + + mal.updateMangaListing(id).status(status).update(); + } + + public void updateMangaListScore(int id, int score) { + if (mal == null) { + throw new IllegalStateException("Not authenticated with MAL"); + } + + mal.updateMangaListing(id).score(score).update(); + } + + public void updateMangaListStartDate(int malId, MediaDate date) { + if (mal == null) { + throw new IllegalStateException("Not authenticated with MAL"); + } + + Instant instant = LocalDate.of(date.year(), date.month(), date.day()).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); + Date startDate = Date.from(instant); + + mal.updateMangaListing(malId).startDate(startDate).update(); + } + + public void updateMangaListEndDate(int malId, MediaDate date) { + if (mal == null) { + throw new IllegalStateException("Not authenticated with MAL"); + } + + if (date.year() == null || date.month() == null || date.day() == null) { + return; + } + + Instant instant = LocalDate.of(date.year(), date.month(), date.day()).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); + Date endDate = Date.from(instant); + + mal.updateMangaListing(malId).finishDate(endDate).update(); + } + + public void removeMangaFromList(int malId) { + if (mal == null) { + throw new IllegalStateException("Not authenticated with MAL"); + } + + mal.deleteMangaListing(malId); + } + + public void updateMangaListProgress(int malId, int value) { + if (mal == null) { + throw new IllegalStateException("Not authenticated with MAL"); + } + + mal.updateMangaListing(malId).chaptersRead(value).update(); + } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java index 63aeddf3..71145ee5 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java @@ -6,11 +6,13 @@ package online.hatsunemiku.tachideskvaadinui.services.tracker; -import java.util.Arrays; import java.util.List; -import javax.annotation.Nullable; -import lombok.Getter; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; +import online.hatsunemiku.tachideskvaadinui.data.tachidesk.ServerVersion; +import online.hatsunemiku.tachideskvaadinui.data.tachidesk.TrackerType; import online.hatsunemiku.tachideskvaadinui.data.tracking.search.TrackerSearchResult; +import online.hatsunemiku.tachideskvaadinui.services.SuwayomiService; import online.hatsunemiku.tachideskvaadinui.services.client.suwayomi.SuwayomiTrackingClient; import org.springframework.stereotype.Service; @@ -19,19 +21,24 @@ * wrapper around the {@link SuwayomiTrackingClient} class and provides methods to abstract the * inner workings of the client. Also provides convenience methods for handling tracking requests. */ +@Slf4j @Service public class SuwayomiTrackingService { private final SuwayomiTrackingClient client; + private final SuwayomiService suwayomiService; /** * Represents a Suwayomi Tracking Service. * - * @param client the {@link SuwayomiTrackingClient} used for handling tracking requests to the - * Suwayomi Server. + * @param client the {@link SuwayomiTrackingClient} used for handling tracking requests + * to the Suwayomi Server. + * @param suwayomiService the {@link SuwayomiService} used for getting meta-data about the + * Suwayomi Server. */ - public SuwayomiTrackingService(SuwayomiTrackingClient client) { + public SuwayomiTrackingService(SuwayomiTrackingClient client, SuwayomiService suwayomiService) { this.client = client; + this.suwayomiService = suwayomiService; } /** @@ -101,7 +108,7 @@ public List searchMAL(String query) { /** * Tracks a manga on AniList using the provided manga ID and external ID. * - * @param mangaId the ID of the manga to be tracked + * @param mangaId the ID of the manga to be tracked * @param externalId the external ID of the manga on AniList */ public void trackOnAniList(int mangaId, int externalId) { @@ -112,7 +119,7 @@ public void trackOnAniList(int mangaId, int externalId) { /** * Tracks a manga on MyAnimeList (MAL) using the provided manga ID and external ID. * - * @param mangaId the ID of the manga to be tracked + * @param mangaId the ID of the manga to be tracked * @param externalId the external ID of the manga on MAL */ public void trackOnMAL(int mangaId, int externalId) { @@ -120,10 +127,20 @@ public void trackOnMAL(int mangaId, int externalId) { client.trackMangaOnTracker(mangaId, externalId, id); } + public boolean isMangaTrackedOnAniList(int mangaId) { + int id = TrackerType.ANILIST.id; + return client.isMangaTracked(mangaId, id); + } + + public boolean isMangaTrackedOnMAL(int mangaId) { + int id = TrackerType.MAL.id; + return client.isMangaTracked(mangaId, id); + } + /** * Logs in to the Suwayomi tracker with the specified URL and tracker ID. * - * @param url the URL used for the login callback + * @param url the URL used for the login callback * @param trackerId the ID of the tracker to log in to */ public void loginSuwayomi(String url, int trackerId) { @@ -163,37 +180,19 @@ private String getStateAuthParam(int id) { return template.formatted(json); } - /** An enumeration representing different types of trackers. */ - @Getter - private enum TrackerType { - MAL(1), - ANILIST(2); - - private final int id; - - /** - * Instantiates a TrackerType object with the specified id. - * - * @param id the id of the Tracker on the Suwayomi Server - */ - TrackerType(int id) { - this.id = id; + public void trackProgress(int mangaId) { + Optional version = suwayomiService.getServerVersion(); + + if (version.isEmpty()) { + log.warn("Failed to get server version"); + return; } - /** - * Retrieves the {@link TrackerType} object based on the provided id. - * - * @param id the id of the TrackerType - * @return the corresponding {@link TrackerType} object, or null if no match is found - */ - @Nullable - public static TrackerType fromId(int id) { - var match = - Arrays.stream(TrackerType.values()) - .filter(trackerType -> trackerType.id == id) - .findFirst(); - - return match.orElse(null); + int revision = version.get().getRevisionNumber(); + + if (revision > 1510) { + log.info("Tracking progress manually"); + client.trackProgress(mangaId); } } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java index 751247f7..3388e792 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java @@ -21,10 +21,10 @@ public class TachideskUtils { private static final Logger logger = LoggerFactory.getLogger(TachideskUtils.class); private static final Pattern JAR_PATTERN = Pattern.compile( - "https://github\\.com/Suwayomi/Suwayomi-Server/releases/download/(v\\d+\\.\\d+\\.\\d+(-r\\d+)?)/(Suwayomi-Server-v\\d+\\.\\d+\\.\\d+-r(\\d+)\\.jar)"); + "https://github\\.com/Suwayomi/Suwayomi-Server-preview/releases/download/(v\\d+\\.\\d+\\.\\d+(-r\\d+)?)/(Suwayomi-Server-v\\d+\\.\\d+\\.\\d+-r(\\d+)\\.jar)"); public static String getNewestJarUrl(RestTemplate client) { - String githubApi = "https://api.github.com/repos/Suwayomi/Suwayomi-Server/releases/latest"; + String githubApi = "https://api.github.com/repos/Suwayomi/Suwayomi-Server-preview/releases/latest"; String json = client.getForObject(githubApi, String.class); if (json == null) { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/MangaView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/MangaView.java index b24ff59e..62fc55af 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/MangaView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/MangaView.java @@ -36,6 +36,7 @@ import online.hatsunemiku.tachideskvaadinui.services.SettingsService; import online.hatsunemiku.tachideskvaadinui.services.TrackingDataService; import online.hatsunemiku.tachideskvaadinui.services.tracker.AniListAPIService; +import online.hatsunemiku.tachideskvaadinui.services.tracker.MyAnimeListAPIService; import online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService; import online.hatsunemiku.tachideskvaadinui.utils.RouteUtils; import online.hatsunemiku.tachideskvaadinui.view.layout.StandardLayout; @@ -54,6 +55,7 @@ public class MangaView extends StandardLayout implements BeforeEnterObserver { private final AniListAPIService aniListAPIService; private final TrackingDataService dataService; private final SuwayomiTrackingService suwayomiTrackingService; + private final MyAnimeListAPIService malAPI; /** * Creates a MangaView object. @@ -70,13 +72,15 @@ public MangaView( SettingsService settingsService, AniListAPIService aniListAPIService, TrackingDataService dataService, - SuwayomiTrackingService suwayomiTrackingService) { + SuwayomiTrackingService suwayomiTrackingService, + MyAnimeListAPIService malAPI) { super("Manga"); this.mangaService = mangaService; this.settingsService = settingsService; this.aniListAPIService = aniListAPIService; this.dataService = dataService; this.suwayomiTrackingService = suwayomiTrackingService; + this.malAPI = malAPI; } @Override @@ -157,8 +161,9 @@ private Div getButtons(Manga manga, List chapters) { trackBtn.addClickListener( e -> { + var dialog = - new TrackingDialog(dataService, manga, aniListAPIService, suwayomiTrackingService); + new TrackingDialog(dataService, manga, aniListAPIService, suwayomiTrackingService, malAPI); dialog.open(); }); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java index 1a0dac49..3aa241e9 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java @@ -23,8 +23,6 @@ import online.hatsunemiku.tachideskvaadinui.data.tachidesk.Chapter; import online.hatsunemiku.tachideskvaadinui.services.MangaService; import online.hatsunemiku.tachideskvaadinui.services.SettingsService; -import online.hatsunemiku.tachideskvaadinui.services.TrackingCommunicationService; -import online.hatsunemiku.tachideskvaadinui.services.TrackingDataService; import online.hatsunemiku.tachideskvaadinui.view.layout.StandardLayout; @Route("reading/:mangaId(\\d+)/:chapterId(\\d+)") @@ -35,20 +33,14 @@ public class ReadingView extends StandardLayout private final MangaService mangaService; private final SettingsService settingsService; - private final TrackingDataService dataService; - private final TrackingCommunicationService communicationService; public ReadingView( MangaService mangaService, - SettingsService settingsService, - TrackingDataService dataService, - TrackingCommunicationService communicationService) { + SettingsService settingsService) { super("Reading"); this.mangaService = mangaService; this.settingsService = settingsService; - this.dataService = dataService; - this.communicationService = communicationService; fullScreen(); } @@ -97,7 +89,7 @@ public void beforeEnter(BeforeEnterEvent event) { var reader = new MangaReader( - chapterObj, settingsService, dataService, mangaService, communicationService, chapters); + chapterObj, settingsService, mangaService, chapters); reader.addReaderChapterChangeEventListener(this::processReaderChapterChangeEvent); @@ -115,9 +107,7 @@ private void processReaderChapterChangeEvent(ReaderChapterChangeEvent event) { new MangaReader( nextChapter, settingsService, - dataService, mangaService, - communicationService, chapters); nextReader.addReaderChapterChangeEventListener(this::processReaderChapterChangeEvent); From 88bf04a90b650bcb56c5f0a02214d0ab1fac59e4 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:14:56 +0000 Subject: [PATCH 2/9] style: format code with Google Java Format This commit fixes the style issues introduced in 3ac4ecd according to the output from Google Java Format. Details: https://github.com/Suwayomi/Suwayomi-VaadinUI/pull/115 --- .../dialog/tracking/TrackingDialog.java | 76 +++++++++---------- .../data/tachidesk/TrackerType.java | 10 +-- .../statistics/AniListMangaStatistics.java | 32 +++++--- .../statistics/MALMangaStatistics.java | 38 ++++++---- .../tracking/statistics/MangaStatistics.java | 3 + .../services/MangaService.java | 31 ++++---- .../suwayomi/SuwayomiTrackingClient.java | 20 +++-- .../tracker/MyAnimeListAPIService.java | 19 +++-- .../tracker/SuwayomiTrackingService.java | 12 +-- .../utils/TachideskUtils.java | 3 +- .../tachideskvaadinui/view/MangaView.java | 4 +- .../tachideskvaadinui/view/ReadingView.java | 15 +--- 12 files changed, 139 insertions(+), 124 deletions(-) diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java index d1e972f1..81a44c58 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java @@ -59,14 +59,14 @@ public class TrackingDialog extends Dialog { /** * Constructs a {@link TrackingDialog} with the given parameters. * - * @param dataService The {@link TrackingDataService} used for storing tracking data. - * @param manga the {@link Manga} to track with the dialog. - * @param aniListAPIService the {@link AniListAPIService} used for making requests to the - * AniList API. + * @param dataService The {@link TrackingDataService} used for storing tracking data. + * @param manga the {@link Manga} to track with the dialog. + * @param aniListAPIService the {@link AniListAPIService} used for making requests to the AniList + * API. * @param suwayomiTrackingService the {@link SuwayomiTrackingService} used for making requests to - * the Suwayomi API. - * @param malAPI the {@link MyAnimeListAPIService} used for making requests to - * the MyAnimeList API. + * the Suwayomi API. + * @param malAPI the {@link MyAnimeListAPIService} used for making requests to the MyAnimeList + * API. */ public TrackingDialog( TrackingDataService dataService, @@ -120,10 +120,10 @@ public TrackingDialog( /** * Adds the tracking buttons to the dialog. * - * @param manga the {@link Manga} to track + * @param manga the {@link Manga} to track * @param aniListAPIService the {@link AniListAPIService} to communicate with AniList with - * @param tracker the {@link Tracker} instance to update the button states via - * {@link #updateButtons(Button, Button, Tracker)} + * @param tracker the {@link Tracker} instance to update the button states via {@link + * #updateButtons(Button, Button, Tracker)} */ private void addTrackingButtons( Manga manga, AniListAPIService aniListAPIService, Tracker tracker) { @@ -159,7 +159,7 @@ private void addTrackingButtons( try { displaySearch(manga.getTitle(), manga.getId(), provider); } catch (WebClientResponseException.InternalServerError - | WebClientRequestException error) { + | WebClientRequestException error) { log.error("Invalid response from AniList", error); Notification notification = new Notification(); notification.addThemeVariants(NotificationVariant.LUMO_ERROR); @@ -188,7 +188,7 @@ private void addTrackingButtons( try { displaySearch(manga.getTitle(), manga.getId(), provider); } catch (WebClientResponseException.InternalServerError - | WebClientRequestException error) { + | WebClientRequestException error) { log.error("Invalid response from MyAnimeList", error); Notification notification = new Notification(); notification.addThemeVariants(NotificationVariant.LUMO_ERROR); @@ -223,7 +223,7 @@ private void addTrackingButtons( private Div getTrackingStatistics(Tracker tracker, TrackerProvider provider) throws RuntimeException { - //get manga stats via provider + // get manga stats via provider MangaStatistics mangaStats; try { mangaStats = provider.getStatistics(tracker); @@ -294,7 +294,6 @@ private Div getTrackingStatistics(Tracker tracker, TrackerProvider provider) Button trackingDeleteBtn = new Button(trashBtnText, VaadinIcon.TRASH.create()); trackingDeleteBtn.addClickListener( e -> { - if (type == TrackerType.ANILIST) { tracker.removeAniListId(); } else { @@ -308,7 +307,6 @@ private Div getTrackingStatistics(Tracker tracker, TrackerProvider provider) var nukeBtn = new Button(nukeBtnText, VaadinIcon.BOMB.create()); nukeBtn.addClickListener( e -> { - if (type == TrackerType.ANILIST) { aniListAPI.removeMangaFromList(tracker.getAniListId()); tracker.removeAniListId(); @@ -456,8 +454,8 @@ private SuperDatePicker getTrackingStartDateField( } @NotNull - private SuperIntegerField getTrackingScoreField(Tracker tracker, MangaStatistics mangaStats, - TrackerProvider provider) { + private SuperIntegerField getTrackingScoreField( + Tracker tracker, MangaStatistics mangaStats, TrackerProvider provider) { AniListScoreFormat format = provider.getScoreFormat(); SuperIntegerField score = new SuperIntegerField(); @@ -527,34 +525,36 @@ private ComboBox getTrackingStatusField(Tracker tracker, MangaStatistics mang return status; } - private ComboBox configureStatusComboBoxAniList(Tracker tracker, - AniListMangaStatistics mangaStats) { + private ComboBox configureStatusComboBoxAniList( + Tracker tracker, AniListMangaStatistics mangaStats) { ComboBox status = new ComboBox<>(); status.setItems(AniListStatus.values()); status.setValue(mangaStats.status()); - status.addValueChangeListener(e -> { - if (e.getValue() == null) { - status.setValue(e.getOldValue()); - } else { - aniListAPI.updateMangaStatus(tracker.getAniListId(), e.getValue()); - } - }); + status.addValueChangeListener( + e -> { + if (e.getValue() == null) { + status.setValue(e.getOldValue()); + } else { + aniListAPI.updateMangaStatus(tracker.getAniListId(), e.getValue()); + } + }); return status; } - private ComboBox configureStatusComboBoxMAL(Tracker tracker, - MALMangaStatistics mangaStats) { + private ComboBox configureStatusComboBoxMAL( + Tracker tracker, MALMangaStatistics mangaStats) { ComboBox status = new ComboBox<>(); status.setItems(MangaStatus.values()); status.setValue(mangaStats.status()); - status.addValueChangeListener(e -> { - if (e.getValue() == null) { - status.setValue(e.getOldValue()); - } else { - malAPI.updateMangaListStatus(tracker.getMalId(), e.getValue()); - } - }); + status.addValueChangeListener( + e -> { + if (e.getValue() == null) { + status.setValue(e.getOldValue()); + } else { + malAPI.updateMangaListStatus(tracker.getMalId(), e.getValue()); + } + }); return status; } @@ -594,9 +594,7 @@ private SuperIntegerField getTrackingChapterField( malAPI.updateMangaListProgress(tracker.getMalId(), e.getValue()); } else { throw new IllegalArgumentException("Unknown MangaStatistics type"); - } - }); return chapter; } @@ -621,8 +619,8 @@ private void displaySearch(String mangaName, int mangaId, TrackerProvider tracke * Updates the tracking buttons with the current tracking status. * * @param aniListBtn the AniList tracking button - * @param malBtn the MyAnimeList tracking button - * @param tracker the tracker to check the status of + * @param malBtn the MyAnimeList tracking button + * @param tracker the tracker to check the status of */ private void updateButtons(Button aniListBtn, Button malBtn, Tracker tracker) { if (tracker.hasAniListId()) { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackerType.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackerType.java index 68e199f3..11017988 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackerType.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackerType.java @@ -9,9 +9,7 @@ import java.util.Arrays; import javax.annotation.Nullable; -/** - * An enumeration representing different types of trackers. - */ +/** An enumeration representing different types of trackers. */ public enum TrackerType { MAL(1), ANILIST(2); @@ -36,10 +34,8 @@ public enum TrackerType { @Nullable public static TrackerType fromId(int id) { var match = - Arrays.stream(TrackerType.values()) - .filter(trackerType -> trackerType.id == id) - .findFirst(); + Arrays.stream(TrackerType.values()).filter(trackerType -> trackerType.id == id).findFirst(); return match.orElse(null); } -} \ No newline at end of file +} diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java index 791f3de4..5f086fa2 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java @@ -56,11 +56,11 @@ public boolean equals(Object obj) { return false; } var that = (AniListMangaStatistics) obj; - return Objects.equals(this.status, that.status) && - this.progress == that.progress && - this.score == that.score && - Objects.equals(this.startedAt, that.startedAt) && - Objects.equals(this.completedAt, that.completedAt); + return Objects.equals(this.status, that.status) + && this.progress == that.progress + && this.score == that.score + && Objects.equals(this.startedAt, that.startedAt) + && Objects.equals(this.completedAt, that.completedAt); } @Override @@ -70,11 +70,21 @@ public int hashCode() { @Override public String toString() { - return "AniListMangaStatistics[" + - "status=" + status + ", " + - "progress=" + progress + ", " + - "score=" + score + ", " + - "startedAt=" + startedAt + ", " + - "completedAt=" + completedAt + ']'; + return "AniListMangaStatistics[" + + "status=" + + status + + ", " + + "progress=" + + progress + + ", " + + "score=" + + score + + ", " + + "startedAt=" + + startedAt + + ", " + + "completedAt=" + + completedAt + + ']'; } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java index 57d18d8a..3f0647b8 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java @@ -18,8 +18,8 @@ public class MALMangaStatistics implements MangaStatistics { private final MediaDate startedAt; private final MediaDate completedAt; - public MALMangaStatistics(MangaStatus status, int progress, int score, MediaDate startedAt, - MediaDate completedAt) { + public MALMangaStatistics( + MangaStatus status, int progress, int score, MediaDate startedAt, MediaDate completedAt) { this.status = status; this.progress = progress; this.score = score; @@ -56,11 +56,11 @@ public boolean equals(Object obj) { return false; } var that = (MALMangaStatistics) obj; - return Objects.equals(this.status, that.status) && - this.progress == that.progress && - this.score == that.score && - Objects.equals(this.startedAt, that.startedAt) && - Objects.equals(this.completedAt, that.completedAt); + return Objects.equals(this.status, that.status) + && this.progress == that.progress + && this.score == that.score + && Objects.equals(this.startedAt, that.startedAt) + && Objects.equals(this.completedAt, that.completedAt); } @Override @@ -70,13 +70,21 @@ public int hashCode() { @Override public String toString() { - return "MALMangaStatistics[" + - "status=" + status + ", " + - "progress=" + progress + ", " + - "score=" + score + ", " + - "startedAt=" + startedAt + ", " + - "completedAt=" + completedAt + ']'; + return "MALMangaStatistics[" + + "status=" + + status + + ", " + + "progress=" + + progress + + ", " + + "score=" + + score + + ", " + + "startedAt=" + + startedAt + + ", " + + "completedAt=" + + completedAt + + ']'; } - - } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java index c4bf2040..2cb948e9 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java @@ -10,7 +10,10 @@ public interface MangaStatistics { int progress(); + int score(); + MediaDate startedAt(); + MediaDate completedAt(); } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java index e049cac3..fd5c25a0 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java @@ -30,7 +30,9 @@ public class MangaService { private final SuwayomiTrackingService suwayomiTrackingService; @Autowired - public MangaService(MangaClient mangaClient, DownloadClient downloadCLient, + public MangaService( + MangaClient mangaClient, + DownloadClient downloadCLient, SuwayomiTrackingService suwayomiTrackingService) { this.mangaClient = mangaClient; this.downloadClient = downloadCLient; @@ -215,19 +217,20 @@ public void addDownloadTrackListener(int chapterId, Runnable callback) { var subscription = downloadChangeEventTracker.subscribe( - events -> events.forEach( - event -> { - if (event.chapter().id() != chapterId) { - return; - } - - if (event.progress() != 1) { - return; - } - - callback.run(); - cancellation.dispose(); - })); + events -> + events.forEach( + event -> { + if (event.chapter().id() != chapterId) { + return; + } + + if (event.progress() != 1) { + return; + } + + callback.run(); + cancellation.dispose(); + })); cancellation.add(subscription); } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java index 1432ac6c..722be3bd 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java @@ -106,7 +106,7 @@ query GetTrackerAuthUrl($id: Int!) { * Logs in to a tracker using the provided redirect URL and tracker ID. * * @param url the redirect URL to log in to the tracker - * @param id the ID of the tracker to log in to + * @param id the ID of the tracker to log in to */ public void loginTracker(String url, int id) { @Language("graphql") @@ -138,7 +138,7 @@ mutation LoginTracker($url: String!, $trackerId: Int!) { * Searches for a manga on a tracker using the provided query and tracker ID. * * @param query the search query for the manga - * @param id the ID of the tracker to search on + * @param id the ID of the tracker to search on * @return a list of {@link TrackerSearchResult} objects representing the search results * @see online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService.TrackerType */ @@ -181,8 +181,7 @@ query searchTracker($query: String!, $id: Int!) { throw new RuntimeException(errorText); } - TypeRef> typeRef = new TypeRef<>() { - }; + TypeRef> typeRef = new TypeRef<>() {}; return response.extractValueAsObject("searchTracker.trackSearches", typeRef); } @@ -190,10 +189,10 @@ query searchTracker($query: String!, $id: Int!) { /** * Tracks a manga on a tracker using the provided manga ID, external ID, and tracker ID. * - * @param mangaId the Suwayomi ID of the manga to be tracked + * @param mangaId the Suwayomi ID of the manga to be tracked * @param externalId the external ID of the manga on the tracker. This is the ID of the manga on - * the tracker's website. - * @param trackerId the ID of the tracker to track the manga on. + * the tracker's website. + * @param trackerId the ID of the tracker to track the manga on. * @see online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService.TrackerType */ @SuppressWarnings("JavadocReference") @@ -261,7 +260,8 @@ mutation TrackProgressOnTrackers($mangaId: Int!) { public boolean isMangaTracked(int mangaId, int trackerId) { @Language("graphql") - var query = """ + var query = + """ query IsMangaTracked($mangaId: Int!) { manga(id: $mangaId) { trackRecords { @@ -283,9 +283,7 @@ query IsMangaTracked($mangaId: Int!) { throw new RuntimeException("Error while checking if manga is tracked"); } - - TypeRef> typeRef = new TypeRef<>() { - }; + TypeRef> typeRef = new TypeRef<>() {}; var trackRecords = response.extractValueAsObject("manga.trackRecords.nodes", typeRef); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java index 51e08ba6..20323b5b 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java @@ -53,13 +53,12 @@ public class MyAnimeListAPIService { private final TrackingDataService tds; private final WebClient webClient; private final Cache pkceCache; - @Nullable - private MyAnimeList mal; + @Nullable private MyAnimeList mal; /** * Initializes an instance of the MyAnimeListAPIService class. * - * @param tds The {@link TrackingDataService} used for storing tokens. + * @param tds The {@link TrackingDataService} used for storing tokens. * @param webClient The {@link WebClient} used for making requests to the MAL API. */ public MyAnimeListAPIService(TrackingDataService tds, WebClient webClient) { @@ -132,7 +131,7 @@ public boolean hasMalToken() { * Exchanges the authorization code for an access and refresh token. Verifies the PKCE ID before * exchanging the code for tokens. * - * @param code The authorization code to exchange for tokens. + * @param code The authorization code to exchange for tokens. * @param pkceId The PKCE ID used for generating the code challenge. */ public void exchangeCodeForTokens(String code, String pkceId) { @@ -234,7 +233,11 @@ public void updateMangaListStartDate(int malId, MediaDate date) { throw new IllegalStateException("Not authenticated with MAL"); } - Instant instant = LocalDate.of(date.year(), date.month(), date.day()).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); + Instant instant = + LocalDate.of(date.year(), date.month(), date.day()) + .atStartOfDay() + .atZone(ZoneId.systemDefault()) + .toInstant(); Date startDate = Date.from(instant); mal.updateMangaListing(malId).startDate(startDate).update(); @@ -249,7 +252,11 @@ public void updateMangaListEndDate(int malId, MediaDate date) { return; } - Instant instant = LocalDate.of(date.year(), date.month(), date.day()).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); + Instant instant = + LocalDate.of(date.year(), date.month(), date.day()) + .atStartOfDay() + .atZone(ZoneId.systemDefault()) + .toInstant(); Date endDate = Date.from(instant); mal.updateMangaListing(malId).finishDate(endDate).update(); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java index 71145ee5..10c0d64f 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java @@ -31,10 +31,10 @@ public class SuwayomiTrackingService { /** * Represents a Suwayomi Tracking Service. * - * @param client the {@link SuwayomiTrackingClient} used for handling tracking requests - * to the Suwayomi Server. + * @param client the {@link SuwayomiTrackingClient} used for handling tracking requests to the + * Suwayomi Server. * @param suwayomiService the {@link SuwayomiService} used for getting meta-data about the - * Suwayomi Server. + * Suwayomi Server. */ public SuwayomiTrackingService(SuwayomiTrackingClient client, SuwayomiService suwayomiService) { this.client = client; @@ -108,7 +108,7 @@ public List searchMAL(String query) { /** * Tracks a manga on AniList using the provided manga ID and external ID. * - * @param mangaId the ID of the manga to be tracked + * @param mangaId the ID of the manga to be tracked * @param externalId the external ID of the manga on AniList */ public void trackOnAniList(int mangaId, int externalId) { @@ -119,7 +119,7 @@ public void trackOnAniList(int mangaId, int externalId) { /** * Tracks a manga on MyAnimeList (MAL) using the provided manga ID and external ID. * - * @param mangaId the ID of the manga to be tracked + * @param mangaId the ID of the manga to be tracked * @param externalId the external ID of the manga on MAL */ public void trackOnMAL(int mangaId, int externalId) { @@ -140,7 +140,7 @@ public boolean isMangaTrackedOnMAL(int mangaId) { /** * Logs in to the Suwayomi tracker with the specified URL and tracker ID. * - * @param url the URL used for the login callback + * @param url the URL used for the login callback * @param trackerId the ID of the tracker to log in to */ public void loginSuwayomi(String url, int trackerId) { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java index 3388e792..077399f7 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java @@ -24,7 +24,8 @@ public class TachideskUtils { "https://github\\.com/Suwayomi/Suwayomi-Server-preview/releases/download/(v\\d+\\.\\d+\\.\\d+(-r\\d+)?)/(Suwayomi-Server-v\\d+\\.\\d+\\.\\d+-r(\\d+)\\.jar)"); public static String getNewestJarUrl(RestTemplate client) { - String githubApi = "https://api.github.com/repos/Suwayomi/Suwayomi-Server-preview/releases/latest"; + String githubApi = + "https://api.github.com/repos/Suwayomi/Suwayomi-Server-preview/releases/latest"; String json = client.getForObject(githubApi, String.class); if (json == null) { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/MangaView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/MangaView.java index 62fc55af..1ab7018d 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/MangaView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/MangaView.java @@ -161,9 +161,9 @@ private Div getButtons(Manga manga, List chapters) { trackBtn.addClickListener( e -> { - var dialog = - new TrackingDialog(dataService, manga, aniListAPIService, suwayomiTrackingService, malAPI); + new TrackingDialog( + dataService, manga, aniListAPIService, suwayomiTrackingService, malAPI); dialog.open(); }); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java index 3aa241e9..06467db0 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java @@ -34,9 +34,7 @@ public class ReadingView extends StandardLayout private final MangaService mangaService; private final SettingsService settingsService; - public ReadingView( - MangaService mangaService, - SettingsService settingsService) { + public ReadingView(MangaService mangaService, SettingsService settingsService) { super("Reading"); this.mangaService = mangaService; @@ -87,9 +85,7 @@ public void beforeEnter(BeforeEnterEvent event) { return; } - var reader = - new MangaReader( - chapterObj, settingsService, mangaService, chapters); + var reader = new MangaReader(chapterObj, settingsService, mangaService, chapters); reader.addReaderChapterChangeEventListener(this::processReaderChapterChangeEvent); @@ -103,12 +99,7 @@ private void processReaderChapterChangeEvent(ReaderChapterChangeEvent event) { var nextChapter = mangaService.getChapter(nextChapterId); var chapters = event.getChapters(); - var nextReader = - new MangaReader( - nextChapter, - settingsService, - mangaService, - chapters); + var nextReader = new MangaReader(nextChapter, settingsService, mangaService, chapters); nextReader.addReaderChapterChangeEventListener(this::processReaderChapterChangeEvent); From c3e6e8ae496181a84f0bd53de0755e1c5f8e62d2 Mon Sep 17 00:00:00 2001 From: Alessandro Schwaiger Date: Tue, 16 Apr 2024 00:15:08 +0200 Subject: [PATCH 3/9] revert back to stable releases auto-download --- .../hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java index 3388e792..751247f7 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/utils/TachideskUtils.java @@ -21,10 +21,10 @@ public class TachideskUtils { private static final Logger logger = LoggerFactory.getLogger(TachideskUtils.class); private static final Pattern JAR_PATTERN = Pattern.compile( - "https://github\\.com/Suwayomi/Suwayomi-Server-preview/releases/download/(v\\d+\\.\\d+\\.\\d+(-r\\d+)?)/(Suwayomi-Server-v\\d+\\.\\d+\\.\\d+-r(\\d+)\\.jar)"); + "https://github\\.com/Suwayomi/Suwayomi-Server/releases/download/(v\\d+\\.\\d+\\.\\d+(-r\\d+)?)/(Suwayomi-Server-v\\d+\\.\\d+\\.\\d+-r(\\d+)\\.jar)"); public static String getNewestJarUrl(RestTemplate client) { - String githubApi = "https://api.github.com/repos/Suwayomi/Suwayomi-Server-preview/releases/latest"; + String githubApi = "https://api.github.com/repos/Suwayomi/Suwayomi-Server/releases/latest"; String json = client.getForObject(githubApi, String.class); if (json == null) { From 12879f6560642135ac1c3b6d0f1992159a63db30 Mon Sep 17 00:00:00 2001 From: Alessandro Schwaiger Date: Tue, 16 Apr 2024 00:24:00 +0200 Subject: [PATCH 4/9] bump version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 10eb8f7f..29b9622e 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ plugins { } group = 'online.hatsunemiku' -version = '1.6.1' +version = '1.7.0' sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 From af34cc6c12e6efae87cb4f775a54aa011a69d1a5 Mon Sep 17 00:00:00 2001 From: Alessandro Schwaiger Date: Tue, 16 Apr 2024 23:18:45 +0200 Subject: [PATCH 5/9] Fix JAVA-D1000 --- .../provider/Suwayomi/MALTrackerProvider.java | 4 ++ .../data/tachidesk/TrackRecord.java | 3 ++ .../statistics/AniListMangaStatistics.java | 3 ++ .../statistics/MALMangaStatistics.java | 41 ++++++++++--------- .../tracking/statistics/MangaStatistics.java | 4 ++ .../services/MangaService.java | 15 ++++--- .../tachideskvaadinui/view/ReadingView.java | 3 ++ 7 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java index 8a3b9c9b..585f23fd 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java @@ -19,6 +19,10 @@ import online.hatsunemiku.tachideskvaadinui.services.tracker.MyAnimeListAPIService; import online.hatsunemiku.tachideskvaadinui.services.tracker.SuwayomiTrackingService; +/** + * A {@link SuwayomiProvider} implementation for MyAnimeList. Uses the MAL API to get data to + * consumers of this provider. + */ public class MALTrackerProvider extends SuwayomiProvider { private final MyAnimeListAPIService malAPI; diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java index 2e23bc47..021047a4 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java @@ -8,6 +8,9 @@ import lombok.Data; +/** + * Represents a track record of a user for a manga. + */ @Data public class TrackRecord { private int trackerId; diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java index 5f086fa2..bd625a02 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java @@ -10,6 +10,9 @@ import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListStatus; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; +/** + * Represents the statistics for a manga on AniList. E.g. the score or the number of chapters. + */ public class AniListMangaStatistics implements MangaStatistics { private final AniListStatus status; diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java index 3f0647b8..9b207a0c 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java @@ -10,6 +10,9 @@ import java.util.Objects; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; +/** + * Represents the statistics for a manga on MyAnimeList. E.g. the score or the number of chapters. + */ public class MALMangaStatistics implements MangaStatistics { private final MangaStatus status; @@ -57,10 +60,10 @@ public boolean equals(Object obj) { } var that = (MALMangaStatistics) obj; return Objects.equals(this.status, that.status) - && this.progress == that.progress - && this.score == that.score - && Objects.equals(this.startedAt, that.startedAt) - && Objects.equals(this.completedAt, that.completedAt); + && this.progress == that.progress + && this.score == that.score + && Objects.equals(this.startedAt, that.startedAt) + && Objects.equals(this.completedAt, that.completedAt); } @Override @@ -71,20 +74,20 @@ public int hashCode() { @Override public String toString() { return "MALMangaStatistics[" - + "status=" - + status - + ", " - + "progress=" - + progress - + ", " - + "score=" - + score - + ", " - + "startedAt=" - + startedAt - + ", " - + "completedAt=" - + completedAt - + ']'; + + "status=" + + status + + ", " + + "progress=" + + progress + + ", " + + "score=" + + score + + ", " + + "startedAt=" + + startedAt + + ", " + + "completedAt=" + + completedAt + + ']'; } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java index 2cb948e9..9ecb69a2 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java @@ -8,7 +8,11 @@ import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; +/** + * Represents the statistics of a manga. For example, the score or the number of chapters read. + */ public interface MangaStatistics { + int progress(); int score(); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java index fd5c25a0..70fcf935 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java @@ -21,9 +21,14 @@ import reactor.core.Disposables; import reactor.core.publisher.Flux; +/** + * This class is responsible for handling all operations related to manga. This includes adding and + * removing manga from the library, fetching chapters, downloading chapters and more. + */ @Service @Slf4j public class MangaService { + private final MangaClient mangaClient; private final DownloadClient downloadClient; private final Flux> downloadChangeEventTracker; @@ -45,7 +50,7 @@ public MangaService( * * @param mangaId the ID of the manga to be added * @return {@code true} if the manga was successfully added to the library, {@code false} - * otherwise + * otherwise */ public boolean addMangaToLibrary(int mangaId) { return mangaClient.addMangaToLibrary(mangaId); @@ -56,7 +61,7 @@ public boolean addMangaToLibrary(int mangaId) { * * @param mangaId the ID of the manga to be removed * @return {@code true} if the manga was successfully removed from the library, {@code false} - * otherwise + * otherwise */ public boolean removeMangaFromLibrary(int mangaId) { return mangaClient.removeMangaFromLibrary(mangaId); @@ -141,7 +146,7 @@ public Manga getManga(long mangaId) { /** * Adds a manga to a category. * - * @param mangaId the ID of the manga to be added + * @param mangaId the ID of the manga to be added * @param categoryId the ID of the category to add the manga to */ public void addMangaToCategory(int mangaId, int categoryId) { @@ -151,7 +156,7 @@ public void addMangaToCategory(int mangaId, int categoryId) { /** * Removes a manga from a category. * - * @param mangaId the ID of the manga to be removed + * @param mangaId the ID of the manga to be removed * @param categoryId the ID of the category to remove the manga from */ public void removeMangaFromCategory(int mangaId, int categoryId) { @@ -210,7 +215,7 @@ public List getLibraryManga() { * Adds a listener to the download change event tracker. * * @param chapterId The id of the chapter to listen for - * @param callback The callback to run when the chapter is downloaded + * @param callback The callback to run when the chapter is downloaded */ public void addDownloadTrackListener(int chapterId, Runnable callback) { Disposable.Composite cancellation = Disposables.composite(); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java index 06467db0..bd1ef07b 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java @@ -25,6 +25,9 @@ import online.hatsunemiku.tachideskvaadinui.services.SettingsService; import online.hatsunemiku.tachideskvaadinui.view.layout.StandardLayout; +/** + * Represents a view for reading manga. + */ @Route("reading/:mangaId(\\d+)/:chapterId(\\d+)") @CssImport("./css/reading.css") @Slf4j From 41c0223f148d5abe9fdfce09167f00328a38948a Mon Sep 17 00:00:00 2001 From: Alessandro Schwaiger Date: Wed, 17 Apr 2024 00:11:49 +0200 Subject: [PATCH 6/9] Fix JAVA-D1001 --- .../dialog/tracking/TrackingDialog.java | 46 +++++++++++++++++++ .../provider/Suwayomi/MALTrackerProvider.java | 1 + .../tracking/provider/TrackerProvider.java | 30 ++++++++++-- .../data/tracking/Tracker.java | 3 ++ .../statistics/AniListMangaStatistics.java | 8 ++++ .../statistics/MALMangaStatistics.java | 8 ++++ .../tracking/statistics/MangaStatistics.java | 16 +++++++ .../suwayomi/SuwayomiTrackingClient.java | 10 ++++ .../tracker/MyAnimeListAPIService.java | 46 +++++++++++++++++-- .../tracker/SuwayomiTrackingService.java | 14 ++++++ .../tachideskvaadinui/view/ReadingView.java | 6 +++ 11 files changed, 181 insertions(+), 7 deletions(-) diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java index 81a44c58..b718cd13 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java @@ -340,6 +340,13 @@ private Checkbox getPrivateCheckboxField(Tracker tracker) { return privateCheckbox; } + /** + * Configures the end date field for tracking a manga. + * @param tracker the tracker to update the end date for + * @param endDate the end date field to configure + * @param mangaStats the statistics for the manga + * @param startDate the start date field to check against + */ private void configureTrackingEndDateField( Tracker tracker, SuperDatePicker endDate, @@ -397,6 +404,13 @@ private void configureTrackingEndDateField( }); } + /** + * Creates a field for tracking the start date of a manga. + * @param tracker the tracker to update the start date for + * @param mangaStats the statistics for the manga + * @param endDate the end date field to check against + * @return a {@link SuperDatePicker} for tracking the start date of the manga + */ @NotNull private SuperDatePicker getTrackingStartDateField( Tracker tracker, MangaStatistics mangaStats, SuperDatePicker endDate) { @@ -453,6 +467,13 @@ private SuperDatePicker getTrackingStartDateField( return startDate; } + /** + * Creates a field for tracking the score of a manga. + * @param tracker the tracker to update the score for + * @param mangaStats the statistics for the manga + * @param provider the provider for the tracker + * @return a {@link SuperIntegerField} for tracking the score of the manga + */ @NotNull private SuperIntegerField getTrackingScoreField( Tracker tracker, MangaStatistics mangaStats, TrackerProvider provider) { @@ -509,6 +530,12 @@ private SuperIntegerField getTrackingScoreField( return score; } + /** + * Creates a ComboBox for selecting the tracking status of a manga. + * @param tracker the tracker to update the status for + * @param mangaStats the statistics for the manga + * @return a {@link ComboBox} for selecting the status of the manga + */ @NotNull private ComboBox getTrackingStatusField(Tracker tracker, MangaStatistics mangaStats) { ComboBox status; @@ -525,6 +552,12 @@ private ComboBox getTrackingStatusField(Tracker tracker, MangaStatistics mang return status; } + /** + * Creates and configures a ComboBox for selecting the status of a manga on AniList. + * @param tracker the tracker to update the status for + * @param mangaStats the statistics for the manga + * @return a {@link ComboBox} for selecting the status of the manga + */ private ComboBox configureStatusComboBoxAniList( Tracker tracker, AniListMangaStatistics mangaStats) { ComboBox status = new ComboBox<>(); @@ -542,6 +575,12 @@ private ComboBox configureStatusComboBoxAniList( return status; } + /** + * Creates and configures a ComboBox for selecting the status of a manga on MyAnimeList. + * @param tracker the tracker to update the status for + * @param mangaStats the statistics for the manga + * @return a {@link ComboBox} for selecting the status of the manga + */ private ComboBox configureStatusComboBoxMAL( Tracker tracker, MALMangaStatistics mangaStats) { ComboBox status = new ComboBox<>(); @@ -559,6 +598,13 @@ private ComboBox configureStatusComboBoxMAL( return status; } + /** + * Creates a Field for tracking the chapter progress of a manga. + * @param tracker the tracker to update the progress for + * @param mangaStats the statistics for the manga + * @param maxChapters the maximum number of chapters for the manga + * @return a {@link SuperIntegerField} for tracking the chapter progress + */ @NotNull private SuperIntegerField getTrackingChapterField( Tracker tracker, MangaStatistics mangaStats, Integer maxChapters) { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java index 585f23fd..d15d7fb7 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java @@ -37,6 +37,7 @@ public TrackerType getTrackerType() { return TrackerType.MAL; } + @Override public MALMangaStatistics getStatistics(Tracker tracker) { int id = tracker.getMalId(); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java index cd202f99..e260e82b 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java @@ -24,7 +24,7 @@ public interface TrackerProvider { * Checks if the tracker supports setting entries to private. * * @return {@code true} if the tracker supports setting entries to private, {@code false} - * otherwise + * otherwise */ boolean canSetPrivate(); @@ -37,19 +37,41 @@ public interface TrackerProvider { List search(String query); /** - * @param isPrivate whether the entry should be set to private - * @param mangaId the id of the manga according to Suwayomi + * @param isPrivate whether the entry should be set to private + * @param mangaId the id of the manga according to Suwayomi * @param externalId the id of the manga on the tracker * @throws IllegalArgumentException if `isPrivate` is set to true and the tracker does not support - * private entries + * private entries */ void submitToTracker(boolean isPrivate, int mangaId, int externalId); + /** + * Gets the type of the tracker that the provider is for. + * @return the {@link TrackerType type} of the tracker + */ TrackerType getTrackerType(); + /** + * Gets the statistics for the manga on the tracker. + * @param tracker the tracker including the external IDs for the manga + * @return the {@link MangaStatistics statistics} for the manga + */ MangaStatistics getStatistics(Tracker tracker); + /** + * Gets the maximum chapter number for the manga on the tracker. If the tracker does not have a + * max chapter number, returns either null. + * + * @param tracker the tracker including the external IDs for the manga + * @return the maximum chapter number for the manga on the tracker, or null if the external tracker does + * not have a max chapter number. + */ Integer getMaxChapter(Tracker tracker); + /** + * Gets the score format used by the tracker. e.g. 1-10, 1-100, etc. + * + * @return the {@link AniListScoreFormat score format} used by the tracker + */ AniListScoreFormat getScoreFormat(); } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java index df3c884e..37ffb416 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java @@ -48,6 +48,9 @@ public void removeAniListId() { aniListId = 0; } + /** + * Removes the MyAnimeList ID from the tracker. + */ public void removeMalId() { malId = 0; } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java index bd625a02..2aba569b 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java @@ -30,22 +30,30 @@ public AniListMangaStatistics( this.completedAt = completedAt; } + /** + * The status of the manga on AniList. + * @return The {@link AniListStatus status} of the manga. + */ public AniListStatus status() { return status; } + @Override public int progress() { return progress; } + @Override public int score() { return score; } + @Override public MediaDate startedAt() { return startedAt; } + @Override public MediaDate completedAt() { return completedAt; } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java index 9b207a0c..5439bbe2 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java @@ -30,22 +30,30 @@ public MALMangaStatistics( this.completedAt = completedAt; } + /** + * The status of the manga on MyAnimeList. + * @return The {@link MangaStatus status} of the manga. + */ public MangaStatus status() { return status; } + @Override public int progress() { return progress; } + @Override public int score() { return score; } + @Override public MediaDate startedAt() { return startedAt; } + @Override public MediaDate completedAt() { return completedAt; } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java index 9ecb69a2..5d55c83b 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java @@ -13,11 +13,27 @@ */ public interface MangaStatistics { + /** + * The number of chapters read (progress) by the user. + * @return The progress of the user for the manga. + */ int progress(); + /** + * The score the user gave to the manga. + * @return The score. + */ int score(); + /** + * The date the user started reading the manga. + * @return The {@link MediaDate} object with the start date. + */ MediaDate startedAt(); + /** + * The date the user completed the manga. + * @return The {@link MediaDate} object with the completion date. + */ MediaDate completedAt(); } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java index 722be3bd..979a375c 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java @@ -230,6 +230,10 @@ mutation TrackManga($mangaId: Int!, $remoteId: LongString!, $trackerId: Int!) { } } + /** + * Syncs the manga data on the server with the tracker. + * @param mangaId the ID of the manga to sync + */ public void trackProgress(int mangaId) { @Language("graphql") var query = @@ -258,6 +262,12 @@ mutation TrackProgressOnTrackers($mangaId: Int!) { log.info("Tracked progress on trackers"); } + /** + * Checks if a manga is tracked on a tracker using the provided manga ID and tracker ID. + * @param mangaId the ID of the manga to check + * @param trackerId the ID of the tracker to check + * @return {@code true} if the manga is tracked on the tracker, {@code false} otherwise + */ public boolean isMangaTracked(int mangaId, int trackerId) { @Language("graphql") var query = diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java index 20323b5b..3d17e5d8 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java @@ -53,12 +53,13 @@ public class MyAnimeListAPIService { private final TrackingDataService tds; private final WebClient webClient; private final Cache pkceCache; - @Nullable private MyAnimeList mal; + @Nullable + private MyAnimeList mal; /** * Initializes an instance of the MyAnimeListAPIService class. * - * @param tds The {@link TrackingDataService} used for storing tokens. + * @param tds The {@link TrackingDataService} used for storing tokens. * @param webClient The {@link WebClient} used for making requests to the MAL API. */ public MyAnimeListAPIService(TrackingDataService tds, WebClient webClient) { @@ -131,7 +132,7 @@ public boolean hasMalToken() { * Exchanges the authorization code for an access and refresh token. Verifies the PKCE ID before * exchanging the code for tokens. * - * @param code The authorization code to exchange for tokens. + * @param code The authorization code to exchange for tokens. * @param pkceId The PKCE ID used for generating the code challenge. */ public void exchangeCodeForTokens(String code, String pkceId) { @@ -204,6 +205,11 @@ public List getMangaWithStatus(MangaStatus status) { return list.stream().map(MangaListStatus::getManga).toList(); } + /** + * Retrieves a {@link Manga} object with the specified ID from MyAnimeList. + * @param id The MyAnimeList ID of the manga. + * @return The {@link Manga} object containing the manga's information. + */ public Manga getManga(int id) { if (mal == null) { throw new IllegalStateException("Not authenticated with MAL"); @@ -212,6 +218,11 @@ public Manga getManga(int id) { return mal.getManga(id); } + /** + * Updates the status of a manga on the user's list. + * @param id The MyAnimeList ID of the manga. + * @param status The new status to be used. + */ public void updateMangaListStatus(int id, MangaStatus status) { if (mal == null) { throw new IllegalStateException("Not authenticated with MAL"); @@ -220,6 +231,12 @@ public void updateMangaListStatus(int id, MangaStatus status) { mal.updateMangaListing(id).status(status).update(); } + /** + * Updates the score of a manga on the user's list. + * + * @param id The MyAnimeList ID of the manga. + * @param score The new score of the manga. + */ public void updateMangaListScore(int id, int score) { if (mal == null) { throw new IllegalStateException("Not authenticated with MAL"); @@ -228,6 +245,12 @@ public void updateMangaListScore(int id, int score) { mal.updateMangaListing(id).score(score).update(); } + /** + * Updates the start date of a manga on the user's list. + * + * @param malId The MyAnimeList ID of the manga. + * @param date The new start date of the manga. + */ public void updateMangaListStartDate(int malId, MediaDate date) { if (mal == null) { throw new IllegalStateException("Not authenticated with MAL"); @@ -243,6 +266,12 @@ public void updateMangaListStartDate(int malId, MediaDate date) { mal.updateMangaListing(malId).startDate(startDate).update(); } + /** + * Updates the end date of a manga on the user's list. + * + * @param malId The MyAnimeList ID of the manga. + * @param date The new end date of the manga. + */ public void updateMangaListEndDate(int malId, MediaDate date) { if (mal == null) { throw new IllegalStateException("Not authenticated with MAL"); @@ -262,6 +291,11 @@ public void updateMangaListEndDate(int malId, MediaDate date) { mal.updateMangaListing(malId).finishDate(endDate).update(); } + /** + * Removes a manga from the user's list. + * + * @param malId The MyAnimeList ID of the manga to remove. + */ public void removeMangaFromList(int malId) { if (mal == null) { throw new IllegalStateException("Not authenticated with MAL"); @@ -270,6 +304,12 @@ public void removeMangaFromList(int malId) { mal.deleteMangaListing(malId); } + /** + * Updates the progress (read chapter count) of a manga on the user's list. + * + * @param malId The MyAnimeList ID of the manga. + * @param value The new progress value. This is the chapter number the user has read. + */ public void updateMangaListProgress(int malId, int value) { if (mal == null) { throw new IllegalStateException("Not authenticated with MAL"); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java index 10c0d64f..ccc474e1 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java @@ -127,11 +127,21 @@ public void trackOnMAL(int mangaId, int externalId) { client.trackMangaOnTracker(mangaId, externalId, id); } + /** + * Checks if a manga is tracked on AniList. + * @param mangaId the ID of the manga to check + * @return {@code true} if the manga is tracked on AniList, {@code false} otherwise + */ public boolean isMangaTrackedOnAniList(int mangaId) { int id = TrackerType.ANILIST.id; return client.isMangaTracked(mangaId, id); } + /** + * Checks if a manga is tracked on MyAnimeList (MAL). + * @param mangaId the ID of the manga to check + * @return {@code true} if the manga is tracked on MAL, {@code false} otherwise + */ public boolean isMangaTrackedOnMAL(int mangaId) { int id = TrackerType.MAL.id; return client.isMangaTracked(mangaId, id); @@ -180,6 +190,10 @@ private String getStateAuthParam(int id) { return template.formatted(json); } + /** + * Syncs the progress of a manga from the Suwayomi server to the tracker. + * @param mangaId the ID of the manga to sync progress for + */ public void trackProgress(int mangaId) { Optional version = suwayomiService.getServerVersion(); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java index bd1ef07b..c47c1f1e 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java @@ -95,6 +95,12 @@ public void beforeEnter(BeforeEnterEvent event) { setContent(reader); } + /** + * Replaces the current reader with a new reader for the next chapter and updates the UI to + * reflect the new chapter url. + * + * @param event The {@link ReaderChapterChangeEvent} to process. + */ private void processReaderChapterChangeEvent(ReaderChapterChangeEvent event) { var nextChapterId = event.getChapterId(); var nextMangaId = event.getMangaId(); From b4ddb7597092d577e4c5d293c82fd76b8e1673f0 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 22:12:25 +0000 Subject: [PATCH 7/9] style: format code with Google Java Format This commit fixes the style issues introduced in 41c0223 according to the output from Google Java Format. Details: https://github.com/Suwayomi/Suwayomi-VaadinUI/pull/115 --- .../dialog/tracking/TrackingDialog.java | 7 ++++ .../tracking/provider/TrackerProvider.java | 14 ++++--- .../data/tachidesk/TrackRecord.java | 4 +- .../data/tracking/Tracker.java | 4 +- .../statistics/AniListMangaStatistics.java | 5 +-- .../statistics/MALMangaStatistics.java | 39 ++++++++++--------- .../tracking/statistics/MangaStatistics.java | 8 ++-- .../services/MangaService.java | 10 ++--- .../suwayomi/SuwayomiTrackingClient.java | 2 + .../tracker/MyAnimeListAPIService.java | 15 +++---- .../tracker/SuwayomiTrackingService.java | 3 ++ .../tachideskvaadinui/view/ReadingView.java | 4 +- 12 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java index b718cd13..b43612c5 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/TrackingDialog.java @@ -342,6 +342,7 @@ private Checkbox getPrivateCheckboxField(Tracker tracker) { /** * Configures the end date field for tracking a manga. + * * @param tracker the tracker to update the end date for * @param endDate the end date field to configure * @param mangaStats the statistics for the manga @@ -406,6 +407,7 @@ private void configureTrackingEndDateField( /** * Creates a field for tracking the start date of a manga. + * * @param tracker the tracker to update the start date for * @param mangaStats the statistics for the manga * @param endDate the end date field to check against @@ -469,6 +471,7 @@ private SuperDatePicker getTrackingStartDateField( /** * Creates a field for tracking the score of a manga. + * * @param tracker the tracker to update the score for * @param mangaStats the statistics for the manga * @param provider the provider for the tracker @@ -532,6 +535,7 @@ private SuperIntegerField getTrackingScoreField( /** * Creates a ComboBox for selecting the tracking status of a manga. + * * @param tracker the tracker to update the status for * @param mangaStats the statistics for the manga * @return a {@link ComboBox} for selecting the status of the manga @@ -554,6 +558,7 @@ private ComboBox getTrackingStatusField(Tracker tracker, MangaStatistics mang /** * Creates and configures a ComboBox for selecting the status of a manga on AniList. + * * @param tracker the tracker to update the status for * @param mangaStats the statistics for the manga * @return a {@link ComboBox} for selecting the status of the manga @@ -577,6 +582,7 @@ private ComboBox configureStatusComboBoxAniList( /** * Creates and configures a ComboBox for selecting the status of a manga on MyAnimeList. + * * @param tracker the tracker to update the status for * @param mangaStats the statistics for the manga * @return a {@link ComboBox} for selecting the status of the manga @@ -600,6 +606,7 @@ private ComboBox configureStatusComboBoxMAL( /** * Creates a Field for tracking the chapter progress of a manga. + * * @param tracker the tracker to update the progress for * @param mangaStats the statistics for the manga * @param maxChapters the maximum number of chapters for the manga diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java index e260e82b..7f8c76c9 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/TrackerProvider.java @@ -24,7 +24,7 @@ public interface TrackerProvider { * Checks if the tracker supports setting entries to private. * * @return {@code true} if the tracker supports setting entries to private, {@code false} - * otherwise + * otherwise */ boolean canSetPrivate(); @@ -37,22 +37,24 @@ public interface TrackerProvider { List search(String query); /** - * @param isPrivate whether the entry should be set to private - * @param mangaId the id of the manga according to Suwayomi + * @param isPrivate whether the entry should be set to private + * @param mangaId the id of the manga according to Suwayomi * @param externalId the id of the manga on the tracker * @throws IllegalArgumentException if `isPrivate` is set to true and the tracker does not support - * private entries + * private entries */ void submitToTracker(boolean isPrivate, int mangaId, int externalId); /** * Gets the type of the tracker that the provider is for. + * * @return the {@link TrackerType type} of the tracker */ TrackerType getTrackerType(); /** * Gets the statistics for the manga on the tracker. + * * @param tracker the tracker including the external IDs for the manga * @return the {@link MangaStatistics statistics} for the manga */ @@ -63,8 +65,8 @@ public interface TrackerProvider { * max chapter number, returns either null. * * @param tracker the tracker including the external IDs for the manga - * @return the maximum chapter number for the manga on the tracker, or null if the external tracker does - * not have a max chapter number. + * @return the maximum chapter number for the manga on the tracker, or null if the external + * tracker does not have a max chapter number. */ Integer getMaxChapter(Tracker tracker); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java index 021047a4..78f55e71 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tachidesk/TrackRecord.java @@ -8,9 +8,7 @@ import lombok.Data; -/** - * Represents a track record of a user for a manga. - */ +/** Represents a track record of a user for a manga. */ @Data public class TrackRecord { private int trackerId; diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java index 37ffb416..5f01cc66 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/Tracker.java @@ -48,9 +48,7 @@ public void removeAniListId() { aniListId = 0; } - /** - * Removes the MyAnimeList ID from the tracker. - */ + /** Removes the MyAnimeList ID from the tracker. */ public void removeMalId() { malId = 0; } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java index 2aba569b..1dc8d50a 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java @@ -10,9 +10,7 @@ import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.AniListStatus; import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; -/** - * Represents the statistics for a manga on AniList. E.g. the score or the number of chapters. - */ +/** Represents the statistics for a manga on AniList. E.g. the score or the number of chapters. */ public class AniListMangaStatistics implements MangaStatistics { private final AniListStatus status; @@ -32,6 +30,7 @@ public AniListMangaStatistics( /** * The status of the manga on AniList. + * * @return The {@link AniListStatus status} of the manga. */ public AniListStatus status() { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java index 5439bbe2..1542bd5d 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java @@ -32,6 +32,7 @@ public MALMangaStatistics( /** * The status of the manga on MyAnimeList. + * * @return The {@link MangaStatus status} of the manga. */ public MangaStatus status() { @@ -68,10 +69,10 @@ public boolean equals(Object obj) { } var that = (MALMangaStatistics) obj; return Objects.equals(this.status, that.status) - && this.progress == that.progress - && this.score == that.score - && Objects.equals(this.startedAt, that.startedAt) - && Objects.equals(this.completedAt, that.completedAt); + && this.progress == that.progress + && this.score == that.score + && Objects.equals(this.startedAt, that.startedAt) + && Objects.equals(this.completedAt, that.completedAt); } @Override @@ -82,20 +83,20 @@ public int hashCode() { @Override public String toString() { return "MALMangaStatistics[" - + "status=" - + status - + ", " - + "progress=" - + progress - + ", " - + "score=" - + score - + ", " - + "startedAt=" - + startedAt - + ", " - + "completedAt=" - + completedAt - + ']'; + + "status=" + + status + + ", " + + "progress=" + + progress + + ", " + + "score=" + + score + + ", " + + "startedAt=" + + startedAt + + ", " + + "completedAt=" + + completedAt + + ']'; } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java index 5d55c83b..7f28e7c2 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MangaStatistics.java @@ -8,31 +8,33 @@ import online.hatsunemiku.tachideskvaadinui.data.tracking.anilist.common.MediaDate; -/** - * Represents the statistics of a manga. For example, the score or the number of chapters read. - */ +/** Represents the statistics of a manga. For example, the score or the number of chapters read. */ public interface MangaStatistics { /** * The number of chapters read (progress) by the user. + * * @return The progress of the user for the manga. */ int progress(); /** * The score the user gave to the manga. + * * @return The score. */ int score(); /** * The date the user started reading the manga. + * * @return The {@link MediaDate} object with the start date. */ MediaDate startedAt(); /** * The date the user completed the manga. + * * @return The {@link MediaDate} object with the completion date. */ MediaDate completedAt(); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java index 70fcf935..c1430977 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java @@ -50,7 +50,7 @@ public MangaService( * * @param mangaId the ID of the manga to be added * @return {@code true} if the manga was successfully added to the library, {@code false} - * otherwise + * otherwise */ public boolean addMangaToLibrary(int mangaId) { return mangaClient.addMangaToLibrary(mangaId); @@ -61,7 +61,7 @@ public boolean addMangaToLibrary(int mangaId) { * * @param mangaId the ID of the manga to be removed * @return {@code true} if the manga was successfully removed from the library, {@code false} - * otherwise + * otherwise */ public boolean removeMangaFromLibrary(int mangaId) { return mangaClient.removeMangaFromLibrary(mangaId); @@ -146,7 +146,7 @@ public Manga getManga(long mangaId) { /** * Adds a manga to a category. * - * @param mangaId the ID of the manga to be added + * @param mangaId the ID of the manga to be added * @param categoryId the ID of the category to add the manga to */ public void addMangaToCategory(int mangaId, int categoryId) { @@ -156,7 +156,7 @@ public void addMangaToCategory(int mangaId, int categoryId) { /** * Removes a manga from a category. * - * @param mangaId the ID of the manga to be removed + * @param mangaId the ID of the manga to be removed * @param categoryId the ID of the category to remove the manga from */ public void removeMangaFromCategory(int mangaId, int categoryId) { @@ -215,7 +215,7 @@ public List getLibraryManga() { * Adds a listener to the download change event tracker. * * @param chapterId The id of the chapter to listen for - * @param callback The callback to run when the chapter is downloaded + * @param callback The callback to run when the chapter is downloaded */ public void addDownloadTrackListener(int chapterId, Runnable callback) { Disposable.Composite cancellation = Disposables.composite(); diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java index 979a375c..32f16f9c 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/client/suwayomi/SuwayomiTrackingClient.java @@ -232,6 +232,7 @@ mutation TrackManga($mangaId: Int!, $remoteId: LongString!, $trackerId: Int!) { /** * Syncs the manga data on the server with the tracker. + * * @param mangaId the ID of the manga to sync */ public void trackProgress(int mangaId) { @@ -264,6 +265,7 @@ mutation TrackProgressOnTrackers($mangaId: Int!) { /** * Checks if a manga is tracked on a tracker using the provided manga ID and tracker ID. + * * @param mangaId the ID of the manga to check * @param trackerId the ID of the tracker to check * @return {@code true} if the manga is tracked on the tracker, {@code false} otherwise diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java index 3d17e5d8..0469a64d 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/MyAnimeListAPIService.java @@ -53,13 +53,12 @@ public class MyAnimeListAPIService { private final TrackingDataService tds; private final WebClient webClient; private final Cache pkceCache; - @Nullable - private MyAnimeList mal; + @Nullable private MyAnimeList mal; /** * Initializes an instance of the MyAnimeListAPIService class. * - * @param tds The {@link TrackingDataService} used for storing tokens. + * @param tds The {@link TrackingDataService} used for storing tokens. * @param webClient The {@link WebClient} used for making requests to the MAL API. */ public MyAnimeListAPIService(TrackingDataService tds, WebClient webClient) { @@ -132,7 +131,7 @@ public boolean hasMalToken() { * Exchanges the authorization code for an access and refresh token. Verifies the PKCE ID before * exchanging the code for tokens. * - * @param code The authorization code to exchange for tokens. + * @param code The authorization code to exchange for tokens. * @param pkceId The PKCE ID used for generating the code challenge. */ public void exchangeCodeForTokens(String code, String pkceId) { @@ -207,6 +206,7 @@ public List getMangaWithStatus(MangaStatus status) { /** * Retrieves a {@link Manga} object with the specified ID from MyAnimeList. + * * @param id The MyAnimeList ID of the manga. * @return The {@link Manga} object containing the manga's information. */ @@ -220,6 +220,7 @@ public Manga getManga(int id) { /** * Updates the status of a manga on the user's list. + * * @param id The MyAnimeList ID of the manga. * @param status The new status to be used. */ @@ -234,7 +235,7 @@ public void updateMangaListStatus(int id, MangaStatus status) { /** * Updates the score of a manga on the user's list. * - * @param id The MyAnimeList ID of the manga. + * @param id The MyAnimeList ID of the manga. * @param score The new score of the manga. */ public void updateMangaListScore(int id, int score) { @@ -249,7 +250,7 @@ public void updateMangaListScore(int id, int score) { * Updates the start date of a manga on the user's list. * * @param malId The MyAnimeList ID of the manga. - * @param date The new start date of the manga. + * @param date The new start date of the manga. */ public void updateMangaListStartDate(int malId, MediaDate date) { if (mal == null) { @@ -270,7 +271,7 @@ public void updateMangaListStartDate(int malId, MediaDate date) { * Updates the end date of a manga on the user's list. * * @param malId The MyAnimeList ID of the manga. - * @param date The new end date of the manga. + * @param date The new end date of the manga. */ public void updateMangaListEndDate(int malId, MediaDate date) { if (mal == null) { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java index ccc474e1..5858f2ab 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/tracker/SuwayomiTrackingService.java @@ -129,6 +129,7 @@ public void trackOnMAL(int mangaId, int externalId) { /** * Checks if a manga is tracked on AniList. + * * @param mangaId the ID of the manga to check * @return {@code true} if the manga is tracked on AniList, {@code false} otherwise */ @@ -139,6 +140,7 @@ public boolean isMangaTrackedOnAniList(int mangaId) { /** * Checks if a manga is tracked on MyAnimeList (MAL). + * * @param mangaId the ID of the manga to check * @return {@code true} if the manga is tracked on MAL, {@code false} otherwise */ @@ -192,6 +194,7 @@ private String getStateAuthParam(int id) { /** * Syncs the progress of a manga from the Suwayomi server to the tracker. + * * @param mangaId the ID of the manga to sync progress for */ public void trackProgress(int mangaId) { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java index c47c1f1e..dc372350 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java @@ -25,9 +25,7 @@ import online.hatsunemiku.tachideskvaadinui.services.SettingsService; import online.hatsunemiku.tachideskvaadinui.view.layout.StandardLayout; -/** - * Represents a view for reading manga. - */ +/** Represents a view for reading manga. */ @Route("reading/:mangaId(\\d+)/:chapterId(\\d+)") @CssImport("./css/reading.css") @Slf4j From 56870a054c2f3afd91bcef65c658ff2b8211cdb9 Mon Sep 17 00:00:00 2001 From: Alessandro Schwaiger Date: Wed, 17 Apr 2024 22:50:44 +0200 Subject: [PATCH 8/9] Fix JAVA-D1002 --- .../provider/Suwayomi/MALTrackerProvider.java | 5 ++ .../statistics/AniListMangaStatistics.java | 48 +++++++++++-------- .../statistics/MALMangaStatistics.java | 10 ++++ .../services/MangaService.java | 6 +++ .../tachideskvaadinui/view/ReadingView.java | 6 +++ 5 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java index d15d7fb7..e1c3ff52 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java @@ -27,6 +27,11 @@ public class MALTrackerProvider extends SuwayomiProvider { private final MyAnimeListAPIService malAPI; + /** + * Creates a new {@link MALTrackerProvider} with the given parameters. + * @param suwayomiAPI the {@link SuwayomiTrackingService} to use for tracking on Suwayomi + * @param malAPI the {@link MyAnimeListAPIService} to use for getting data from MAL + */ public MALTrackerProvider(SuwayomiTrackingService suwayomiAPI, MyAnimeListAPIService malAPI) { super(suwayomiAPI); this.malAPI = malAPI; diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java index 2aba569b..0812972e 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java @@ -21,6 +21,15 @@ public class AniListMangaStatistics implements MangaStatistics { private final MediaDate startedAt; private final MediaDate completedAt; + /** + * Creates a new {@link AniListMangaStatistics} object with the given parameters. + * + * @param status The status of the manga on AniList. + * @param progress The number of chapters read by the user. + * @param score The score the user gave to the manga. + * @param startedAt The date the user started reading the manga. + * @param completedAt The date the user completed the manga. + */ public AniListMangaStatistics( AniListStatus status, int progress, int score, MediaDate startedAt, MediaDate completedAt) { this.status = status; @@ -32,6 +41,7 @@ public AniListMangaStatistics( /** * The status of the manga on AniList. + * * @return The {@link AniListStatus status} of the manga. */ public AniListStatus status() { @@ -68,10 +78,10 @@ public boolean equals(Object obj) { } var that = (AniListMangaStatistics) obj; return Objects.equals(this.status, that.status) - && this.progress == that.progress - && this.score == that.score - && Objects.equals(this.startedAt, that.startedAt) - && Objects.equals(this.completedAt, that.completedAt); + && this.progress == that.progress + && this.score == that.score + && Objects.equals(this.startedAt, that.startedAt) + && Objects.equals(this.completedAt, that.completedAt); } @Override @@ -82,20 +92,20 @@ public int hashCode() { @Override public String toString() { return "AniListMangaStatistics[" - + "status=" - + status - + ", " - + "progress=" - + progress - + ", " - + "score=" - + score - + ", " - + "startedAt=" - + startedAt - + ", " - + "completedAt=" - + completedAt - + ']'; + + "status=" + + status + + ", " + + "progress=" + + progress + + ", " + + "score=" + + score + + ", " + + "startedAt=" + + startedAt + + ", " + + "completedAt=" + + completedAt + + ']'; } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java index 5439bbe2..59c8c170 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java @@ -21,6 +21,15 @@ public class MALMangaStatistics implements MangaStatistics { private final MediaDate startedAt; private final MediaDate completedAt; + /** + * Creates a new {@link MALMangaStatistics} object with the given parameters. + * + * @param status The status of the manga on MyAnimeList. + * @param progress The number of chapters read by the user. + * @param score The score the user gave to the manga. + * @param startedAt The date the user started reading the manga. + * @param completedAt The date the user completed the manga. + */ public MALMangaStatistics( MangaStatus status, int progress, int score, MediaDate startedAt, MediaDate completedAt) { this.status = status; @@ -32,6 +41,7 @@ public MALMangaStatistics( /** * The status of the manga on MyAnimeList. + * * @return The {@link MangaStatus status} of the manga. */ public MangaStatus status() { diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java index 70fcf935..c6f603b8 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java @@ -34,6 +34,12 @@ public class MangaService { private final Flux> downloadChangeEventTracker; private final SuwayomiTrackingService suwayomiTrackingService; + /** + * Creates a new MangaService. + * @param mangaClient the {@link MangaClient} to use for fetching manga data + * @param downloadCLient the {@link DownloadClient} to use for downloading chapters + * @param suwayomiTrackingService the {@link SuwayomiTrackingService} to use for tracking progress + */ @Autowired public MangaService( MangaClient mangaClient, diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java index c47c1f1e..49faa2de 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java @@ -37,6 +37,12 @@ public class ReadingView extends StandardLayout private final MangaService mangaService; private final SettingsService settingsService; + /** + * Creates a new ReadingView and sets it to full screen. + * + * @param mangaService The {@link MangaService} to use for fetching manga. + * @param settingsService The {@link SettingsService} to use for managing settings. + */ public ReadingView(MangaService mangaService, SettingsService settingsService) { super("Reading"); From 58fafedcf29dd1ac6f36ed45c68d8faf779f8922 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 20:51:32 +0000 Subject: [PATCH 9/9] style: format code with Google Java Format This commit fixes the style issues introduced in 10286cb according to the output from Google Java Format. Details: https://github.com/Suwayomi/Suwayomi-VaadinUI/pull/115 --- .../provider/Suwayomi/MALTrackerProvider.java | 1 + .../statistics/AniListMangaStatistics.java | 46 +++++++++---------- .../statistics/MALMangaStatistics.java | 8 ++-- .../services/MangaService.java | 1 + .../tachideskvaadinui/view/ReadingView.java | 2 +- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java index e1c3ff52..32d50344 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/component/dialog/tracking/provider/Suwayomi/MALTrackerProvider.java @@ -29,6 +29,7 @@ public class MALTrackerProvider extends SuwayomiProvider { /** * Creates a new {@link MALTrackerProvider} with the given parameters. + * * @param suwayomiAPI the {@link SuwayomiTrackingService} to use for tracking on Suwayomi * @param malAPI the {@link MyAnimeListAPIService} to use for getting data from MAL */ diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java index 8a35f189..0fa36f39 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/AniListMangaStatistics.java @@ -22,10 +22,10 @@ public class AniListMangaStatistics implements MangaStatistics { /** * Creates a new {@link AniListMangaStatistics} object with the given parameters. * - * @param status The status of the manga on AniList. - * @param progress The number of chapters read by the user. - * @param score The score the user gave to the manga. - * @param startedAt The date the user started reading the manga. + * @param status The status of the manga on AniList. + * @param progress The number of chapters read by the user. + * @param score The score the user gave to the manga. + * @param startedAt The date the user started reading the manga. * @param completedAt The date the user completed the manga. */ public AniListMangaStatistics( @@ -76,10 +76,10 @@ public boolean equals(Object obj) { } var that = (AniListMangaStatistics) obj; return Objects.equals(this.status, that.status) - && this.progress == that.progress - && this.score == that.score - && Objects.equals(this.startedAt, that.startedAt) - && Objects.equals(this.completedAt, that.completedAt); + && this.progress == that.progress + && this.score == that.score + && Objects.equals(this.startedAt, that.startedAt) + && Objects.equals(this.completedAt, that.completedAt); } @Override @@ -90,20 +90,20 @@ public int hashCode() { @Override public String toString() { return "AniListMangaStatistics[" - + "status=" - + status - + ", " - + "progress=" - + progress - + ", " - + "score=" - + score - + ", " - + "startedAt=" - + startedAt - + ", " - + "completedAt=" - + completedAt - + ']'; + + "status=" + + status + + ", " + + "progress=" + + progress + + ", " + + "score=" + + score + + ", " + + "startedAt=" + + startedAt + + ", " + + "completedAt=" + + completedAt + + ']'; } } diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java index 865bed00..b7854809 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/data/tracking/statistics/MALMangaStatistics.java @@ -24,10 +24,10 @@ public class MALMangaStatistics implements MangaStatistics { /** * Creates a new {@link MALMangaStatistics} object with the given parameters. * - * @param status The status of the manga on MyAnimeList. - * @param progress The number of chapters read by the user. - * @param score The score the user gave to the manga. - * @param startedAt The date the user started reading the manga. + * @param status The status of the manga on MyAnimeList. + * @param progress The number of chapters read by the user. + * @param score The score the user gave to the manga. + * @param startedAt The date the user started reading the manga. * @param completedAt The date the user completed the manga. */ public MALMangaStatistics( diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java index ba10adc5..65d7f471 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/services/MangaService.java @@ -36,6 +36,7 @@ public class MangaService { /** * Creates a new MangaService. + * * @param mangaClient the {@link MangaClient} to use for fetching manga data * @param downloadCLient the {@link DownloadClient} to use for downloading chapters * @param suwayomiTrackingService the {@link SuwayomiTrackingService} to use for tracking progress diff --git a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java index 2441255f..fd251312 100644 --- a/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java +++ b/src/main/java/online/hatsunemiku/tachideskvaadinui/view/ReadingView.java @@ -38,7 +38,7 @@ public class ReadingView extends StandardLayout /** * Creates a new ReadingView and sets it to full screen. * - * @param mangaService The {@link MangaService} to use for fetching manga. + * @param mangaService The {@link MangaService} to use for fetching manga. * @param settingsService The {@link SettingsService} to use for managing settings. */ public ReadingView(MangaService mangaService, SettingsService settingsService) {