Skip to content

Commit 40ce11b

Browse files
committed
RealmInputScreen: Offer nicer experience for copied URL
Prompted by Alya's comment on CZO [1]: > One would have to play with pasting behavior to make sure > something reasonable happens if you are trying to paste in a whole > URL, including the `https` part. [1] https://chat.zulip.org/#narrow/stream/48-mobile/topic/can't.20paste.20org.20URL/near/1327170 Fixes: #5228
1 parent 87d4cf4 commit 40ce11b

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

src/start/RealmInputScreen.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import React, { useState, useCallback } from 'react';
33
import type { Node } from 'react';
44
import { Keyboard } from 'react-native';
5+
import Clipboard from '@react-native-clipboard/clipboard';
56

67
import type { RouteProp } from '../react-navigation';
78
import type { AppNavigationProp } from '../nav/AppNavigator';
@@ -15,6 +16,7 @@ import ZulipButton from '../common/ZulipButton';
1516
import { tryParseUrl } from '../utils/url';
1617
import * as api from '../api';
1718
import { navigateToAuth } from '../actions';
19+
import { useClipboardHasURL } from '../@react-native-clipboard/clipboard';
1820

1921
type Props = $ReadOnly<{|
2022
navigation: AppNavigationProp<'realm-input'>,
@@ -69,6 +71,24 @@ export default function RealmInputScreen(props: Props): Node {
6971
button: { marginTop: 8 },
7072
};
7173

74+
const tryCopiedUrl = useCallback(async () => {
75+
// The copied string might not be a valid realm URL:
76+
// - It might not be a URL because useClipboardHasURL is subject to
77+
// races (and Clipboard.getString is itself async).
78+
// - It might not be a valid Zulip realm that the client can connect to.
79+
//
80+
// So…
81+
const url = await Clipboard.getString();
82+
83+
// …let the user see what string is being tried and edit it if it fails…
84+
setRealmInputValue(url);
85+
86+
// …and run it through our usual validation.
87+
await tryRealm(url);
88+
}, [tryRealm]);
89+
90+
const clipboardHasURL = useClipboardHasURL();
91+
7292
return (
7393
<Screen
7494
title="Welcome"
@@ -99,6 +119,32 @@ export default function RealmInputScreen(props: Props): Node {
99119
onPress={handleInputSubmit}
100120
disabled={tryParseUrl(realmInputValue) === undefined}
101121
/>
122+
{clipboardHasURL === true && (
123+
// We prepopulate the input with https:// to help when you're not
124+
// copy-pasting. (Not everyone has memorized that sequence of
125+
// characters.)
126+
//
127+
// When you *do* want to copy-paste, though, prepopulating with
128+
// https:// can be annoying -- you don't want to end up with
129+
// https://https://chat.zulip.org.
130+
//
131+
// To solve that, it's risky to fuss around with the input value
132+
// while the user is focused on it, or overcomplicate the input
133+
// field (like by breaking it into two parts). I think these kinds
134+
// of solutions risk leaving some users confused or dissatisfied.
135+
//
136+
// Instead, we try to recognize when the user has copied a URL, and
137+
// let them use it without making them enter it into the input.
138+
//
139+
// TODO(?): Instead, use a FAB that persists while
140+
// clipboardHasURL !== true && !progress
141+
<ZulipButton
142+
style={styles.button}
143+
text="Use copied URL"
144+
progress={progress}
145+
onPress={tryCopiedUrl}
146+
/>
147+
)}
102148
</Screen>
103149
);
104150
}

static/translations/messages_en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"Welcome": "Welcome",
4949
"Enter your Zulip server URL:": "Enter your Zulip server URL:",
5050
"e.g. zulip.example.com": "e.g. zulip.example.com",
51+
"Use copied URL": "Use copied URL",
5152
"Subscriptions": "Subscriptions",
5253
"Search": "Search",
5354
"Log in": "Log in",

0 commit comments

Comments
 (0)