-
-
Notifications
You must be signed in to change notification settings - Fork 678
ServerCompatNotice: Add, to nag users connected to servers <2.0.0. #4750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
199a190
5681963
15105fb
cb40334
f3b6143
9559a75
aab915f
c8a8812
fddcaf9
a240def
65c3b49
a208713
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,32 @@ | ||
/* @flow strict-local */ | ||
import React, { PureComponent } from 'react'; | ||
import { Text } from 'react-native'; | ||
import TranslatedText from './TranslatedText'; | ||
|
||
import type { ThemeData } from '../styles'; | ||
import { ThemeContext } from '../styles'; | ||
import type { BoundedDiff } from '../generics'; | ||
import RawLabel from './RawLabel'; | ||
import type { LocalizableText } from '../types'; | ||
|
||
type Props = $ReadOnly<{| | ||
...$Exact<React$ElementConfig<typeof Text>>, | ||
...BoundedDiff<$Exact<React$ElementConfig<typeof RawLabel>>, {| children: ?React$Node |}>, | ||
text: LocalizableText, | ||
|}>; | ||
|
||
/** | ||
* A component that on top of a standard Text component | ||
* provides seamless translation and ensures consistent | ||
* styling for the default and night themes. | ||
* A wrapper for `RawLabel` that also translates the text. | ||
* | ||
* Use `RawLabel` if you don't want the text translated. | ||
* Use `RawLabel` instead if you don't want the text translated. | ||
* | ||
* @prop text - Translated before putting inside Text. | ||
* @prop [style] - Can override our default style for this component. | ||
* @prop ...all other Text props - Passed through verbatim to Text. | ||
* See upstream: https://reactnative.dev/docs/text | ||
* Unlike `RawLabel`, only accepts a `LocalizableText`, as the `text` | ||
* prop, and doesn't support `children`. | ||
*/ | ||
export default class Label extends PureComponent<Props> { | ||
static contextType = ThemeContext; | ||
context: ThemeData; | ||
|
||
styles = { | ||
label: { | ||
fontSize: 15, | ||
}, | ||
}; | ||
|
||
render() { | ||
const { text, style, ...restProps } = this.props; | ||
const { text, ...restProps } = this.props; | ||
|
||
return ( | ||
<Text style={[this.styles.label, { color: this.context.color }, style]} {...restProps}> | ||
<RawLabel {...restProps}> | ||
<TranslatedText text={text} /> | ||
</Text> | ||
</RawLabel> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* @flow strict-local */ | ||
|
||
import React from 'react'; | ||
import { View } from 'react-native'; | ||
import { SafeAreaView } from 'react-native-safe-area-context'; | ||
|
||
import store from '../boot/store'; | ||
import { createStyleSheet, HALF_COLOR } from '../styles'; | ||
import { useSelector, useDispatch } from '../react-redux'; | ||
import Label from './Label'; | ||
import { getActiveAccount } from '../account/accountsSelectors'; | ||
import { getIsAdmin, getSession } from '../directSelectors'; | ||
import { dismissCompatNotice } from '../session/sessionActions'; | ||
import ZulipTextButton from './ZulipTextButton'; | ||
import { openLinkWithUserPreference } from '../utils/openLink'; | ||
|
||
// In fact the oldest version we currently support is 2.1.0, per our docs: | ||
// https://zulip.readthedocs.io/en/4.2/overview/release-lifecycle.html | ||
// For now we only show this banner for servers older than 2.0.0, though, | ||
// in order to phase that in gradually. | ||
const minSupportedVersion = '2.0.0'; | ||
|
||
const styles = createStyleSheet({ | ||
wrapper: { | ||
backgroundColor: HALF_COLOR, | ||
paddingLeft: 16, | ||
paddingRight: 8, | ||
paddingBottom: 8, | ||
}, | ||
textRow: { | ||
flexDirection: 'row', | ||
}, | ||
text: { | ||
marginTop: 16, | ||
lineHeight: 20, | ||
}, | ||
buttonsRow: { | ||
marginTop: 12, | ||
flexDirection: 'row', | ||
justifyContent: 'flex-end', | ||
}, | ||
}); | ||
|
||
type Props = $ReadOnly<{||}>; | ||
|
||
/** | ||
* A "nag banner" saying the server version is unsupported, if so. | ||
*/ | ||
// Made with somewhat careful attention to | ||
// https://material.io/components/banners. Please consult that before making | ||
// layout changes, and try to make them in a direction that brings us closer | ||
// to those guidelines. | ||
export default function ServerCompatBanner(props: Props) { | ||
const dispatch = useDispatch(); | ||
const hasDismissedServerCompatNotice = useSelector( | ||
state => getSession(state).hasDismissedServerCompatNotice, | ||
); | ||
const zulipVersion = useSelector(state => getActiveAccount(state).zulipVersion); | ||
const realm = useSelector(state => getActiveAccount(state).realm); | ||
const isAdmin = useSelector(getIsAdmin); | ||
|
||
if (!zulipVersion || zulipVersion.isAtLeast(minSupportedVersion)) { | ||
return null; | ||
} else if (hasDismissedServerCompatNotice) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<SafeAreaView mode="padding" edges={['right', 'left']} style={styles.wrapper}> | ||
<View style={styles.textRow}> | ||
<Label | ||
style={styles.text} | ||
text={ | ||
isAdmin | ||
? { | ||
text: | ||
'{realm} is running Zulip Server {serverVersion}, which is unsupported. Please upgrade your server as soon as possible.', | ||
values: { realm: realm.toString(), serverVersion: zulipVersion.raw() }, | ||
} | ||
: { | ||
text: | ||
'{realm} is running Zulip Server {serverVersion}, which is unsupported. Please contact your administrator about upgrading.', | ||
values: { realm: realm.toString(), serverVersion: zulipVersion.raw() }, | ||
} | ||
} | ||
/> | ||
</View> | ||
<View style={styles.buttonsRow}> | ||
<ZulipTextButton | ||
label="Dismiss" | ||
onPress={() => { | ||
dispatch(dismissCompatNotice()); | ||
}} | ||
/> | ||
<ZulipTextButton | ||
leftMargin | ||
label={isAdmin ? 'Fix now' : 'Learn more'} | ||
onPress={() => { | ||
openLinkWithUserPreference( | ||
'https://zulip.readthedocs.io/en/stable/overview/release-lifecycle.html#compatibility-and-upgrading', | ||
store.getState, | ||
); | ||
}} | ||
/> | ||
</View> | ||
</SafeAreaView> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* @flow strict-local */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason we can't pull in a button like this from a library of material components? I guess this is BRAND_COLOR by default, is there anything else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, good idea—maybe https://callstack.github.io/react-native-paper/button.html; I'll give it a try. We may like to make our own thin wrapper around that component, for potentially a few reasons:
|
||
import React, { useMemo } from 'react'; | ||
import { View } from 'react-native'; | ||
|
||
import type { LocalizableText } from '../types'; | ||
import { BRAND_COLOR, createStyleSheet } from '../styles'; | ||
import { Label } from '.'; | ||
import Touchable from './Touchable'; | ||
|
||
// When adding a variant, take care that it's legitimate, it addresses a | ||
// common use case consistently, and its styles are defined coherently. We | ||
// don't want this component to grow brittle with lots of little flags to | ||
// micromanage its styles. | ||
type Variant = 'standard'; | ||
|
||
const styleSheetForVariant = (variant: Variant) => | ||
createStyleSheet({ | ||
// See https://material.io/components/buttons#specs. | ||
touchable: { | ||
height: 36, | ||
paddingHorizontal: 8, | ||
minWidth: 64, | ||
}, | ||
|
||
// Chosen because of the value for this distance at | ||
// https://material.io/components/banners#specs. A banner is one context | ||
// where we've wanted to use text buttons. | ||
leftMargin: { | ||
marginLeft: 8, | ||
}, | ||
rightMargin: { | ||
marginRight: 8, | ||
}, | ||
|
||
// `Touchable` only accepts one child, so make sure it fills the | ||
// `Touchable`'s full area and centers everything in it. | ||
childOfTouchable: { | ||
flex: 1, | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
}, | ||
|
||
text: { | ||
// From the spec: | ||
// > Text labels need to be distinct from other elements. If the text | ||
// > label isn’t fully capitalized, it should use a different color, | ||
// > style, or layout from other text. | ||
textTransform: 'uppercase', | ||
color: BRAND_COLOR, | ||
|
||
textAlign: 'center', | ||
textAlignVertical: 'center', | ||
}, | ||
}); | ||
|
||
type Props = $ReadOnly<{| | ||
/** See note on the `Variant` type. */ | ||
variant?: Variant, | ||
|
||
/** | ||
* Give a left margin of the correct in-between space for a set of buttons | ||
*/ | ||
leftMargin?: true, | ||
|
||
/** | ||
* Give a right margin of the correct in-between space for a set of | ||
* buttons | ||
*/ | ||
rightMargin?: true, | ||
|
||
/** | ||
* The text label: https://material.io/components/buttons#text-button | ||
* | ||
* Should be short. | ||
*/ | ||
label: LocalizableText, | ||
|
||
onPress: () => void | Promise<void>, | ||
|}>; | ||
|
||
/** | ||
* A button modeled on Material Design's "text button" concept. | ||
* | ||
* See https://material.io/components/buttons#text-button : | ||
* | ||
* > Text buttons are typically used for less-pronounced actions, including | ||
* > those located | ||
* > | ||
* > - In dialogs | ||
* > - In cards | ||
* > | ||
* > In cards, text buttons help maintain an emphasis on card content. | ||
*/ | ||
// TODO: Consider making this a thin wrapper around something like | ||
// react-native-paper's `Button` | ||
// (https://callstack.github.io/react-native-paper/button.html), encoding | ||
// things like project-specific styles and making any sensible adjustments | ||
// to the interface. | ||
export default function ZulipTextButton(props: Props) { | ||
const { variant = 'standard', leftMargin, rightMargin, label, onPress } = props; | ||
|
||
const variantStyles = useMemo(() => styleSheetForVariant(variant), [variant]); | ||
|
||
return ( | ||
<Touchable | ||
style={[ | ||
variantStyles.touchable, | ||
leftMargin && variantStyles.leftMargin, | ||
rightMargin && variantStyles.rightMargin, | ||
]} | ||
onPress={onPress} | ||
> | ||
<View style={variantStyles.childOfTouchable}> | ||
<Label style={variantStyles.text} text={label} /> | ||
</View> | ||
</Touchable> | ||
); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.