@@ -18,6 +18,7 @@ import { TranslationContext } from '../boot/TranslationProvider';
1818import type { LocalizableText } from '../types' ;
1919import { getGlobalSettings } from '../directSelectors' ;
2020import { useGlobalSelector } from '../react-redux' ;
21+ import { BRAND_COLOR } from '../styles/constants' ;
2122import ZulipText from '../common/ZulipText' ;
2223import WebLink from '../common/WebLink' ;
2324
@@ -86,7 +87,10 @@ const tryParseInput = (realmInputValue: string): MaybeParsedInput => {
8687 return { valid : true , value : url } ;
8788} ;
8889
89- type Suggestion = ValidationError | null ;
90+ type Suggestion =
91+ | ValidationError // Display relevant validation error message
92+ | string // Suggest this string as the server URL
93+ | null ; // No suggestion
9094
9195function getSuggestion ( realmInputValue , maybeParsedInput ) : Suggestion {
9296 if ( ! maybeParsedInput . valid ) {
@@ -105,8 +109,26 @@ function getSuggestion(realmInputValue, maybeParsedInput): Suggestion {
105109 }
106110 }
107111
108- // TODO(?): Suggest e.g. CZO or a zulipchat.com URL
109- return null ;
112+ const normalizedValue = realmInputValue . trim ( ) . replace ( / ^ h t t p s ? : \/ \/ / , '' ) ;
113+
114+ if (
115+ // This couldn't be a valid Zulip Cloud server subdomain. (Criteria
116+ // copied from check_subdomain_available in zerver/forms.py.)
117+ normalizedValue . length < 3
118+ || ! / ^ [ a - z 0 - 9 - ] * $ / . test ( normalizedValue )
119+ || normalizedValue [ 0 ] === '-'
120+ || normalizedValue [ normalizedValue . length - 1 ] === '-'
121+ // TODO(?): Catch strings hard-coded as off-limits, like "your-org".
122+ // (See check_subdomain_available in zerver/forms.py.)
123+ ) {
124+ return null ;
125+ }
126+
127+ if ( 'chat' . startsWith ( normalizedValue ) ) {
128+ return 'https://chat.zulip.org/' ;
129+ }
130+
131+ return `https://${ normalizedValue } .zulipchat.com/` ;
110132}
111133
112134export default function RealmInputScreen ( props : Props ) : Node {
@@ -169,6 +191,10 @@ export default function RealmInputScreen(props: Props): Node {
169191
170192 const suggestion = getSuggestion ( realmInputValue , maybeParsedInput ) ;
171193
194+ const handlePressSuggestion = React . useCallback ( suggestion_ => {
195+ setRealmInputValue ( suggestion_ ) ;
196+ } , [ ] ) ;
197+
172198 const styles = React . useMemo (
173199 ( ) =>
174200 createStyleSheet ( {
@@ -185,6 +211,11 @@ export default function RealmInputScreen(props: Props): Node {
185211 color : themeContext . color ,
186212 } ,
187213 suggestionText : { fontSize : 12 , fontStyle : 'italic' } ,
214+ suggestionTextLink : {
215+ fontSize : 12 ,
216+ fontStyle : 'normal' ,
217+ color : BRAND_COLOR , // chosen to mimic WebLink
218+ } ,
188219 button : { marginTop : 8 } ,
189220 } ) ,
190221 [ themeContext ] ,
@@ -202,10 +233,30 @@ export default function RealmInputScreen(props: Props): Node {
202233 return (
203234 < ZulipText style = { styles . suggestionText } text = { '\u200b' } /* U+200B ZERO WIDTH SPACE */ />
204235 ) ;
236+ } else if ( typeof suggestion === 'string' ) {
237+ return (
238+ < ZulipTextIntl
239+ style = { styles . suggestionText }
240+ text = { {
241+ text : 'Suggestion: <z-link>{suggestedServerUrl}</z-link>' ,
242+ values : {
243+ suggestedServerUrl : suggestion ,
244+ 'z-link' : chunks => (
245+ < ZulipText
246+ style = { styles . suggestionTextLink }
247+ onPress = { ( ) => handlePressSuggestion ( suggestion ) }
248+ >
249+ { chunks }
250+ </ ZulipText >
251+ ) ,
252+ } ,
253+ } }
254+ />
255+ ) ;
205256 } else {
206257 return < ZulipTextIntl style = { styles . suggestionText } text = { validationErrorMsg ( suggestion ) } /> ;
207258 }
208- } , [ suggestion , styles ] ) ;
259+ } , [ suggestion , handlePressSuggestion , styles ] ) ;
209260
210261 return (
211262 < Screen
0 commit comments