Merge branch 'develop' into feature/email-confirmation-banner

* develop:
  Fix GH-168: Rehabilitate the `Modal` props.style
  Fix GH-162: Show "user deletion canceled" modal
  Set empty states on sign out
  Clean up activity item rendering logic
  Add some padding to the empty message
  Make sure boxes aren't transparent
  Add empty state for What's Happening box
  Fix GH-124: Fix studio thumbnail URLs
  Only show "Welcome" panel if user is < 2 weeks old
  Fix #152: Don't output activity without a message
  Fix #102: Add remix and love icons
  Update arrow icons on Carousels

# Conflicts:
#	src/components/modal/modal.jsx
#	src/views/splash/splash.jsx
This commit is contained in:
Matthew Taylor 2015-10-25 09:03:44 -04:00
commit ccd048893f
19 changed files with 305 additions and 74 deletions

View file

@ -44,6 +44,8 @@
"json-loader": "0.5.2",
"json2po-stream": "1.0.3",
"jsx-loader": "0.13.2",
"lodash.clone": "3.0.3",
"lodash.defaultsdeep": "3.10.0",
"lodash.omit": "3.1.0",
"minilog": "2.0.8",
"node-sass": "3.3.3",

View file

@ -1,6 +1,7 @@
var React = require('react');
var ReactIntl = require('react-intl');
var defineMessages = ReactIntl.defineMessages;
var FormattedMessage = ReactIntl.FormattedMessage;
var FormattedRelative = ReactIntl.FormattedRelative;
var injectIntl = ReactIntl.injectIntl;
@ -32,28 +33,46 @@ var Activity = React.createClass({
className="activity"
title={formatMessage(defaultMessages.whatsHappening)}>
<ul>
{this.props.items.map(function (item) {
var actorProfileUrl = '/users/' + item.actor.username + '/';
var actionDate = new Date(item.datetime_created + 'Z');
var activityMessageHTML = '<a href=' + actorProfileUrl + '>' +
item.actor.username + '</a>' + item.message;
return (
<li key={item.pk}>
<a href={actorProfileUrl}>
<img src={item.actor.thumbnail_url} width="34" height="34" />
<p dangerouslySetInnerHTML={{__html: activityMessageHTML}}></p>
<p>
<span className="stamp">
<FormattedRelative value={actionDate} />
</span>
</p>
</a>
</li>
);
})}
</ul>
{this.props.items && this.props.items.length > 0 ? [
<ul>
{this.props.items.map(function (item) {
if (item.message.replace(/\s/g, '')) {
var actorProfileUrl = '/users/' + item.actor.username + '/';
var actionDate = new Date(item.datetime_created + 'Z');
var activityMessageHTML = (
'<a href=' + actorProfileUrl + '>' + item.actor.username + '</a>' +
item.message
);
return (
<li key={item.pk}>
<a href={actorProfileUrl}>
<img src={item.actor.thumbnail_url} width="34" height="34" />
<p dangerouslySetInnerHTML={{__html: activityMessageHTML}}></p>
<p>
<span className="stamp">
<FormattedRelative value={actionDate} />
</span>
</p>
</a>
</li>
);
}
})}
</ul>
] : [
<div className="empty">
<h4>
<FormattedMessage
id="activity.seeUpdates"
defaultMessage="This is where you will see updates from Scratchers you follow" />
</h4>
<a href="/studios/146521/">
<FormattedMessage
id="activity.checkOutScratchers"
defaultMessage="Check out some Scratchers you might like to follow" />
</a>
</div>
]}
</Box>
);
}

View file

@ -6,6 +6,7 @@ $base-bg: $ui-white;
display: inline-block;
border: 1px solid $ui-border;
border-radius: 10px 10px 0 0;
background-color: $ui-white;
width: 100%;
.box-header {
@ -44,4 +45,8 @@ $base-bg: $ui-white;
background-color: $base-bg;
padding: 8px 20px;
}
.empty {
margin-top: 20px;
}
}

View file

@ -38,7 +38,7 @@ var Carousel = React.createClass({
var href = '';
switch (item.type) {
case 'gallery':
href = '/studio/' + item.id + '/';
href = '/studios/' + item.id + '/';
break;
case 'project':
href = '/projects/' + item.id + '/';

View file

@ -18,8 +18,14 @@
height: $icon-size;
&:before {
color: $ui-dark-gray;
display: block;
background-repeat: no-repeat;
background-position: center center;
background-size: 70%;
width: $icon-size;
height: $icon-size;
font-size: $icon-size;
content: "";
}
&.slick-disabled:before {
@ -30,6 +36,16 @@
.slick-prev {
left: 0;
&:before {
background-image: url("/svgs/carousel/prev_ui-dark-gray.svg");
}
&:hover:before {
background-image: url("/svgs/carousel/prev_ui-blue.svg");
background-size: 90%;
}
.box-content & {
left: -$box-content-offset;
}
@ -38,6 +54,15 @@
.slick-next {
right: 0;
&:before {
background-image: url("/svgs/carousel/next_ui-dark-gray.svg");
}
&:hover:before {
background-image: url("/svgs/carousel/next_ui-blue.svg");
background-size: 90%;
}
.box-content & {
right: -$box-content-offset;
}

View file

@ -44,7 +44,7 @@ var Intro = React.createClass({
this.closeRegistration();
},
render: function () {
var frameSettings = {
var frameProps = {
width: 570,
height: 357,
padding: 15
@ -140,10 +140,10 @@ var Intro = React.createClass({
className="video-modal"
isOpen={this.state.videoOpen}
onRequestClose={this.closeVideo}
frameSettings={frameSettings}>
style={{content:frameProps}}>
<iframe
src="//player.vimeo.com/video/65583694?title=0&amp;byline=0&amp;portrait=0"
{...omit(frameSettings, 'padding')} />
{...omit(frameProps, 'padding')} />
</Modal>
</div>
);

View file

@ -1,58 +1,55 @@
var defaults = require('lodash.defaults');
var omit = require('lodash.omit');
var clone = require('lodash.clone');
var defaultsDeep = require('lodash.defaultsdeep');
var React = require('react');
var ReactModal = require('react-modal');
require('./modal.scss');
var defaultStyle = {
overlay: {
zIndex: 100,
backgroundColor: 'rgba(0, 0, 0, .75)'
},
content: {
overflow: 'visible',
borderRadius: '6px',
width: 500,
height: 250,
padding: 0,
top: '50%',
right: 'auto',
bottom: 'auto',
left: '50%',
marginTop: -125,
marginLeft: -250
}
};
var Modal = React.createClass({
type: 'Modal',
statics: {
setAppElement: ReactModal.setAppElement,
defaultFrameSettings: {
width: 500,
height: 250,
padding: 0
}
setAppElement: ReactModal.setAppElement
},
getDefaultProps: function () {
return {
frameSettings: null
style: defaultStyle
};
},
calculateStyle: function () {
var style = clone(this.props.style, true);
defaultsDeep(style, defaultStyle);
style.content.marginTop = (style.content.height + style.content.padding*2) / -2;
style.content.marginLeft = (style.content.width + style.content.padding*2) / -2;
return style;
},
requestClose: function () {
return this.refs.modal.portal.requestClose();
},
render: function () {
var frameSettings = this.props.frameSettings;
var style = this.props.style || {};
defaults(style, {
overlay: {
zIndex: 100,
backgroundColor: 'rgba(0, 0, 0, .75)'
},
content: {
overflow: 'visible',
borderRadius: '6px'
}
});
var modalProps = omit(this.props, ['frameSettings', 'style']);
if (frameSettings) {
defaults(frameSettings, Modal.defaultFrameSettings);
defaults(style.content, {
top: '50%',
right: 'auto',
bottom: 'auto',
left: '50%',
marginTop: (frameSettings.height + 2*frameSettings.padding) / -2,
marginLeft: (frameSettings.width + 2*frameSettings.padding) / -2,
height: frameSettings.height,
width: frameSettings.width,
padding: frameSettings.padding
});
}
return (
<ReactModal ref="modal" style={style} {...modalProps}>
<ReactModal ref="modal"
{...this.props}
style={this.calculateStyle()}>
<div className="modal-close" onClick={this.requestClose}></div>
{this.props.children}
</ReactModal>

View file

@ -12,11 +12,14 @@ var Dropdown = require('./dropdown.jsx');
var Input = require('../forms/input.jsx');
var log = require('../../lib/log.js');
var Login = require('../login/login.jsx');
var Modal = require('../modal/modal.jsx');
var Registration = require('../registration/registration.jsx');
var Session = require('../../mixins/session.jsx');
require('./navigation.scss');
Modal.setAppElement(document.getElementById('view'));
var defaultMessages = defineMessages({
messages: {
id: 'general.messages',
@ -36,12 +39,13 @@ var Navigation = React.createClass({
],
getInitialState: function () {
return {
'accountNavOpen': false,
'loginOpen': false,
'loginError': null,
'registrationOpen': false,
'unreadMessageCount': 0,
'messageCountIntervalId': -1
accountNavOpen: false,
canceledDeletionOpen: false,
loginOpen: false,
loginError: null,
registrationOpen: false,
unreadMessageCount: 0,
messageCountIntervalId: -1
};
},
componentDidMount: function () {
@ -103,6 +107,7 @@ var Navigation = React.createClass({
},
handleLogIn: function (formData) {
this.setState({'loginError': null});
formData['useMessages'] = true;
this.api({
method: 'post',
host: '',
@ -119,6 +124,11 @@ var Navigation = React.createClass({
this.setState({'loginError': body.msg});
} else {
this.closeLogin();
body.messages.map(function (message) {
if (message.message == 'canceled-deletion') {
this.showCanceledDeletion();
}
}.bind(this));
window.refreshSession();
}
}
@ -145,6 +155,12 @@ var Navigation = React.createClass({
closeAccountNav: function () {
this.setState({'accountNavOpen': false});
},
showCanceledDeletion: function () {
this.setState({'canceledDeletionOpen': true});
},
closeCanceledDeletion: function () {
this.setState({'canceledDeletionOpen': false});
},
closeRegistration: function () {
this.setState({'registrationOpen': false});
},
@ -299,6 +315,17 @@ var Navigation = React.createClass({
</li>
]}
</ul>
<Modal isOpen={this.state.canceledDeletionOpen}
onRequestClose={this.closeCanceledDeletion}
frameSettings={{padding: 15}}>
<h4>Your Account Will Not Be Deleted</h4>
<p>
Your account was scheduled for deletion but you logged in. Your account has been reactivated.
If you didnt request for your account to be deleted, you should
{' '}<a href="/accounts/password_reset/">change your password</a>{' '}
to make sure your account is secure.
</p>
</Modal>
</div>
);
}

View file

@ -22,7 +22,7 @@ var Registration = React.createClass({
window.removeEventListener('message', this.onMessage);
},
render: function () {
var frameSettings = {
var frameProps = {
width: 610,
height: 438
};
@ -31,8 +31,8 @@ var Registration = React.createClass({
isOpen={this.props.isOpen}
onRequestClose={this.props.onRequestClose}
className="registration"
frameSettings={frameSettings}>
<iframe src="/accounts/standalone-registration/" {...frameSettings} />
style={{content:frameProps}}>
<iframe src="/accounts/standalone-registration/" {...frameProps} />
</Modal>
);
}

View file

@ -43,6 +43,29 @@
}
}
.thumbnail-loves,
.thumbnail-remixes {
&:before {
display: inline-block;
margin-right: .1rem;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
width: .93rem;
height: .93rem;
vertical-align: text-top;
content: "";
}
}
.thumbnail-loves:before {
background-image: url("/svgs/love/love_type-gray.svg");
}
.thumbnail-remixes:before {
background-image: url("/svgs/remix/remix_type-gray.svg");
}
&.project {
$project-width: 144px;
$project-height: 108px;

View file

@ -52,6 +52,21 @@ a:hover {
width: 942px;
}
.empty {
$bg-blue: #d9edf7;
$bg-blue-accent: #bce8f1;
border: 1px solid $bg-blue-accent;
border-radius: 5px;
background-color: $bg-blue;
padding: 10px;
text-align: center;
line-height: 2rem;
color: $type-gray;
h4 {
color: $type-gray;
}
}
#view {
/* NOTE: Margin should match height in navigation.scss */
margin-top: 50px;

View file

@ -1,6 +1,7 @@
var React = require('react');
var render = require('../../lib/render.jsx');
var Activity = require('../../components/activity/activity.jsx');
var Box = require('../../components/box/box.jsx');
var Button = require('../../components/forms/button.jsx');
var Carousel = require('../../components/carousel/carousel.jsx');
@ -32,6 +33,10 @@ var Components = React.createClass({
title="Carousel component in a box!">
<Carousel />
</Box>
<h1>{'What\'s Happening??'}</h1>
<Activity />
<h1>{'Nothing!!!'}</h1>
<Activity items={[]} />
</div>
);
}

View file

@ -1,5 +1,4 @@
var injectIntl = require('react-intl').injectIntl;
var omit = require('lodash.omit');
var React = require('react');
var render = require('../../lib/render.jsx');
@ -50,6 +49,8 @@ var Splash = injectIntl(React.createClass({
}
} else {
this.setState({featuredCustom: []});
this.setState({activity: []});
this.setState({news: []});
this.getProjectCount();
window.removeEventListener('message', this.onMessage);
}
@ -138,6 +139,13 @@ var Splash = injectIntl(React.createClass({
if (!err) window.refreshSession();
});
},
shouldShowWelcome: function () {
if (!this.state.session.user || !this.state.session.flags.show_welcome) return false;
return (
new Date(this.state.session.user.dateJoined) >
new Date(new Date - 2*7*24*60*60*1000) // Two weeks ago
);
},
renderHomepageRows: function () {
var formatMessage = this.props.intl.formatMessage;

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="22px" height="22px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve">
<g>
<g>
<g>
<path style="fill:#25AFF4;" d="M17.3,4.7C15.6,2.9,13.3,2,11,2S6.4,2.9,4.6,4.7c-3.5,3.5-3.5,9.2,0,12.7C6.3,19,8.6,20,11,20
c2.4,0,4.6-0.9,6.3-2.6C19,15.7,20,13.4,20,11S19,6.3,17.3,4.7z M16.1,16.2c-1.4,1.4-3.2,2.1-5.1,2.1s-3.8-0.8-5.1-2.1
C3,13.3,3,8.7,5.9,5.9C7.2,4.5,9.1,3.7,11,3.7s3.8,0.8,5.1,2.1c1.4,1.4,2.1,3.2,2.1,5.1S17.5,14.8,16.1,16.2z"/>
</g>
<g>
<path style="fill:#25AFF4;" d="M15.8,11.6l-3,3c-0.2,0.2-0.4,0.2-0.6,0.2c-0.2,0-0.4-0.1-0.6-0.2c-0.3-0.3-0.3-0.9,0-1.2l1.5-1.5
H7.2c-0.5,0-0.8-0.4-0.8-0.8c0-0.5,0.4-0.8,0.8-0.8h5.9l-1.5-1.5c-0.3-0.3-0.3-0.9,0-1.2c0.3-0.3,0.9-0.3,1.2,0l3,3
C16.1,10.7,16.1,11.3,15.8,11.6z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="22px" height="22px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve">
<g>
<g>
<g>
<path style="fill:#B3B3B3;" d="M17.3,4.7C15.6,2.9,13.3,2,11,2S6.4,2.9,4.6,4.7c-3.5,3.5-3.5,9.2,0,12.7C6.3,19,8.6,20,11,20
c2.4,0,4.6-0.9,6.3-2.6C19,15.7,20,13.4,20,11S19,6.3,17.3,4.7z M16.1,16.2c-1.4,1.4-3.2,2.1-5.1,2.1s-3.8-0.8-5.1-2.1
C3,13.3,3,8.7,5.9,5.9C7.2,4.5,9.1,3.7,11,3.7s3.8,0.8,5.1,2.1c1.4,1.4,2.1,3.2,2.1,5.1S17.5,14.8,16.1,16.2z"/>
</g>
<g>
<path style="fill:#B3B3B3;" d="M15.8,11.6l-3,3c-0.2,0.2-0.4,0.2-0.6,0.2c-0.2,0-0.4-0.1-0.6-0.2c-0.3-0.3-0.3-0.9,0-1.2l1.5-1.5
H7.2c-0.5,0-0.8-0.4-0.8-0.8c0-0.5,0.4-0.8,0.8-0.8h5.9l-1.5-1.5c-0.3-0.3-0.3-0.9,0-1.2c0.3-0.3,0.9-0.3,1.2,0l3,3
C16.1,10.7,16.1,11.3,15.8,11.6z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="22px" height="22px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve">
<g>
<g>
<g>
<path style="fill:#25AFF4;" d="M17.3,4.6c-3.5-3.5-9.2-3.5-12.7,0s-3.5,9.2,0,12.7C6.3,19,8.6,20,11,20c1.2,0,2.4-0.2,3.4-0.7
c1.1-0.5,2.1-1.1,2.9-2C19,15.6,20,13.4,20,11S19,6.3,17.3,4.6z M16.1,16.1c-1.4,1.4-3.2,2.1-5.1,2.1s-3.8-0.8-5.1-2.1
C3,13.3,3,8.7,5.9,5.9C7.2,4.5,9.1,3.7,11,3.7s3.8,0.8,5.1,2.1c1.4,1.4,2.1,3.2,2.1,5.1S17.5,14.8,16.1,16.1z"/>
</g>
<g>
<path style="fill:#25AFF4;" d="M15.6,11c0,0.5-0.4,0.8-0.8,0.8H8.9l1.5,1.5c0.3,0.3,0.3,0.9,0,1.2c-0.2,0.2-0.4,0.2-0.6,0.2
c-0.2,0-0.4-0.1-0.6-0.2l-3-3c-0.3-0.3-0.3-0.9,0-1.2l3-3c0.3-0.3,0.9-0.3,1.2,0c0.3,0.3,0.3,0.9,0,1.2l-1.5,1.5h5.9
C15.2,10.1,15.6,10.5,15.6,11z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="22px" height="22px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve">
<g>
<g>
<g>
<path style="fill:#B3B3B3;" d="M17.3,4.6c-3.5-3.5-9.2-3.5-12.7,0s-3.5,9.2,0,12.7C6.3,19,8.6,20,11,20c1.2,0,2.4-0.2,3.4-0.7
c1.1-0.5,2.1-1.1,2.9-2C19,15.6,20,13.4,20,11S19,6.3,17.3,4.6z M16.1,16.1c-1.4,1.4-3.2,2.1-5.1,2.1s-3.8-0.8-5.1-2.1
C3,13.3,3,8.7,5.9,5.9C7.2,4.5,9.1,3.7,11,3.7s3.8,0.8,5.1,2.1c1.4,1.4,2.1,3.2,2.1,5.1S17.5,14.8,16.1,16.1z"/>
</g>
<g>
<path style="fill:#B3B3B3;" d="M15.6,11c0,0.5-0.4,0.8-0.8,0.8H8.9l1.5,1.5c0.3,0.3,0.3,0.9,0,1.2c-0.2,0.2-0.4,0.2-0.6,0.2
c-0.2,0-0.4-0.1-0.6-0.2l-3-3c-0.3-0.3-0.3-0.9,0-1.2l3-3c0.3-0.3,0.9-0.3,1.2,0c0.3,0.3,0.3,0.9,0,1.2l-1.5,1.5h5.9
C15.2,10.1,15.6,10.5,15.6,11z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="22px" height="22px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve">
<g>
<path style="fill:#6B6B6B;" d="M11,18.9c-0.7,0-1.4-0.3-1.9-0.8l-5.5-5.5c-2.2-2.2-2.2-5.7,0-7.9c1.1-1.1,2.5-1.6,4-1.6
c1.2,0,2.4,0.4,3.4,1.1c1-0.7,2.1-1.1,3.4-1.1c1.5,0,2.9,0.6,4,1.6c1.1,1.1,1.6,2.5,1.6,4c0,1.5-0.6,2.9-1.6,4l-5.5,5.5
C12.4,18.6,11.7,18.9,11,18.9L11,18.9z M7.6,4.8c-1,0-2,0.4-2.8,1.1c-1.5,1.5-1.5,4,0,5.5l5.5,5.5c0.4,0.4,1,0.4,1.3,0l5.5-5.5
c0,0,0,0,0,0c0.7-0.7,1.1-1.7,1.1-2.8c0-1-0.4-2-1.1-2.8c-1.5-1.5-4-1.5-5.5,0c-0.3,0.3-0.9,0.3-1.2,0C9.7,5.2,8.7,4.8,7.6,4.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 889 B

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="22px" height="22px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve">
<g>
<g>
<path style="fill:#6B6B6B;" d="M11.3,20c-2.9,0-5.8-1.7-7.2-4.2c-1.5-2.5-1.6-5.8-0.3-8.5c1.4-2.8,3.7-4.1,4.6-4.5
C9,2.5,9.6,2.3,10.2,2.1L10.8,2c0.5-0.1,0.9,0.2,1,0.6c0.1,0.5-0.2,0.9-0.6,1l-0.6,0.1c-0.5,0.1-1,0.3-1.5,0.5
C8.5,4.6,6.5,5.6,5.3,8c-0.9,1.8-1.1,4.6,0.2,6.9c1.3,2.2,3.8,3.6,6.3,3.4c2.3-0.2,4.4-1.7,5.2-3.8c0.8-1.9,0.3-3.9-0.6-5.1
c-1-1.4-2.2-1.8-2.7-1.9c-0.1,0-1.8-0.5-3.3,0.2C9.8,8,8.9,8.7,8.5,9.8c-0.5,1.1-0.4,2.5,0.3,3.5c0.7,1,1.9,1.6,3,1.4
c1.1-0.2,1.8-1,2-1.7c0.2-0.8-0.1-1.4-0.4-1.6c-0.4-0.4-0.8-0.4-0.8-0.4c-0.2,0-0.3,0-0.3,0c0,0,0,0,0,0c-0.2,0.1-0.5,0.2-0.5,0.4
c0,0,0,0.1,0,0.3c0.2,0.4-0.1,0.9-0.5,1.1c-0.4,0.2-0.9-0.1-1.1-0.5c-0.3-0.7-0.1-1.3,0.1-1.6c0.5-0.9,1.5-1.2,1.6-1.3
c0.1,0,0.1,0,0.2,0c0.1,0,0.3,0,0.6,0c0.6,0,1.4,0.3,1.9,0.9c0.7,0.6,1.2,1.9,0.8,3.3c-0.4,1.5-1.7,2.7-3.3,2.9
c-1.7,0.3-3.7-0.5-4.7-2.1c-1-1.4-1.2-3.5-0.5-5.1c0.8-1.9,2.3-2.7,2.7-2.9c2.1-1.1,4.4-0.4,4.5-0.3c0.6,0.1,2.3,0.7,3.6,2.6
c1,1.4,1.8,4,0.8,6.7c-1,2.7-3.7,4.7-6.6,4.9C11.8,20,11.5,20,11.3,20z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB