Commit 2a48f8a9 authored by Matthew Taylor's avatar Matthew Taylor Committed by GitHub

Merge pull request #1523 from LLK/release/messages-page

[Develop] Release: new notifications page
parents af0f0d30 d18fd006
......@@ -121,3 +121,9 @@ file_filter = localizations/camp/<lang>.json
source_file = src/views/camp/l10n.json
source_lang = en
type = KEYVALUEJSON
[scratch-website.messages-l10njson]
file_filter = localizations/messages/<lang>.json
source_file = src/views/messages/l10n.json
source_lang = en
type = KEYVALUEJSON
......@@ -36,12 +36,18 @@ var Activity = React.createClass({
<ul key="activity-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 username = '';
if (item.type === 22) {
username = item.recipient_username;
} else {
username = item.actor.username;
}
var actorProfileUrl = '/users/' + username + '/';
var activityMessageHTML = (
'<a href=' + actorProfileUrl + '>' + item.actor.username + '</a>' +
'<a href=' + actorProfileUrl + '>' + username + '</a>' +
item.message
);
var actionDate = new Date(item.datetime_created + 'Z');
return (
<li key={item.pk}>
<a href={actorProfileUrl}>
......
......@@ -12,7 +12,7 @@
position: absolute;
top: -1px;
left: -13px;
border-top: 12px solid $ui-border;
border-bottom: 12px solid $ui-border;
border-left: 13px solid $ui-border;
border-radius: 0 0 0 13px;
width: 0;
......@@ -24,7 +24,7 @@
position: absolute;
top: 0;
left: -12px;
border-top: 10px solid $ui-white;
border-bottom: 10px solid $ui-white;
border-left: 12px solid $ui-white;
border-radius: 0 0 0 12px;
width: 0;
......
......@@ -138,7 +138,7 @@ var Navigation = React.createClass({
}, function (err) {
if (err) log.error(err);
this.closeLogin();
this.props.dispatch(sessionActions.refreshSession());
window.location = '/';
}.bind(this));
},
handleAccountNavClick: function (e) {
......
......@@ -10,7 +10,10 @@ var SocialMessage = React.createClass({
type: 'SocialMessage',
propTypes: {
as: React.PropTypes.string,
datetime: React.PropTypes.string.isRequired
datetime: React.PropTypes.string.isRequired,
iconSrc: React.PropTypes.string,
iconAlt: React.PropTypes.string,
imgClassName: React.PropTypes.string
},
getDefaultProps: function () {
return {
......@@ -22,11 +25,25 @@ var SocialMessage = React.createClass({
'social-message',
this.props.className
);
var imgClass = classNames(
'social-message-icon',
this.props.imgClassName
);
return (
<this.props.as className={classes}>
<FlexRow className="mod-social-message">
<div className="social-message-content">
{this.props.children}
{typeof this.props.iconSrc !== 'undefined' ? [
<img
key="social-message-icon"
className={imgClass}
src={this.props.iconSrc}
alt={this.props.iconAlt}
/>
] : []}
<div>
{this.props.children}
</div>
</div>
<span className="social-message-date">
<FormattedRelative value={new Date(this.props.datetime)} />
......
......@@ -8,8 +8,12 @@
list-style-type: none;
}
.social-message.mod-unread {
background-color: $ui-gray;
.social-message-icon {
opacity: .25;
}
.social-message.mod-unread .social-message-icon {
opacity: 1;
}
.flex-row.mod-social-message {
......@@ -18,7 +22,15 @@
}
.social-message-content {
max-width: 60%;
display: flex;
max-width: 38.75rem;
text-align: left;
align-items: flex-start;
}
.social-message-icon {
margin: .2rem 1rem 0 0;
min-width: 1.25rem;
}
a.social-messages-profile-link {
......
......@@ -51,9 +51,8 @@ module.exports.messagesReducer = function (state, action) {
messages: {socialOffset: action.offset}
}, state);
case 'SET_SCRATCHER_INVITE':
return defaultsDeep({
messages: {invite: action.invite}
}, state);
state.messages.invite = action.invite;
return state;
case 'ADMIN_STATUS':
return defaultsDeep({status: {admin: action.status}}, state);
case 'MESSAGE_STATUS':
......@@ -112,6 +111,10 @@ module.exports.setStatus = function (type, status){
};
};
/**
* Sends a request to mark one's unread messages count as cleared.
* @return {null} returns nothing
*/
module.exports.clearMessageCount = function () {
return function (dispatch) {
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
......@@ -126,7 +129,7 @@ module.exports.clearMessageCount = function () {
dispatch(module.exports.setMessagesError(err));
return;
}
if (!body.success) {
if (typeof body !== 'undefined' && !body.success) {
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
dispatch(module.exports.setMessagesError('messages not cleared'));
return;
......@@ -136,6 +139,14 @@ module.exports.clearMessageCount = function () {
};
};
/**
* Marks an admin message as read, dismissing it from the page
* @param {string} messageType type of message to delete (invite or notification)
* @param {number} messageId id of the message to delete
* @param {number} messageCount current number of unread notifications
* @param {object[]} adminMessages current list of admin messages retrieved
* @return {null} returns nothing
*/
module.exports.clearAdminMessage = function (messageType, messageId, messageCount, adminMessages) {
return function (dispatch) {
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
......@@ -181,6 +192,14 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
};
};
/**
* Gets a user's messages to be displayed on the /messages page
* @param {string} username username of the user for whom the messages should be gotten
* @param {string} token the user's unique token for auth
* @param {object[]} messages an array of existing messages on the page, if there are any
* @param {number} offset offset of messages to get, based on the number retrieved already
* @return {null} returns nothing
*/
module.exports.getMessages = function (username, token, messages, offset) {
return function (dispatch) {
dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.FETCHING));
......@@ -206,6 +225,12 @@ module.exports.getMessages = function (username, token, messages, offset) {
};
};
/**
* Gets the messages from the Scratch Team for a user
* @param {string} username user's username for whom to get the admin messages
* @param {string} token the user's unique token for auth
* @return {null} returns nothing
*/
module.exports.getAdminMessages = function (username, token) {
return function (dispatch) {
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHING));
......@@ -231,6 +256,12 @@ module.exports.getAdminMessages = function (username, token) {
};
};
/**
* Gets the invitation to become a Scratcher for a user, if one exists
* @param {string} username user's username for whom to get the invite
* @param {string} token the user's unique token for auth
* @return {null} returns nothing
*/
module.exports.getScratcherInvite = function (username, token) {
return function (dispatch) {
api({
......
var combineReducers = require('redux').combineReducers;
var defaults = require('lodash.defaults');
var messageCountReducer = require('./message-count.js').messageCountReducer;
var permissionsReducer = require('./permissions.js').permissionsReducer;
......@@ -16,7 +17,7 @@ var sessionReducer = require('./session.js').sessionReducer;
*/
module.exports = function (opts) {
opts = opts || {};
return combineReducers(Object.assign(opts, {
return combineReducers(defaults(opts, {
session: sessionReducer,
permissions: permissionsReducer,
messageCount: messageCountReducer
......
......@@ -97,7 +97,9 @@ module.exports.refreshSession = function () {
// get the permissions from the updated session
dispatch(permissionsActions.storePermissions(body.permissions));
dispatch(messageCountActions.getCount(body.user.username));
if (typeof body.user !== 'undefined') {
dispatch(messageCountActions.getCount(body.user.username));
}
return;
}
});
......
......@@ -144,7 +144,7 @@
{
"name": "messages",
"pattern": "^/messages/?$",
"routeAlias": "/messages",
"routeAlias": "/messages(?!/ajax)",
"view": "messages/container",
"title": "Messages"
},
......
......@@ -44,6 +44,12 @@ var Messages = React.createClass({
this.props.dispatch(
messageActions.getScratcherInvite(this.props.user.username, this.props.user.token)
);
} else {
// user is logged out, empty messages
this.props.dispatch(messageActions.setMessages([]));
this.props.dispatch(messageActions.setAdminMessages([]));
this.props.dispatch(messageActions.setScratcherInvite({}));
this.props.dispatch(messageActions.setMessagesOffset(0));
}
}
},
......
......@@ -16,7 +16,7 @@
"messages.messageTitle": "Messages",
"messages.profileComment": "{profileLink} commented on {commentLink}",
"messages.commentReply": "{profileLink} replied to your comment on {commentLink}",
"messages.profileOther": "{username}'s reply",
"messages.profileOther": "{username}'s profile",
"messages.profileSelf": "your profile",
"messages.projectComment": "{profileLink} commented on your project {commentLink}",
"messages.remixText": "{profileLink} remixed your project {remixedProjectLink} as {projectLink}",
......
......@@ -24,6 +24,8 @@ var BecomeManagerMessage = React.createClass({
<SocialMessage
className={classes}
datetime={this.props.datetimePromoted}
iconSrc="/svgs/messages/owner-invite.svg"
iconAlt="become owner notification image"
>
<FormattedMessage
id='messages.becomeManagerText'
......
var classNames = require('classnames');
var connect = require('react-redux').connect;
var FormattedMessage = require('react-intl').FormattedMessage;
var injectIntl = require('react-intl').injectIntl;
var React = require('react');
......@@ -50,21 +51,21 @@ var CommentMessage = injectIntl(React.createClass({
/>;
}
} else if (objectType === 1) {
var profileLink = '/users/' + this.props.objectId + '/#comments-' + this.props.commentId;
var profileLink = '/users/' + this.props.objectTitle + '/#comments-' + this.props.commentId;
var linkText = '';
if (typeof commentee !== 'undefined' && commentee === this.props.user.username) {
// is a profile comment, and is a reply
if (this.props.objectTitle === this.props.user.username) {
linkText = this.props.intl.formatMessage({
id: 'messages.profileSelf'
});
linkText = <FormattedMessage
id='messages.profileSelf'
/>;
} else {
linkText = this.props.intl.formatMessage({
id: 'messages.profileOther',
values: {
username: this.props.objectId
}
});
linkText = <FormattedMessage
id='messages.profileOther'
values={{
username: this.props.objectTitle
}}
/>;
}
return <FormattedMessage
id='messages.commentReply'
......@@ -141,6 +142,8 @@ var CommentMessage = injectIntl(React.createClass({
<SocialMessage
className={classes}
datetime={this.props.commentDateTime}
iconSrc="/svgs/messages/comment.svg"
iconAlt="comment notification image"
>
<p className="comment-message-info">{messageText}</p>
<FlexRow className="mod-comment-message">
......@@ -158,4 +161,11 @@ var CommentMessage = injectIntl(React.createClass({
}
}));
module.exports = CommentMessage;
var mapStateToProps = function (state) {
return {
user: state.session.session.user
};
};
var ConnectedCommentMessage = connect(mapStateToProps)(CommentMessage);
module.exports = ConnectedCommentMessage;
......@@ -15,6 +15,7 @@ var CuratorInviteMessage = injectIntl(React.createClass({
},
render: function () {
var studioLink = '/studios/' + this.props.studioId + '/';
var tabLink = '/studios/' + this.props.studioId + '/curators/';
var actorLink = '/users/' + this.props.actorUsername + '/';
var tabText = this.props.intl.formatMessage({id: 'messages.curatorTabText'});
......@@ -26,6 +27,8 @@ var CuratorInviteMessage = injectIntl(React.createClass({
<SocialMessage
className={classes}
datetime={this.props.datetimePromoted}
iconSrc="/svgs/messages/curator-invite.svg"
iconAlt="curator invite notification image"
>
<FormattedMessage
id='messages.curatorInviteText'
......@@ -37,7 +40,7 @@ var CuratorInviteMessage = injectIntl(React.createClass({
{this.props.actorUsername}
</a>,
studioLink: <a href={studioLink}>{this.props.studioTitle}</a>,
tabLink: <a href={studioLink}>{tabText}</a>
tabLink: <a href={tabLink}>{tabText}</a>
}}
/>
</SocialMessage>
......
......@@ -24,6 +24,8 @@ var FavoriteProjectMessage = React.createClass({
<SocialMessage
className={classes}
datetime={this.props.favoriteDateTime}
iconSrc="/svgs/messages/favorite.svg"
iconAlt="favorite notification image"
>
<FormattedMessage
id='messages.favoriteText'
......
......@@ -21,6 +21,8 @@ var FollowUserMessage = React.createClass({
<SocialMessage
className={classes}
datetime={this.props.followDateTime}
iconSrc="/svgs/messages/follow.svg"
iconAlt="follow notification image"
>
<FormattedMessage
id='messages.followText'
......
......@@ -23,6 +23,8 @@ var ForumPostMessage = React.createClass({
<SocialMessage
className={classes}
datetime={this.props.datetimeCreated}
iconSrc="/svgs/messages/forum-activity.svg"
iconAlt="forum activity notification image"
>
<FormattedMessage
id='messages.forumPostText'
......
......@@ -24,6 +24,8 @@ var LoveProjectMessage = React.createClass({
<SocialMessage
className={classes}
datetime={this.props.loveDateTime}
iconSrc="/svgs/messages/love.svg"
iconAlt="love notification image"
>
<FormattedMessage
id='messages.loveText'
......
......@@ -27,6 +27,8 @@ var RemixProjectMessage = React.createClass({
<SocialMessage
className={classes}
datetime={this.props.remixDate}
iconSrc="/svgs/messages/remix.svg"
iconAlt="remix notification image"
>
<FormattedMessage
id='messages.remixText'
......
......@@ -22,6 +22,8 @@ var StudioActivityMessage = React.createClass({
<SocialMessage
className={classes}
datetime={this.props.datetimeCreated}
iconSrc="/svgs/messages/studio-activity.svg"
iconAlt="studio activity notification image"
>
<FormattedMessage
id='messages.studioActivityText'
......
......@@ -97,10 +97,12 @@
.comment-message-info {
margin-top: 0;
line-height: 1.5rem;
}
.comment-text {
background-color: $ui-white;
max-width: 30.25rem;
}
.flex-row.mod-comment-message {
......@@ -108,6 +110,10 @@
align-items: flex-start;
}
.comment-message-info {
text-align: left;
}
.comment-message-info-img {
width: 40px;
height: 40px;
......@@ -118,6 +124,10 @@
margin: 1rem auto;
}
.emoji-text.mod-comment {
word-wrap: break-word;
}
@media only screen and (max-width: $mobile - 1) {
.flex-row.admin-message-header,
.flex-row.mod-comment-message {
......@@ -125,7 +135,7 @@
}
.comment-text {
max-width: 60%;
max-width: 9.75rem;
}
}
......@@ -134,4 +144,14 @@
.flex-row.mod-comment-message {
flex-direction: row;
}
.comment-text {
max-width: 19.5rem;
}
}
@media only screen and (min-width: $tablet) and (max-width: $desktop - 1) {
.comment-text {
max-width: 23.75rem;
}
}
......@@ -84,7 +84,7 @@ var SocialMessagesList = React.createClass({
commentText={message.comment_fragment}
commentDateTime={message.datetime_created}
objectTitle={message.comment_obj_title}
commentee={message.commentee}
commentee={message.commentee_username}
/>;
case 'curatorinvite':
return <CuratorInviteMessage
......@@ -163,6 +163,14 @@ var SocialMessagesList = React.createClass({
}
return null;
},
renderMessageCounter: function (numNewMessages) {
if (numNewMessages > 0) {
return <div className="messages-header-unread">
<FormattedNumber value={numNewMessages} />
</div>;
}
return null;
},
render: function () {
if (this.props.loadStatus === messageStatuses.MESSAGES_ERROR) {
return (
......@@ -183,9 +191,7 @@ var SocialMessagesList = React.createClass({
<div className="messages-social-title" key="messages-social-title">
<h4 className="messages-header">
<FormattedMessage id='messages.messageTitle' />
<div className="messages-header-unread">
<FormattedNumber value={this.props.numNewMessages} />
</div>
{this.renderMessageCounter(this.props.numNewMessages)}
</h4>
</div>,
<ul className="messages-social-list" key="messages-social-list">
......@@ -225,6 +231,9 @@ var MessagesPresentation = injectIntl(React.createClass({
adminMessageLength = adminMessageLength + 1;
}
var numNewSocialMessages = this.props.numNewMessages - adminMessageLength;
if (numNewSocialMessages < 0) {
numNewSocialMessages = 0;
}
return (
<div className="messages">
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>comment</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="comment" fill="#4C97FF">
<path d="M15,7.21237458 C15,10.3651059 11.87068,12.9169454 8,12.9169454 C7.50055741,12.9169454 7.00891862,12.8779264 6.54069119,12.7920847 L3.83830017,14.2549007 C3.34822888,14.5201785 3.02364821,14.2935995 3.11351579,13.7476875 L3.47380156,11.5590858 C1.95986622,10.5211817 1,8.96042363 1,7.21237458 C1,4.05964326 4.13712375,1.5 8,1.5 C11.87068,1.5 15,4.05964326 15,7.21237458 Z" id="comment-icon"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>studio invite - curate</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="studio-invite---curate" stroke="#0FBD8C" stroke-width="0.5" fill="#0FBD8C">
<path d="M4.79352402,7.91196409 C4.33236847,7.83924198 3.91176749,7.63827758 3.57628648,7.34408654 C3.04395515,6.8971911 2.70878357,6.22027595 2.70878357,5.47106889 C2.70878357,4.10409461 3.81287818,3 5.17985246,3 C6.54025475,3 7.64434936,4.10409461 7.64434936,5.47106889 C7.64434936,6.22684794 7.31574978,6.8971911 6.77684646,7.34408654 C7.45371132,7.66612766 8.03860896,8.17873799 8.4789266,8.82278468 C8.97839797,9.55884775 9.29385357,10.4723546 9.33985751,11.4647254 C9.3530015,11.5567332 9.3530015,11.6487411 9.3530015,11.740749 C9.3530015,14.3761177 1,14.3761177 1,11.740749 C1,9.7625795 2.05809067,8.06700563 3.57622076,7.34408654 C3.91171688,7.63829082 4.33234063,7.83926008 4.7935206,7.91197391 Z M5.1798196,7.94213779 C5.78443046,7.94212969 6.34960901,7.71868328 6.77678074,7.34408654 Z M13.2978164,6.8129558 C13.0257815,7.03908523 12.6711936,7.17255025 12.2923394,7.17255025 C11.8914479,7.17255025 11.5234164,7.02796643 11.2473927,6.78480274 C10.6953454,7.04768241 10.235306,7.50772183 9.93956636,8.09262909 C10.4390377,8.82869217 10.7544933,9.74219902 10.8004973,10.7345698 C12.3974913,11.1814652 15,10.8200057 15,9.63704715 C15,8.34893677 14.3099409,7.25141415 13.3307141,6.78480274 C13.3198884,6.79433966 13.3089212,6.80372494 13.2978164,6.8129558 C13.3089156,6.80357933 13.3199522,6.79413636 13.3308455,6.78453986 C13.6725891,6.48880023 13.8894648,6.05504878 13.8894648,5.56872139 C13.8894648,4.6815025 13.1731177,3.9651554 12.2924708,3.9651554 C11.405252,3.9651554 10.6889049,4.6815025 10.6889049,5.56872139 C10.6889049,6.05504878 10.9057806,6.48880023 11.2475242,6.78453986 C11.5235478,7.02770355 11.8915793,7.17228737 12.2924708,7.17228737 C12.6712285,7.17228737 13.0257321,7.03889042 13.2977397,6.81286593 Z" id="studio-icon"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>fav it</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="fav-it" fill="#FFBF00">
<path d="M7.79682903,12.7180764 L4.14747875,14.6359379 C3.8273196,14.8052975 3.45225394,14.5330849 3.51334711,14.1758058 L4.21011859,10.11195 C4.23486519,9.97043037 4.18769198,9.82581742 4.08483892,9.72528435 L1.13226012,6.84771868 C0.873194139,6.59483936 1.01626042,6.15481386 1.3743128,6.10222733 L5.45440861,5.50985558 C5.59670156,5.48897563 5.71966123,5.40004254 5.7830744,5.27089621 L7.6081362,1.57359939 C7.76821578,1.24880026 8.23144121,1.24880026 8.39229411,1.57359939 L10.2165826,5.27089621 C10.2799958,5.40004254 10.4029554,5.48897563 10.5452484,5.50985558 L14.6253442,6.10222733 C14.9833966,6.15481386 15.1272362,6.59483936 14.8673969,6.84771868 L11.9155914,9.72528435 C11.811965,9.82581742 11.7655651,9.97043037 11.7895384,10.11195 L12.4863099,14.1758058 C12.5481764,14.5330849 12.1731107,14.8052975 11.8529516,14.6359379 L8.20360128,12.7180764 C8.07600162,12.6507966 7.92442869,12.6507966 7.79682903,12.7180764 Z" id="fav-icon"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>follow</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="follow" stroke="#4C97FF" stroke-width="0.5" fill="#4C97FF">
<path d="M6.38456232,6.86401962 C4.68473095,7.67346313 3.5,9.57197608 3.5,11.7869079 C3.5,14.7376974 12.8527518,14.7376974 12.8527518,11.7869079 C12.8527518,11.6838878 12.8527518,11.5808677 12.8380346,11.4778476 C12.7865246,10.3667024 12.4333129,9.34386019 11.874061,8.51969952 C11.3810428,7.79856846 10.7261402,7.22460506 9.96821949,6.8640339 C10.5716664,6.36363636 10.9395953,5.61306147 10.9395953,4.76682508 C10.9395953,3.23624099 9.70335428,2 8.18012878,2 C6.64954469,2 5.41330369,3.23624099 5.41330369,4.76682508 C5.41330369,5.60567961 5.78857031,6.36359429 6.38458627,6.86397796 Z" id="follow-icon"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>forum activitiy</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="forum-activitiy" stroke="#CF63CF" stroke-width="0.5" fill="#CF63CF">
<path d="M2,9.99895656 C2,9.44724809 2.45576096,9 3.00247329,9 L12.9975267,9 C13.5511774,9 14,9.44266033 14,9.99895656 L14,11.0010434 C14,11.5527519 13.5488094,12 12.9909224,12 L6.03259277,12 L4.71090232,13.3004971 C4.31828181,13.6868219 4,13.5561352 4,13 L4,12 L3,12 C2.44771525,12 2,11.5573397 2,11.0010434 L2,9.99895656 Z" id="Rectangle-2"></path>
<path d="M2,3.99895656 C2,3.44724809 2.45576096,3 3.00247329,3 L12.9975267,3 C13.5511774,3 14,3.44266033 14,3.99895656 L14,5.00104344 C14,5.55275191 13.5488094,6 12.9909224,6 L6.03259277,6 L4.71090232,7.30049706 C4.31828181,7.68682186 4,7.55613518 4,7 L4,6 L3,6 C2.44771525,6 2,5.55733967 2,5.00104344 L2,3.99895656 Z" id="Rectangle-2-Copy" transform="translate(8.000000, 5.500000) scale(-1, 1) translate(-8.000000, -5.500000) "></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>love it</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="love-it" stroke="#FF6680" stroke-width="0.5" fill="#FF6680">
<path d="M15,6.08333333 C15,6.235 14.9883333,6.38666667 14.9766667,6.53833333 C14.5683333,11.1816667 8,14.8333333 8,14.8333333 C8,14.8333333 1.43166667,11.1816667 1.02333333,6.53833333 C1.01166667,6.38666667 1,6.235 1,6.08333333 C1,3.83166667 2.83166667,2 5.08333333,2 C6.22666667,2 7.265,2.46666667 8,3.23666667 C8.735,2.46666667 9.77333333,2 10.9166667,2 C13.1683333,2 15,3.83166667 15,6.08333333" id="love-icon"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>studio invite - owner</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="studio-invite---owner" stroke="#0FBD8C" stroke-width="0.5" fill="#0FBD8C">
<path d="M4.79352402,7.91196409 C4.33236847,7.83924198 3.91176749,7.63827758 3.57628648,7.34408654 C3.04395515,6.8971911 2.70878357,6.22027595 2.70878357,5.47106889 C2.70878357,4.10409461 3.81287818,3 5.17985246,3 C6.54025475,3 7.64434936,4.10409461 7.64434936,5.47106889 C7.64434936,6.22684794 7.31574978,6.8971911 6.77684646,7.34408654 C7.45371132,7.66612766 8.03860896,8.17873799 8.4789266,8.82278468 C8.97839797,9.55884775 9.29385357,10.4723546 9.33985751,11.4647254 C9.3530015,11.5567332 9.3530015,11.6487411 9.3530015,11.740749 C9.3530015,14.3761177 1,14.3761177 1,11.740749 C1,9.7625795 2.05809067,8.06700563 3.57622076,7.34408654 C3.91171688,7.63829082 4.33234063,7.83926008 4.7935206,7.91197391 Z M5.1798196,7.94213779 C5.78443046,7.94212969 6.34960901,7.71868328 6.77678074,7.34408654 Z M13.2978164,6.8129558 C13.0257815,7.03908523 12.6711936,7.17255025 12.2923394,7.17255025 C11.8914479,7.17255025 11.5234164,7.02796643 11.2473927,6.78480274 C10.6953454,7.04768241 10.235306,7.50772183 9.93956636,8.09262909 C10.4390377,8.82869217 10.7544933,9.74219902 10.8004973,10.7345698 C12.3974913,11.1814652 15,10.8200057 15,9.63704715 C15,8.34893677 14.3099409,7.25141415 13.3307141,6.78480274 C13.3198884,6.79433966 13.3089212,6.80372494 13.2978164,6.8129558 C13.3089156,6.80357933 13.3199522,6.79413636 13.3308455,6.78453986 C13.6725891,6.48880023 13.8894648,6.05504878 13.8894648,5.56872139 C13.8894648,4.6815025 13.1731177,3.9651554 12.2924708,3.9651554 C11.405252,3.9651554 10.6889049,4.6815025 10.6889049,5.56872139 C10.6889049,6.05504878 10.9057806,6.48880023 11.2475242,6.78453986 C11.5235478,7.02770355 11.8915793,7.17228737 12.2924708,7.17228737 C12.6712285,7.17228737 13.0257321,7.03889042 13.2977397,6.81286593 Z" id="studio-icon"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>remix</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="remix" stroke="#9966FF" stroke-width="1.7">
<path d="M7.62751256,2.12174044 C7.62751256,2.12174044 7.47299968,2.15558612 7.18236832,2.21886282 C6.89541583,2.28655418 6.47381641,2.40501405 5.97863943,2.63825492 C4.99049279,3.07236252 3.67051136,4.04579364 2.88691035,5.66891462 C2.08932959,7.2589257 2.0076585,9.52069991 3.09292751,11.3255575 C4.12448491,13.1443948 6.2810432,14.2988267 8.32355628,14.0994315 C10.3800491,13.950069 12.0841054,12.5189855 12.6705185,10.8921856 C13.3054929,9.25949957 12.9008163,7.58045964 12.1613618,6.5805406 C11.4138138,5.54677587 10.4477404,5.08323724 9.79290016,4.92357394 C9.79290016,4.92357394 8.30589766,4.44605557 6.93661927,5.13842042 C6.36345007,5.41654359 5.61516628,6.0404813 5.21269703,6.99478226 C4.80287002,7.92112375 4.84480923,9.21535303 5.49670641,10.1453734 C6.12579455,11.1018817 7.30818594,11.6073595 8.32355628,11.4219441 C9.35290635,11.2586019 10.08206,10.4757366 10.2674754,9.71200156 C10.4690779,8.95488847 10.1865401,8.24339346 9.83483937,7.90640823 C9.46989467,7.54072776 9.07551894,7.42741832 8.83492032,7.42447521 C8.5913786,7.41417436 8.46629674,7.45758512 8.46629674,7.44875581 C8.46629674,7.44875581 7.26477518,7.79677767 7.63413454,8.7790381" id="remix-icon" transform="translate(7.634254, 8.121740) scale(-1, -1) rotate(30.000000) translate(-7.634254, -8.121740) "></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>studio activity</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="studio-activity" fill="#0FBD8C">
<path d="M2,6.99703014 C2,6.4463856 2.44266033,6 2.99895656,6 L5,6 L5,9.49983513 C5,10.3283533 5.67432225,11 6.50016487,11 L10,11 L10,13.0010434 C10,13.5527519 9.54696369,14 9.00296986,14 L2.99703014,14 C2.4463856,14 2,13.5469637 2,13.0029699 L2,6.99703014 Z M6,2.99703014 C6,2.4463856 6.45303631,2 6.99703014,2 L13.0029699,2 C13.5536144,2 14,2.45303631 14,2.99703014 L14,9.00296986 C14,9.5536144 13.5469637,10 13.0029699,10 L6.99703014,10 C6.4463856,10 6,9.54696369 6,9.00296986 L6,2.99703014 Z" id="Combined-Shape" transform="translate(8.000000, 8.000000) rotate(-180.000000) translate(-8.000000, -8.000000) "></path>
</g>
</g>
</svg>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment