@@ -18,6 +18,7 @@ import { TranslationContext } from '../boot/TranslationProvider';
18
18
import type { LocalizableText } from '../types' ;
19
19
import { getGlobalSettings } from '../directSelectors' ;
20
20
import { useGlobalSelector } from '../react-redux' ;
21
+ import { BRAND_COLOR } from '../styles/constants' ;
21
22
import ZulipText from '../common/ZulipText' ;
22
23
import WebLink from '../common/WebLink' ;
23
24
@@ -86,7 +87,10 @@ const tryParseInput = (realmInputValue: string): MaybeParsedInput => {
86
87
return { valid : true , value : url } ;
87
88
} ;
88
89
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
90
94
91
95
function getSuggestion ( realmInputValue , maybeParsedInput ) : Suggestion {
92
96
if ( ! maybeParsedInput . valid ) {
@@ -105,8 +109,26 @@ function getSuggestion(realmInputValue, maybeParsedInput): Suggestion {
105
109
}
106
110
}
107
111
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/` ;
110
132
}
111
133
112
134
export default function RealmInputScreen ( props : Props ) : Node {
@@ -169,6 +191,10 @@ export default function RealmInputScreen(props: Props): Node {
169
191
170
192
const suggestion = getSuggestion ( realmInputValue , maybeParsedInput ) ;
171
193
194
+ const handlePressSuggestion = React . useCallback ( suggestion_ => {
195
+ setRealmInputValue ( suggestion_ ) ;
196
+ } , [ ] ) ;
197
+
172
198
const styles = React . useMemo (
173
199
( ) =>
174
200
createStyleSheet ( {
@@ -185,6 +211,11 @@ export default function RealmInputScreen(props: Props): Node {
185
211
color : themeContext . color ,
186
212
} ,
187
213
suggestionText : { fontSize : 12 , fontStyle : 'italic' } ,
214
+ suggestionTextLink : {
215
+ fontSize : 12 ,
216
+ fontStyle : 'normal' ,
217
+ color : BRAND_COLOR , // chosen to mimic WebLink
218
+ } ,
188
219
button : { marginTop : 8 } ,
189
220
} ) ,
190
221
[ themeContext ] ,
@@ -202,10 +233,30 @@ export default function RealmInputScreen(props: Props): Node {
202
233
return (
203
234
< ZulipText style = { styles . suggestionText } text = { '\u200b' } /* U+200B ZERO WIDTH SPACE */ />
204
235
) ;
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
+ ) ;
205
256
} else {
206
257
return < ZulipTextIntl style = { styles . suggestionText } text = { validationErrorMsg ( suggestion ) } /> ;
207
258
}
208
- } , [ suggestion , styles ] ) ;
259
+ } , [ suggestion , handlePressSuggestion , styles ] ) ;
209
260
210
261
return (
211
262
< Screen
0 commit comments