initial commit

This commit is contained in:
unknown 2021-07-28 18:45:52 +05:30
commit 026f835a87
176 changed files with 10613 additions and 0 deletions

View file

@ -0,0 +1,149 @@
import React from 'react';
import PropTypes from 'prop-types';
import './Message.scss';
import Linkify from 'linkifyjs/react';
import ReactMarkdown from 'react-markdown';
import gfm from 'remark-gfm';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { coy } from 'react-syntax-highlighter/dist/esm/styles/prism';
import Text from '../../atoms/text/Text';
import RawIcon from '../../atoms/system-icons/RawIcon';
import Avatar from '../../atoms/avatar/Avatar';
import ReplyArrowIC from '../../../../public/res/ic/outlined/reply-arrow.svg';
const components = {
code({
// eslint-disable-next-line react/prop-types
inline, className, children,
}) {
const match = /language-(\w+)/.exec(className || '');
return !inline && match ? (
<SyntaxHighlighter
style={coy}
language={match[1]}
PreTag="div"
showLineNumbers
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<code className={className}>{String(children)}</code>
);
},
};
function linkifyContent(content) {
return <Linkify options={{ target: { url: '_blank' } }}>{content}</Linkify>;
}
function genMarkdown(content) {
return <ReactMarkdown remarkPlugins={[gfm]} components={components} linkTarget="_blank">{content}</ReactMarkdown>;
}
function PlaceholderMessage() {
return (
<div className="ph-msg">
<div className="ph-msg__avatar-container">
<div className="ph-msg__avatar" />
</div>
<div className="ph-msg__main-container">
<div className="ph-msg__header" />
<div className="ph-msg__content">
<div />
<div />
<div />
<div />
</div>
</div>
</div>
);
}
function Message({
color, avatarSrc, name, content,
time, markdown, contentOnly, reply,
edited, reactions,
}) {
const msgClass = contentOnly ? 'message--content-only' : 'message--full';
return (
<div className={`message ${msgClass}`}>
<div className="message__avatar-container">
{!contentOnly && <Avatar imageSrc={avatarSrc} text={name.slice(0, 1)} bgColor={color} size="small" />}
</div>
<div className="message__main-container">
{ !contentOnly && (
<div className="message__header">
<div style={{ color }} className="message__profile">
<Text variant="b1">{name}</Text>
</div>
<div className="message__time">
<Text variant="b3">{time}</Text>
</div>
</div>
)}
<div className="message__content">
{ reply !== null && (
<div className="message__reply-content">
<Text variant="b2">
<RawIcon color={reply.color} size="extra-small" src={ReplyArrowIC} />
<span style={{ color: reply.color }}>{reply.to}</span>
<>{` ${reply.content}`}</>
</Text>
</div>
)}
<div className="text text-b1">
{ markdown ? genMarkdown(content) : linkifyContent(content) }
</div>
{ edited && <Text className="message__edited" variant="b3">(edited)</Text>}
{ reactions && (
<div className="message__reactions text text-b3 noselect">
{
reactions.map((reaction) => (
<button key={reaction.id} onClick={() => alert('Sending reactions is yet to be implemented.')} type="button" className={`msg__reaction${reaction.active ? ' msg__reaction--active' : ''}`}>
{`${reaction.key} ${reaction.count}`}
</button>
))
}
</div>
)}
</div>
</div>
</div>
);
}
Message.defaultProps = {
color: 'var(--tc-surface-high)',
avatarSrc: null,
markdown: false,
contentOnly: false,
reply: null,
edited: false,
reactions: null,
};
Message.propTypes = {
color: PropTypes.string,
avatarSrc: PropTypes.string,
name: PropTypes.string.isRequired,
content: PropTypes.node.isRequired,
time: PropTypes.string.isRequired,
markdown: PropTypes.bool,
contentOnly: PropTypes.bool,
reply: PropTypes.shape({
color: PropTypes.string.isRequired,
to: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
}),
edited: PropTypes.bool,
reactions: PropTypes.arrayOf(PropTypes.exact({
id: PropTypes.string,
key: PropTypes.string,
count: PropTypes.number,
active: PropTypes.bool,
})),
};
export { Message as default, PlaceholderMessage };

View file

@ -0,0 +1,293 @@
@use '../../atoms/scroll/scrollbar';
.message,
.ph-msg {
padding: var(--sp-ultra-tight) var(--sp-normal);
padding-right: var(--sp-extra-tight);
display: flex;
&:hover {
background-color: var(--bg-surface-hover);
}
[dir=rtl] & {
padding: {
left: var(--sp-extra-tight);
right: var(--sp-normal);
}
}
&__avatar-container {
padding-top: 6px;
}
&__avatar-container,
&__profile {
margin-right: var(--sp-tight);
[dir=rtl] & {
margin: {
left: var(--sp-tight);
right: 0;
}
}
}
&__main-container {
flex: 1;
min-width: 0;
}
}
.message {
&--full + &--full,
&--content-only + &--full,
& + .timeline-change,
.timeline-change + & {
margin-top: var(--sp-normal);
}
&__avatar-container {
width: var(--av-small);
}
&__reply-content {
.text {
color: var(--tc-surface-low);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ic-raw {
width: 16px;
height: 14px;
}
}
&__edited {
color: var(--tc-surface-low);
}
&__reactions {
margin-top: var(--sp-ultra-tight);
}
}
.ph-msg {
&__avatar {
width: var(--av-small);
height: var(--av-small);
background-color: var(--bg-surface-hover);
border-radius: var(--bo-radius);
}
&__header,
&__content > div {
margin: var(--sp-ultra-tight) 0;
margin-right: var(--sp-extra-tight);
height: var(--fs-b1);
width: 100%;
max-width: 100px;
background-color: var(--bg-surface-hover);
border-radius: calc(var(--bo-radius) / 2);
[dir=rtl] & {
margin: {
right: 0;
left: var(--sp-extra-tight);
}
}
}
&__content {
display: flex;
flex-wrap: wrap;
}
&__content > div:nth-child(1n) {
max-width: 10%;
}
&__content > div:nth-child(2n) {
max-width: 50%;
}
}
.message__header {
display: flex;
align-items: baseline;
& .message__profile {
flex: 1;
min-width: 0;
color: var(--tc-surface-high);
& > .text {
color: inherit;
font-weight: 500;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
& .message__time {
& > .text {
color: var(--tc-surface-low);
}
}
}
.message__content {
max-width: 640px;
word-break: break-word;
& > .text > * {
white-space: pre-wrap;
}
& a {
word-break: break-all;
}
}
.msg__reaction {
--reaction-height: 24px;
--reaction-padding: 6px;
--reaction-radius: calc(var(--bo-radius) / 2);
display: inline-flex;
align-items: center;
color: var(--tc-surface-normal);
border: 1px solid var(--bg-surface-border);
padding: 0 var(--reaction-padding);
border-radius: var(--reaction-radius);
cursor: pointer;
height: var(--reaction-height);
margin-right: var(--sp-extra-tight);
[dir=rtl] & {
margin: {
right: 0;
left: var(--sp-extra-tight);
}
}
@media (hover: hover) {
&:hover {
background-color: var(--bg-surface-hover);
}
}
&:active {
background-color: var(--bg-surface-active)
}
&--active {
background-color: var(--bg-caution-active);
@media (hover: hover) {
&:hover {
background-color: var(--bg-caution-hover);
}
}
&:active {
background-color: var(--bg-caution-active)
}
}
}
// markdown formating
.message {
& h1,
& h2 {
color: var(--tc-surface-high);
margin: var(--sp-extra-loose) 0 var(--sp-normal);
line-height: var(--lh-h1);
}
& h3,
& h4 {
color: var(--tc-surface-high);
margin: var(--sp-loose) 0 var(--sp-tight);
line-height: var(--lh-h2);
}
& h5,
& h6 {
color: var(--tc-surface-high);
margin: var(--sp-normal) 0 var(--sp-extra-tight);
line-height: var(--lh-s1);
}
& hr {
border-color: var(--bg-surface-border);
}
.text img {
margin: var(--sp-ultra-tight) 0;
max-width: 296px;
border-radius: calc(var(--bo-radius) / 2);
}
& p,
& pre,
& blockquote {
margin: 0;
padding: 0;
}
& pre,
& blockquote {
margin: var(--sp-ultra-tight) 0;
padding: var(--sp-extra-tight);
background-color: var(--bg-surface-hover) !important;
border-radius: calc(var(--bo-radius) / 2);
}
& pre {
div {
background: none !important;
margin: 0 !important;
}
span {
background: none !important;
}
.linenumber {
min-width: 2.25em !important;
}
}
& code {
padding: 0 !important;
color: var(--tc-code) !important;
white-space: pre-wrap;
@include scrollbar.scroll;
@include scrollbar.scroll__h;
@include scrollbar.scroll--auto-hide;
}
& pre code {
color: var(--tc-surface-normal) !important;
}
& blockquote {
padding-left: var(--sp-extra-tight);
border-left: 4px solid var(--bg-surface-active);
white-space: initial !important;
& > * {
white-space: pre-wrap;
}
[dir=rtl] & {
padding: {
left: 0;
right: var(--sp-extra-tight);
}
border: {
left: none;
right: 4px solid var(--bg-surface-active);
}
}
}
& ul,
& ol {
margin: var(--sp-ultra-tight) 0;
padding-left: 24px;
white-space: initial !important;
& > * {
white-space: pre-wrap;
}
[dir=rtl] & {
padding: {
left: 0;
right: 24px;
}
}
}
}

View file

@ -0,0 +1,79 @@
import React from 'react';
import PropTypes from 'prop-types';
import './TimelineChange.scss';
// import Linkify from 'linkifyjs/react';
import Text from '../../atoms/text/Text';
import RawIcon from '../../atoms/system-icons/RawIcon';
import JoinArraowIC from '../../../../public/res/ic/outlined/join-arrow.svg';
import LeaveArraowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
import InviteArraowIC from '../../../../public/res/ic/outlined/invite-arrow.svg';
import InviteCancelArraowIC from '../../../../public/res/ic/outlined/invite-cancel-arrow.svg';
import UserIC from '../../../../public/res/ic/outlined/user.svg';
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
function TimelineChange({ variant, content, time }) {
let iconSrc;
switch (variant) {
case 'join':
iconSrc = JoinArraowIC;
break;
case 'leave':
iconSrc = LeaveArraowIC;
break;
case 'invite':
iconSrc = InviteArraowIC;
break;
case 'invite-cancel':
iconSrc = InviteCancelArraowIC;
break;
case 'avatar':
iconSrc = UserIC;
break;
case 'follow':
iconSrc = TickMarkIC;
break;
default:
iconSrc = JoinArraowIC;
break;
}
return (
<div className="timeline-change">
<div className="timeline-change__avatar-container">
<RawIcon src={iconSrc} size="extra-small" />
</div>
<div className="timeline-change__content">
<Text variant="b2">
{content}
{/* <Linkify options={{ target: { url: '_blank' } }}>{content}</Linkify> */}
</Text>
</div>
<div className="timeline-change__time">
<Text variant="b3">{time}</Text>
</div>
</div>
);
}
TimelineChange.defaultProps = {
variant: 'other',
};
TimelineChange.propTypes = {
variant: PropTypes.oneOf([
'join', 'leave', 'invite',
'invite-cancel', 'avatar', 'other',
'follow',
]),
content: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
]).isRequired,
time: PropTypes.string.isRequired,
};
export default TimelineChange;

View file

@ -0,0 +1,39 @@
.timeline-change {
padding: var(--sp-ultra-tight) var(--sp-normal);
padding-right: var(--sp-extra-tight);
display: flex;
align-items: center;
&:hover {
background-color: var(--bg-surface-hover);
}
[dir=rtl] & {
padding: {
left: var(--sp-extra-tight);
right: var(--sp-normal);
}
}
&__avatar-container {
width: var(--av-small);
display: inline-flex;
justify-content: center;
align-items: center;
opacity: 0.38;
.ic-raw {
background-color: var(--tc-surface-low);
}
}
& .text {
color: var(--tc-surface-low);
}
&__content {
flex: 1;
min-width: 0;
margin: 0 var(--sp-tight);
}
}