MM-12906 - Add thumbnails to theme selection (#2381)

This commit is contained in:
Chris Duarte 2018-12-12 04:40:09 -08:00 committed by Elias Nahum
parent 317d26ed64
commit 008d81caab
20 changed files with 507 additions and 198 deletions

View file

@ -19,51 +19,206 @@ exports[`Theme should match snapshot 1`] = `
}
}
>
<section
disableHeader={true}
theme={
<View
style={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
"backgroundColor": "#ffffff",
"borderBottomColor": "rgba(61,60,64,0.1)",
"borderBottomWidth": 1,
"borderTopColor": "rgba(61,60,64,0.1)",
"borderTopWidth": 1,
"flexDirection": "row",
"flexWrap": "wrap",
"justifyContent": "center",
"marginBottom": 30,
}
}
>
<FlatList
disableVirtualization={false}
horizontal={false}
initialNumToRender={10}
keyExtractor={[Function]}
maxToRenderPerBatch={10}
numColumns={1}
onEndReachedThreshold={2}
renderItem={[Function]}
scrollEventThrottle={50}
updateCellsBatchingPeriod={50}
windowSize={21}
<ThemeTile
action={[Function]}
actionValue="default"
imageSrc={
Object {
"testUri": "../../../dist/assets/images/themes/mattermost.png",
}
}
isLandscape={false}
isTablet={false}
label={
<FormattedText
defaultMessage="Mattermost"
id="user.settings.display.Mattermost"
/>
}
selected={true}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
</section>
<ThemeTile
action={[Function]}
actionValue="organization"
imageSrc={
Object {
"testUri": "../../../dist/assets/images/themes/organization.png",
}
}
isLandscape={false}
isTablet={false}
label={
<FormattedText
defaultMessage="Organization"
id="user.settings.display.Organization"
/>
}
selected={false}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ThemeTile
action={[Function]}
actionValue="mattermostDark"
imageSrc={
Object {
"testUri": "../../../dist/assets/images/themes/mattermost_dark.png",
}
}
isLandscape={false}
isTablet={false}
label={
<FormattedText
defaultMessage="Mattermost Dark"
id="user.settings.display.Mattermost Dark"
/>
}
selected={false}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ThemeTile
action={[Function]}
actionValue="windows10"
imageSrc={
Object {
"testUri": "../../../dist/assets/images/themes/windows_dark.png",
}
}
isLandscape={false}
isTablet={false}
label={
<FormattedText
defaultMessage="Windows Dark"
id="user.settings.display.Windows Dark"
/>
}
selected={false}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
</View>
</View>
</View>
`;

View file

@ -8,15 +8,18 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {savePreferences} from 'mattermost-redux/actions/preferences';
import {getAllowedThemes, getCustomTheme} from 'app/selectors/theme';
import {isLandscape, isTablet} from 'app/selectors/device';
import Theme from './theme';
const mapStateToProps = (state) => ({
userId: getCurrentUserId(state),
teamId: getCurrentTeamId(state),
theme: getTheme(state),
allowedThemes: getAllowedThemes(state),
customTheme: getCustomTheme(state),
isLandscape: isLandscape(state),
isTablet: isTablet(state),
teamId: getCurrentTeamId(state),
theme: getTheme(state),
userId: getCurrentUserId(state),
});
const mapDispatchToProps = (dispatch) => ({

View file

@ -2,18 +2,26 @@
// See LICENSE.txt for license information.
import React from 'react';
import {View, FlatList} from 'react-native';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import {intlShape} from 'react-intl';
import StatusBar from 'app/components/status_bar';
import Section from 'app/screens/settings/section';
import SectionItem from 'app/screens/settings/section_item';
import ThemeTile from './theme_tile';
import FormattedText from 'app/components/formatted_text';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import Preferences from 'mattermost-redux/constants/preferences';
const thumbnailImages = {
default: require('assets/images/themes/mattermost.png'),
organization: require('assets/images/themes/organization.png'),
mattermostDark: require('assets/images/themes/mattermost_dark.png'),
windows10: require('assets/images/themes/windows_dark.png'),
};
export default class Theme extends React.PureComponent {
static propTypes = {
actions: PropTypes.shape({
@ -21,6 +29,8 @@ export default class Theme extends React.PureComponent {
}).isRequired,
allowedThemes: PropTypes.arrayOf(PropTypes.object),
customTheme: PropTypes.object,
isLandscape: PropTypes.bool.isRequired,
isTablet: PropTypes.bool.isRequired,
navigator: PropTypes.object.isRequired,
teamId: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired,
@ -63,57 +73,67 @@ export default class Theme extends React.PureComponent {
}]);
}
renderThemeRow = ({item, title}) => {
renderAllowedThemeTiles = () => {
const {theme, allowedThemes, isLandscape, isTablet} = this.props;
return allowedThemes.map((allowedTheme) => (
<ThemeTile
key={allowedTheme.key}
label={(
<FormattedText
id={`user.settings.display.${allowedTheme.type}`}
defaultMessage={allowedTheme.type}
/>
)}
action={this.setTheme}
actionValue={allowedTheme.key}
selected={allowedTheme.type.toLowerCase() === theme.type.toLowerCase()}
theme={theme}
imageSrc={thumbnailImages[allowedTheme.key]}
isLandscape={isLandscape}
isTablet={isTablet}
/>
));
};
renderCustomThemeRow = ({item}) => {
const {theme} = this.props;
const style = getStyleSheet(theme);
return (
<React.Fragment>
<SectionItem
label={(
<FormattedText
id={`user.settings.display.${item.type}`}
defaultMessage={title || item.type}
/>
)}
action={this.setTheme}
actionType='select'
actionValue={item.key}
selected={item.type.toLowerCase() === theme.type.toLowerCase()}
theme={theme}
/>
<View style={style.divider}/>
</React.Fragment>
<SectionItem
label={(
<FormattedText
id={`user.settings.display.${item.type}`}
defaultMessage={'Custom Theme'}
/>
)}
action={this.setTheme}
actionType='select'
actionValue={item.key}
selected={item.type.toLowerCase() === theme.type.toLowerCase()}
theme={theme}
/>
);
};
keyExtractor = (item) => item.key;
render() {
const {theme, allowedThemes} = this.props;
const {theme} = this.props;
const {customTheme} = this.state;
const style = getStyleSheet(theme);
return (
<View style={style.container}>
<StatusBar/>
<View style={style.wrapper}>
<Section
disableHeader={true}
theme={theme}
>
<FlatList
data={allowedThemes}
renderItem={this.renderThemeRow}
keyExtractor={this.keyExtractor}
/>
</Section>
<View style={style.tilesContainer}>
{this.renderAllowedThemeTiles()}
</View>
{customTheme &&
<Section
disableHeader={true}
theme={theme}
>
{this.renderThemeRow({item: customTheme, title: 'Custom'})}
{this.renderCustomThemeRow({item: customTheme})}
</Section>
}
</View>
@ -133,9 +153,16 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
flex: 1,
paddingTop: 35,
},
divider: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
height: 1,
tilesContainer: {
marginBottom: 30,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
backgroundColor: theme.centerChannelBg,
borderTopWidth: 1,
borderBottomWidth: 1,
borderTopColor: changeOpacity(theme.centerChannelColor, 0.1),
borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1),
},
};
});

View file

@ -2,9 +2,9 @@
// See LICENSE.txt for license information.
import React from 'react';
import {FlatList} from 'react-native';
import {shallow} from 'enzyme';
import Section from 'app/screens/settings/section';
import ThemeTile from './theme_tile';
import Preferences from 'mattermost-redux/constants/preferences';
@ -12,12 +12,125 @@ import Theme from './theme';
jest.mock('react-intl');
const allowedThemes = [
{
type: 'Mattermost',
sidebarBg: '#145dbf',
sidebarText: '#ffffff',
sidebarUnreadText: '#ffffff',
sidebarTextHoverBg: '#4578bf',
sidebarTextActiveBorder: '#579eff',
sidebarTextActiveColor: '#ffffff',
sidebarHeaderBg: '#1153ab',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#06d6a0',
awayIndicator: '#ffbc42',
dndIndicator: '#f74343',
mentionBg: '#ffffff',
mentionColor: '#145dbf',
centerChannelBg: '#ffffff',
centerChannelColor: '#3d3c40',
newMessageSeparator: '#ff8800',
linkColor: '#2389d7',
buttonBg: '#166de0',
buttonColor: '#ffffff',
errorTextColor: '#fd5960',
mentionHighlightBg: '#ffe577',
mentionHighlightLink: '#166de0',
codeTheme: 'github',
key: 'default',
},
{
type: 'Organization',
sidebarBg: '#2071a7',
sidebarText: '#ffffff',
sidebarUnreadText: '#ffffff',
sidebarTextHoverBg: '#136197',
sidebarTextActiveBorder: '#7ab0d6',
sidebarTextActiveColor: '#ffffff',
sidebarHeaderBg: '#2f81b7',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#7dbe00',
awayIndicator: '#dcbd4e',
dndIndicator: '#ff6a6a',
mentionBg: '#fbfbfb',
mentionColor: '#2071f7',
centerChannelBg: '#f2f4f8',
centerChannelColor: '#333333',
newMessageSeparator: '#ff8800',
linkColor: '#2f81b7',
buttonBg: '#1dacfc',
buttonColor: '#ffffff',
errorTextColor: '#a94442',
mentionHighlightBg: '#f3e197',
mentionHighlightLink: '#2f81b7',
codeTheme: 'github',
key: 'organization',
},
{
type: 'Mattermost Dark',
sidebarBg: '#1b2c3e',
sidebarText: '#ffffff',
sidebarUnreadText: '#ffffff',
sidebarTextHoverBg: '#4a5664',
sidebarTextActiveBorder: '#66b9a7',
sidebarTextActiveColor: '#ffffff',
sidebarHeaderBg: '#1b2c3e',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#65dcc8',
awayIndicator: '#c1b966',
dndIndicator: '#e81023',
mentionBg: '#b74a4a',
mentionColor: '#ffffff',
centerChannelBg: '#2f3e4e',
centerChannelColor: '#dddddd',
newMessageSeparator: '#5de5da',
linkColor: '#a4ffeb',
buttonBg: '#4cbba4',
buttonColor: '#ffffff',
errorTextColor: '#ff6461',
mentionHighlightBg: '#984063',
mentionHighlightLink: '#a4ffeb',
codeTheme: 'solarized-dark',
key: 'mattermostDark',
},
{
type: 'Windows Dark',
sidebarBg: '#171717',
sidebarText: '#ffffff',
sidebarUnreadText: '#ffffff',
sidebarTextHoverBg: '#302e30',
sidebarTextActiveBorder: '#196caf',
sidebarTextActiveColor: '#ffffff',
sidebarHeaderBg: '#1f1f1f',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#399fff',
awayIndicator: '#c1b966',
dndIndicator: '#e81023',
mentionBg: '#0177e7',
mentionColor: '#ffffff',
centerChannelBg: '#1f1f1f',
centerChannelColor: '#dddddd',
newMessageSeparator: '#cc992d',
linkColor: '#0d93ff',
buttonBg: '#0177e7',
buttonColor: '#ffffff',
errorTextColor: '#ff6461',
mentionHighlightBg: '#784098',
mentionHighlightLink: '#a4ffeb',
codeTheme: 'monokai',
key: 'windows10',
},
];
describe('Theme', () => {
const baseProps = {
actions: {
savePreferences: jest.fn(),
},
allowedThemes,
isLandscape: false,
isTablet: false,
navigator: {
setOnNavigatorEvent: jest.fn(),
},
@ -32,122 +145,6 @@ describe('Theme', () => {
);
expect(wrapper.getElement()).toMatchSnapshot();
expect(wrapper.find(Section).first().exists()).toEqual(true);
expect(wrapper.find(FlatList).first().exists()).toEqual(true);
expect(wrapper.find(ThemeTile)).toHaveLength(4);
});
});
const allowedThemes = [
{
default: {
type: 'Mattermost',
sidebarBg: '#145dbf',
sidebarText: '#ffffff',
sidebarUnreadText: '#ffffff',
sidebarTextHoverBg: '#4578bf',
sidebarTextActiveBorder: '#579eff',
sidebarTextActiveColor: '#ffffff',
sidebarHeaderBg: '#1153ab',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#06d6a0',
awayIndicator: '#ffbc42',
dndIndicator: '#f74343',
mentionBg: '#ffffff',
mentionColor: '#145dbf',
centerChannelBg: '#ffffff',
centerChannelColor: '#3d3c40',
newMessageSeparator: '#ff8800',
linkColor: '#2389d7',
buttonBg: '#166de0',
buttonColor: '#ffffff',
errorTextColor: '#fd5960',
mentionHighlightBg: '#ffe577',
mentionHighlightLink: '#166de0',
codeTheme: 'github',
},
},
{
organization: {
type: 'Organization',
sidebarBg: '#2071a7',
sidebarText: '#ffffff',
sidebarUnreadText: '#ffffff',
sidebarTextHoverBg: '#136197',
sidebarTextActiveBorder: '#7ab0d6',
sidebarTextActiveColor: '#ffffff',
sidebarHeaderBg: '#2f81b7',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#7dbe00',
awayIndicator: '#dcbd4e',
dndIndicator: '#ff6a6a',
mentionBg: '#fbfbfb',
mentionColor: '#2071f7',
centerChannelBg: '#f2f4f8',
centerChannelColor: '#333333',
newMessageSeparator: '#ff8800',
linkColor: '#2f81b7',
buttonBg: '#1dacfc',
buttonColor: '#ffffff',
errorTextColor: '#a94442',
mentionHighlightBg: '#f3e197',
mentionHighlightLink: '#2f81b7',
codeTheme: 'github',
},
},
{
mattermostDark: {
type: 'Mattermost Dark',
sidebarBg: '#1b2c3e',
sidebarText: '#ffffff',
sidebarUnreadText: '#ffffff',
sidebarTextHoverBg: '#4a5664',
sidebarTextActiveBorder: '#66b9a7',
sidebarTextActiveColor: '#ffffff',
sidebarHeaderBg: '#1b2c3e',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#65dcc8',
awayIndicator: '#c1b966',
dndIndicator: '#e81023',
mentionBg: '#b74a4a',
mentionColor: '#ffffff',
centerChannelBg: '#2f3e4e',
centerChannelColor: '#dddddd',
newMessageSeparator: '#5de5da',
linkColor: '#a4ffeb',
buttonBg: '#4cbba4',
buttonColor: '#ffffff',
errorTextColor: '#ff6461',
mentionHighlightBg: '#984063',
mentionHighlightLink: '#a4ffeb',
codeTheme: 'solarized-dark',
},
},
{
windows10: {
type: 'Windows Dark',
sidebarBg: '#171717',
sidebarText: '#ffffff',
sidebarUnreadText: '#ffffff',
sidebarTextHoverBg: '#302e30',
sidebarTextActiveBorder: '#196caf',
sidebarTextActiveColor: '#ffffff',
sidebarHeaderBg: '#1f1f1f',
sidebarHeaderTextColor: '#ffffff',
onlineIndicator: '#399fff',
awayIndicator: '#c1b966',
dndIndicator: '#e81023',
mentionBg: '#0177e7',
mentionColor: '#ffffff',
centerChannelBg: '#1f1f1f',
centerChannelColor: '#dddddd',
newMessageSeparator: '#cc992d',
linkColor: '#0d93ff',
buttonBg: '#0177e7',
buttonColor: '#ffffff',
errorTextColor: '#ff6461',
mentionHighlightBg: '#784098',
mentionHighlightLink: '#a4ffeb',
codeTheme: 'monokai',
},
},
];

View file

@ -0,0 +1,127 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import PropTypes from 'prop-types';
import {
Dimensions,
Image,
TouchableOpacity,
View,
} from 'react-native';
import {makeStyleSheetFromTheme} from 'app/utils/theme';
const {width: deviceWidth, height: deviceHeight} = Dimensions.get('window');
const checkmark = require('assets/images/themes/check.png');
const tilePadding = 8;
const ThemeTile = (props) => {
const {
action,
actionValue,
imageSrc,
isLandscape,
isTablet,
label,
selected,
theme,
} = props;
const style = getStyleSheet(theme);
const labelComponent = React.cloneElement(
label,
{style: style.label}
);
const tilesPerLine = isLandscape || isTablet ? 4 : 2;
const fullWidth = isLandscape ? deviceHeight - 40 : deviceWidth;
const layoutStyle = {
container: {
width: (fullWidth / tilesPerLine) - tilePadding,
},
thumbnail: {
width: (fullWidth / tilesPerLine) - (tilePadding + 20),
},
};
return (
<TouchableOpacity
style={[style.container, layoutStyle.container]}
onPress={() => action(actionValue)}
>
<View style={[style.imageWrapper, layoutStyle.thumbnail]}>
{imageSrc && (
<Image
source={imageSrc}
style={[
style.thumbnail,
layoutStyle.thumbnail,
selected ? style.selectedThumbnail : null,
]}
/>
)}
{selected && (
<Image
source={checkmark}
style={style.check}
/>
)}
</View>
{labelComponent}
</TouchableOpacity>
);
};
ThemeTile.propTypes = {
action: PropTypes.func,
actionValue: PropTypes.string,
imageSrc: PropTypes.oneOfType([
PropTypes.number,
PropTypes.object,
]).isRequired,
isLandscape: PropTypes.bool.isRequired,
isTablet: PropTypes.bool.isRequired,
label: PropTypes.node.isRequired,
selected: PropTypes.bool,
theme: PropTypes.object.isRequired,
};
ThemeTile.defaultProps = {
action: () => true,
};
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
container: {
flexDirection: 'column',
padding: tilePadding,
marginTop: 8,
},
imageWrapper: {
position: 'relative',
alignItems: 'flex-start',
marginBottom: 12,
},
thumbnail: {
resizeMode: 'stretch',
},
selectedThumbnail: {
opacity: 0.5,
},
check: {
position: 'absolute',
right: 10,
bottom: 10,
},
label: {
color: theme.centerChannelColor,
fontSize: 15,
},
};
});
export default ThemeTile;

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB