Merge pull request #1336 from Bilb/remove-jazzicon
commit
56a81ccc93
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group/group-28</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M18.7272727,13.2857143 C20.6890909,13.2857143 22.2609091,11.6585714 22.2609091,9.64285714 C22.2609091,7.62714286 20.6890909,6 18.7272727,6 C16.7654545,6 15.1818182,7.62714286 15.1818182,9.64285714 C15.1818182,11.6585714 16.7654545,13.2857143 18.7272727,13.2857143 Z M9.27272727,13.2857143 C11.2345455,13.2857143 12.8063636,11.6585714 12.8063636,9.64285714 C12.8063636,7.62714286 11.2345455,6 9.27272727,6 C7.31090909,6 5.72727273,7.62714286 5.72727273,9.64285714 C5.72727273,11.6585714 7.31090909,13.2857143 9.27272727,13.2857143 Z M9.27272727,15.7142857 C6.51909091,15.7142857 1,17.135 1,19.9642857 L1,23 L17.5454545,23 L17.5454545,19.9642857 C17.5454545,17.135 12.0263636,15.7142857 9.27272727,15.7142857 Z M18.7272727,15.7142857 C18.3845455,15.7142857 17.9945455,15.7385714 17.5809091,15.775 C18.9518182,16.795 19.9090909,18.1671429 19.9090909,19.9642857 L19.9090909,23 L27,23 L27,19.9642857 C27,17.135 21.4809091,15.7142857 18.7272727,15.7142857 Z" id="path-1"></path>
|
||||
<rect id="path-3" x="0" y="0" width="28" height="28"></rect>
|
||||
</defs>
|
||||
<g id="Group/group-28" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="Shape" fill="#000000" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="Color/UI/Black" mask="url(#mask-2)">
|
||||
<mask id="mask-4" fill="white">
|
||||
<use xlink:href="#path-3"></use>
|
||||
</mask>
|
||||
<use id="fill" fill="#000000" fill-rule="evenodd" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.9 KiB |
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Profile/profile-28</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M14,14 C16.7625,14 19,11.7625 19,9 C19,6.2375 16.7625,4 14,4 C11.2375,4 9,6.2375 9,9 C9,11.7625 11.2375,14 14,14 Z M14,16.5 C10.6625,16.5 4,18.175 4,21.5 L4,24 L24,24 L24,21.5 C24,18.175 17.3375,16.5 14,16.5 Z" id="path-1"></path>
|
||||
<rect id="path-3" x="0" y="0" width="28" height="28"></rect>
|
||||
</defs>
|
||||
<g id="Profile/profile-28" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="Shape" fill="#000000" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="Color/UI/Black" mask="url(#mask-2)">
|
||||
<mask id="mask-4" fill="white">
|
||||
<use xlink:href="#path-3"></use>
|
||||
</mask>
|
||||
<use id="fill" fill="#000000" fill-rule="evenodd" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import { getInitials } from '../../util/getInitials';
|
||||
|
||||
interface Props {
|
||||
diameter: number;
|
||||
phoneNumber: string;
|
||||
colors: Array<string>;
|
||||
borderColor: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
sha512Seed?: string;
|
||||
}
|
||||
|
||||
export class AvatarPlaceHolder extends React.PureComponent<Props, State> {
|
||||
public constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
sha512Seed: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
void this.sha512(this.props.phoneNumber).then((sha512Seed: string) => {
|
||||
this.setState({ sha512Seed });
|
||||
});
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
if (this.props.phoneNumber === prevProps.phoneNumber) {
|
||||
return;
|
||||
}
|
||||
void this.sha512(this.props.phoneNumber).then((sha512Seed: string) => {
|
||||
this.setState({ sha512Seed });
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
if (!this.state.sha512Seed) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const { borderColor, colors, diameter, phoneNumber, name } = this.props;
|
||||
const r = diameter / 2;
|
||||
const initial =
|
||||
getInitials(name)?.toLocaleUpperCase() ||
|
||||
getInitials(phoneNumber)?.toLocaleUpperCase() ||
|
||||
'0';
|
||||
const viewBox = `0 0 ${diameter} ${diameter}`;
|
||||
const fontSize = diameter * 0.5;
|
||||
|
||||
// Generate the seed simulate the .hashCode as Java
|
||||
const hash = parseInt(this.state.sha512Seed.substring(0, 12), 16) || 0;
|
||||
|
||||
const bgColorIndex = hash % colors.length;
|
||||
|
||||
const bgColor = colors[bgColorIndex];
|
||||
|
||||
return (
|
||||
<svg viewBox={viewBox}>
|
||||
<g id="UrTavla">
|
||||
<circle
|
||||
cx={r}
|
||||
cy={r}
|
||||
r={r}
|
||||
fill={bgColor}
|
||||
shape-rendering="geometricPrecision"
|
||||
stroke={borderColor}
|
||||
stroke-width="1"
|
||||
/>
|
||||
<text
|
||||
font-size={fontSize}
|
||||
x="50%"
|
||||
y="50%"
|
||||
fill="white"
|
||||
text-anchor="middle"
|
||||
stroke="white"
|
||||
stroke-width={1}
|
||||
alignment-baseline="central"
|
||||
>
|
||||
{initial}
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
private async sha512(str: string) {
|
||||
// tslint:disable-next-line: await-promise
|
||||
const buf = await crypto.subtle.digest(
|
||||
'SHA-512',
|
||||
new TextEncoder().encode(str)
|
||||
);
|
||||
|
||||
// tslint:disable: prefer-template restrict-plus-operands
|
||||
return Array.prototype.map
|
||||
.call(new Uint8Array(buf), (x: any) => ('00' + x.toString(16)).slice(-2))
|
||||
.join('');
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import { Avatar } from '../Avatar';
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
import { ConversationAttributes } from '../../../js/models/conversations';
|
||||
|
||||
interface Props {
|
||||
size: number;
|
||||
conversations: Array<ConversationAttributes>;
|
||||
i18n: LocalizerType;
|
||||
}
|
||||
|
||||
export class ClosedGroupAvatar extends React.PureComponent<Props> {
|
||||
public render() {
|
||||
const { conversations, size, i18n } = this.props;
|
||||
|
||||
if (conversations.length === 1) {
|
||||
const conv = conversations[0];
|
||||
return (
|
||||
<Avatar
|
||||
avatarPath={conv.avatarPath}
|
||||
noteToSelf={conv.isMe}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
name={name}
|
||||
phoneNumber={conv.id}
|
||||
profileName={conv.name}
|
||||
size={size}
|
||||
isPublic={false}
|
||||
/>
|
||||
);
|
||||
} else if (conversations.length > 1) {
|
||||
// in a closed group avatar, each visible avatar member size is 2/3 of the group avatar in size
|
||||
// Always use the size directly under the one requested
|
||||
let avatarsDiameter = 0;
|
||||
switch (size) {
|
||||
case 36: {
|
||||
avatarsDiameter = 28;
|
||||
break;
|
||||
}
|
||||
case 48: {
|
||||
avatarsDiameter = 36;
|
||||
break;
|
||||
}
|
||||
case 64: {
|
||||
avatarsDiameter = 48;
|
||||
break;
|
||||
}
|
||||
case 80: {
|
||||
avatarsDiameter = 64;
|
||||
break;
|
||||
}
|
||||
case 300: {
|
||||
avatarsDiameter = 80;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Invalid size request for closed group avatar: ${size}`
|
||||
);
|
||||
}
|
||||
const conv1 = conversations[0];
|
||||
const conv2 = conversations[1];
|
||||
// use the 2 first members as group avatars
|
||||
return (
|
||||
<div className="module-avatar__icon-closed">
|
||||
<Avatar
|
||||
avatarPath={conv1.avatarPath}
|
||||
noteToSelf={conv1.isMe}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
name={name}
|
||||
phoneNumber={conv1.id}
|
||||
profileName={conv1.name}
|
||||
size={avatarsDiameter}
|
||||
isPublic={false}
|
||||
/>
|
||||
<Avatar
|
||||
avatarPath={conv2.avatarPath}
|
||||
noteToSelf={conv2.isMe}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
name={name}
|
||||
phoneNumber={conv2.id}
|
||||
profileName={conv2.name}
|
||||
size={avatarsDiameter}
|
||||
isPublic={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export { AvatarPlaceHolder } from './AvatarPlaceHolder';
|
||||
export { ClosedGroupAvatar } from './ClosedGroupAvatar';
|
@ -1,166 +0,0 @@
|
||||
// Modified from https://github.com/redlanta/react-jazzicon
|
||||
|
||||
import React from 'react';
|
||||
import Color from 'color';
|
||||
import { Paper } from './Paper';
|
||||
import { RNG } from './RNG';
|
||||
|
||||
const defaultColors = [
|
||||
'#01888c', // teal
|
||||
'#fc7500', // bright orange
|
||||
'#034f5d', // dark teal
|
||||
'#E784BA', // light pink
|
||||
'#81C8B6', // bright green
|
||||
'#c7144c', // raspberry
|
||||
'#f3c100', // goldenrod
|
||||
'#1598f2', // lightning blue
|
||||
'#2465e1', // sail blue
|
||||
'#f19e02', // gold
|
||||
];
|
||||
|
||||
const isColor = (str: string) => /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(str);
|
||||
const isColors = (arr: Array<string>) => {
|
||||
if (!Array.isArray(arr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arr.every(value => typeof value === 'string' && isColor(value))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
diameter: number;
|
||||
seed: number;
|
||||
paperStyles?: Object;
|
||||
svgStyles?: Object;
|
||||
shapeCount?: number;
|
||||
wobble?: number;
|
||||
colors?: Array<string>;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-http-string
|
||||
const svgns = 'http://www.w3.org/2000/svg';
|
||||
const shapeCount = 4;
|
||||
const wobble = 30;
|
||||
|
||||
export class JazzIcon extends React.PureComponent<Props> {
|
||||
public render() {
|
||||
const {
|
||||
colors: customColors,
|
||||
diameter,
|
||||
paperStyles,
|
||||
seed,
|
||||
svgStyles,
|
||||
} = this.props;
|
||||
|
||||
const generator = new RNG(seed);
|
||||
|
||||
const colors = customColors || defaultColors;
|
||||
|
||||
const newColours = this.hueShift(
|
||||
this.colorsForIcon(colors).slice(),
|
||||
generator
|
||||
);
|
||||
const shapesArr = Array(shapeCount).fill(null);
|
||||
const shuffledColours = this.shuffleArray(newColours, generator);
|
||||
|
||||
return (
|
||||
<Paper color={shuffledColours[0]} diameter={diameter} style={paperStyles}>
|
||||
<svg
|
||||
xmlns={svgns}
|
||||
x="0"
|
||||
y="0"
|
||||
height={diameter}
|
||||
width={diameter}
|
||||
style={svgStyles}
|
||||
>
|
||||
{shapesArr.map((_, i) =>
|
||||
this.genShape(
|
||||
shuffledColours[i + 1],
|
||||
diameter,
|
||||
i,
|
||||
shapeCount - 1,
|
||||
generator
|
||||
)
|
||||
)}
|
||||
</svg>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
private hueShift(colors: Array<string>, generator: RNG) {
|
||||
const amount = generator.random() * 30 - wobble / 2;
|
||||
|
||||
return colors.map(hex =>
|
||||
Color(hex)
|
||||
.rotate(amount)
|
||||
.hex()
|
||||
);
|
||||
}
|
||||
|
||||
private genShape(
|
||||
colour: string,
|
||||
diameter: number,
|
||||
i: number,
|
||||
total: number,
|
||||
generator: RNG
|
||||
) {
|
||||
const center = diameter / 2;
|
||||
const firstRot = generator.random();
|
||||
const angle = Math.PI * 2 * firstRot;
|
||||
const velocity =
|
||||
(diameter / total) * generator.random() + (i * diameter) / total;
|
||||
const tx = Math.cos(angle) * velocity;
|
||||
const ty = Math.sin(angle) * velocity;
|
||||
const translate = `translate(${tx} ${ty})`;
|
||||
|
||||
// Third random is a shape rotation on top of all of that.
|
||||
const secondRot = generator.random();
|
||||
const rot = firstRot * 360 + secondRot * 180;
|
||||
const rotate = `rotate(${rot.toFixed(1)} ${center} ${center})`;
|
||||
const transform = `${translate} ${rotate}`;
|
||||
|
||||
return (
|
||||
<rect
|
||||
key={i}
|
||||
x="0"
|
||||
y="0"
|
||||
rx="0"
|
||||
ry="0"
|
||||
height={diameter}
|
||||
width={diameter}
|
||||
transform={transform}
|
||||
fill={colour}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private colorsForIcon(arr: Array<string>) {
|
||||
if (isColors(arr)) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
return defaultColors;
|
||||
}
|
||||
|
||||
private shuffleArray<T>(array: Array<T>, generator: RNG) {
|
||||
let currentIndex = array.length;
|
||||
const newArray = [...array];
|
||||
|
||||
// While there remain elements to shuffle...
|
||||
while (currentIndex > 0) {
|
||||
// Pick a remaining element...
|
||||
const randomIndex = generator.next() % currentIndex;
|
||||
currentIndex -= 1;
|
||||
// And swap it with the current element.
|
||||
const temporaryValue = newArray[currentIndex];
|
||||
newArray[currentIndex] = newArray[randomIndex];
|
||||
newArray[randomIndex] = temporaryValue;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const styles = {
|
||||
borderRadius: '50%',
|
||||
display: 'inline-block',
|
||||
margin: 0,
|
||||
overflow: 'hidden',
|
||||
padding: 0,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
export const Paper = ({ children, color, diameter, style: styleOverrides }) => (
|
||||
<div
|
||||
className="paper"
|
||||
style={{
|
||||
...styles,
|
||||
backgroundColor: color,
|
||||
height: diameter,
|
||||
width: diameter,
|
||||
...(styleOverrides || {}),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
@ -1,21 +0,0 @@
|
||||
export class RNG {
|
||||
private _seed: number;
|
||||
constructor(seed: number) {
|
||||
this._seed = seed % 2147483647;
|
||||
if (this._seed <= 0) {
|
||||
this._seed += 2147483646;
|
||||
}
|
||||
}
|
||||
|
||||
public next() {
|
||||
return (this._seed = (this._seed * 16807) % 2147483647);
|
||||
}
|
||||
|
||||
public nextFloat() {
|
||||
return (this.next() - 1) / 2147483646;
|
||||
}
|
||||
|
||||
public random() {
|
||||
return this.nextFloat();
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
import { JazzIcon } from './JazzIcon';
|
||||
export { JazzIcon };
|
@ -0,0 +1,63 @@
|
||||
import { GroupUtils } from '../../session/utils';
|
||||
import { UserUtil } from '../../util';
|
||||
import { PubKey } from '../../session/types';
|
||||
import React from 'react';
|
||||
import { ConversationAttributes } from '../../../js/models/conversations';
|
||||
type State = {
|
||||
closedMemberConversations?: Array<ConversationAttributes>;
|
||||
};
|
||||
|
||||
export function usingClosedConversationDetails(WrappedComponent: any) {
|
||||
return class extends React.Component<any, State> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
closedMemberConversations: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
void this.fetchClosedConversationDetails();
|
||||
}
|
||||
|
||||
public componentDidUpdate() {
|
||||
void this.fetchClosedConversationDetails();
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<WrappedComponent
|
||||
closedMemberConversations={this.state.closedMemberConversations}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private async fetchClosedConversationDetails() {
|
||||
const {
|
||||
isPublic,
|
||||
type,
|
||||
conversationType,
|
||||
isGroup,
|
||||
phoneNumber,
|
||||
id,
|
||||
} = this.props;
|
||||
if (
|
||||
!isPublic &&
|
||||
(conversationType === 'group' || type === 'group' || isGroup)
|
||||
) {
|
||||
const groupId = id || phoneNumber;
|
||||
let members = await GroupUtils.getGroupMembers(PubKey.cast(groupId));
|
||||
const ourPrimary = await UserUtil.getPrimary();
|
||||
members = members.filter(m => m.key !== ourPrimary.key);
|
||||
members.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
||||
const membersConvos = members.map(
|
||||
m => window.ConversationController.get(m.key).cachedProps
|
||||
);
|
||||
// no need to forward more than 2 conversation for rendering the group avatar
|
||||
membersConvos.slice(0, 2);
|
||||
this.setState({ closedMemberConversations: membersConvos });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue