MOBILE-4459 core: Create and use core-sites-list component

main
Dani Palou 2023-11-01 15:52:20 +01:00
parent d27d3ddec3
commit a9bae5937c
15 changed files with 234 additions and 260 deletions

View File

@ -65,6 +65,7 @@ import { CoreGroupSelectorComponent } from './group-selector/group-selector';
import { CoreRefreshButtonModalComponent } from './refresh-button-modal/refresh-button-modal'; import { CoreRefreshButtonModalComponent } from './refresh-button-modal/refresh-button-modal';
import { CoreSheetModalComponent } from '@components/sheet-modal/sheet-modal'; import { CoreSheetModalComponent } from '@components/sheet-modal/sheet-modal';
import { CoreCourseImageComponent } from '@components/course-image/course-image'; import { CoreCourseImageComponent } from '@components/course-image/course-image';
import { CoreSitesListComponent } from './sites-list/sites-list';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -112,6 +113,7 @@ import { CoreCourseImageComponent } from '@components/course-image/course-image'
CoreSwipeNavigationTourComponent, CoreSwipeNavigationTourComponent,
CoreRefreshButtonModalComponent, CoreRefreshButtonModalComponent,
CoreSheetModalComponent, CoreSheetModalComponent,
CoreSitesListComponent,
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -166,6 +168,7 @@ import { CoreCourseImageComponent } from '@components/course-image/course-image'
CoreSwipeNavigationTourComponent, CoreSwipeNavigationTourComponent,
CoreRefreshButtonModalComponent, CoreRefreshButtonModalComponent,
CoreSheetModalComponent, CoreSheetModalComponent,
CoreSitesListComponent,
], ],
}) })
export class CoreComponentsModule {} export class CoreComponentsModule {}

View File

@ -0,0 +1,56 @@
<ion-list class="core-sites-list" *ngIf="accountsList">
<ion-card *ngIf="accountsList.currentSite">
<ng-container *ngTemplateOutlet="siteCardHeader; context: {site: accountsList.currentSite, isCurrentSite: true}"></ng-container>
<ng-container *ngTemplateOutlet="siteItem; context: {site: accountsList.currentSite, isCurrentSite: true}"></ng-container>
<ng-container *ngFor="let site of accountsList.sameSite">
<ng-container *ngTemplateOutlet="siteItem; context: {site: site, isCurrentSite: false}"></ng-container>
</ng-container>
</ion-card>
<ion-card *ngFor="let sites of accountsList.otherSites">
<ng-container *ngTemplateOutlet="siteCardHeader; context: {site: sites[0], isCurrentSite: false}"></ng-container>
<ng-container *ngFor="let site of sites">
<ng-container *ngTemplateOutlet="siteItem; context: {site: site, isCurrentSite: false}"></ng-container>
</ng-container>
</ion-card>
</ion-list>
<!-- Template to render the header of a site card. -->
<ng-template #siteCardHeader let-site="site" let-isCurrentSite="isCurrentSite">
<ion-item-divider sticky="true" *ngIf="site" class="core-sites-list-sitename">
<ion-label>
<h2>
<core-format-text [text]="site.siteName" clean="true" [siteId]="site.id"></core-format-text>
</h2>
<p *ngIf="!site.isDemoModeSite">
<a [href]="site.siteUrl" core-link [autoLogin]="isCurrentSite ? 'yes' : 'no'">
{{ site.siteUrlWithoutProtocol }}
</a>
</p>
</ion-label>
</ion-item-divider>
</ng-template>
<!-- Template to render a site item. -->
<ng-template #siteItem let-site="site" let-isCurrentSite="isCurrentSite">
<ion-item [attr.button]="isSiteClickable(isCurrentSite) ? true : null" (click)="siteClicked($event, site, isCurrentSite)"
[attr.detail]="isSiteClickable(isCurrentSite) ? 'true' : 'false'" [class.item-current]="isCurrentSite">
<core-user-avatar [user]="site" slot="start" [linkProfile]="false" [siteId]="site.id"></core-user-avatar>
<ion-label>
<p class="item-heading">{{site.fullname}}</p>
<ng-container *ngIf="siteLabelTemplate" [ngTemplateOutlet]="siteLabelTemplate"
[ngTemplateOutletContext]="{site: site, isCurrentSite: isCurrentSite}">
</ng-container>
</ion-label>
<ng-container *ngIf="siteItemTemplate" [ngTemplateOutlet]="siteItemTemplate"
[ngTemplateOutletContext]="{site: site, isCurrentSite: isCurrentSite}">
</ng-container>
</ion-item>
</ng-template>

View File

@ -1,5 +1,5 @@
ion-list.core-sitelist { ion-list.core-sites-list {
.core-sitelist-sitename { .core-sites-list-sitename {
ion-label { ion-label {
margin-top: 8px; margin-top: 8px;
margin-bottom: 8px; margin-bottom: 8px;

View File

@ -0,0 +1,80 @@
// (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 { Component, ContentChild, Input, Output, TemplateRef, EventEmitter } from '@angular/core';
import { CoreSiteBasicInfo } from '@services/sites';
import { CoreAccountsList } from '@features/login/services/login-helper';
/**
* Component to display a list of sites (accounts).
*
* By default this component will display the avatar and user fullname for each site, but it allows adding more information
* in the item and in the label for each site, using #siteItem and #siteLabel ng-templates. These templates will receive the
* site being rendered and whether it's the current site or not. Example:
*
* <core-sites-list [accountsList]="accountsList">
* <ng-template #siteLabel let-site="site" let-isCurrentSite="isCurrentSite">
* <!-- Content to be placed in the label, after the user full name.
* </ng-template>
*
* <ng-template #siteItem let-site="site" let-isCurrentSite="isCurrentSite">
* <!-- Content to be placed in the item.
* </ng-template>
* </core-sites-list>
*/
@Component({
selector: 'core-sites-list',
templateUrl: 'sites-list.html',
styleUrls: ['sites-list.scss'],
})
export class CoreSitesListComponent<T extends CoreSiteBasicInfo> {
@Input() accountsList!: CoreAccountsList<T>;
@Input() sitesClickable = false; // Whether the sites are clickable.
@Input() currentSiteClickable?: boolean; // If set, specify a different clickable value for current site.
@Output() onSiteClicked = new EventEmitter<T>();
@ContentChild('siteItem') siteItemTemplate?: TemplateRef<unknown>;
@ContentChild('siteLabel') siteLabelTemplate?: TemplateRef<unknown>;
/**
* Check whether a site is clickable.
*
* @param isCurrentSite Whether the site is current site.
* @returns Whether it's clickable.
*/
isSiteClickable(isCurrentSite: boolean): boolean {
return isCurrentSite ? this.currentSiteClickable ?? this.sitesClickable : this.sitesClickable;
}
/**
* A site was clicked.
*
* @param ev Event.
* @param site Site clicked.
* @param isCurrentSite Whether the site is current site.
*/
siteClicked(ev: Event, site: T, isCurrentSite: boolean): void {
if (!this.isSiteClickable(isCurrentSite)) {
return;
}
ev.preventDefault();
ev.stopPropagation();
this.onSiteClicked.emit(site);
}
}

View File

@ -20,68 +20,12 @@
</ion-header> </ion-header>
<ion-content> <ion-content>
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<ion-list class="core-sitelist"> <core-sites-list [accountsList]="accountsList" [sitesClickable]="true" [currentSiteClickable]="false"
<ion-card *ngIf="accountsList.currentSite"> (onSiteClicked)="login($event)">
<ion-item-divider sticky="true" class="core-sitelist-sitename"> <ng-template #siteItem let-site="site" let-isCurrentSite="isCurrentSite">
<ion-label> <ion-icon *ngIf="isCurrentSite" color="success" name="fas-check"></ion-icon>
<h2>
<core-format-text [text]="accountsList.currentSite.siteName" clean="true"
[siteId]="accountsList.currentSite.id"></core-format-text>
</h2>
<p *ngIf="!accountsList.currentSite.isDemoModeSite">
<a [href]="accountsList.currentSite.siteUrl" core-link autoLogin="yes">
{{ accountsList.currentSite.siteUrlWithoutProtocol }}
</a>
</p>
</ion-label>
</ion-item-divider>
<ion-item detail="false"> <ng-container *ngIf="!isCurrentSite">
<core-user-avatar [user]="accountsList.currentSite" slot="start" [linkProfile]="false"
[siteId]="accountsList.currentSite.id"></core-user-avatar>
<ion-label>
<p class="item-heading">{{accountsList.currentSite.fullname}}</p>
</ion-label>
<ion-icon color="success" name="fas-check"></ion-icon>
</ion-item>
<ng-container *ngTemplateOutlet="siteList; context: {sites: accountsList.sameSite}"></ng-container>
</ion-card>
<ion-card *ngFor="let sites of accountsList.otherSites">
<ion-item-divider sticky="true" *ngIf="sites[0]" class="core-sitelist-sitename">
<ion-label>
<h2>
<core-format-text [text]="sites[0].siteName" clean="true" [siteId]="sites[0].id"></core-format-text>
</h2>
<p *ngIf="!sites[0].isDemoModeSite">
<a [href]="sites[0].siteUrl" core-link autoLogin="no">{{ sites[0].siteUrlWithoutProtocol }}</a>
</p>
</ion-label>
</ion-item-divider>
<ng-container *ngTemplateOutlet="siteList; context: {sites: sites}"></ng-container>
</ion-card>
</ion-list>
</core-loading>
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end">
<ion-fab-button (click)="add($event)" [attr.aria-label]="'core.login.add' | translate">
<ion-icon name="fas-plus" aria-hidden="true"></ion-icon>
<span class="sr-only">{{ 'core.login.add' | translate }}</span>
</ion-fab-button>
</ion-fab>
</ion-content>
<!-- Template to render a list of sites. -->
<ng-template #siteList let-sites="sites">
<ion-item button *ngFor="let site of sites" (click)="login($event, site.id)" detail="true">
<core-user-avatar [user]="site" slot="start" [linkProfile]="false" [siteId]="site.id"></core-user-avatar>
<ion-label>
<p class="item-heading">{{site.fullname}}</p>
</ion-label>
<ion-badge slot="end" *ngIf="!showDelete && site.badge" @coreShowHideAnimation> <ion-badge slot="end" *ngIf="!showDelete && site.badge" @coreShowHideAnimation>
<span aria-hidden="true">{{site.badge}}</span> <span aria-hidden="true">{{site.badge}}</span>
<span class="sr-only">{{ 'core.login.sitebadgedescription' | translate:{ count: site.badge } <span class="sr-only">{{ 'core.login.sitebadgedescription' | translate:{ count: site.badge }
@ -91,5 +35,14 @@
[attr.aria-label]="'core.login.removeaccount' | translate" [@coreSlideInOut]="'fromRight'"> [attr.aria-label]="'core.login.removeaccount' | translate" [@coreSlideInOut]="'fromRight'">
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
</ion-item> </ng-container>
</ng-template> </ng-template>
</core-sites-list>
</core-loading>
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end">
<ion-fab-button (click)="add($event)" [attr.aria-label]="'core.login.add' | translate">
<ion-icon name="fas-plus" aria-hidden="true"></ion-icon>
<span class="sr-only">{{ 'core.login.add' | translate }}</span>
</ion-fab-button>
</ion-fab>
</ion-content>

View File

@ -28,7 +28,6 @@ import { ModalController } from '@singletons';
@Component({ @Component({
selector: 'core-login-sites', selector: 'core-login-sites',
templateUrl: 'sites.html', templateUrl: 'sites.html',
styleUrls: ['../../sitelist.scss'],
animations: [CoreAnimations.SLIDE_IN_OUT, CoreAnimations.SHOW_HIDE], animations: [CoreAnimations.SLIDE_IN_OUT, CoreAnimations.SHOW_HIDE],
}) })
export class CoreLoginSitesComponent implements OnInit { export class CoreLoginSitesComponent implements OnInit {
@ -99,15 +98,14 @@ export class CoreLoginSitesComponent implements OnInit {
/** /**
* Login in a site. * Login in a site.
* *
* @param event Click event. * @param site The site.
* @param siteId The site ID.
* @returns Promise resolved when done. * @returns Promise resolved when done.
*/ */
async login(event: Event, siteId: string): Promise<void> { async login(site: CoreSiteBasicInfo): Promise<void> {
await this.close(event, true); await this.close(undefined, true);
// This navigation will logout and navigate to the site home. // This navigation will logout and navigate to the site home.
await CoreNavigator.navigateToSiteHome({ preferCurrentTab: false , siteId }); await CoreNavigator.navigateToSiteHome({ preferCurrentTab: false , siteId: site.id });
} }
/** /**
@ -122,9 +120,9 @@ export class CoreLoginSitesComponent implements OnInit {
* *
* @param event Click event. * @param event Click event.
*/ */
async close(event: Event, closeAll = false): Promise<void> { async close(event?: Event, closeAll = false): Promise<void> {
event.preventDefault(); event?.preventDefault();
event.stopPropagation(); event?.stopPropagation();
await ModalController.dismiss(closeAll); await ModalController.dismiss(closeAll);
} }

View File

@ -21,25 +21,8 @@
</ion-header> </ion-header>
<ion-content class="limited-width"> <ion-content class="limited-width">
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<ion-list class="core-sitelist"> <core-sites-list [accountsList]="accountsList" [sitesClickable]="true" (onSiteClicked)="login($event)">
<ion-card *ngFor="let sites of accountsList.otherSites"> <ng-template #siteItem let-site="site" let-isCurrentSite="isCurrentSite">
<ion-item-divider sticky="true" *ngIf="sites[0]" class="core-sitelist-sitename">
<ion-label>
<h2>
<core-format-text [text]="sites[0].siteName" clean="true" [siteId]="sites[0].id"></core-format-text>
</h2>
<p *ngIf="!sites[0].isDemoModeSite">
<a [href]="sites[0].siteUrl" core-link autoLogin="no">{{ sites[0].siteUrlWithoutProtocol }}</a>
</p>
</ion-label>
</ion-item-divider>
<ion-item button *ngFor="let site of sites" (click)="login($event, site.id)" detail="true">
<core-user-avatar [user]="site" slot="start" [linkProfile]="false" [siteId]="site.id"></core-user-avatar>
<ion-label>
<p class="item-heading">{{site.fullname}}</p>
</ion-label>
<ion-badge slot="end" *ngIf="!showDelete && site.badge" @coreShowHideAnimation> <ion-badge slot="end" *ngIf="!showDelete && site.badge" @coreShowHideAnimation>
<span aria-hidden="true">{{site.badge}}</span> <span aria-hidden="true">{{site.badge}}</span>
<span class="sr-only">{{ 'core.login.sitebadgedescription' | translate:{ count: site.badge } <span class="sr-only">{{ 'core.login.sitebadgedescription' | translate:{ count: site.badge }
@ -49,9 +32,8 @@
[attr.aria-label]="'core.login.removeaccount' | translate" [@coreSlideInOut]="'fromRight'"> [attr.aria-label]="'core.login.removeaccount' | translate" [@coreSlideInOut]="'fromRight'">
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
</ion-item> </ng-template>
</ion-card> </core-sites-list>
</ion-list>
</core-loading> </core-loading>
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end"> <ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end">
<ion-fab-button (click)="add()" [attr.aria-label]="'core.login.add' | translate"> <ion-fab-button (click)="add()" [attr.aria-label]="'core.login.add' | translate">

View File

@ -22,12 +22,11 @@ import { CoreFilter } from '@features/filter/services/filter';
import { CoreAnimations } from '@components/animations'; import { CoreAnimations } from '@components/animations';
/** /**
* Page that displays a "splash screen" while the app is being initialized. * Page that displays the list of sites stored in the device.
*/ */
@Component({ @Component({
selector: 'page-core-login-sites', selector: 'page-core-login-sites',
templateUrl: 'sites.html', templateUrl: 'sites.html',
styleUrls: ['../../sitelist.scss'],
animations: [CoreAnimations.SLIDE_IN_OUT, CoreAnimations.SHOW_HIDE], animations: [CoreAnimations.SLIDE_IN_OUT, CoreAnimations.SHOW_HIDE],
}) })
export class CoreLoginSitesPage implements OnInit { export class CoreLoginSitesPage implements OnInit {
@ -102,18 +101,14 @@ export class CoreLoginSitesPage implements OnInit {
/** /**
* Login in a site. * Login in a site.
* *
* @param event Click event. * @param site The site.
* @param siteId The site ID.
* @returns Promise resolved when done. * @returns Promise resolved when done.
*/ */
async login(event: Event, siteId: string): Promise<void> { async login(site: CoreSiteBasicInfo): Promise<void> {
event.preventDefault();
event.stopPropagation();
const modal = await CoreDomUtils.showModalLoading(); const modal = await CoreDomUtils.showModalLoading();
try { try {
const loggedIn = await CoreSites.loadSite(siteId); const loggedIn = await CoreSites.loadSite(site.id);
if (loggedIn) { if (loggedIn) {
await CoreNavigator.navigateToSiteHome(); await CoreNavigator.navigateToSiteHome();

View File

@ -1541,10 +1541,10 @@ export const CoreLoginHelper = makeSingleton(CoreLoginHelperProvider);
/** /**
* Accounts list for selecting sites interfaces. * Accounts list for selecting sites interfaces.
*/ */
export type CoreAccountsList = { export type CoreAccountsList<T extends CoreSiteBasicInfo = CoreSiteBasicInfo> = {
currentSite?: CoreSiteBasicInfo; // If logged in, current site info. currentSite?: T; // If logged in, current site info.
sameSite: CoreSiteBasicInfo[]; // If logged in, accounts info on the same site. sameSite: T[]; // If logged in, accounts info on the same site.
otherSites: CoreSiteBasicInfo[][]; // Other accounts in other sites. otherSites: T[][]; // Other accounts in other sites.
count: number; // Number of sites. count: number; // Number of sites.
}; };

View File

@ -17,47 +17,21 @@
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher> </ion-refresher>
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<ion-list class="core-sitelist limited-width"> <ion-list class="limited-width">
<ion-card *ngIf="accountsList.currentSite"> <core-sites-list [accountsList]="accountsList">
<ion-item-divider sticky="true" class="core-sitelist-sitename"> <ng-template #siteLabel let-site="site">
<ion-label> <ion-badge color="light" *ngIf="site.spaceUsage !== undefined">{{ site.spaceUsage | coreBytesToSize }}</ion-badge>
<p class="item-heading"> </ng-template>
<core-format-text [text]="accountsList.currentSite.siteName" clean="true"
[siteId]="accountsList.currentSite.id"></core-format-text>
</p>
<p *ngIf="!accountsList.currentSite.isDemoModeSite">
<a [href]="accountsList.currentSite.siteUrl" core-link autoLogin="yes">
{{ accountsList.currentSite.siteUrlWithoutProtocol }}
</a>
</p>
</ion-label>
</ion-item-divider>
<ion-item class="item-current"> <ng-template #siteItem let-site="site">
<ng-container *ngTemplateOutlet="siteUsage; context: {site: accountsList.currentSite}"></ng-container> <ion-button fill="clear" color="danger" slot="end" (click)="deleteSiteStorage(site)"
</ion-item> [hidden]="site.spaceUsage <= 0 && !site.hasCacheEntries">
<ion-icon name="fas-trash" slot="icon-only"
<ion-item *ngFor="let site of accountsList.sameSite"> [attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: site.siteName }">
<ng-container *ngTemplateOutlet="siteUsage; context: {site: site}"></ng-container> </ion-icon>
</ion-item> </ion-button>
</ion-card> </ng-template>
</core-sites-list>
<ion-card *ngFor="let sites of accountsList.otherSites">
<ion-item-divider sticky="true" *ngIf="sites[0]" class="core-sitelist-sitename">
<ion-label>
<p class="item-heading">
<core-format-text [text]="sites[0].siteName" clean="true" [siteId]="sites[0].id"></core-format-text>
</p>
<p *ngIf="!sites[0].isDemoModeSite">
<a [href]="sites[0].siteUrl" core-link autoLogin="no">{{ sites[0].siteUrlWithoutProtocol }}</a>
</p>
</ion-label>
</ion-item-divider>
<ion-item *ngFor="let site of sites">
<ng-container *ngTemplateOutlet="siteUsage; context: {site: site}"></ng-container>
</ion-item>
</ion-card>
<ion-item-divider> <ion-item-divider>
<ion-label> <ion-label>
@ -70,19 +44,3 @@
</ion-list> </ion-list>
</core-loading> </core-loading>
</ion-content> </ion-content>
<!-- Template to render a site space usage. -->
<ng-template #siteUsage let-site="site">
<core-user-avatar [user]="site" slot="start" [linkProfile]="false" [siteId]="site.id"></core-user-avatar>
<ion-label class="ion-text-wrap">
<p class="item-heading">{{site.fullname}}</p>
<ion-badge color="light" *ngIf="site.spaceUsage !== undefined">{{ site.spaceUsage | coreBytesToSize }}</ion-badge>
</ion-label>
<ion-button fill="clear" color="danger" slot="end" (click)="deleteSiteStorage(site)"
[hidden]="site.spaceUsage <= 0 && !site.hasCacheEntries">
<ion-icon name="fas-trash" slot="icon-only"
[attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: site.siteName }">
</ion-icon>
</ion-button>
</ng-template>

View File

@ -20,6 +20,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSettingsHelper } from '../../services/settings-helper'; import { CoreSettingsHelper } from '../../services/settings-helper';
import { CoreAccountsList } from '@features/login/services/login-helper';
/** /**
* Page that displays the space usage settings. * Page that displays the space usage settings.
@ -33,9 +34,10 @@ export class CoreSettingsSpaceUsagePage implements OnInit, OnDestroy {
loaded = false; loaded = false;
totalSpaceUsage = 0; totalSpaceUsage = 0;
accountsList: CoreAccountsListWithUsage = { accountsList: CoreAccountsList<CoreSiteBasicInfoWithUsage> = {
sameSite: [], sameSite: [],
otherSites: [], otherSites: [],
count: 0,
}; };
protected sitesObserver: CoreEventObserver; protected sitesObserver: CoreEventObserver;
@ -128,6 +130,7 @@ export class CoreSettingsSpaceUsagePage implements OnInit, OnDestroy {
}); });
this.accountsList.otherSites = CoreUtils.objectToArray(otherSites); this.accountsList.otherSites = CoreUtils.objectToArray(otherSites);
this.accountsList.count = sites.length;
this.totalSpaceUsage = totalSize; this.totalSpaceUsage = totalSize;
} }
@ -192,12 +195,3 @@ interface CoreSiteBasicInfoWithUsage extends CoreSiteBasicInfo {
hasCacheEntries: boolean; // If has cached entries that can be cleared. hasCacheEntries: boolean; // If has cached entries that can be cleared.
spaceUsage: number; // Space used in this site. spaceUsage: number; // Space used in this site.
} }
/**
* Accounts list for selecting sites interfaces.
*/
type CoreAccountsListWithUsage = {
currentSite?: CoreSiteBasicInfoWithUsage; // If logged in, current site info.
sameSite: CoreSiteBasicInfoWithUsage[]; // If logged in, accounts info on the same site.
otherSites: CoreSiteBasicInfoWithUsage[][]; // Other accounts in other sites.
};

View File

@ -17,7 +17,7 @@
</ion-header> </ion-header>
<ion-content class="limited-width"> <ion-content class="limited-width">
<core-loading [hideUntil]="sitesLoaded"> <core-loading [hideUntil]="sitesLoaded">
<ion-list class="core-sitelist limited-width"> <ion-list class="limited-width">
<ion-item-divider> <ion-item-divider>
<ion-label> <ion-label>
<h2>{{ 'core.settings.syncsettings' | translate }}</h2> <h2>{{ 'core.settings.syncsettings' | translate }}</h2>
@ -51,65 +51,25 @@
</ion-label> </ion-label>
</ion-item-divider> </ion-item-divider>
<ion-card *ngIf="accountsList.currentSite"> <core-sites-list [accountsList]="accountsList">
<ion-item-divider sticky="true" class="core-sitelist-sitename"> <ng-template #siteLabel let-site="site">
<ion-label> <p class="text-danger" *ngIf="site.loggedOut">{{ 'core.settings.logintosync' | translate }}</p>
<p class="item-heading"> </ng-template>
<core-format-text [text]="accountsList.currentSite.siteName" clean="true"
[siteId]="accountsList.currentSite.id"></core-format-text>
</p>
<p *ngIf="!accountsList.currentSite.isDemoModeSite">
<a [href]="accountsList.currentSite.siteUrl" core-link autoLogin="yes">
{{ accountsList.currentSite.siteUrlWithoutProtocol }}
</a>
</p>
</ion-label>
</ion-item-divider>
<ion-item class="item-current"> <ng-template #siteItem let-site="site">
<ng-container *ngTemplateOutlet="siteSync; context: {site: accountsList.currentSite}"></ng-container> <core-button-with-spinner [loading]="isSynchronizing(site.id)" slot="end" *ngIf="!site.loggedOut">
</ion-item> <ion-button fill="clear" (click)="synchronize(site.id)"
[attr.aria-label]="'core.settings.synchronizenow' | translate">
<ion-item *ngFor="let site of accountsList.sameSite"> <ion-icon name="fas-rotate" slot="icon-only" aria-hidden="true"></ion-icon>
<ng-container *ngTemplateOutlet="siteSync; context: {site: site}"></ng-container> </ion-button>
</ion-item> </core-button-with-spinner>
</ion-card> <ion-button fill="clear" (click)="login(site.id)" [attr.aria-label]="'core.login.login' | translate"
*ngIf="site.loggedOut" slot="end">
<ion-card *ngFor="let sites of accountsList.otherSites"> <ion-icon name="fas-right-to-bracket" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-item-divider sticky="true" *ngIf="sites[0]" class="core-sitelist-sitename"> </ion-button>
<ion-label> </ng-template>
<p class="item-heading"> </core-sites-list>
<core-format-text [text]="sites[0].siteName" clean="true" [siteId]="sites[0].id"></core-format-text>
</p>
<p *ngIf="!sites[0].isDemoModeSite">
<a [href]="sites[0].siteUrl" core-link autoLogin="no">{{ sites[0].siteUrlWithoutProtocol }}</a>
</p>
</ion-label>
</ion-item-divider>
<ion-item *ngFor="let site of sites">
<ng-container *ngTemplateOutlet="siteSync; context: {site: site}"></ng-container>
</ion-item>
</ion-card>
</ng-container> </ng-container>
</ion-list> </ion-list>
</core-loading> </core-loading>
</ion-content> </ion-content>
<!-- Template to render a site to sync. -->
<ng-template #siteSync let-site="site">
<core-user-avatar [user]="site" slot="start" [linkProfile]="false" [siteId]="site.id"></core-user-avatar>
<ion-label>
<p class="item-heading">{{site.fullname}}</p>
<p class="text-danger" *ngIf="site.loggedOut">{{ 'core.settings.logintosync' | translate }}</p>
</ion-label>
<core-button-with-spinner [loading]="isSynchronizing(site.id)" slot="end" *ngIf="!site.loggedOut">
<ion-button fill="clear" (click)="synchronize(site.id)" [attr.aria-label]="'core.settings.synchronizenow' | translate">
<ion-icon name="fas-rotate" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</core-button-with-spinner>
<ion-button fill="clear" (click)="login(site.id)" [attr.aria-label]="'core.login.login' | translate" *ngIf="site.loggedOut" slot="end">
<ion-icon name="fas-right-to-bracket" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</ng-template>

View File

@ -32,7 +32,6 @@ import { CoreNavigator } from '@services/navigator';
@Component({ @Component({
selector: 'page-core-app-settings-synchronization', selector: 'page-core-app-settings-synchronization',
templateUrl: 'synchronization.html', templateUrl: 'synchronization.html',
styleUrls: ['../../../login/sitelist.scss'],
}) })
export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy { export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {

View File

@ -17,17 +17,8 @@
<p>{{fileName}}</p> <p>{{fileName}}</p>
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-item *ngFor="let site of sites" (click)="storeInSite(site.id)" detail="false" button> <core-sites-list [accountsList]="accountsList" [sitesClickable]="true" (onSiteClicked)="storeInSite($event)">
<core-user-avatar [user]="site" slot="start" [linkProfile]="false" [siteId]="site.id"></core-user-avatar> </core-sites-list>
<ion-label>
<p class="item-heading">{{site.fullname}}</p>
<p>
<core-format-text clean="true" [text]="site.siteName" [siteId]="site.id"></core-format-text>
</p>
<p *ngIf="!site.isDemoModeSite">{{site.siteUrl}}</p>
</ion-label>
</ion-item>
</ion-list> </ion-list>
</core-loading> </core-loading>
</ion-content> </ion-content>

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { CoreAccountsList, CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreSharedFilesHelper } from '@features/sharedfiles/services/sharedfiles-helper'; import { CoreSharedFilesHelper } from '@features/sharedfiles/services/sharedfiles-helper';
import { FileEntry } from '@ionic-native/file/ngx'; import { FileEntry } from '@ionic-native/file/ngx';
import { CoreFile } from '@services/file'; import { CoreFile } from '@services/file';
@ -30,8 +31,12 @@ import { CoreDomUtils } from '@services/utils/dom';
export class CoreSharedFilesChooseSitePage implements OnInit { export class CoreSharedFilesChooseSitePage implements OnInit {
fileName?: string; fileName?: string;
sites?: CoreSiteBasicInfo[];
loaded = false; loaded = false;
accountsList: CoreAccountsList = {
sameSite: [],
otherSites: [],
count: 0,
};
protected filePath?: string; protected filePath?: string;
protected fileEntry?: FileEntry; protected fileEntry?: FileEntry;
@ -89,15 +94,15 @@ export class CoreSharedFilesChooseSitePage implements OnInit {
* @returns Promise resolved when done. * @returns Promise resolved when done.
*/ */
protected async loadSites(): Promise<void> { protected async loadSites(): Promise<void> {
this.sites = await CoreSites.getSites(); this.accountsList = await CoreLoginHelper.getAccountsList(CoreSites.getCurrentSiteId());
} }
/** /**
* Store the file in a certain site. * Store the file in a certain site.
* *
* @param siteId Site ID. * @param site Site.
*/ */
async storeInSite(siteId: string): Promise<void> { async storeInSite(site: CoreSiteBasicInfo): Promise<void> {
if (!this.fileEntry) { if (!this.fileEntry) {
return; return;
} }
@ -105,7 +110,7 @@ export class CoreSharedFilesChooseSitePage implements OnInit {
this.loaded = false; this.loaded = false;
try { try {
await CoreSharedFilesHelper.storeSharedFileInSite(this.fileEntry, siteId, this.isInbox); await CoreSharedFilesHelper.storeSharedFileInSite(this.fileEntry, site.id, this.isInbox);
CoreNavigator.back(); CoreNavigator.back();
} finally { } finally {