Skip to content

Commit

Permalink
RealmInputScreen: Offer nicer experience for copied URL
Browse files Browse the repository at this point in the history
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: zulip#5228
  • Loading branch information
chrisbobbe committed Mar 10, 2022
1 parent 87d4cf4 commit 40ce11b
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/start/RealmInputScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import React, { useState, useCallback } from 'react';
import type { Node } from 'react';
import { Keyboard } from 'react-native';
import Clipboard from '@react-native-clipboard/clipboard';

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

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

const tryCopiedUrl = useCallback(async () => {
// The copied string might not be a valid realm URL:
// - It might not be a URL because useClipboardHasURL is subject to
// races (and Clipboard.getString is itself async).
// - It might not be a valid Zulip realm that the client can connect to.
//
// So…
const url = await Clipboard.getString();

// …let the user see what string is being tried and edit it if it fails…
setRealmInputValue(url);

// …and run it through our usual validation.
await tryRealm(url);
}, [tryRealm]);

const clipboardHasURL = useClipboardHasURL();

return (
<Screen
title="Welcome"
Expand Down Expand Up @@ -99,6 +119,32 @@ export default function RealmInputScreen(props: Props): Node {
onPress={handleInputSubmit}
disabled={tryParseUrl(realmInputValue) === undefined}
/>
{clipboardHasURL === true && (
// We prepopulate the input with https:// to help when you're not
// copy-pasting. (Not everyone has memorized that sequence of
// characters.)
//
// When you *do* want to copy-paste, though, prepopulating with
// https:// can be annoying -- you don't want to end up with
// https://https://chat.zulip.org.
//
// To solve that, it's risky to fuss around with the input value
// while the user is focused on it, or overcomplicate the input
// field (like by breaking it into two parts). I think these kinds
// of solutions risk leaving some users confused or dissatisfied.
//
// Instead, we try to recognize when the user has copied a URL, and
// let them use it without making them enter it into the input.
//
// TODO(?): Instead, use a FAB that persists while
// clipboardHasURL !== true && !progress
<ZulipButton
style={styles.button}
text="Use copied URL"
progress={progress}
onPress={tryCopiedUrl}
/>
)}
</Screen>
);
}
1 change: 1 addition & 0 deletions static/translations/messages_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"Welcome": "Welcome",
"Enter your Zulip server URL:": "Enter your Zulip server URL:",
"e.g. zulip.example.com": "e.g. zulip.example.com",
"Use copied URL": "Use copied URL",
"Subscriptions": "Subscriptions",
"Search": "Search",
"Log in": "Log in",
Expand Down

0 comments on commit 40ce11b

Please sign in to comment.