mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-10 03:32:52 +00:00
fix: make keyboard dismissable on mobile devices
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<manifest package="de.anyschool.app" xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest package="de.anyschool.app" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">
|
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">
|
||||||
<activity android:exported="true" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/title_activity_main" android:launchMode="singleTask" android:name="de.anyschool.app.MainActivity" android:theme="@style/AppTheme.NoActionBarLaunch">
|
<activity android:exported="true" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:windowSoftInputMode="adjustPan" android:label="@string/title_activity_main" android:launchMode="singleTask" android:name="de.anyschool.app.MainActivity" android:theme="@style/AppTheme.NoActionBarLaunch">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {environment} from '../environments/environment';
|
|||||||
import {StatusBar, Style} from '@capacitor/status-bar';
|
import {StatusBar, Style} from '@capacitor/status-bar';
|
||||||
import {Capacitor} from '@capacitor/core';
|
import {Capacitor} from '@capacitor/core';
|
||||||
import {ScheduleSyncService} from './modules/background/schedule/schedule-sync.service';
|
import {ScheduleSyncService} from './modules/background/schedule/schedule-sync.service';
|
||||||
|
import {Keyboard, KeyboardResize} from '@capacitor/keyboard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
@@ -45,6 +46,11 @@ export class AppComponent implements AfterContentInit {
|
|||||||
title: string;
|
title: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Angular component selectors that should not infulence keyboard state
|
||||||
|
*/
|
||||||
|
ommitedEventSources = ['ion-input', 'ion-searchbar'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param platform TODO
|
* @param platform TODO
|
||||||
@@ -104,6 +110,11 @@ export class AppComponent implements AfterContentInit {
|
|||||||
'others',
|
'others',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.addEventListener('touchmove', this.touchMoveEvent, true);
|
||||||
|
if (Capacitor.isNativePlatform()) {
|
||||||
|
Keyboard.setResizeMode({mode: KeyboardResize.None});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async authNotificationsInit() {
|
private async authNotificationsInit() {
|
||||||
@@ -129,4 +140,30 @@ export class AppComponent implements AfterContentInit {
|
|||||||
});
|
});
|
||||||
await toast.present();
|
await toast.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if keyboard should be dissmissed
|
||||||
|
*/
|
||||||
|
touchMoveEvent = (event: Event): void => {
|
||||||
|
if (
|
||||||
|
this.ommitedEventSources.includes(
|
||||||
|
(event?.target as unknown as Record<string, string>)?.[
|
||||||
|
's-hn'
|
||||||
|
]?.toLowerCase(),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.unfocusActiveElement();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loses focus on the currently active element (meant for input fields).
|
||||||
|
* Results in virtual keyboard being dissmissed on native and web plattforms.
|
||||||
|
*/
|
||||||
|
unfocusActiveElement() {
|
||||||
|
const activeElement = document.activeElement;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(activeElement as any)?.blur();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,10 @@
|
|||||||
|
|
||||||
<ion-content fullscreen="true" #ionContent>
|
<ion-content fullscreen="true" #ionContent>
|
||||||
<stapps-navigation-section></stapps-navigation-section>
|
<stapps-navigation-section></stapps-navigation-section>
|
||||||
<stapps-search-section></stapps-search-section>
|
<stapps-search-section
|
||||||
|
#search
|
||||||
|
(focusin)="onSearchBarFocus($event)"
|
||||||
|
></stapps-search-section>
|
||||||
<stapps-news-section></stapps-news-section>
|
<stapps-news-section></stapps-news-section>
|
||||||
<stapps-mensa-section></stapps-mensa-section>
|
<stapps-mensa-section></stapps-mensa-section>
|
||||||
<stapps-favorites-section></stapps-favorites-section>
|
<stapps-favorites-section></stapps-favorites-section>
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
@ViewChild('schedule', {read: ElementRef}) scheduleRef: ElementRef;
|
@ViewChild('schedule', {read: ElementRef}) scheduleRef: ElementRef;
|
||||||
|
|
||||||
|
@ViewChild('search', {read: ElementRef}) searchRef: ElementRef;
|
||||||
|
|
||||||
@ViewChild('ionContent') ionContentRef: IonContent;
|
@ViewChild('ionContent') ionContentRef: IonContent;
|
||||||
|
|
||||||
collapseAnimation: DashboardCollapse;
|
collapseAnimation: DashboardCollapse;
|
||||||
@@ -88,6 +90,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset from search bar to top
|
||||||
|
*/
|
||||||
|
searchToTopOffset = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly dataRoutingService: DataRoutingService,
|
private readonly dataRoutingService: DataRoutingService,
|
||||||
private scheduleProvider: ScheduleProvider,
|
private scheduleProvider: ScheduleProvider,
|
||||||
@@ -121,6 +128,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ionViewDidEnter() {
|
||||||
|
this.searchToTopOffset =
|
||||||
|
this.searchRef.nativeElement?.getBoundingClientRect().top - 100;
|
||||||
|
}
|
||||||
|
|
||||||
async loadNextEvent() {
|
async loadNextEvent() {
|
||||||
const dataSeries = await this.scheduleProvider.getDateSeries(
|
const dataSeries = await this.scheduleProvider.getDateSeries(
|
||||||
this.uuids,
|
this.uuids,
|
||||||
@@ -142,4 +154,15 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
this._uuidSubscription.unsubscribe();
|
this._uuidSubscription.unsubscribe();
|
||||||
this.collapseAnimation.destroy();
|
this.collapseAnimation.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onSearchBarFocus(_event: Event) {
|
||||||
|
this.ionContentRef.getScrollElement().then(element => {
|
||||||
|
if (
|
||||||
|
element.scrollHeight - element.clientHeight >=
|
||||||
|
this.searchToTopOffset
|
||||||
|
) {
|
||||||
|
this.ionContentRef.scrollToPoint(0, this.searchToTopOffset, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,11 @@
|
|||||||
<div class="searchbar">
|
<div class="searchbar">
|
||||||
<ion-input
|
<ion-input
|
||||||
type="search"
|
type="search"
|
||||||
|
enterkeyhint="search"
|
||||||
placeholder="{{ 'search.search_bar.placeholder' | translate }}"
|
placeholder="{{ 'search.search_bar.placeholder' | translate }}"
|
||||||
(submit)="onSubmitSearch()"
|
(submit)="onSubmitSearch()"
|
||||||
(keyup.enter)="onSubmitSearch()"
|
(keyup.enter)="onSubmitSearch()"
|
||||||
|
(search)="onSubmitSearch()"
|
||||||
[(ngModel)]="searchTerm"
|
[(ngModel)]="searchTerm"
|
||||||
></ion-input>
|
></ion-input>
|
||||||
<ion-icon
|
<ion-icon
|
||||||
|
|||||||
@@ -22,10 +22,8 @@
|
|||||||
border-radius: var(--border-radius-default);
|
border-radius: var(--border-radius-default);
|
||||||
--padding-start: var(--spacing-md);
|
--padding-start: var(--spacing-md);
|
||||||
--padding-end: var(--spacing-md);
|
--padding-end: var(--spacing-md);
|
||||||
--padding-top: var(--spacing-xl);
|
--padding-top: var(--spacing-md);
|
||||||
--padding-bottom: var(--spacing-xl);
|
--padding-bottom: var(--spacing-md);
|
||||||
font-size: var(--font-size-xs);
|
|
||||||
--placeholder-font-weight: var(--font-weight-bold);
|
|
||||||
box-shadow: var(--shadow-default);
|
box-shadow: var(--shadow-default);
|
||||||
}
|
}
|
||||||
ion-icon {
|
ion-icon {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
*/
|
*/
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
|
import {Capacitor} from '@capacitor/core';
|
||||||
|
import {Keyboard} from '@capacitor/keyboard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a search input field
|
* Shows a search input field
|
||||||
@@ -32,6 +34,17 @@ export class SearchSectionComponent {
|
|||||||
* User submits search
|
* User submits search
|
||||||
*/
|
*/
|
||||||
onSubmitSearch() {
|
onSubmitSearch() {
|
||||||
this.router.navigate(['/search', this.searchTerm]);
|
this.router
|
||||||
|
.navigate(['/search', this.searchTerm])
|
||||||
|
.then(() => this.hideKeyboard());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides keyboard in native app environments
|
||||||
|
*/
|
||||||
|
hideKeyboard() {
|
||||||
|
if (Capacitor.isNativePlatform()) {
|
||||||
|
Keyboard.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
<ion-searchbar
|
<ion-searchbar
|
||||||
(ngModelChange)="searchStringChanged($event)"
|
(ngModelChange)="searchStringChanged($event)"
|
||||||
(keyup.enter)="hideKeyboard()"
|
(keyup.enter)="hideKeyboard()"
|
||||||
|
(search)="hideKeyboard()"
|
||||||
[(ngModel)]="queryText"
|
[(ngModel)]="queryText"
|
||||||
showClearButton="always"
|
showClearButton="always"
|
||||||
placeholder="{{ 'search.search_bar.placeholder' | translate }}"
|
placeholder="{{ 'search.search_bar.placeholder' | translate }}"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<ion-searchbar
|
<ion-searchbar
|
||||||
(ngModelChange)="searchStringChanged($event)"
|
(ngModelChange)="searchStringChanged($event)"
|
||||||
(keyup.enter)="hideKeyboard()"
|
(keyup.enter)="hideKeyboard()"
|
||||||
|
(search)="hideKeyboard()"
|
||||||
[(ngModel)]="queryText"
|
[(ngModel)]="queryText"
|
||||||
mode="md"
|
mode="md"
|
||||||
placeholder="{{ 'hebisSearch.search_bar.placeholder' | translate }}"
|
placeholder="{{ 'hebisSearch.search_bar.placeholder' | translate }}"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
<ion-searchbar
|
<ion-searchbar
|
||||||
(keyup)="searchKeyUp($event)"
|
(keyup)="searchKeyUp($event)"
|
||||||
(keyup.enter)="hideKeyboard()"
|
(keyup.enter)="hideKeyboard()"
|
||||||
|
(search)="hideKeyboard()"
|
||||||
[(ngModel)]="queryText"
|
[(ngModel)]="queryText"
|
||||||
(ionClear)="searchStringChanged()"
|
(ionClear)="searchStringChanged()"
|
||||||
mode="md"
|
mode="md"
|
||||||
|
|||||||
Reference in New Issue
Block a user