commit
ecab609487
|
@ -37,32 +37,34 @@
|
|||
<div class="safe-padding-horizontal" [hidden]="showFilter || !showSelectorFilter">
|
||||
<!-- "Time" selector. -->
|
||||
<core-combobox [label]="'core.show' | translate" [selection]="selectedFilter" (onChange)="selectedChanged($event)">
|
||||
<ion-select-option value="allincludinghidden" *ngIf="showFilters.allincludinghidden != 'hidden'">
|
||||
<ion-select-option class="ion-text-wrap" value="allincludinghidden" *ngIf="showFilters.allincludinghidden != 'hidden'">
|
||||
{{ 'addon.block_myoverview.allincludinghidden' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option value="all" *ngIf="showFilters.all != 'hidden'">
|
||||
<ion-select-option class="ion-text-wrap" value="all" *ngIf="showFilters.all != 'hidden'">
|
||||
{{ 'addon.block_myoverview.all' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option value="inprogress" *ngIf="showFilters.inprogress != 'hidden'"
|
||||
<ion-select-option class="ion-text-wrap" value="inprogress" *ngIf="showFilters.inprogress != 'hidden'"
|
||||
[disabled]="showFilters.inprogress == 'disabled'">
|
||||
{{ 'addon.block_myoverview.inprogress' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option value="future" *ngIf="showFilters.future != 'hidden'" [disabled]="showFilters.future == 'disabled'">
|
||||
<ion-select-option class="ion-text-wrap" value="future" *ngIf="showFilters.future != 'hidden'"
|
||||
[disabled]="showFilters.future == 'disabled'">
|
||||
{{ 'addon.block_myoverview.future' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option value="past" *ngIf="showFilters.past != 'hidden'" [disabled]="showFilters.past == 'disabled'">
|
||||
<ion-select-option class="ion-text-wrap" value="past" *ngIf="showFilters.past != 'hidden'" [disabled]="showFilters.past == 'disabled'">
|
||||
{{ 'addon.block_myoverview.past' | translate }}
|
||||
</ion-select-option>
|
||||
<ng-container *ngIf="showFilters.custom != 'hidden'">
|
||||
<ng-container *ngFor="let customOption of customFilter; let index = index">
|
||||
<ion-select-option value="custom-{{index}}">{{ customOption.name }}</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="custom-{{index}}">{{ customOption.name }}</ion-select-option>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ion-select-option value="favourite" *ngIf="showFilters.favourite != 'hidden'"
|
||||
<ion-select-option class="ion-text-wrap" value="favourite" *ngIf="showFilters.favourite != 'hidden'"
|
||||
[disabled]="showFilters.favourite == 'disabled'">
|
||||
{{ 'addon.block_myoverview.favourites' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option value="hidden" *ngIf="showFilters.hidden != 'hidden'" [disabled]="showFilters.hidden == 'disabled'">
|
||||
<ion-select-option class="ion-text-wrap" value="hidden" *ngIf="showFilters.hidden != 'hidden'"
|
||||
[disabled]="showFilters.hidden == 'disabled'">
|
||||
{{ 'addon.block_myoverview.hiddencourses' | translate }}
|
||||
</ion-select-option>
|
||||
</core-combobox>
|
||||
|
|
|
@ -12,13 +12,27 @@
|
|||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
<div class="safe-padding-horizontal">
|
||||
<core-combobox [selection]="filter" (onChange)="switchFilter($event)">
|
||||
<ion-select-option value="all">{{ 'core.all' | translate }}</ion-select-option>
|
||||
<ion-select-option value="overdue">{{ 'addon.block_timeline.overdue' | translate }}</ion-select-option>
|
||||
<ion-select-option disabled value="disabled">{{ 'addon.block_timeline.duedate' | translate }}</ion-select-option>
|
||||
<ion-select-option value="next7days">{{ 'addon.block_timeline.next7days' | translate }}</ion-select-option>
|
||||
<ion-select-option value="next30days">{{ 'addon.block_timeline.next30days' | translate }}</ion-select-option>
|
||||
<ion-select-option value="next3months">{{ 'addon.block_timeline.next3months' | translate }}</ion-select-option>
|
||||
<ion-select-option value="next6months">{{ 'addon.block_timeline.next6months' | translate }}</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="all">
|
||||
{{ 'core.all' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="overdue">
|
||||
{{ 'addon.block_timeline.overdue' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" disabled value="disabled">
|
||||
{{ 'addon.block_timeline.duedate' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="next7days">
|
||||
{{ 'addon.block_timeline.next7days' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="next30days">
|
||||
{{ 'addon.block_timeline.next30days' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="next3months">
|
||||
{{ 'addon.block_timeline.next3months' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="next6months">
|
||||
{{ 'addon.block_timeline.next6months' | translate }}
|
||||
</ion-select-option>
|
||||
</core-combobox>
|
||||
</div>
|
||||
<core-loading [hideUntil]="timeline.loaded" [hidden]="sort != 'sortbydates'" class="core-loading-center">
|
||||
|
|
|
@ -32,10 +32,16 @@ function buildRoutes(injector: Injector): Routes {
|
|||
return [
|
||||
{
|
||||
path: 'index',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
loadChildren: () => import('@/addons/calendar/pages/index/index.module').then(m => m.AddonCalendarIndexPageModule),
|
||||
},
|
||||
{
|
||||
path: 'list',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
loadChildren: () => import('@/addons/calendar/pages/list/list.module').then(m => m.AddonCalendarListPageModule),
|
||||
},
|
||||
{
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
<ion-label><h2 [core-mark-required]="true">{{ 'core.course' | translate }}</h2></ion-label>
|
||||
<ion-select formControlName="groupcourseid"
|
||||
interface="action-sheet" [placeholder]="'core.noselection' | translate"
|
||||
(ionChange)="groupCourseSelected($event)">
|
||||
(ionChange)="groupCourseSelected()">
|
||||
<ion-select-option *ngFor="let course of courses" [value]="course.id">
|
||||
{{ course.fullname }}
|
||||
</ion-select-option>
|
||||
|
|
|
@ -396,10 +396,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
|||
|
||||
/**
|
||||
* A course was selected, get its groups.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
*/
|
||||
async groupCourseSelected(courseId: number): Promise<void> {
|
||||
async groupCourseSelected(): Promise<void> {
|
||||
const courseId = this.form.controls.groupcourseid.value;
|
||||
if (!courseId) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@
|
|||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.calendar.when' | translate }}</h2>
|
||||
<p [innerHTML]="event.formattedtime"></p>
|
||||
<core-format-text [text]="event.formattedtime" [contextLevel]="event.contextLevel"
|
||||
[contextInstanceId]="event.contextInstanceId"></core-format-text>
|
||||
</ion-label>
|
||||
<ion-note slot="end" *ngIf="!isSplitViewOn && event.deleted">
|
||||
<ion-icon name="fas-trash" aria-hidden="true"></ion-icon> {{ 'core.deletedoffline' | translate }}
|
||||
|
|
|
@ -103,8 +103,6 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
this.canEdit = AddonCalendar.canEditEventsInSite();
|
||||
this.canDelete = AddonCalendar.canDeleteEventsInSite();
|
||||
|
||||
this.asyncConstructor();
|
||||
|
||||
// Listen for event edited. If current event is edited, reload the data.
|
||||
this.editEventObserver = CoreEvents.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => {
|
||||
if (data && data.eventId == this.eventId) {
|
||||
|
@ -136,7 +134,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
protected async asyncConstructor(): Promise<void> {
|
||||
protected async initReminders(): Promise<void> {
|
||||
if (this.notificationsEnabled) {
|
||||
this.monthNames = CoreLang.getMonthNames();
|
||||
|
||||
|
@ -159,6 +157,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
this.syncIcon = CoreConstants.ICON_LOADING;
|
||||
|
||||
this.fetchEvent();
|
||||
this.initReminders();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,17 +8,17 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-item *ngIf="defaultTime != -1">
|
||||
<ion-label>{{ 'addon.calendar.defaultnotificationtime' | translate }}</ion-label>
|
||||
<ion-select [(ngModel)]="defaultTime" (ionChange)="updateDefaultTime($event)" interface="action-sheet">
|
||||
<ion-select-option value="0">{{ 'core.settings.disabled' | translate }}</ion-select-option>
|
||||
<ion-select-option value="10">{{ 600 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option value="30">{{ 1800 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option value="60">{{ 3600 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option value="120">{{ 7200 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option value="360">{{ 21600 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option value="720">{{ 43200 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option value="1440">{{ 86400 | coreDuration }}</ion-select-option>
|
||||
<ion-select [(ngModel)]="defaultTime" (ionChange)="updateDefaultTime(defaultTime)" interface="action-sheet">
|
||||
<ion-select-option [value]="0">{{ 'core.settings.disabled' | translate }}</ion-select-option>
|
||||
<ion-select-option [value]="10">{{ 600 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option [value]="30">{{ 1800 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option [value]="60">{{ 3600 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option [value]="120">{{ 7200 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option [value]="360">{{ 21600 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option [value]="720">{{ 43200 | coreDuration }}</ion-select-option>
|
||||
<ion-select-option [value]="1440">{{ 86400 | coreDuration }}</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
|
|
@ -26,7 +26,7 @@ import { CoreSites } from '@services/sites';
|
|||
})
|
||||
export class AddonCalendarSettingsPage implements OnInit {
|
||||
|
||||
defaultTime = 0;
|
||||
defaultTime = -1;
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, UrlTree } from '@angular/router';
|
||||
import { Router } from '@singletons';
|
||||
import { AddonMessagesMainMenuHandlerService } from '../services/handlers/mainmenu';
|
||||
import { AddonMessages } from '../services/messages';
|
||||
|
||||
/**
|
||||
* Guard to redirect to the right page based on the current Moodle site version.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AddonMessagesIndexGuard implements CanActivate {
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
canActivate(): UrlTree {
|
||||
return this.guard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a pending redirect and trigger it.
|
||||
*/
|
||||
private guard(): UrlTree {
|
||||
const enabled = AddonMessages.isGroupMessagingEnabled();
|
||||
const path = `/main/${AddonMessagesMainMenuHandlerService.PAGE_NAME}/` + ( enabled ? 'group-conversations' : 'index');
|
||||
|
||||
return Router.parseUrl(path);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@ import { Injector, NgModule } from '@angular/core';
|
|||
import { Route, RouterModule, ROUTES, Routes } from '@angular/router';
|
||||
|
||||
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { AddonMessagesIndexGuard } from './guards';
|
||||
|
||||
export const AddonMessagesDiscussionRoute: Route = {
|
||||
path: 'discussion',
|
||||
|
@ -51,8 +52,7 @@ function buildRoutes(injector: Injector): Routes {
|
|||
.then(m => m.AddonMessagesContactsPageModule),
|
||||
},
|
||||
...buildTabMainRoutes(injector, {
|
||||
redirectTo: 'index',
|
||||
pathMatch: 'full',
|
||||
canActivate: [AddonMessagesIndexGuard],
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ import { AddonMessagesDiscussions35Page } from './discussions.page';
|
|||
const mobileRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
component: AddonMessagesDiscussions35Page,
|
||||
},
|
||||
AddonMessagesDiscussionRoute,
|
||||
|
@ -34,6 +37,9 @@ const mobileRoutes: Routes = [
|
|||
const tabletRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
component: AddonMessagesDiscussions35Page,
|
||||
children: [
|
||||
AddonMessagesDiscussionRoute,
|
||||
|
|
|
@ -25,6 +25,9 @@ import { AddonMessagesGroupConversationsPage } from './group-conversations.page'
|
|||
const mobileRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
component: AddonMessagesGroupConversationsPage,
|
||||
},
|
||||
AddonMessagesDiscussionRoute,
|
||||
|
@ -33,6 +36,9 @@ const mobileRoutes: Routes = [
|
|||
const tabletRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
component: AddonMessagesGroupConversationsPage,
|
||||
children: [
|
||||
AddonMessagesDiscussionRoute,
|
||||
|
|
|
@ -18,6 +18,7 @@ import { CoreContentLinksAction } from '@features/contentlinks/services/contentl
|
|||
import { CoreNavigator } from '@services/navigator';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { AddonMessages } from '../messages';
|
||||
import { AddonMessagesMainMenuHandlerService } from './mainmenu';
|
||||
|
||||
/**
|
||||
* Content links handler for messaging index.
|
||||
|
@ -37,9 +38,7 @@ export class AddonMessagesIndexLinkHandlerService extends CoreContentLinksHandle
|
|||
getActions(): CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
|
||||
return [{
|
||||
action: async (siteId): Promise<void> => {
|
||||
const pageName = await AddonMessages.getMainMessagesPagePathInSite(siteId);
|
||||
|
||||
CoreNavigator.navigateToSitePath(pageName, {
|
||||
CoreNavigator.navigateToSitePath(AddonMessagesMainMenuHandlerService.PAGE_NAME, {
|
||||
siteId,
|
||||
preferCurrentTab: false,
|
||||
});
|
||||
|
|
|
@ -43,7 +43,7 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler,
|
|||
protected handler: CoreMainMenuHandlerToDisplay = {
|
||||
icon: 'fas-comments',
|
||||
title: 'addon.messages.messages',
|
||||
page: AddonMessages.getMainMessagesPagePath(),
|
||||
page: AddonMessagesMainMenuHandlerService.PAGE_NAME,
|
||||
class: 'addon-messages-handler',
|
||||
showBadge: true, // Do not check isMessageCountEnabled because we'll use fallback it not enabled.
|
||||
badge: '',
|
||||
|
@ -108,8 +108,6 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler,
|
|||
* @return Data needed to render the handler.
|
||||
*/
|
||||
getDisplayData(): CoreMainMenuHandlerToDisplay {
|
||||
this.handler.page = AddonMessages.getMainMessagesPagePath();
|
||||
|
||||
if (this.handler.loading) {
|
||||
this.refreshBadge();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { CoreNavigator } from '@services/navigator';
|
|||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { AddonMessages } from '../messages';
|
||||
import { AddonMessagesMainMenuHandlerService } from './mainmenu';
|
||||
|
||||
/**
|
||||
* Handler for messaging push notifications clicks.
|
||||
|
@ -61,7 +62,6 @@ export class AddonMessagesPushClickHandlerService implements CorePushNotificatio
|
|||
|
||||
// Check if group messaging is enabled, to determine which page should be loaded.
|
||||
const enabled = await AddonMessages.isGroupMessagingEnabledInSite(notification.site);
|
||||
const pageName = await AddonMessages.getMainMessagesPagePathInSite(notification.site);
|
||||
|
||||
let nextPageParams: Params | undefined;
|
||||
|
||||
|
@ -76,7 +76,7 @@ export class AddonMessagesPushClickHandlerService implements CorePushNotificatio
|
|||
};
|
||||
}
|
||||
|
||||
await CoreNavigator.navigateToSitePath(pageName, {
|
||||
await CoreNavigator.navigateToSitePath(AddonMessagesMainMenuHandlerService.PAGE_NAME, {
|
||||
siteId: notification.site,
|
||||
preferCurrentTab: false,
|
||||
nextNavigation: nextPageParams ?
|
||||
|
|
|
@ -30,7 +30,6 @@ import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
|||
import { CoreWSExternalWarning } from '@services/ws';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { AddonMessagesMainMenuHandlerService } from './handlers/mainmenu';
|
||||
import { AddonMessagesSyncEvents, AddonMessagesSyncProvider } from './messages-sync';
|
||||
|
||||
const ROOT_CACHE_KEY = 'mmaMessages:';
|
||||
|
@ -1411,29 +1410,6 @@ export class AddonMessagesProvider {
|
|||
throw new CoreError('Error getting message preferences');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the site main messages page path for a site.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @return Main messages page path of the site.
|
||||
*/
|
||||
async getMainMessagesPagePathInSite(siteId?: string): Promise<string> {
|
||||
const enabled = await this.isGroupMessagingEnabledInSite(siteId);
|
||||
|
||||
return AddonMessagesMainMenuHandlerService.PAGE_NAME + ( enabled ? '/group-conversations' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the site main messages page path.
|
||||
*
|
||||
* @return Main messages page path of the site.
|
||||
*/
|
||||
getMainMessagesPagePath(): string {
|
||||
const enabled = this.isGroupMessagingEnabled();
|
||||
|
||||
return AddonMessagesMainMenuHandlerService.PAGE_NAME + ( enabled ? '/group-conversations' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get messages according to the params.
|
||||
*
|
||||
|
|
|
@ -188,11 +188,11 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
|
|||
/**
|
||||
* Sync all quizzes on a site.
|
||||
*
|
||||
* @param siteId Site ID to sync.
|
||||
* @param force Wether to force sync not depending on last execution.
|
||||
* @param siteId Site ID to sync.
|
||||
* @param Promise resolved if sync is successful, rejected if sync fails.
|
||||
*/
|
||||
protected async syncAllQuizzesFunc(siteId: string, force: boolean): Promise<void> {
|
||||
protected async syncAllQuizzesFunc(force: boolean, siteId: string): Promise<void> {
|
||||
// Get all offline attempts.
|
||||
const attempts = await AddonModQuizOffline.getAllAttempts(siteId);
|
||||
|
||||
|
|
|
@ -36,9 +36,15 @@
|
|||
</ion-item>
|
||||
|
||||
<core-combobox [selection]="type" (onChange)="typeChanged($event)">
|
||||
<ion-select-option value="site">{{ 'addon.notes.sitenotes' | translate }}</ion-select-option>
|
||||
<ion-select-option value="course">{{ 'addon.notes.coursenotes' | translate }}</ion-select-option>
|
||||
<ion-select-option value="personal">{{ 'addon.notes.personalnotes' | translate }}</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="site">
|
||||
{{ 'addon.notes.sitenotes' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="course">
|
||||
{{ 'addon.notes.coursenotes' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="personal">
|
||||
{{ 'addon.notes.personalnotes' | translate }}
|
||||
</ion-select-option>
|
||||
</core-combobox>
|
||||
|
||||
<ion-card class="core-warning-card" *ngIf="hasOffline">
|
||||
|
|
|
@ -21,6 +21,9 @@ function buildRoutes(injector: Injector): Routes {
|
|||
return [
|
||||
{
|
||||
path: 'list',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
loadChildren: () => import('./pages/list/list.module').then(m => m.AddonNotificationsListPageModule),
|
||||
},
|
||||
...buildTabMainRoutes(injector, {
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<!-- Show processor selector. -->
|
||||
<core-combobox *ngIf="preferences && preferences.processors && preferences.processors.length > 0"
|
||||
[selection]="currentProcessor!.name" (onChange)="changeProcessor($event)">
|
||||
<ion-select-option *ngFor="let processor of preferences.processors" [value]="processor.name">
|
||||
<ion-select-option class="ion-text-wrap" *ngFor="let processor of preferences.processors" [value]="processor.name">
|
||||
{{ processor.displayname }}
|
||||
</ion-select-option>
|
||||
</core-combobox>
|
||||
|
|
|
@ -15,8 +15,12 @@
|
|||
<core-loading [hideUntil]="filesLoaded" *ngIf="showPrivateFiles || showSiteFiles">
|
||||
<!-- Allow selecting the files to see: private or site. -->
|
||||
<core-combobox [selection]="root" (onChange)="rootChanged($event)" *ngIf="showPrivateFiles && showSiteFiles && !path">
|
||||
<ion-select-option value="my">{{ 'addon.privatefiles.privatefiles' | translate }}</ion-select-option>
|
||||
<ion-select-option value="site">{{ 'addon.privatefiles.sitefiles' | translate }}</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="my">
|
||||
{{ 'addon.privatefiles.privatefiles' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" value="site">
|
||||
{{ 'addon.privatefiles.sitefiles' | translate }}
|
||||
</ion-select-option>
|
||||
</core-combobox>
|
||||
|
||||
<!-- Display info about space used and space left. -->
|
||||
|
|
|
@ -19,12 +19,19 @@ import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.modu
|
|||
|
||||
function buildRoutes(injector: Injector): Routes {
|
||||
return [
|
||||
{
|
||||
path: 'root',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
loadChildren: () => import('./pages/index/index.module').then(m => m.AddonPrivateFilesIndexPageModule),
|
||||
},
|
||||
{
|
||||
path: ':hash',
|
||||
loadChildren: () => import('./pages/index/index.module').then(m => m.AddonPrivateFilesIndexPageModule),
|
||||
},
|
||||
...buildTabMainRoutes(injector, {
|
||||
redirectTo: 'root', // Fake "hash".
|
||||
redirectTo: 'root',
|
||||
pathMatch: 'full',
|
||||
}),
|
||||
];
|
||||
|
|
|
@ -48,7 +48,6 @@ export class AddonPrivateFilesMainMenuHandlerService implements CoreMainMenuHand
|
|||
icon: 'fas-folder',
|
||||
title: 'addon.privatefiles.files',
|
||||
page: AddonPrivateFilesMainMenuHandlerService.PAGE_NAME,
|
||||
subPage: 'root',
|
||||
class: 'addon-privatefiles-handler',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import { CoreUtils } from '@services/utils/utils';
|
|||
})
|
||||
export class CoreAutoFocusDirective implements AfterViewInit {
|
||||
|
||||
@Input('core-auto-focus') showKeyboard: boolean | string = true;
|
||||
@Input('core-auto-focus') autoFocus: boolean | string = true;
|
||||
|
||||
protected element: HTMLElement;
|
||||
|
||||
|
@ -42,7 +42,7 @@ export class CoreAutoFocusDirective implements AfterViewInit {
|
|||
* @inheritdoc
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
if (this.showKeyboard === 'nofocus') {
|
||||
if (CoreUtils.isFalseOrZero(this.autoFocus)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -78,8 +78,7 @@ export class CoreAutoFocusDirective implements AfterViewInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const showKeyboard = this.showKeyboard === '' || CoreUtils.isTrueOrOne(this.showKeyboard);
|
||||
CoreDomUtils.focusElement(element, showKeyboard);
|
||||
CoreDomUtils.focusElement(element);
|
||||
|
||||
if (element != document.activeElement) {
|
||||
this.setFocus(retries - 1);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<ion-label class="sr-only">{{ 'core.login.username' | translate }}</ion-label>
|
||||
<ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}"
|
||||
formControlName="username" autocapitalize="none" autocorrect="off" autocomplete="username" enterkeyhint="next"
|
||||
required="true" core-auto-focus>
|
||||
required="true">
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="siteChecked && !isBrowserSSO" class="ion-margin-bottom">
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<ion-item>
|
||||
<ion-label></ion-label>
|
||||
<ion-input type="text" name="value" placeholder="{{ 'core.login.usernameoremail' | translate }}"
|
||||
formControlName="value" autocapitalize="none" autocorrect="off" [core-auto-focus]="showKeyboard">
|
||||
formControlName="value" autocapitalize="none" autocorrect="off" [core-auto-focus]="autoFocus">
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
<ion-button type="submit" class="ion-margin" expand="block" [disabled]="!myForm.valid">
|
||||
|
|
|
@ -35,7 +35,7 @@ export class CoreLoginForgottenPasswordPage implements OnInit {
|
|||
|
||||
myForm!: FormGroup;
|
||||
siteUrl!: string;
|
||||
showKeyboard!: boolean;
|
||||
autoFocus!: boolean;
|
||||
|
||||
constructor(
|
||||
protected formBuilder: FormBuilder,
|
||||
|
@ -55,7 +55,7 @@ export class CoreLoginForgottenPasswordPage implements OnInit {
|
|||
}
|
||||
|
||||
this.siteUrl = siteUrl;
|
||||
this.showKeyboard = Platform.is('tablet');
|
||||
this.autoFocus = Platform.is('tablet');
|
||||
this.myForm = this.formBuilder.group({
|
||||
field: ['username', Validators.required],
|
||||
value: [CoreNavigator.getRouteParam<string>('username') || '', Validators.required],
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<core-show-password name="password">
|
||||
<ion-input class="core-ioninput-password" name="password" type="password"
|
||||
placeholder="{{ 'core.login.password' | translate }}" formControlName="password" [clearOnEdit]="false"
|
||||
autocomplete="current-password" enterkeyhint="go" required="true" core-auto-focus>
|
||||
autocomplete="current-password" enterkeyhint="go" required="true">
|
||||
</ion-input>
|
||||
</core-show-password>
|
||||
</ion-item>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<h2>{{ 'core.login.siteaddress' | translate }}</h2>
|
||||
</ion-label>
|
||||
<ion-input name="url" type="url" placeholder="{{ 'core.login.siteaddressplaceholder' | translate }}"
|
||||
formControlName="siteUrl" [core-auto-focus]="showKeyboard">
|
||||
formControlName="siteUrl" [core-auto-focus]="showKeyboard && !showScanQR">
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
@ -36,7 +36,7 @@
|
|||
<h2>{{ 'core.login.siteaddress' | translate }}</h2>
|
||||
</ion-label>
|
||||
<ion-input name="url" placeholder="{{ 'core.login.siteaddressplaceholder' | translate }}" formControlName="siteUrl"
|
||||
[core-auto-focus]="showKeyboard" (ionChange)="searchSite($event, siteForm.value.siteUrl)">
|
||||
[core-auto-focus]="showKeyboard && !showScanQR" (ionChange)="searchSite($event, siteForm.value.siteUrl)">
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ function buildRoutes(injector: Injector): Routes {
|
|||
return [
|
||||
...buildTabMainRoutes(injector, {
|
||||
path: '',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
component: CoreMainMenuHomePage,
|
||||
children: routes.children,
|
||||
}),
|
||||
|
|
|
@ -13,13 +13,12 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { IonTabs } from '@ionic/angular';
|
||||
import { BackButtonEvent } from '@ionic/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreEvents, CoreEventObserver } from '@singletons/events';
|
||||
import { CoreMainMenu, CoreMainMenuProvider } from '../../services/mainmenu';
|
||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../services/mainmenu-delegate';
|
||||
|
@ -27,6 +26,7 @@ import { CoreDomUtils } from '@services/utils/dom';
|
|||
import { Translate } from '@singletons';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from '@classes/aria-role-tab';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
|
||||
/**
|
||||
* Page that displays the main menu of the app.
|
||||
|
@ -61,7 +61,6 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
|
|||
constructor(
|
||||
protected route: ActivatedRoute,
|
||||
protected changeDetector: ChangeDetectorRef,
|
||||
protected router: Router,
|
||||
) {
|
||||
this.resizeFunction = this.initHandlers.bind(this);
|
||||
this.backButtonFunction = this.backButtonClicked.bind(this);
|
||||
|
@ -172,28 +171,26 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
const trimmedUrl = CoreTextUtils.trimCharacter(this.router.url, '/');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
// Current tab was clicked. Check if user is already at root level.
|
||||
if (trimmedUrl == CoreTextUtils.trimCharacter(page, '/')) {
|
||||
// Already at root level, nothing to do.
|
||||
return;
|
||||
const mainMenuRootRoute = CoreNavigator.getCurrentRoute({ routeData: { isMainMenuRoot: true } });
|
||||
if (mainMenuRootRoute) {
|
||||
return; // Already at root level, nothing to do.
|
||||
}
|
||||
|
||||
// Current route doesn't define isMainMenuRoot. Check if the current path is the tab one.
|
||||
const currentPath = CoreNavigator.getCurrentPath();
|
||||
if (currentPath == `/main/${page}`) {
|
||||
return; // Already at root level, nothing to do.
|
||||
}
|
||||
|
||||
// Ask the user if he wants to go back to the root page of the tab.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
try {
|
||||
const tab = this.tabs.find((tab) => tab.page == page);
|
||||
|
||||
// Use tab's subPage to check if user is already at root level.
|
||||
if (tab?.subPage && trimmedUrl ==
|
||||
CoreTextUtils.trimCharacter(CoreTextUtils.concatenatePaths(tab.page, tab.subPage), '/')) {
|
||||
// Already at root level, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab?.title) {
|
||||
await CoreDomUtils.showConfirm(Translate.instant('core.confirmgotabroot', {
|
||||
name: Translate.instant(tab.title),
|
||||
|
|
|
@ -136,7 +136,6 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy {
|
|||
* Open a handler.
|
||||
*
|
||||
* @param handler Handler to open.
|
||||
* @todo: use subPage?
|
||||
*/
|
||||
openHandler(handler: CoreMainMenuHandlerData): void {
|
||||
const params = handler.pageParams;
|
||||
|
|
|
@ -42,7 +42,6 @@ export class CoreMainMenuHomeHandlerService implements CoreMainMenuHandler {
|
|||
icon: 'fas-tachometer-alt',
|
||||
title: 'core.mainmenu.home',
|
||||
page: CoreMainMenuHomeHandlerService.PAGE_NAME,
|
||||
// @todo: subPage? The page can change due to core-tabs.
|
||||
class: 'core-home-handler',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,13 +33,6 @@ export interface CoreMainMenuHandlerData {
|
|||
*/
|
||||
page: string;
|
||||
|
||||
/**
|
||||
* Sub page loaded when the handler page is loaded.
|
||||
* If your module performs a redirect when it's opened you need to specify the sub page in here.
|
||||
* E.g. if page is 'foo' but it redirects to 'foo/bar' when opened, this value must be 'bar'.
|
||||
*/
|
||||
subPage?: string;
|
||||
|
||||
/**
|
||||
* Title to display for the handler.
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<ion-item>
|
||||
<ion-label></ion-label>
|
||||
<ion-input type="search" name="search" [(ngModel)]="searchText" [placeholder]="placeholder"
|
||||
[autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus || 'nofocus'"
|
||||
[autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus"
|
||||
[disabled]="disabled" role="searchbox" (ionFocus)="focus($event)">
|
||||
</ion-input>
|
||||
<ion-button slot="end" fill="clear" type="submit" size="small" [attr.aria-label]="searchLabel"
|
||||
|
|
|
@ -19,8 +19,10 @@
|
|||
</ion-col>
|
||||
<ion-col size="12" size-sm="6" *ngIf="collections && collections.length > 1">
|
||||
<core-combobox [selection]="collectionId" (onChange)="searchTags($event)" [disabled]="searching">
|
||||
<ion-select-option [value]="0">{{ 'core.tag.inalltagcoll' | translate }}</ion-select-option>
|
||||
<ion-select-option *ngFor="let collection of collections" [value]="collection.id">
|
||||
<ion-select-option class="ion-text-wrap" [value]="0">
|
||||
{{ 'core.tag.inalltagcoll' | translate }}
|
||||
</ion-select-option>
|
||||
<ion-select-option class="ion-text-wrap" *ngFor="let collection of collections" [value]="collection.id">
|
||||
{{ collection.name }}</ion-select-option>
|
||||
</core-combobox>
|
||||
</ion-col>
|
||||
|
|
|
@ -31,6 +31,9 @@ function buildRoutes(injector: Injector): Routes {
|
|||
},
|
||||
{
|
||||
path: 'search',
|
||||
data: {
|
||||
isMainMenuRoot: true,
|
||||
},
|
||||
loadChildren: () => import('@features/tag//pages/search/search.page.module').then(m => m.CoreTagSearchPageModule),
|
||||
},
|
||||
CoreTagIndexAreaRoute,
|
||||
|
|
|
@ -519,6 +519,31 @@ export class CoreNavigatorService {
|
|||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full path of a certain route, including parent routes paths.
|
||||
*
|
||||
* @param route Route.
|
||||
* @return Path.
|
||||
*/
|
||||
getRouteFullPath(route: ActivatedRoute | null): string {
|
||||
if (!route) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const parentPath = this.getRouteFullPath(route.parent);
|
||||
const routePath = route.snapshot.url.join('/');
|
||||
|
||||
if (!parentPath && !routePath) {
|
||||
return '';
|
||||
} else if (parentPath && !routePath) {
|
||||
return parentPath;
|
||||
} else if (!parentPath && routePath) {
|
||||
return '/' + routePath;
|
||||
} else {
|
||||
return parentPath + '/' + routePath;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreNavigator = makeSingleton(CoreNavigatorService);
|
||||
|
|
|
@ -391,12 +391,11 @@ export class CoreDomUtilsProvider {
|
|||
* Focus an element and open keyboard.
|
||||
*
|
||||
* @param el HTML element to focus.
|
||||
* @param showKeyboard Show keyboard when focusing the element.
|
||||
*/
|
||||
focusElement(el: HTMLElement, showKeyboard = true): void {
|
||||
focusElement(el: HTMLElement): void {
|
||||
if (el?.focus) {
|
||||
el.focus();
|
||||
if (showKeyboard && CoreApp.isAndroid() && this.supportsInputKeyboard(el)) {
|
||||
if (CoreApp.isAndroid() && this.supportsInputKeyboard(el)) {
|
||||
// On some Android versions the keyboard doesn't open automatically.
|
||||
CoreApp.openKeyboard();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue