Skip to content

Commit 8b87edc

Browse files
achou11staltz
authored andcommitted
ux: show dialog when pressing unrecognized links
1 parent cb669fe commit 8b87edc

8 files changed

Lines changed: 82 additions & 7 deletions

File tree

android/app/src/main/assets/translations/en-US.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
"external_link_confirmation": {
2828
"title": "Open link?",
2929
"description": "Do you want to open {{link}} in your browser?"
30+
},
31+
"unrecognized_link_confirmation": {
32+
"title": "Unrecognized link",
33+
"description": "This is not a web link neither an SSB link. Are you sure you want to open this? {{link}}"
3034
}
3135
},
3236
"date": {

android/app/src/main/assets/translations/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
"external_link_confirmation": {
2828
"title": "Open link?",
2929
"description": "Do you want to open {{link}} in your browser?"
30+
},
31+
"unrecognized_link_confirmation": {
32+
"title": "Unrecognized link",
33+
"description": "This is not a web link neither an SSB link. Are you sure you want to open this? {{link}}"
3034
}
3135
},
3236
"date": {

src/frontend/components/Markdown.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
import {
1616
isFeedSSBURI,
1717
isMessageSSBURI,
18-
isSSBURI,
1918
toFeedSigil,
2019
toMessageSigil,
2120
getFeedSSBURIRegex,
@@ -43,7 +42,7 @@ const ELLIPSIS = '\u2026';
4342
* Match URIs *except* SSB URIs and File URIs
4443
*/
4544
function getMiscURIRegex() {
46-
return /\b((?=[a-z]+:)(?!(ssb:|file:)))[a-z]+:(\/\/)?[^ )\n]+/g;
45+
return /\b((?=[a-z-]+:)(?!(ssb:|file:)))[a-z-]+:(\/\/)?[^ )\n]+/g;
4746
}
4847

4948
const textProps: TextProps = {
@@ -421,6 +420,14 @@ function makeRenderers(
421420
style: styles.link,
422421
onPress: (event) => {
423422
event.stopPropagation();
423+
424+
if (!isHTTPLink) {
425+
event.preventDefault();
426+
GlobalEventBus.dispatch({
427+
type: 'triggerUnrecognizedLink',
428+
url: properHref,
429+
});
430+
}
424431
},
425432
['href' as any]: properHref,
426433
},
@@ -434,7 +441,15 @@ function makeRenderers(
434441
style: styles.link,
435442
onPress: (event) => {
436443
event.stopPropagation();
437-
Linking.openURL(props.href);
444+
if (isHTTPLink) {
445+
Linking.openURL(props.href);
446+
} else {
447+
event.preventDefault();
448+
GlobalEventBus.dispatch({
449+
type: 'triggerUnrecognizedLink',
450+
url: props.href,
451+
});
452+
}
438453
},
439454
},
440455
child ?? props.children,
@@ -517,8 +532,7 @@ export interface Props {
517532
}
518533

519534
function transformLinkUri(uri: string) {
520-
if (isSSBURI(uri)) return uri; // don't interfere with SSB URIs
521-
return ReactMarkdown.uriTransformer(uri); // interfere with all others
535+
return uri; // don't interfere with any URIs
522536
}
523537

524538
export default class Markdown extends PureComponent<Props> {
@@ -527,7 +541,6 @@ export default class Markdown extends PureComponent<Props> {
527541
const linkifySsbSigilMsgs = linkifyRegex(Ref.msgIdRegex);
528542
const linkifySsbUriFeeds = linkifyRegex(getFeedSSBURIRegex());
529543
const linkifySsbUriMsgs = linkifyRegex(getMessageSSBURIRegex());
530-
const linkifyMiscUris = linkifyRegex(getMiscURIRegex());
531544
const linkifyHashtags = linkifyRegex(
532545
new RegExp('#(' + getUnicodeWordRegex().source + '|\\d|-)+', 'gu'),
533546
);
@@ -540,7 +553,6 @@ export default class Markdown extends PureComponent<Props> {
540553
.use(linkifySsbUriMsgs)
541554
.use(linkifySsbSigilFeeds)
542555
.use(linkifySsbSigilMsgs)
543-
.use(linkifyMiscUris)
544556
.use(linkifyHashtags)
545557
.use(imagesToSsbServeBlobs, {port: portMappings.get('ssb-serve-blobs')})
546558
.processSync(this.props.text).contents,

src/frontend/drivers/eventbus.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ export interface TriggerHashtagLink {
2121
hashtag: string;
2222
}
2323

24+
export interface TriggerUnrecognizedLink {
25+
type: 'triggerUnrecognizedLink';
26+
url: string;
27+
}
28+
2429
export interface HardwareBackOnCentralScreen {
2530
type: 'hardwareBackOnCentralScreen';
2631
}
@@ -95,6 +100,7 @@ export type GlobalEvent =
95100
| TriggerFeedCypherlink
96101
| TriggerMsgCypherlink
97102
| TriggerHashtagLink
103+
| TriggerUnrecognizedLink
98104
| HardwareBackOnCentralScreen
99105
| DrawerToggleOnCentralScreen
100106
| AudioBlobComposed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-FileCopyrightText: 2023 The Manyverse Authors
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
import {Stream} from 'xstream';
6+
import {DialogSource} from '~frontend/drivers/dialogs';
7+
import {t} from '~frontend/drivers/localization';
8+
import {Palette} from '~frontend/global-styles/palette';
9+
10+
export interface Actions {
11+
openUnrecognizedLinkDialog$: Stream<string>;
12+
}
13+
14+
export default function dialog(actions: Actions, dialogSource: DialogSource) {
15+
return {
16+
openUnrecognizedLink$: actions.openUnrecognizedLinkDialog$
17+
.map((link) =>
18+
dialogSource
19+
.alert(
20+
t('call_to_action.unrecognized_link_confirmation.title'),
21+
t('call_to_action.unrecognized_link_confirmation.description', {
22+
link,
23+
}),
24+
{
25+
...Palette.dialogColors,
26+
positiveText: t('call_to_action.yes'),
27+
negativeText: t('call_to_action.no'),
28+
},
29+
)
30+
.filter((res) => res.action === 'actionPositive')
31+
.mapTo(link),
32+
)
33+
.flatten(),
34+
};
35+
}

src/frontend/screens/global/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import ssb from './ssb';
2020
import localization from './localization';
2121
import toast from './toast';
2222
import asyncStorage from './asyncstorage';
23+
import dialog from './dialog';
2324

2425
export interface Sources {
2526
state: StateSource<State>;
@@ -40,6 +41,7 @@ export interface Sinks {
4041
toast: Stream<Toast>;
4142
globalEventBus: Stream<GlobalEvent>;
4243
asyncstorage: Stream<StorageCommand>;
44+
linking: Stream<string>;
4345
}
4446

4547
export function global(sources: Sources): Sinks {
@@ -58,6 +60,7 @@ export function global(sources: Sources): Sinks {
5860
const updateLocalization$ = localization(sources.fs);
5961
const req$ = ssb(updateLocalization$, actions);
6062
const toast$ = toast(actions, sources.ssb);
63+
const dialogActions$ = dialog(actions, sources.dialog);
6164

6265
const event$ = xs.merge<GlobalEvent>(
6366
actions.readCheckingNewVersionSetting$
@@ -84,5 +87,6 @@ export function global(sources: Sources): Sinks {
8487
toast: toast$,
8588
globalEventBus: event$,
8689
asyncstorage: storageCommand$,
90+
linking: dialogActions$.openUnrecognizedLink$,
8791
};
8892
}

src/frontend/screens/global/intent.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
GlobalEvent,
2222
TriggerFeedCypherlink,
2323
TriggerHashtagLink,
24+
TriggerUnrecognizedLink,
2425
TriggerMsgCypherlink,
2526
} from '~frontend/drivers/eventbus';
2627
import {SSBSource} from '~frontend/drivers/ssb';
@@ -204,6 +205,12 @@ export default function intent(
204205
.flatten()
205206
.take(1);
206207

208+
const openUnrecognizedLinkDialog$ = (
209+
globalEventBus.filter(
210+
(ev) => ev.type === 'triggerUnrecognizedLink',
211+
) as Stream<TriggerUnrecognizedLink>
212+
).map((ev) => ev.url);
213+
207214
return {
208215
handleUriClaimInvite$,
209216
handleUriConsumeAlias$,
@@ -217,5 +224,6 @@ export default function intent(
217224
goToSearch$,
218225
goToCompact$,
219226
readCheckingNewVersionSetting$,
227+
openUnrecognizedLinkDialog$,
220228
};
221229
}

typings/i18n-js.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ declare module 'i18n-js' {
2323
| 'call_to_action.close_dialog.accessibility_label'
2424
| 'call_to_action.external_link_confirmation.title'
2525
| 'call_to_action.external_link_confirmation.description'
26+
| 'call_to_action.unrecognized_link_confirmation.title'
27+
| 'call_to_action.unrecognized_link_confirmation.description'
2628
| 'date.relative.past.years'
2729
| 'date.relative.past.months'
2830
| 'date.relative.past.weeks'

0 commit comments

Comments
 (0)