diff --git a/eslint.config.js b/eslint.config.js index cb0233c52..a1c5222b9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -13,8 +13,6 @@ module.exports = defineConfig([ extends: [angular.configs.tsRecommended], processor: angular.processInlineTemplates, rules: { - // Ionic's base level; `recommended` makes it an error, which is meant for libraries. - '@typescript-eslint/explicit-module-boundary-types': 'warn', '@typescript-eslint/explicit-member-accessibility': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/no-inferrable-types': ['error', { ignoreParameters: true }], diff --git a/package.json b/package.json index 0a5879459..b69a8a78d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "lint": "npm run eslint && npm run lint:sass && npm run prettier -- --check", "lint:sass": "stylelint \"src/**/*.scss\"", "lint:fix": "npm run eslint -- --fix && npm run prettier -- --write", - "eslint": "ng lint", + "eslint": "ng lint --max-warnings 0", "prettier": "prettier \"**/*.{ts,html}\"" }, "repository": { diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 139522c33..35793189a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -114,7 +114,7 @@ export class AppComponent implements OnInit { }); } - async ngOnInit() { + async ngOnInit(): Promise { this.initializeApp(); await this.storage.create(); this.checkLoginStatus(); @@ -141,7 +141,7 @@ export class AppComponent implements OnInit { }); } - initializeApp() { + initializeApp(): void { this.platform.ready().then(() => { if (this.platform.is('hybrid')) { StatusBar.hide(); @@ -150,19 +150,19 @@ export class AppComponent implements OnInit { }); } - checkLoginStatus() { + checkLoginStatus(): Promise { return this.userService.isLoggedIn().then((loggedIn) => { return this.updateLoggedInStatus(loggedIn); }); } - updateLoggedInStatus(loggedIn: boolean) { + updateLoggedInStatus(loggedIn: boolean): void { setTimeout(() => { this.loggedIn.set(loggedIn); }, 300); } - listenForLoginEvents() { + listenForLoginEvents(): void { window.addEventListener('user:login', () => { this.updateLoggedInStatus(true); }); @@ -176,19 +176,19 @@ export class AppComponent implements OnInit { }); } - logout() { + logout(): void { this.userService.logout().then(() => { return this.router.navigateByUrl('/app/tabs/schedule'); }); } - openTutorial() { + openTutorial(): void { this.menu.enable(false); this.storage.set('ion_did_tutorial', false); this.router.navigateByUrl('/tutorial'); } - toggleDarkMode() { + toggleDarkMode(): void { document.documentElement.classList.toggle('ion-palette-dark', this.dark); } } diff --git a/src/app/pages/about-popover/about-popover.ts b/src/app/pages/about-popover/about-popover.ts index b18a9dd3c..8988c9ab8 100644 --- a/src/app/pages/about-popover/about-popover.ts +++ b/src/app/pages/about-popover/about-popover.ts @@ -29,12 +29,12 @@ export class PopoverPage { private router = inject(Router); private popoverCtrl = inject(PopoverController); - support() { + support(): void { this.router.navigate(['/support']); this.popoverCtrl.dismiss(); } - close(url: string) { + close(url: string): void { window.open(url, '_blank'); this.popoverCtrl.dismiss(); } diff --git a/src/app/pages/about/about.ts b/src/app/pages/about/about.ts index 70acbb246..c70c3beea 100644 --- a/src/app/pages/about/about.ts +++ b/src/app/pages/about/about.ts @@ -64,7 +64,7 @@ export class AboutPage { addIcons({ ellipsisHorizontal, ellipsisVertical }); } - async presentPopover(event: Event) { + async presentPopover(event: Event): Promise { const popover = await this.popoverCtrl.create({ component: PopoverPage, event, diff --git a/src/app/pages/account/account.ts b/src/app/pages/account/account.ts index f4b70e193..96bb661f3 100644 --- a/src/app/pages/account/account.ts +++ b/src/app/pages/account/account.ts @@ -30,18 +30,18 @@ export class AccountPage implements AfterViewInit { username = signal(''); - ngAfterViewInit() { + ngAfterViewInit(): void { this.getUsername(); } - updatePicture() { + updatePicture(): void { console.log('Clicked to update picture'); } // Present an alert with the current username populated // clicking OK will update the username and display it // clicking Cancel will close the alert and do nothing - async changeUsername() { + async changeUsername(): Promise { const alert = await this.alertCtrl.create({ header: 'Change Username', buttons: [ @@ -67,22 +67,22 @@ export class AccountPage implements AfterViewInit { await alert.present(); } - getUsername() { + getUsername(): void { this.user.getUsername().then((username) => { this.username.set(username ?? ''); }); } - changePassword() { + changePassword(): void { console.log('Clicked to change password'); } - logout() { + logout(): void { this.user.logout(); this.router.navigateByUrl('/login'); } - support() { + support(): void { this.router.navigateByUrl('/support'); } } diff --git a/src/app/pages/login/login.ts b/src/app/pages/login/login.ts index 0a1b72d6e..a44cc8ad2 100644 --- a/src/app/pages/login/login.ts +++ b/src/app/pages/login/login.ts @@ -43,7 +43,7 @@ export class LoginPage { login: UserOptions = { username: '', password: '' }; submitted = false; - onLogin(form: NgForm) { + onLogin(form: NgForm): void { this.submitted = true; if (form.valid) { @@ -52,7 +52,7 @@ export class LoginPage { } } - onSignup() { + onSignup(): void { this.router.navigateByUrl('/signup'); } } diff --git a/src/app/pages/map/map.ts b/src/app/pages/map/map.ts index 0ab0f228b..2b0148019 100644 --- a/src/app/pages/map/map.ts +++ b/src/app/pages/map/map.ts @@ -37,7 +37,7 @@ export class MapPage implements AfterViewInit { @ViewChild('mapCanvas', { static: true }) mapElement!: ElementRef; - ngAfterViewInit() { + ngAfterViewInit(): void { this.locationService .loadLocations() .pipe(takeUntilDestroyed(this.destroyRef)) diff --git a/src/app/pages/schedule-filter/schedule-filter.ts b/src/app/pages/schedule-filter/schedule-filter.ts index 6f519bc13..8cee16558 100644 --- a/src/app/pages/schedule-filter/schedule-filter.ts +++ b/src/app/pages/schedule-filter/schedule-filter.ts @@ -82,7 +82,7 @@ export class ScheduleFilterPage { }); } - ionViewWillEnter() { + ionViewWillEnter(): void { this.ios.set(this.config.get('mode') === 'ios'); // passed in array of track names that should be excluded (unchecked) @@ -100,12 +100,12 @@ export class ScheduleFilterPage { }); } - selectAll(check: boolean) { + selectAll(check: boolean): void { // set all to checked or unchecked this.tracks.update((tracks) => tracks.map((track) => ({ ...track, isChecked: check }))); } - applyFilters() { + applyFilters(): void { // Pass back a new array of track names to exclude const excludedTrackNames = this.tracks() .filter((c) => !c.isChecked) @@ -113,7 +113,7 @@ export class ScheduleFilterPage { this.dismiss(excludedTrackNames); } - dismiss(data?: string[]) { + dismiss(data?: string[]): void { // using the injected ModalController this page // can "dismiss" itself and pass back data this.modalCtrl.dismiss(data); diff --git a/src/app/pages/schedule/schedule.ts b/src/app/pages/schedule/schedule.ts index 5e30bb1e8..654de9c80 100644 --- a/src/app/pages/schedule/schedule.ts +++ b/src/app/pages/schedule/schedule.ts @@ -110,12 +110,12 @@ export class SchedulePage implements OnInit { }); } - ngOnInit() { + ngOnInit(): void { this.updateSchedule(); this.ios = this.config.get('mode') === 'ios'; } - updateSchedule() { + updateSchedule(): void { // Close any open sliding items when the schedule updates if (this.scheduleList) { this.scheduleList.closeSlidingItems(); @@ -134,7 +134,7 @@ export class SchedulePage implements OnInit { }); } - async presentFilter() { + async presentFilter(): Promise { const modal = await this.modalCtrl.create({ component: ScheduleFilterPage, presentingElement: this.routerOutlet.nativeEl, @@ -149,7 +149,7 @@ export class SchedulePage implements OnInit { } } - async addFavorite(slidingItem: IonItemSliding, sessionData: Session) { + async addFavorite(slidingItem: IonItemSliding, sessionData: Session): Promise { if (this.user.hasFavorite(sessionData.name)) { // Prompt to remove favorite this.removeFavorite(slidingItem, sessionData, 'Favorite already added'); @@ -177,7 +177,7 @@ export class SchedulePage implements OnInit { } } - async removeFavorite(slidingItem: IonItemSliding, sessionData: Session, title: string) { + async removeFavorite(slidingItem: IonItemSliding, sessionData: Session, title: string): Promise { const alert = await this.alertCtrl.create({ header: title, message: 'Would you like to remove this session from your favorites?', @@ -207,7 +207,7 @@ export class SchedulePage implements OnInit { await alert.present(); } - async openSocial(network: string, fab: IonFab) { + async openSocial(network: string, fab: IonFab): Promise { const loading = await this.loadingCtrl.create({ message: `Posting to ${network}`, duration: Math.random() * 1000 + 500, diff --git a/src/app/pages/session-detail/session-detail.ts b/src/app/pages/session-detail/session-detail.ts index 040f97142..76d2b3b74 100644 --- a/src/app/pages/session-detail/session-detail.ts +++ b/src/app/pages/session-detail/session-detail.ts @@ -51,7 +51,7 @@ export class SessionDetailPage { addIcons({ shareOutline, starOutline, star, cloudDownload, share }); } - ionViewWillEnter() { + ionViewWillEnter(): void { this.confService.load().subscribe((data) => { if (data && data.schedule && data.schedule[0] && data.schedule[0].groups) { const sessionId = this.route.snapshot.paramMap.get('sessionId'); @@ -72,15 +72,15 @@ export class SessionDetailPage { }); } - ionViewDidEnter() { + ionViewDidEnter(): void { this.defaultHref.set('/app/tabs/schedule'); } - sessionClick(item: string) { + sessionClick(item: string): void { console.log('Clicked', item); } - toggleFavorite() { + toggleFavorite(): void { const sessionName = this.session()?.name; if (!sessionName) { return; @@ -94,7 +94,7 @@ export class SessionDetailPage { } } - shareSession() { + shareSession(): void { console.log('Clicked share session'); } } diff --git a/src/app/pages/signup/signup.ts b/src/app/pages/signup/signup.ts index 2321e8f83..4dd72070a 100644 --- a/src/app/pages/signup/signup.ts +++ b/src/app/pages/signup/signup.ts @@ -43,7 +43,7 @@ export class SignupPage { signup: UserOptions = { username: '', password: '' }; submitted = false; - onSignup(form: NgForm) { + onSignup(form: NgForm): void { this.submitted = true; if (form.valid) { diff --git a/src/app/pages/speaker-detail/speaker-detail.ts b/src/app/pages/speaker-detail/speaker-detail.ts index e20dbcfe2..73265373a 100644 --- a/src/app/pages/speaker-detail/speaker-detail.ts +++ b/src/app/pages/speaker-detail/speaker-detail.ts @@ -64,7 +64,7 @@ export class SpeakerDetailPage { }); } - ionViewWillEnter() { + ionViewWillEnter(): void { this.confService.load().subscribe((data) => { const speakerId = this.route.snapshot.paramMap.get('speakerId'); if (data && data.speakers) { @@ -78,11 +78,11 @@ export class SpeakerDetailPage { }); } - openExternalUrl(url: string) { + openExternalUrl(url: string): void { window.open(url, '_blank'); } - async openSpeakerShare(speaker: any) { + async openSpeakerShare(speaker: any): Promise { const actionSheet = await this.actionSheetCtrl.create({ header: 'Share ' + speaker.name, buttons: [ @@ -108,7 +108,7 @@ export class SpeakerDetailPage { await actionSheet.present(); } - async openContact(speaker: Speaker) { + async openContact(speaker: Speaker): Promise { const mode = 'ios'; // this.config.get('mode'); const actionSheet = await this.actionSheetCtrl.create({ diff --git a/src/app/pages/speaker-list/speaker-list.ts b/src/app/pages/speaker-list/speaker-list.ts index ca1c560dc..70ff4473b 100644 --- a/src/app/pages/speaker-list/speaker-list.ts +++ b/src/app/pages/speaker-list/speaker-list.ts @@ -51,7 +51,7 @@ export class SpeakerListPage { speakers = signal([]); - ionViewDidEnter() { + ionViewDidEnter(): void { this.confData.getSpeakers().subscribe((speakers) => { this.speakers.set(speakers); }); diff --git a/src/app/pages/support/support.ts b/src/app/pages/support/support.ts index 0186ddb54..214b6a3f2 100644 --- a/src/app/pages/support/support.ts +++ b/src/app/pages/support/support.ts @@ -42,7 +42,7 @@ export class SupportPage { submitted = false; supportMessage: string; - async ionViewDidEnter() { + async ionViewDidEnter(): Promise { const toast = await this.toastCtrl.create({ message: 'This does not actually send a support request.', duration: 3000, @@ -50,7 +50,7 @@ export class SupportPage { await toast.present(); } - async submit(form: NgForm) { + async submit(form: NgForm): Promise { this.submitted = true; if (form.valid) { diff --git a/src/app/pages/tutorial/tutorial.ts b/src/app/pages/tutorial/tutorial.ts index 42d7471b5..d4f3c431a 100644 --- a/src/app/pages/tutorial/tutorial.ts +++ b/src/app/pages/tutorial/tutorial.ts @@ -25,13 +25,13 @@ export class TutorialPage { }); } - startApp() { + startApp(): void { this.router .navigateByUrl('/app/tabs/schedule', { replaceUrl: true }) .then(() => this.storage.set('ion_did_tutorial', true)); } - ionViewWillEnter() { + ionViewWillEnter(): void { this.storage.get('ion_did_tutorial').then((res) => { if (res === true) { this.router.navigateByUrl('/app/tabs/schedule', { replaceUrl: true }); @@ -41,7 +41,7 @@ export class TutorialPage { this.menu.enable(false); } - ionViewWillLeave() { + ionViewWillLeave(): void { // enable the root left menu when leaving the tutorial page this.menu.enable(true); } diff --git a/src/app/providers/check-tutorial.guard.ts b/src/app/providers/check-tutorial.guard.ts index a68b13cd0..1851ac6f8 100644 --- a/src/app/providers/check-tutorial.guard.ts +++ b/src/app/providers/check-tutorial.guard.ts @@ -1,9 +1,10 @@ import { inject } from '@angular/core'; import { Router } from '@angular/router'; import { Storage } from '@ionic/storage-angular'; +import type { Observable } from 'rxjs'; import { from, map, tap } from 'rxjs'; -export const checkTutorialGuard = () => { +export const checkTutorialGuard = (): Observable => { const storage = inject(Storage); const router = inject(Router); diff --git a/src/app/providers/conference.service.ts b/src/app/providers/conference.service.ts index 201cb7fe4..7a7f0506c 100644 --- a/src/app/providers/conference.service.ts +++ b/src/app/providers/conference.service.ts @@ -1,9 +1,18 @@ import { HttpClient } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; +import type { Observable } from 'rxjs'; import { of } from 'rxjs'; import { map } from 'rxjs/operators'; -import type { ConferenceData, Group, ScheduleDay, Session, Speaker } from '../interfaces/conference.interfaces'; +import type { + ConferenceData, + Group, + MapLocation, + ScheduleDay, + Session, + Speaker, + Track, +} from '../interfaces/conference.interfaces'; import { UserService } from './user.service'; @@ -15,7 +24,7 @@ export class ConferenceService { user = inject(UserService); data: ConferenceData | null = null; - load() { + load(): Observable { if (this.data) { return of(this.data); } else { @@ -54,7 +63,12 @@ export class ConferenceService { return data; } - getTimeline(dayIndex: number, queryText = '', excludeTracks: string[] = [], segment = 'all') { + getTimeline( + dayIndex: number, + queryText = '', + excludeTracks: string[] = [], + segment = 'all', + ): Observable { return this.load().pipe( map((data: ConferenceData) => { const day = data.schedule[dayIndex]; @@ -91,7 +105,7 @@ export class ConferenceService { ); } - filterSession(session: Session, queryWords: string[], excludeTracks: string[], segment: string) { + filterSession(session: Session, queryWords: string[], excludeTracks: string[], segment: string): void { let matchesQueryText = false; if (queryWords.length) { // of any query word is in the session name than it passes the query test @@ -129,15 +143,15 @@ export class ConferenceService { session.hide = !(matchesQueryText && matchesTracks && matchesSegment); } - getSpeakers() { + getSpeakers(): Observable { return this.load().pipe(map((data: ConferenceData) => data.speakers)); } - getTracks() { + getTracks(): Observable { return this.load().pipe(map((data: ConferenceData) => data.tracks)); } - getMap() { + getMap(): Observable { return this.load().pipe(map((data: ConferenceData) => data.map)); } }