Skip to content

Commit

Permalink
[New Feature] Stickers (#131)
Browse files Browse the repository at this point in the history
* Refactor filePrice

* Refactor Wallet Tip Components

* Add backend sticker support for comments

* Add stickers

* Refactor commentCreate

* Add Sticker Selector and sticker comment creation

* Add stickers display to comments and hyperchats

* Fix wrong checks for total Super Chats
  • Loading branch information
rafael-xmr committed Oct 28, 2021
1 parent a77e59c commit 5f1f702
Show file tree
Hide file tree
Showing 26 changed files with 1,404 additions and 1,357 deletions.
7 changes: 7 additions & 0 deletions ui/component/comment/view.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import CommentCreate from 'component/commentCreate';
import CommentMenuList from 'component/commentMenuList';
import UriIndicator from 'component/uriIndicator';
import CreditAmount from 'component/common/credit-amount';
import OptimizedImage from 'component/optimizedImage';
import { parseSticker } from 'util/comments';

const AUTO_EXPAND_ALL_REPLIES = false;

Expand Down Expand Up @@ -138,6 +140,7 @@ function Comment(props: Props) {
const totalLikesAndDislikes = likesCount + dislikesCount;
const slimedToDeath = totalLikesAndDislikes >= 5 && dislikesCount / totalLikesAndDislikes > 0.8;
const commentByOwnerOfContent = claim && claim.signing_channel && claim.signing_channel.permanent_url === authorUri;
const stickerFromMessage = parseSticker(message);

let channelOwnerOfContent;
try {
Expand Down Expand Up @@ -338,6 +341,10 @@ function Comment(props: Props) {
<div onClick={() => setDisplayDeadComment(true)} className="comment__dead">
{__('This comment was slimed to death.')} <Icon icon={ICONS.SLIME_ACTIVE} />
</div>
) : stickerFromMessage ? (
<div className="sticker__comment">
<OptimizedImage src={stickerFromMessage.url} waitLoad />
</div>
) : editedMessage.length >= LENGTH_TO_COLLAPSE ? (
<Expandable>
<MarkdownPreview
Expand Down
25 changes: 14 additions & 11 deletions ui/component/commentCreate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ import {
selectFetchingMyChannels,
makeSelectTagInClaimOrChannelForUri,
} from 'redux/selectors/claims';
import { doSendTip } from 'redux/actions/wallet';
import { CommentCreate } from './view';
import { DISABLE_SUPPORT_TAG } from 'constants/tags';
import { doCommentCreate, doFetchCreatorSettings, doCommentById } from 'redux/actions/comments';
import { doSendTip, doSendCashTip } from 'redux/actions/wallet';
import { doToast } from 'redux/actions/notifications';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import { selectSettingsByChannelId } from 'redux/selectors/comments';
import { CommentCreate } from './view';
import { doToast } from 'redux/actions/notifications';
import { DISABLE_SUPPORT_TAG } from 'constants/tags';

const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
channels: selectMyChannelClaims(state),
isFetchingChannels: selectFetchingMyChannels(state),
activeChannelClaim: selectActiveChannelClaim(state),
channels: selectMyChannelClaims(state),
claim: makeSelectClaimForUri(props.uri)(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
isFetchingChannels: selectFetchingMyChannels(state),
settingsByChannelId: selectSettingsByChannelId(state),
supportDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_SUPPORT_TAG)(state),
});

const perform = (dispatch, ownProps) => ({
createComment: (comment, claimId, parentId, txid, payment_intent_id, environment) =>
createComment: (comment, claimId, parentId, txid, payment_intent_id, environment, sticker) =>
dispatch(
doCommentCreate(
comment,
Expand All @@ -35,13 +35,16 @@ const perform = (dispatch, ownProps) => ({
ownProps.livestream,
txid,
payment_intent_id,
environment
environment,
sticker
)
),
sendTip: (params, callback, errorCallback) => dispatch(doSendTip(params, false, callback, errorCallback, false)),
doToast: (options) => dispatch(doToast(options)),
doFetchCreatorSettings: (channelClaimId) => dispatch(doFetchCreatorSettings(channelClaimId)),
doToast: (options) => dispatch(doToast(options)),
fetchComment: (commentId) => dispatch(doCommentById(commentId, false)),
sendCashTip: (tipParams, userParams, claimId, environment, successCallback) =>
dispatch(doSendCashTip(tipParams, false, userParams, claimId, environment, successCallback)),
sendTip: (params, callback, errorCallback) => dispatch(doSendTip(params, false, callback, errorCallback, false)),
});

export default connect(select, perform)(CommentCreate);
94 changes: 94 additions & 0 deletions ui/component/commentCreate/sticker-selector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// @flow
import 'scss/component/_sticker-selector.scss';
import { FREE_GLOBAL_STICKERS, PAID_GLOBAL_STICKERS } from 'constants/stickers';
import * as ICONS from 'constants/icons';
import Button from 'component/button';
import CreditAmount from 'component/common/credit-amount';
import OptimizedImage from 'component/optimizedImage';
import React from 'react';

const buildStickerSideLink = (section: string, icon: string) => ({ section, icon });

const STICKER_SIDE_LINKS = [
buildStickerSideLink(__('Free'), ICONS.TAG),
buildStickerSideLink(__('Tips'), ICONS.FINANCE),
// Future work may include Channel, Subscriptions, ...
];

type Props = { claimIsMine: boolean, onSelect: (any) => void };

export default function StickerSelector(props: Props) {
const { claimIsMine, onSelect } = props;

function scrollToStickerSection(section: string) {
const listBodyEl = document.querySelector('.stickerSelector__listBody');
const sectionToScroll = document.getElementById(section);

if (listBodyEl && sectionToScroll) {
// $FlowFixMe
listBodyEl.scrollTo({
top: sectionToScroll.offsetTop - sectionToScroll.getBoundingClientRect().height * 2,
behavior: 'smooth',
});
}
}

const getListRow = (rowTitle: string, rowStickers: any) => (
<div className="stickerSelector__listBody-row">
<div id={rowTitle} className="stickerSelector__listBody-rowTitle">
{rowTitle}
</div>
<div className="stickerSelector__listBody-rowItems">
{rowStickers.map((sticker) => (
<Button
key={sticker.name}
title={sticker.name}
button="alt"
className="button--file-action"
onClick={() => onSelect(sticker)}
>
<OptimizedImage src={sticker.url} waitLoad />
{sticker.price && sticker.price > 0 && (
<CreditAmount superChatLight amount={sticker.price} size={2} isFiat />
)}
</Button>
))}
</div>
</div>
);

return (
<div className="stickerSelector">
<div className="stickerSelector__header card__header--between">
<div className="stickerSelector__headerTitle card__title-section--small">{__('Stickers')}</div>
</div>

<div className="stickerSelector__list">
<div className="stickerSelector__listBody">
{getListRow(__('Free'), FREE_GLOBAL_STICKERS)}
{!claimIsMine && getListRow(__('Tips'), PAID_GLOBAL_STICKERS)}
</div>

<div className="navigation__wrapper">
<ul className="navigation-links">
{STICKER_SIDE_LINKS.map(
(linkProps) =>
((claimIsMine && linkProps.section !== 'Tips') || !claimIsMine) && (
<li key={linkProps.section}>
<Button
label={__(linkProps.section)}
title={__(linkProps.section)}
icon={linkProps.icon}
iconSize={1}
className="navigation-link"
onClick={() => scrollToStickerSection(linkProps.section)}
/>
</li>
)
)}
</ul>
</div>
</div>
</div>
);
}
Loading

0 comments on commit 5f1f702

Please sign in to comment.