Skip to content

Commit

Permalink
Merge pull request #3 from chytanka/develop
Browse files Browse the repository at this point in the history
Read by link to JSON, Save User settings, Base64 in URL
  • Loading branch information
rodzyk authored Feb 9, 2024
2 parents 031d75b + c8053da commit 336a5e4
Show file tree
Hide file tree
Showing 29 changed files with 563 additions and 235 deletions.
4 changes: 4 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const routes: Routes = [
path: 'md',
loadChildren: () => import('./mangadex/mangadex.module').then(m => m.MangadexModule)
},
{
path: 'read',
loadChildren: () => import('./read/read.module').then(m => m.ReadModule)
},
{
path: '**',
component: PageNotFoundComponent
Expand Down
18 changes: 12 additions & 6 deletions src/app/imgur/imgur-shell/imgur-shell.component.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
<app-viewer *ngIf="(episode$ | async) as episode" [episode]="episode">
@if(episode$ | async; as episode){
<app-viewer [episode]="episode">
<div style="direction: ltr; user-select: text !important; text-wrap: balance; padding: 1rem; text-align: center; display: grid;
place-content: center;
justify-items: center; min-height: 50vh;">
<a href="https://imgur.com" target="_blank" rel="noopener noreferrer" style="display: flex; gap: 1ch; ">
<img src="/assets/logos/imgur-logo.svg" alt="Imgur logo">
</a>
<p>{{lang.phrases.imagesVia}}<a href="https://imgur.com" target="_blank" rel="noopener noreferrer">Imgur</a> API.
<p>{{lang.phrases.imagesVia}}<a href="https://imgur.com" target="_blank" rel="noopener noreferrer">Imgur</a>
API.
{{lang.phrases.thanks}}<br>{{lang.phrases.detalisCopy}}</p>
</div>
</app-viewer>
<div *ngIf="error$ | async as error" class="error-message">
}
@if(error$ | async; as error){
<div class="error-message">
{{ error }} <br>
<button (click)="refreshData()">Ше раз</button> <br>
<a [routerLink]="'/'">🏠</a>
</div>
<div *ngIf="loading$ | async as loading" class="loading">
</div>
}
@if(loading$ | async; as loading){
<div class="loading"></div>
}
38 changes: 14 additions & 24 deletions src/app/imgur/imgur-shell/imgur-shell.component.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
import { Component, OnDestroy, WritableSignal, signal } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Component } from '@angular/core';
import { ImgurService } from '../data-access/imgur.service';
import { CompositionEpisode } from '../../shared/utils';
import { BehaviorSubject, Subject, catchError, finalize, forkJoin, of, switchMap, takeUntil } from 'rxjs';
import { LangService } from '../../shared/data-access/lang.service';
import { Base64, ReadBaseComponent } from '../../shared/utils';
import { of, switchMap } from 'rxjs';

@Component({
selector: 'app-imgur-shell',
templateUrl: './imgur-shell.component.html',
styleUrl: './imgur-shell.component.scss'
})
export class ImgurShellComponent {
error$ = new BehaviorSubject<string | null>(null);
loading$ = new BehaviorSubject<boolean>(false);
export class ImgurShellComponent extends ReadBaseComponent {

episode$ = this.route.paramMap.pipe(
switchMap(params => {
const id = params?.get('id');
override episode$ = this.combineParamMapAndRefresh()
.pipe(this.tapStartLoading(),
switchMap(([params]) => {
const idParam = params?.get('id');

if (!id) return of(null);
if (!idParam) return of(null);

this.loading$.next(true);
const id = (Base64.isBase64(idParam)) ? Base64.fromBase64(idParam) : idParam;

return (this.imgur.getComposition(id)).pipe(
catchError(() => {
this.error$.next(this.lang.phrases.dataLoadErr);
return (this.imgur.getComposition(id)).pipe(this.catchError(), this.tapSetTitle(), this.finalizeLoading());
})
);

return of(null);
}),
finalize(() => this.loading$.next(false))
);
})
);

constructor(private route: ActivatedRoute, public imgur: ImgurService, public lang: LangService) { }
constructor(public imgur: ImgurService) { super() }
}
198 changes: 117 additions & 81 deletions src/app/link-parser/link-parser/link-parser.component.html

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions src/app/link-parser/link-parser/link-parser.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Component, Signal, WritableSignal, computed, signal } from '@angular/core';
import { LinkParserService } from '../data-access/link-parser.service';
import { ImgurLinkParser, MangadexLinkParser } from '../utils';
import { ImgurLinkParser, JsonLinkParser, MangadexLinkParser } from '../utils';
import { ActivatedRoute , Router} from '@angular/router';
import { LangService } from '../../shared/data-access/lang.service';
import { ViewModeOption } from '../../shared/data-access';
import { Base64 } from '../../shared/utils';

const LANG_OPTIONS: ViewModeOption[] = [
{ dir: "rtl", mode: "pages", hintPhraceKey: "english", code: "en", emoji: "🇬🇧" },
Expand All @@ -21,6 +22,13 @@ export class LinkParserComponent {

link: WritableSignal<string> = signal('');
linkParams: Signal<any> = computed(() => this.parser.parse(this.link()));
linkParams64: Signal<any> = computed(() => {
const foo = this.linkParams()
return {
site: foo.site,
id: Base64.toBase64(foo.id)
};
});

langOpt = LANG_OPTIONS

Expand All @@ -35,6 +43,7 @@ export class LinkParserComponent {
initParser() {
this.parser.parsers.push(new ImgurLinkParser)
this.parser.parsers.push(new MangadexLinkParser)
this.parser.parsers.push(new JsonLinkParser)
}

inputLink(event: Event) {
Expand Down Expand Up @@ -64,7 +73,7 @@ export class LinkParserComponent {
onSubmit() {
if(!this.linkParams) return;

const link = `/${this.linkParams().site}/${this.linkParams().id}`
const link = `/${this.linkParams().site}/${this.linkParams64().id}`

this.router.navigateByUrl(link);
}
Expand Down
3 changes: 2 additions & 1 deletion src/app/link-parser/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './link-parser';
export * from './imgur-link-parser';
export * from './mangadex-link-parser';
export * from './mangadex-link-parser';
export * from './json-link-parser';
6 changes: 6 additions & 0 deletions src/app/link-parser/utils/json-link-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { LinkParser } from "./link-parser";

export class JsonLinkParser extends LinkParser {
override regex = /((?:https?:\/\/)?(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
override site = 'read'
};
7 changes: 3 additions & 4 deletions src/app/mangadex/data-access/mangadex.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '../../../environments/environment';
import { Observable, map } from 'rxjs';
import { Observable, catchError, map, throwError } from 'rxjs';
import { CompositionEpisode, CompositionImage } from '../../shared/utils';

interface MdChapterImages {
Expand Down Expand Up @@ -91,9 +91,8 @@ export class MangadexService {
chapter: data.data.attributes.chapter,
images: []
} as unknown as CompositionEpisode
}

)
}),
catchError(error => throwError(() => error))
)
}

Expand Down
17 changes: 10 additions & 7 deletions src/app/mangadex/mangadex-shell/mangadex-shell.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<app-viewer *ngIf="(episode$ | async) as episode" [episode]="episode">
@if(episode$ | async; as episode){
<app-viewer [episode]="episode">
<div style="direction: ltr; user-select: text !important; text-wrap: balance; padding: 1rem; text-align: center; display: grid;
place-content: center;
justify-items: center; min-height: 50vh;">
Expand All @@ -10,12 +11,14 @@
Thanks!<br>Details on their site. Respect copyrights.</p>
</div>
</app-viewer>

<div *ngIf="error$ | async as error" class="error-message">
}
@if(error$ | async; as error){
<div class="error-message">
{{ error }} <br>
<button (click)="refreshData()">Ше раз</button> <br>
<a [routerLink]="'/'">🏠</a>
</div>

<div *ngIf="loading$ | async as loading" class="loading">
</div>
}
@if(loading$ | async; as loading){
<div class="loading"></div>
}
110 changes: 36 additions & 74 deletions src/app/mangadex/mangadex-shell/mangadex-shell.component.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,45 @@
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { BehaviorSubject, Observable, catchError, finalize, forkJoin, map, of, switchMap, tap, throwError } from 'rxjs';
import { forkJoin, map, of, switchMap } from 'rxjs';
import { MangadexService } from '../data-access/mangadex.service';
import { Title } from '@angular/platform-browser';
import { Base64, ReadBaseComponent } from '../../shared/utils';

@Component({
selector: 'app-mangadex-shell',
templateUrl: './mangadex-shell.component.html',
styleUrl: './mangadex-shell.component.scss'
})
export class MangadexShellComponent {
error$ = new BehaviorSubject<string | null>(null);
loading$ = new BehaviorSubject<boolean>(false);

// episode$ = this.route.paramMap.pipe(
// switchMap(params => {
// const id = params?.get('id');

// if (!id) return of(null);

// this.loading$.next(true);

// const ch$ = this.mangadex.getChapter(id);
// const imgs$ = this.mangadex.getChapterImages(id);

// return forkJoin([ch$, imgs$]).pipe(
// map(([ch, imgs]) => {
// ch.images = imgs;
// return ch
// }),
// tap(episode=>{
// this.title.setTitle(`${episode.title} | Chytanka`)
// }),
// catchError(() => {
// this.error$.next('Data loading error. Please try again later.');

// return of(null);
// }),
// finalize(() => this.loading$.next(false))
// );
// })
// );

episode$ = this.route.paramMap.pipe(
switchMap(params => {
const id = params?.get('id');

if (!id) return of(null);

this.loading$.next(true);

const ch$ = this.mangadex.getChapter(id);

return ch$.pipe(
switchMap(ch => {
const imgs$ = this.mangadex.getChapterImages(id);
const manga$ = (ch.mangaId)? this.mangadex.getManga(ch.mangaId): of(null);

return forkJoin([imgs$, manga$]).pipe(
map(([imgs, manga]) => {
ch.images = imgs;
ch.nsfw = manga?.nsfw ?? undefined;
return ch;
}),
catchError(() => {
this.error$.next('Data loading error. Please try again later.');
return of(null);
})
);
}),
tap(episode => {
if (episode) {
this.title.setTitle(`${episode.title} | Chytanka`);
}
}),
finalize(() => this.loading$.next(false))
);
})
);

constructor(private route: ActivatedRoute, public mangadex: MangadexService, private title: Title) { }
export class MangadexShellComponent extends ReadBaseComponent {

override episode$ = this.combineParamMapAndRefresh()
.pipe(this.tapStartLoading(),
switchMap(([params]) => {
const idParam = params?.get('id');

if (!idParam) return of(null);

const id = (Base64.isBase64(idParam)) ? Base64.fromBase64(idParam) : idParam;
const ch$ = this.mangadex.getChapter(id);

return ch$.pipe(
switchMap(ch => {
const imgs$ = this.mangadex.getChapterImages(id);
const manga$ = (ch.mangaId) ? this.mangadex.getManga(ch.mangaId) : of(null);

return forkJoin([imgs$, manga$]).pipe(
map(([imgs, manga]) => {
ch.images = imgs;
ch.nsfw = manga?.nsfw ?? undefined;
return ch;
}),
this.catchError()
);
}),
this.catchError(),
this.tapSetTitle(),
this.finalizeLoading()
);
})
);

constructor(public mangadex: MangadexService) { super() }
}
20 changes: 20 additions & 0 deletions src/app/read/data-access/read.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, catchError, tap, throwError } from 'rxjs';
import { CompositionEpisode, isCompositionEpisode } from '../../shared/utils';

@Injectable({
providedIn: 'root'
})
export class ReadService {

constructor(private http: HttpClient) { }

getComposition(url: string): Observable<CompositionEpisode> {
return this.http.get<CompositionEpisode>(url)
.pipe(
tap(data => { if (!isCompositionEpisode(data)) throw new Error() }),
catchError(error => throwError(() => error))
);
}
}
17 changes: 17 additions & 0 deletions src/app/read/read-routing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ReadShellComponent } from './read-shell/read-shell.component';

const routes: Routes = [
{ path: '', redirectTo: '/', pathMatch: 'full' },
{
path: ':url',
component: ReadShellComponent
}
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ReadRoutingModule { }
22 changes: 22 additions & 0 deletions src/app/read/read-shell/read-shell.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@if(episode$ | async; as episode){
<app-viewer [episode]="episode">
<div style="direction: ltr; user-select: text !important; text-wrap: balance; padding: 1rem; text-align: center; display: grid;
place-content: center;
justify-items: center; min-height: 50vh;">
<p>End of episode</p><br>
<a [routerLink]="'/'">
<h2>🏠</h2>
</a>
</div>
</app-viewer>
}
@if(error$ | async; as error){
<div class="error-message">
{{ error }} <br>
<button (click)="refreshData()">Ше раз</button> <br>
<a [routerLink]="'/'">🏠</a>
</div>
}
@if(loading$ | async; as loading){
<div class="loading"></div>
}
Empty file.
Loading

0 comments on commit 336a5e4

Please sign in to comment.