Skip to content

Commit

Permalink
Save palette screen improvements (#207)
Browse files Browse the repository at this point in the history
* drag and drop support for changing color positions

* working drag and drop and lock unlock

* fix scrolling for large list of colors

* revert to master version
  • Loading branch information
kamalkishor1991 authored Apr 6, 2024
1 parent 8abfbaf commit 70cffe0
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 38 deletions.
2 changes: 1 addition & 1 deletion components/SavePalette.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const SavePalette = (props) => {
setTimeout(() => {
setIsUnlockProNotification(false);
}, 5000);
}, [colorList]);
}, [colorList, isPro]);

const [paletteName, setPaletteName] = useState(suggestedName ?? '');
useEffect(() => {
Expand Down
99 changes: 75 additions & 24 deletions components/SingleColorView.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,88 @@
import * as React from 'react';
import { Platform, StyleSheet, Text, Clipboard, TouchableOpacity } from 'react-native';
import { Platform, StyleSheet, Text, Clipboard, TouchableOpacity, View } from 'react-native';
import { notifyMessage } from '../libs/Helpers';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';

export class SingleColorView extends React.Component {
render() {
return (
<TouchableOpacity
onPress={() => {
if (Platform?.OS === 'android' || Platform.OS === 'ios') {
notifyMessage(this.props.color.color + ' copied to clipboard!');
}
Clipboard.setString(this.props.color.color);
}}
style={[styles.container, { backgroundColor: this.props.color.color }]}>
<Text style={styles.colorText}>
{this.props.color.color +
(this.props.color.name ? ' (' + this.props.color.name + ')' : '')}
</Text>
</TouchableOpacity>
);
}
function getContrastColor(bgColor) {
var color = bgColor.charAt(0) === '#' ? bgColor.substring(1, 7) : bgColor;
var r = parseInt(color.substring(0, 2), 16); // hexToR
var g = parseInt(color.substring(2, 4), 16); // hexToG
var b = parseInt(color.substring(4, 6), 16); // hexToB
var uicolors = [r / 255, g / 255, b / 255];
var c = uicolors.map((col) => {
if (col <= 0.03928) {
return col / 12.92;
}
return Math.pow((col + 0.055) / 1.055, 2.4);
});
var L = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
return L > 0.179 ? 'black' : 'white';
}

export const SingleColorView = ({ color, onColorChange, drag }) => {
const handlePress = () => {
if (Platform?.OS === 'android' || Platform.OS === 'ios') {
notifyMessage(color.color + ' copied to clipboard!');
}
Clipboard.setString(color.color);
};

const textColor = getContrastColor(color.color);

return (
<TouchableOpacity
onPress={handlePress}
//onPressIn={drag}
onLongPress={drag}
style={[styles.container, { backgroundColor: color.color }]}>
<Text style={[styles.colorText, { color: textColor }]}>
{color.color.toUpperCase() + (color.name ? ' (' + color.name + ')' : '')}
</Text>
<View style={styles.actionArea}>
<TouchableOpacity
style={styles.actionAreaItem}
onPress={() => {
onColorChange({ ...color, color: color.color, locked: !color.locked });
}}>
<FontAwesome5
style={[styles.icon, { color: textColor }]}
name={color.locked ? 'lock' : 'unlock'}
/>
</TouchableOpacity>
<TouchableOpacity style={styles.actionAreaItem} onPressIn={drag}>
<MaterialIcons style={[styles.icon, { color: textColor }]} name="drag-indicator" />
</TouchableOpacity>
</View>
</TouchableOpacity>
);
};

const styles = StyleSheet.create({
container: {
height: 56,
justifyContent: 'center',
alignItems: 'center'
height: 72,
//justifyContent: 'center',
alignItems: 'center',
flex: 1,
flexDirection: 'row'
},
colorText: {
fontWeight: '700',
backgroundColor: 'rgba(255, 255, 255, .3)',
paddingLeft: 8,
paddingLeft: 16,
paddingRight: 8
},
actionArea: {
position: 'absolute',
right: 0,
padding: 8,
flex: 1,
flexDirection: 'row'
},
icon: {
fontSize: 24
},
actionAreaItem: {
marginRight: 8,
marginLeft: 8
}
});
94 changes: 81 additions & 13 deletions screens/ColorListScreen.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React, { useLayoutEffect } from 'react';
import { SingleColorView } from '../components/SingleColorView';
import { ScrollView, StyleSheet, View, Text, Platform } from 'react-native';
import { StyleSheet, View, Text, Platform } from 'react-native';
import CromaButton from '../components/CromaButton';
import { logEvent } from '../libs/Helpers';
import { logEvent, notifyMessage } from '../libs/Helpers';
import { CromaContext } from '../store/store';
import { useTranslation } from 'react-i18next';
import DraggableFlatList from 'react-native-draggable-flatlist';

export default function ColorListScreen({ navigation }) {
const { t } = useTranslation();

const { colorList } = React.useContext(CromaContext);
const { colorList, setColorList } = React.useContext(CromaContext);
const colors = uniqueColors(colorList);

useLayoutEffect(() => {
Expand All @@ -20,29 +21,80 @@ export default function ColorListScreen({ navigation }) {
: t('Colors')
});
}, []);
const renderItem = ({ item, drag }) => (
<SingleColorView
onColorChange={(updatedColor) => {
const index = colors.findIndex((color) => color.color === updatedColor.color);
const updatedColors = [...colors];
updatedColors[index] = updatedColor;
setColorList(updatedColors);
}}
key={item.color + '-' + item.locked}
color={item}
drag={drag}
/>
);

const onDragEnd = ({ data }) => {
logEvent('drag_end_event_color_list');
setColorList(data);
};
const regenerateUnlockedColors = () => {
logEvent('regenerate_unlocked_colors', colors.filter((color) => !color.locked).length);
if (colors.filter((color) => !color.locked).length == 0) {
notifyMessage(t('Please unlock at least one color'));
} else {
// TODO: improve this algorithm.
const newColors = colors.map((color) => {
if (!color.locked) {
color.color = '#' + Math.floor(Math.random() * 16777215).toString(16);
}
return color;
});
setColorList(newColors);
}
};

logEvent('color_list_screen');
return (
<ScrollView style={styles.listview} showsVerticalScrollIndicator={false}>
<View>
{colors.map((color) => (
<SingleColorView key={color.color} color={color} />
))}
<View style={styles.container}>
{/* this is to make sure scrolling works */}
<View style={{ flex: 1 }}>
<DraggableFlatList
data={colors}
renderItem={renderItem}
keyExtractor={(item) => item.color + '-' + item.locked}
onDragEnd={onDragEnd}
autoscrollThreshold={100}
/>
</View>
<Text style={styles.hintText}>Generate new colors for unlocked colors</Text>
<CromaButton
style={styles.button}
onPress={() => {
regenerateUnlockedColors();
}}>
{t('Generate new colors')}
</CromaButton>
<View style={styles.separator} />
<CromaButton
style={styles.button}
onPress={() => {
navigation.navigate('SavePalette');
}}>
{t('SAVE AS NEW PALETTE')}
</CromaButton>
</ScrollView>
</View>
);
}
function uniqueColors(colors) {
let set = new Set();
let uniqueColors = [];
colors.forEach((color) => {
if (!set.has(color.color)) {
if (color.locked === undefined) {
color.locked = true;
}
uniqueColors.push(color);
}
set.add(color.color);
Expand Down Expand Up @@ -73,10 +125,26 @@ const CustomHeader = () => {
};

const styles = StyleSheet.create({
listview: {
margin: 8
container: {
flex: 1,
marginTop: 8,
marginBottom: 8
},
hintText: {
fontSize: 12,
color: '#888',
marginTop: 8,
marginBottom: 4,
textAlign: 'center'
},
separator: {
height: 1,
backgroundColor: '#ccc',
marginVertical: 16,
marginHorizontal: 8
},
doneButton: {
marginRight: '20%'
button: {
marginLeft: 32,
marginRight: 32
}
});

0 comments on commit 70cffe0

Please sign in to comment.