Skip to content

Commit 8ee7428

Browse files
authored
Merge pull request #4724 from crazyserver/MOBILE-5011
Mobile 5011 When handling a site url, add a button to add an account on the site chooser modal
2 parents 6185aec + ede0d1c commit 8ee7428

File tree

7 files changed

+184
-78
lines changed

7 files changed

+184
-78
lines changed
Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,29 @@
1-
<ion-header>
2-
<ion-toolbar>
3-
<ion-title>
4-
<h1>{{ 'core.contentlinks.chooseaccount' | translate }}</h1>
5-
</ion-title>
6-
<ion-buttons slot="end">
7-
<ion-button fill="clear" (click)="closeModal()" [ariaLabel]="'core.close' | translate">
8-
<ion-icon slot="icon-only" name="fas-xmark" aria-hidden="true" />
9-
</ion-button>
10-
</ion-buttons>
11-
</ion-toolbar>
12-
</ion-header>
13-
<ion-content>
14-
<core-loading [hideUntil]="loaded">
15-
<ion-list>
16-
<ion-item class="ion-text-wrap">
17-
<ion-label>
18-
<h2 class="item-heading">{{ 'core.contentlinks.chooseaccounttoopenlink' | translate }}</h2>
19-
@if (displaySiteUrl) {
20-
<p>{{ url }}</p>
21-
}
22-
</ion-label>
23-
</ion-item>
24-
<ion-item *ngFor="let site of sites" (click)="siteClicked(site.id)" [detail]="false" button>
25-
<core-user-avatar [site]="site" slot="start" [linkProfile]="false" />
1+
<core-loading [hideUntil]="loaded">
2+
<ion-list class="ion-margin">
3+
<ion-item class="ion-text-wrap">
4+
<ion-label>
5+
<h2 class="item-heading">{{ 'core.contentlinks.chooseaccounttoopenlink' | translate }}</h2>
6+
@if (sites().length && siteName()) {
7+
<p><core-format-text [text]="siteName()" [clean]="true" [siteId]="sites()[0].id" /></p>
8+
}
9+
@if (siteUrl()) {
10+
<p>{{ siteUrl() }}</p>
11+
}
12+
</ion-label>
13+
</ion-item>
2614

15+
@for (site of sites(); track site.id) {
16+
<ion-item (click)="siteClicked(site.id)" [detail]="false" button>
17+
<core-user-avatar [site]="site" slot="start" [linkProfile]="false" />
2718
<ion-label>
2819
<p class="item-heading">{{site.fullname}}</p>
29-
<p>
30-
<core-format-text [text]="site.siteName" [clean]="true" [siteId]="site.id" />
31-
</p>
32-
@if (displaySiteUrl) {
33-
<p>{{site.siteUrl}}</p>
34-
}
3520
</ion-label>
3621
</ion-item>
37-
</ion-list>
38-
</core-loading>
39-
</ion-content>
22+
}
23+
</ion-list>
24+
25+
<ion-button expand="block" class="ion-margin" (click)="addNewSite()" [ariaLabel]="'core.login.add' | translate">
26+
{{ 'core.login.add' | translate }}
27+
</ion-button>
28+
29+
</core-loading>

src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.ts

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import { Component, Input, OnInit } from '@angular/core';
15+
import { Component, OnInit, computed, input, signal } from '@angular/core';
1616
import { CoreSiteBasicInfo, CoreSites } from '@services/sites';
1717
import { ModalController, Translate } from '@singletons';
1818
import { CoreContentLinksAction } from '../../services/contentlinks-delegate';
@@ -22,6 +22,7 @@ import { CoreNavigator } from '@services/navigator';
2222
import { CoreSitesFactory } from '@services/sites-factory';
2323
import { CoreSharedModule } from '@/core/shared.module';
2424
import { CoreAlerts } from '@services/overlays/alerts';
25+
import { NO_SITE_ID } from '@features/login/constants';
2526

2627
/**
2728
* Page to display the list of sites to choose one to perform a content link action.
@@ -35,35 +36,52 @@ import { CoreAlerts } from '@services/overlays/alerts';
3536
})
3637
export class CoreContentLinksChooseSiteModalComponent implements OnInit {
3738

38-
@Input({ required: true }) url!: string;
39+
readonly url = input.required<string>();
40+
41+
readonly sites = signal<CoreSiteBasicInfo[]>([]);
42+
readonly loaded = signal(false);
43+
readonly siteUrl = computed(() => {
44+
if (!this.sites().length) {
45+
return false;
46+
}
47+
const displayUrl = CoreSitesFactory.makeUnauthenticatedSite(this.sites()[0].siteUrl).shouldDisplayInformativeLinks();
48+
49+
// All sites have the same URL, use the first one.
50+
return displayUrl
51+
? this.sites()[0].siteUrl.replace(/^https?:\/\//, '').toLowerCase()
52+
: false;
53+
});
54+
55+
readonly siteName = computed(() => {
56+
if (!this.sites().length) {
57+
return false;
58+
}
59+
60+
// All sites have the same name, use the first one.
61+
return this.sites()[0].siteName;
62+
});
3963

40-
sites: CoreSiteBasicInfo[] = [];
41-
loaded = false;
42-
displaySiteUrl = false;
4364
protected action?: CoreContentLinksAction;
4465
protected isRootURL = false;
4566

4667
/**
4768
* @inheritdoc
4869
*/
4970
async ngOnInit(): Promise<void> {
50-
if (!this.url) {
51-
return this.closeModal();
52-
}
53-
71+
const url = this.url();
5472
let siteIds: string[] | undefined = [];
5573

5674
try {
5775
// Check if it's the root URL.
58-
const data = await CoreSites.isStoredRootURL(this.url);
76+
const data = await CoreSites.isStoredRootURL(url);
5977
if (data.site) {
6078
// It's the root URL.
6179
this.isRootURL = true;
6280

6381
siteIds = data.siteIds;
6482
} else if (data.siteIds.length) {
6583
// Not root URL, but the URL belongs to at least 1 site. Check if there is any action to treat the link.
66-
this.action = await CoreContentLinksHelper.getFirstValidActionFor(this.url);
84+
this.action = await CoreContentLinksHelper.getFirstValidActionFor(url);
6785
if (!this.action) {
6886
throw new CoreError(Translate.instant('core.contentlinks.errornoactions'));
6987
}
@@ -75,16 +93,14 @@ export class CoreContentLinksChooseSiteModalComponent implements OnInit {
7593
}
7694

7795
// Get the sites that can perform the action.
78-
this.sites = await CoreSites.getSites(siteIds);
79-
80-
// All sites have the same URL, use the first one.
81-
this.displaySiteUrl = CoreSitesFactory.makeUnauthenticatedSite(this.sites[0].siteUrl).shouldDisplayInformativeLinks();
96+
const sites = await CoreSites.getSites(siteIds);
97+
this.sites.set(sites);
8298
} catch (error) {
8399
CoreAlerts.showError(error, { default: Translate.instant('core.contentlinks.errornosites') });
84100
this.closeModal();
85101
}
86102

87-
this.loaded = true;
103+
this.loaded.set(true);
88104
}
89105

90106
/**
@@ -102,6 +118,45 @@ export class CoreContentLinksChooseSiteModalComponent implements OnInit {
102118
}
103119
}
104120

121+
/**
122+
* Handler for adding a new site.
123+
*/
124+
async addNewSite(): Promise<void> {
125+
if (!this.sites().length) {
126+
return;
127+
}
128+
129+
const siteUrl = this.sites()[0].siteUrl;
130+
131+
const pageParams = {
132+
siteUrl,
133+
urlToOpen: this.url(),
134+
};
135+
136+
if (CoreSites.isLoggedIn()) {
137+
// Ask the user before changing site.
138+
try {
139+
await CoreAlerts.confirm(Translate.instant('core.contentlinks.confirmurlothersite'));
140+
} catch {
141+
return; // User canceled.
142+
}
143+
144+
this.closeModal();
145+
146+
await CoreSites.logout({
147+
siteId: NO_SITE_ID,
148+
redirectPath: '/login/credentials',
149+
redirectOptions: { params: pageParams },
150+
});
151+
152+
return;
153+
}
154+
155+
this.closeModal();
156+
157+
await CoreNavigator.navigateToLoginCredentials(pageParams);
158+
}
159+
105160
/**
106161
* Close the modal.
107162
*/

src/core/features/contentlinks/services/contentlinks-delegate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export class CoreContentLinksDelegateService {
163163
}
164164

165165
// Get the list of sites the URL belongs to.
166-
const siteIds = await CoreSites.getSiteIdsFromUrl(url, true, username);
166+
const siteIds = await CoreSites.getSiteIdsFromUrl(url, { prioritize: true, username });
167167
if (!siteIds.length) {
168168
// No sites, no actions.
169169
return [];

src/core/features/contentlinks/services/contentlinks-helper.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,11 @@ export class CoreContentLinksHelperProvider {
102102
await CoreModals.openModal({
103103
component: CoreContentLinksChooseSiteModalComponent,
104104
componentProps: {
105-
url: url,
105+
url,
106106
},
107-
cssClass: 'core-modal-fullscreen',
107+
initialBreakpoint: 1,
108+
breakpoints: [0, 1],
109+
cssClass: 'no-header core-modal-auto-height',
108110
});
109111
}
110112

src/core/services/sites.ts

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ class CoreSitesDB {
261261
*
262262
* @param tableName Site table name.
263263
* @param options Options to configure table initialization.
264+
* @param options.siteId Site ID.
265+
* @param options.config Table configuration.
266+
* @param options.database Database instance to create the table in.
267+
* @param options.primaryKeyColumns Columns to use as primary key.
268+
* @param options.rowIdColumn Column to use as row ID. If null, the table won't have a row ID column.
269+
* @param options.onDestroy Callback to be called when the table is destroyed.
264270
* @returns Site table.
265271
*/
266272
async getSiteTable<
@@ -1938,21 +1944,37 @@ export class CoreSitesProvider {
19381944
await this.updateSiteInfo(siteId);
19391945
}
19401946

1947+
/**
1948+
* @deprecated since 5.2. Use getSiteIdsFromUrl with options instead.
1949+
*/
1950+
async getSiteIdsFromUrl(url: string, prioritize?: boolean, username?: string): Promise<string[]>;
19411951
/**
19421952
* Get the site IDs a URL belongs to.
19431953
* Someone can have more than one account in the same site, that's why this function returns an array of IDs.
19441954
*
19451955
* @param url URL to check.
1946-
* @param prioritize True if it should prioritize current site. If the URL belongs to current site then it won't
1947-
* check any other site, it will only return current site.
1948-
* @param username If set, it will return only the sites where the current user has this username.
1956+
* @param options Options to filter the sites.
19491957
* @returns Promise resolved with the site IDs (array).
19501958
*/
1951-
async getSiteIdsFromUrl(url: string, prioritize?: boolean, username?: string): Promise<string[]> {
1959+
async getSiteIdsFromUrl(url: string, options: GetSiteFromUrlOptions): Promise<string[]>;
1960+
async getSiteIdsFromUrl(
1961+
url: string,
1962+
prioritizeOrOptions: boolean | GetSiteFromUrlOptions = {},
1963+
username?: string,
1964+
): Promise<string[]> {
1965+
const prioritize = typeof prioritizeOrOptions === 'boolean' ? prioritizeOrOptions : prioritizeOrOptions.prioritize;
1966+
const userId = typeof prioritizeOrOptions === 'boolean' ? undefined : prioritizeOrOptions.userId;
1967+
username = typeof prioritizeOrOptions === 'boolean' ? username : prioritizeOrOptions.username;
1968+
19521969
// If prioritize is true, check current site first.
1953-
if (prioritize && this.currentSite && this.currentSite.containsUrl(url)) {
1954-
if (!username || this.currentSite?.getInfo()?.username === username) {
1970+
if (prioritize && this.currentSite?.containsUrl(url)) {
1971+
if (!username && !userId) {
19551972
return [this.currentSite.getId()];
1973+
} else {
1974+
const info = this.currentSite?.getInfo();
1975+
if (info?.username === username || info?.userid === userId) {
1976+
return [this.currentSite.getId()];
1977+
}
19561978
}
19571979
}
19581980

@@ -1982,8 +2004,14 @@ export class CoreSitesProvider {
19822004
await this.addSiteFromSiteListEntry(site);
19832005

19842006
if (this.sites[site.id].containsUrl(url)) {
1985-
if (!username || this.sites[site.id].getInfo()?.username === username) {
2007+
if (!username && !userId) {
19862008
ids.push(site.id);
2009+
} else {
2010+
const info = this.sites[site.id].getInfo();
2011+
2012+
if (info?.username === username || info?.userid === userId) {
2013+
ids.push(site.id);
2014+
}
19872015
}
19882016
}
19892017
}));
@@ -2096,7 +2124,7 @@ export class CoreSitesProvider {
20962124
*/
20972125
async isStoredRootURL(url: string, username?: string): Promise<{ site?: CoreSite; siteIds: string[] }> {
20982126
// Check if the site is stored.
2099-
const siteIds = await this.getSiteIdsFromUrl(url, true, username);
2127+
const siteIds = await this.getSiteIdsFromUrl(url, { prioritize: true, username });
21002128

21012129
const result: { site?: CoreSite; siteIds: string[] } = {
21022130
siteIds,
@@ -2365,6 +2393,7 @@ export class CoreSitesProvider {
23652393
* after 'checkAll'.
23662394
* @param checkAll True if it should check all the sites, false if it should check only 1 and treat them all
23672395
* depending on this result.
2396+
* @param args Params to pass to the isEnabledFn function.
23682397
* @returns Promise resolved with the list of enabled sites.
23692398
*/
23702399
async filterEnabledSites<P extends unknown[]>(
@@ -2644,3 +2673,13 @@ export type CoreSitesAfterLoginNavigationProcess = {
26442673
priority: number;
26452674
callback: () => Promise<void>;
26462675
};
2676+
2677+
/**
2678+
* Options to get a site from a URL.
2679+
*/
2680+
type GetSiteFromUrlOptions = {
2681+
username?: string; // If set, it will return the site only if the current user has this username.
2682+
userId?: number; // If set, it will return the site only if the current user has this ID.
2683+
prioritize?: boolean; // If true, it will prioritize current site.
2684+
// If the URL belongs to current site then it won't check any other site, it will only return current site.
2685+
};

0 commit comments

Comments
 (0)