Open /connect sidecar links in iframe (#5203) [deploy]
* Use iframe for connect sidecar * Modify css of lefthand sidebar * Add proper sidecar markup * Update tests
This commit is contained in:
parent
cb3a0c462c
commit
5812ecc383
16 changed files with 234 additions and 567 deletions
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
function initializePWAFunctionality() {
|
||||
if (window.matchMedia('(display-mode: standalone)').matches) {
|
||||
if (window.matchMedia('(display-mode: standalone)').matches || window.frameElement) {
|
||||
document
|
||||
.getElementById('pwa-nav-buttons')
|
||||
.classList.add('pwa-nav-buttons--showing');
|
||||
|
|
|
|||
|
|
@ -147,8 +147,8 @@ article {
|
|||
width: 81%;
|
||||
font-family: $helvetica;
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
width: 88%;
|
||||
@media screen and (max-width: 750px) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 430px) {
|
||||
|
|
@ -576,8 +576,8 @@ article {
|
|||
margin-top: -0.8em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
width: 90%;
|
||||
@media screen and (max-width: 750px) {
|
||||
width: 92%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 430px) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
.chat-page-wrapper {
|
||||
margin: 85px auto 0px;
|
||||
width: 96%;
|
||||
max-width: calc(1440px + 10vw);
|
||||
max-width: calc(1540px + 10vw);
|
||||
}
|
||||
|
||||
.live-chat {
|
||||
|
|
@ -91,8 +91,8 @@
|
|||
}
|
||||
|
||||
@media screen and (min-width: 1300px) {
|
||||
width: calc(280px + 3vw);
|
||||
min-width: calc(280px);
|
||||
width: calc(220px + 3vw);
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
&.chat__channels--placeholder {
|
||||
|
|
@ -423,8 +423,10 @@
|
|||
flex-direction: column;
|
||||
flex: 0 1 auto;
|
||||
width: 100%;
|
||||
min-width: 55%;
|
||||
|
||||
min-width: 50%;
|
||||
@media screen and (min-width: 1440px) {
|
||||
min-width: 55%;
|
||||
}
|
||||
@media screen and (max-width: 426px) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
|
@ -465,7 +467,7 @@
|
|||
}
|
||||
|
||||
.activechatchannel__messages {
|
||||
font-size: 15px;
|
||||
font-size: calc(15px + 0.05vw);
|
||||
padding: 10px 0px;
|
||||
flex-grow: 1;
|
||||
height: 300px;
|
||||
|
|
@ -505,7 +507,7 @@
|
|||
theme-container-accent-background,
|
||||
#ededed);
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.activechatchannel__incomingcall {
|
||||
|
|
@ -523,26 +525,32 @@
|
|||
.activechatchannel__activecontent {
|
||||
order: 2;
|
||||
flex: 0 1 auto;
|
||||
border: 1px solid $outline-color;
|
||||
border-left: 2px solid $outline-color;
|
||||
@include themeable(border,
|
||||
theme-border,
|
||||
1px solid $outline-color);
|
||||
padding: 13px;
|
||||
min-width: 45%;
|
||||
min-width: 50%;
|
||||
@media screen and (min-width: 1440px) {
|
||||
min-width: 45%;
|
||||
}
|
||||
-webkit-overflow-scrolling: touch;
|
||||
@include themeable(background,
|
||||
theme-container-accent-background,
|
||||
white);
|
||||
theme-background,
|
||||
$lightest-gray);
|
||||
z-index: 200;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
max-width: 96%;
|
||||
max-width: 100%;
|
||||
|
||||
@media screen and (max-width: 426px) {
|
||||
padding: 10px;
|
||||
min-width: 50%;
|
||||
width: calc(95% - 45px);
|
||||
width: calc(100% - 16px);
|
||||
position: fixed;
|
||||
height: calc(100% - 90px);
|
||||
height: calc(100% - 86px);
|
||||
margin-left: -48px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
|
|
@ -574,52 +582,40 @@
|
|||
padding: 0px;
|
||||
background: transparent;
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: -3px;
|
||||
left: 4px;
|
||||
top: 4px;
|
||||
@include themeable(background,
|
||||
theme-container-accent-background,
|
||||
white);
|
||||
z-index: 10;
|
||||
height: 29px;
|
||||
width: 29px;
|
||||
line-height: 10px;
|
||||
padding-bottom: 5px;
|
||||
border-radius: 100px;
|
||||
@include themeable(border,
|
||||
theme-border,
|
||||
1px solid $medium-gray);
|
||||
|
||||
}
|
||||
|
||||
.activechatchannel__activeArticle {
|
||||
position: absolute;
|
||||
top: 36px;
|
||||
top: 0px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-top: 1px solid $light-medium-gray !important;
|
||||
}
|
||||
|
||||
.activechatchannel__activeArticleDetails {
|
||||
.activechatchannel__activeArticle iframe {
|
||||
position: absolute;
|
||||
top: -26px;
|
||||
right: 2px;
|
||||
left: 35px;
|
||||
font-family: $monospace;
|
||||
font-size: 0.8em;
|
||||
|
||||
a {
|
||||
@include themeable(color,
|
||||
theme-container-color,
|
||||
$dark-gray);
|
||||
}
|
||||
|
||||
.activechatchannel__activeArticleDetailsPath {
|
||||
display: inline-block;
|
||||
max-width: calc(100% - 15px);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 1.1em;
|
||||
width: 1.1em;
|
||||
margin-left: 10px;
|
||||
vertical-align: 2px;
|
||||
opacity: 0.86;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -678,68 +674,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.chat .activechatchannel__activeArticle .container {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 30px;
|
||||
overflow-y: scroll;
|
||||
border-radius: 0px;
|
||||
margin-top: 0px !important;
|
||||
|
||||
pre {
|
||||
width: 97%;
|
||||
margin-left: -3%;
|
||||
padding-left: 4%;
|
||||
padding-right: 7%;
|
||||
}
|
||||
}
|
||||
|
||||
.activechatchannel__activeArticleActions {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
padding: 17px;
|
||||
@include themeable(background,
|
||||
theme-container-accent-background,
|
||||
white);
|
||||
z-index: 20;
|
||||
border-top: 1px solid $light-medium-gray;
|
||||
border-bottom: 1px solid $light-medium-gray;
|
||||
border-right: 1px solid $light-medium-gray;
|
||||
|
||||
button {
|
||||
margin-right: 15px;
|
||||
width: 54px;
|
||||
@include themeable(background,
|
||||
theme-container-background,
|
||||
#f2f3f5);
|
||||
border-radius: 100px !important;
|
||||
border-width: 0;
|
||||
padding: 5px 10px;
|
||||
|
||||
&.active {
|
||||
background-color: $green;
|
||||
|
||||
&.unicorn-reaction-button {
|
||||
background-color: $purple;
|
||||
}
|
||||
|
||||
&.readinglist-reaction-button {
|
||||
background: lighten($bold-blue, 32%);
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.activecontent__githubrepo {}
|
||||
|
||||
.activecontent__githubrepoheader {
|
||||
|
|
@ -1200,26 +1134,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.chat .container {
|
||||
box-shadow: 0px 0px 0px #ffffff !important;
|
||||
border: 0px !important;
|
||||
margin-top: 8px !important;
|
||||
|
||||
.title {
|
||||
width: 95%;
|
||||
font-size: 18px;
|
||||
|
||||
h1 {
|
||||
font-size: 28px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
font-size: 20px;
|
||||
width: 96%;
|
||||
}
|
||||
}
|
||||
|
||||
.chatchanneljumpback {
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -1267,6 +1181,64 @@
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.chatchannels__richlink {
|
||||
border: 1px solid darken($light-medium-gray, 15%);
|
||||
border-radius: 3px;
|
||||
box-shadow: 3px 3px 0px darken($light-medium-gray, 15%);
|
||||
color: $black;
|
||||
display: block;
|
||||
max-width: 670px;
|
||||
padding: 0px;
|
||||
margin-bottom: 6px;
|
||||
@include themeable(background,
|
||||
theme-container-background,
|
||||
white);
|
||||
.chatchannels__richlinkmainimage {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
background: transparent no-repeat center center;
|
||||
background-size: cover;
|
||||
z-index: 2;
|
||||
padding-top: 42%;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 15px;
|
||||
padding: 0px calc(10px + 0.2vw);
|
||||
img {
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
margin-right: 5px;
|
||||
vertical-align: -0.25em;
|
||||
&.chatchannels__richlinkprofilepic {
|
||||
border-radius: 100px;
|
||||
@include themeable(border,
|
||||
theme-border,
|
||||
1px solid $light-medium-gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
h4 {
|
||||
margin-top: -12px;
|
||||
margin-bottom: 15px;
|
||||
@include themeable(color,
|
||||
theme-secondary-color,
|
||||
$medium-gray);
|
||||
font-weight: 500;
|
||||
padding: 0px calc(10px + 0.2vw);
|
||||
img {
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
border-radius: 100px;
|
||||
margin-right: 3px;
|
||||
@include themeable(border,
|
||||
theme-border,
|
||||
1px solid $light-medium-gray);
|
||||
vertical-align: -6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message__delete__modal {
|
||||
position: fixed;
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
|
|
|
|||
|
|
@ -261,6 +261,19 @@ body.trusted-status-true .trusted-visible-block {
|
|||
display: block !important;
|
||||
}
|
||||
|
||||
body.hidden-shell {
|
||||
.top-bar, footer {
|
||||
display: none;
|
||||
}
|
||||
.container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.universal-page-content-wrapper {
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-confirm.container {
|
||||
padding: 150px 10px;
|
||||
min-height: 300px;
|
||||
|
|
|
|||
|
|
@ -4,113 +4,8 @@ exports[`<Article /> should load article 1`] = `
|
|||
<div
|
||||
class="activechatchannel__activeArticle"
|
||||
>
|
||||
<div
|
||||
class="activechatchannel__activeArticleDetails"
|
||||
>
|
||||
<a
|
||||
href="/princesscarolyn/your-approval-means-nothing-to-me-42640"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<span
|
||||
class="activechatchannel__activeArticleDetailsPath"
|
||||
>
|
||||
/princesscarolyn/your-approval-means-nothing-to-me-42640
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<section>
|
||||
<div
|
||||
class="image image-final"
|
||||
style={
|
||||
Object {
|
||||
"backgroundImage": "url(princess_carolyn_is_perfect.png",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
<h1>
|
||||
Your Approval Means Nothing to Me
|
||||
</h1>
|
||||
<h3>
|
||||
<a
|
||||
class="author"
|
||||
data-content="/users/00001"
|
||||
href="/princesscarolyn"
|
||||
>
|
||||
<img
|
||||
alt="princesscarolyn"
|
||||
class="profile-pic"
|
||||
src="/princesscarolyn.png"
|
||||
/>
|
||||
<span>
|
||||
Princess Carolyn
|
||||
</span>
|
||||
<span
|
||||
class="published-at"
|
||||
>
|
||||
| July 30, 2014
|
||||
</span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
>
|
||||
<div
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "That woman can knock a drink back like a Kennedy at a wake for another Kennedy, but I'll be damned if she doesn't get s*** done!",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="activechatchannel__activeArticleActions"
|
||||
>
|
||||
<button
|
||||
class="heart-reaction-button "
|
||||
data-category="like"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
alt="like reaction"
|
||||
data-category="like"
|
||||
src=""
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="unicorn-reaction-button "
|
||||
data-category="unicorn"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
alt="unicorn reaction"
|
||||
data-category="unicorn"
|
||||
src=""
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="readinglist-reaction-button "
|
||||
data-category="readinglist"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
alt="readinglist reaction"
|
||||
data-category="readinglist"
|
||||
src=""
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<iframe
|
||||
src="/princesscarolyn/your-approval-means-nothing-to-me-42640"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ exports[`<ChannelDetails /> as a moderator should render and test snapshot 1`] =
|
|||
<img
|
||||
alt="channeluser1 profile"
|
||||
class="channeldetails__userprofileimage"
|
||||
data-content="users/userid1"
|
||||
data-content="sidecar-user"
|
||||
/>
|
||||
<a
|
||||
data-content="users/by_username?url=channeluser1"
|
||||
data-content="sidecar-user"
|
||||
href="/channeluser1"
|
||||
style={
|
||||
Object {
|
||||
|
|
@ -48,10 +48,10 @@ exports[`<ChannelDetails /> as a moderator should render and test snapshot 1`] =
|
|||
<img
|
||||
alt="channeluser2 profile"
|
||||
class="channeldetails__userprofileimage"
|
||||
data-content="users/userid2"
|
||||
data-content="sidecar-user"
|
||||
/>
|
||||
<a
|
||||
data-content="users/by_username?url=channeluser2"
|
||||
data-content="sidecar-user"
|
||||
href="/channeluser2"
|
||||
style={
|
||||
Object {
|
||||
|
|
@ -131,12 +131,12 @@ preact-render-spy (1 nodes)
|
|||
<img
|
||||
class="channeldetails__userprofileimage"
|
||||
alt="channeluser1 profile"
|
||||
data-content="users/userid1"
|
||||
data-content="sidecar-user"
|
||||
/>
|
||||
<a
|
||||
href="/channeluser1"
|
||||
style="padding: 3px 0px;"
|
||||
data-content="users/by_username?url=channeluser1"
|
||||
data-content="sidecar-user"
|
||||
>
|
||||
channel user 1
|
||||
</a>
|
||||
|
|
@ -145,12 +145,12 @@ preact-render-spy (1 nodes)
|
|||
<img
|
||||
class="channeldetails__userprofileimage"
|
||||
alt="channeluser2 profile"
|
||||
data-content="users/userid2"
|
||||
data-content="sidecar-user"
|
||||
/>
|
||||
<a
|
||||
href="/channeluser2"
|
||||
style="padding: 3px 0px;"
|
||||
data-content="users/by_username?url=channeluser2"
|
||||
data-content="sidecar-user"
|
||||
>
|
||||
channel user 2
|
||||
</a>
|
||||
|
|
@ -244,12 +244,12 @@ preact-render-spy (1 nodes)
|
|||
<img
|
||||
class="channeldetails__userprofileimage"
|
||||
alt="channeluser1 profile"
|
||||
data-content="users/userid1"
|
||||
data-content="sidecar-user"
|
||||
/>
|
||||
<a
|
||||
href="/channeluser1"
|
||||
style="padding: 3px 0px;"
|
||||
data-content="users/by_username?url=channeluser1"
|
||||
data-content="sidecar-user"
|
||||
>
|
||||
channel user 1
|
||||
</a>
|
||||
|
|
@ -258,12 +258,12 @@ preact-render-spy (1 nodes)
|
|||
<img
|
||||
class="channeldetails__userprofileimage"
|
||||
alt="channeluser2 profile"
|
||||
data-content="users/userid2"
|
||||
data-content="sidecar-user"
|
||||
/>
|
||||
<a
|
||||
href="/channeluser2"
|
||||
style="padding: 3px 0px;"
|
||||
data-content="users/by_username?url=channeluser2"
|
||||
data-content="sidecar-user"
|
||||
>
|
||||
channel user 2
|
||||
</a>
|
||||
|
|
@ -314,10 +314,10 @@ exports[`<ChannelDetails /> as a user should render and test snapshot 1`] = `
|
|||
<img
|
||||
alt="channeluser1 profile"
|
||||
class="channeldetails__userprofileimage"
|
||||
data-content="users/userid1"
|
||||
data-content="sidecar-user"
|
||||
/>
|
||||
<a
|
||||
data-content="users/by_username?url=channeluser1"
|
||||
data-content="sidecar-user"
|
||||
href="/channeluser1"
|
||||
style={
|
||||
Object {
|
||||
|
|
@ -335,10 +335,10 @@ exports[`<ChannelDetails /> as a user should render and test snapshot 1`] = `
|
|||
<img
|
||||
alt="channeluser2 profile"
|
||||
class="channeldetails__userprofileimage"
|
||||
data-content="users/userid2"
|
||||
data-content="sidecar-user"
|
||||
/>
|
||||
<a
|
||||
data-content="users/by_username?url=channeluser2"
|
||||
data-content="sidecar-user"
|
||||
href="/channeluser2"
|
||||
style={
|
||||
Object {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ exports[`<Message /> should render and test snapshot 1`] = `
|
|||
class="chatmessage__profilepic"
|
||||
>
|
||||
<a
|
||||
data-content="users/undefined"
|
||||
data-content="sidecar-user"
|
||||
href="/asdf"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
|
|
@ -16,7 +16,7 @@ exports[`<Message /> should render and test snapshot 1`] = `
|
|||
<img
|
||||
alt="asdf profile"
|
||||
class="chatmessagebody__profileimage"
|
||||
data-content="users/undefined"
|
||||
data-content="sidecar-user"
|
||||
role="presentation"
|
||||
src=""
|
||||
/>
|
||||
|
|
@ -42,7 +42,7 @@ exports[`<Message /> should render and test snapshot 1`] = `
|
|||
>
|
||||
<a
|
||||
class="chatmessagebody__username--link"
|
||||
data-content="users/undefined"
|
||||
data-content="sidecar-user"
|
||||
href="/asdf"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
|
|
|
|||
|
|
@ -33,19 +33,7 @@ const sampleResponse = JSON.stringify({
|
|||
|
||||
const userArticle = {
|
||||
type_of: 'article',
|
||||
id: '42640',
|
||||
cover_image: 'princess_carolyn_is_perfect.png',
|
||||
path: '/princesscarolyn/your-approval-means-nothing-to-me-42640',
|
||||
title: 'Your Approval Means Nothing to Me',
|
||||
readable_publish_date: 'July 30, 2014',
|
||||
body_html:
|
||||
"That woman can knock a drink back like a Kennedy at a wake for another Kennedy, but I'll be damned if she doesn't get s*** done!",
|
||||
user: {
|
||||
id: '00001',
|
||||
username: 'princesscarolyn',
|
||||
name: 'Princess Carolyn',
|
||||
profile_image_90: '/princesscarolyn.png',
|
||||
},
|
||||
};
|
||||
|
||||
const getArticle = () => <Article resource={userArticle} />;
|
||||
|
|
@ -67,59 +55,7 @@ describe('<Article />', () => {
|
|||
true,
|
||||
);
|
||||
expect(
|
||||
context.find('.activechatchannel__activeArticleDetails').exists(),
|
||||
context.find('iframe').exists(),
|
||||
).toEqual(true);
|
||||
expect(
|
||||
context
|
||||
.find('.activechatchannel__activeArticleDetails')
|
||||
.childAt(0)
|
||||
.attr('href'),
|
||||
).toEqual(userArticle.path);
|
||||
expect(
|
||||
context.find('.activechatchannel__activeArticleDetailsPath').text(),
|
||||
).toEqual(userArticle.path);
|
||||
expect(
|
||||
context
|
||||
.find('.title')
|
||||
.childAt(0)
|
||||
.text(),
|
||||
).toEqual(userArticle.title);
|
||||
expect(context.find('.author').attr('href')).toEqual(
|
||||
`/${userArticle.user.username}`,
|
||||
);
|
||||
expect(context.find('.profile-pic').attr('src')).toEqual(
|
||||
userArticle.user.profile_image_90,
|
||||
);
|
||||
expect(context.find('.author').text()).toEqual(
|
||||
`${userArticle.user.name} | ${userArticle.readable_publish_date}`,
|
||||
);
|
||||
expect(context.find('.published-at').text()).toEqual(
|
||||
` | ${userArticle.readable_publish_date}`,
|
||||
);
|
||||
expect(
|
||||
context
|
||||
.find('.body')
|
||||
.childAt(0)
|
||||
.attr('dangerouslySetInnerHTML'),
|
||||
).toEqual({ __html: userArticle.body_html });
|
||||
|
||||
// checks reaction
|
||||
expect(
|
||||
context.find('.activechatchannel__activeArticleActions').exists(),
|
||||
).toEqual(true);
|
||||
expect(context.find('.heart-reaction-button').exists()).toEqual(true);
|
||||
expect(context.find('.unicorn-reaction-button').exists()).toEqual(true);
|
||||
expect(context.find('.readinglist-reaction-button').exists()).toEqual(true);
|
||||
|
||||
// checks that only heart class has active
|
||||
expect(context.find('.heart-reaction-button').attr('className')).toEqual(
|
||||
'heart-reaction-button active',
|
||||
);
|
||||
expect(context.find('.unicorn-reaction-button').attr('className')).toEqual(
|
||||
'unicorn-reaction-button ',
|
||||
);
|
||||
expect(
|
||||
context.find('.readinglist-reaction-button').attr('className'),
|
||||
).toEqual('readinglist-reaction-button ');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ describe('<ChannelDetails />', () => {
|
|||
.childAt(1)
|
||||
.attr('data-content'),
|
||||
).toEqual(
|
||||
`users/by_username?url=${moddetails.channel_users[i].username}`,
|
||||
`sidecar-user`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +293,7 @@ describe('<ChannelDetails />', () => {
|
|||
.childAt(1)
|
||||
.attr('data-content'),
|
||||
).toEqual(
|
||||
`users/by_username?url=${userdetails.channel_users[i].username}`,
|
||||
`sidecar-user`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
import { h, Component } from 'preact';
|
||||
import PropTypes from 'prop-types';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import heartImage from 'images/emoji/emoji-one-heart.png';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import unicornImage from 'images/emoji/emoji-one-unicorn.png';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import bookmarkImage from 'images/emoji/emoji-one-bookmark.png';
|
||||
|
||||
export default class Article extends Component {
|
||||
static propTypes = {
|
||||
|
|
@ -13,205 +7,12 @@ export default class Article extends Component {
|
|||
id: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
userReactions: [],
|
||||
optimisticUserReaction: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { resource } = this.props;
|
||||
fetch(`/reactions?article_id=${resource.id}`, {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(this.displayReactions)
|
||||
.catch(this.displayReactionsFailure);
|
||||
}
|
||||
|
||||
displayReactions = response => {
|
||||
this.setState({ userReactions: response.reactions });
|
||||
};
|
||||
|
||||
displayReactionsFailure = response => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(response);
|
||||
};
|
||||
|
||||
handleNewReactionResponse = response => {
|
||||
let { userReactions: oldUserReactions } = this.state;
|
||||
const foundReactions = oldUserReactions.filter(obj => {
|
||||
return obj.category === response.category;
|
||||
});
|
||||
if (foundReactions.length === 0 && response.result === 'create') {
|
||||
oldUserReactions.push({ category: response.category });
|
||||
} else {
|
||||
oldUserReactions = oldUserReactions.filter(obj => {
|
||||
return obj.category !== response.category;
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
userReactions: oldUserReactions,
|
||||
optimisticUserReaction: null,
|
||||
});
|
||||
};
|
||||
|
||||
handleNewReactionFailure = response => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(response);
|
||||
};
|
||||
|
||||
handleReactionClick = e => {
|
||||
e.preventDefault();
|
||||
const { target } = e;
|
||||
const { resource: article } = this.props;
|
||||
this.setState({ optimisticUserReaction: target.dataset.category });
|
||||
fetch('/reactions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': window.csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
reactable_type: 'Article',
|
||||
reactable_id: article.id,
|
||||
category: target.dataset.category,
|
||||
}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(this.handleNewReactionResponse)
|
||||
.catch(this.handleNewReactionFailure);
|
||||
};
|
||||
|
||||
actionButton = props => {
|
||||
const types = {
|
||||
heart: ['heart-reaction-button', 'like', heartImage],
|
||||
unicorn: ['unicorn-reaction-button', 'unicorn', unicornImage],
|
||||
readinglist: [
|
||||
'readinglist-reaction-button',
|
||||
'readinglist',
|
||||
bookmarkImage,
|
||||
],
|
||||
};
|
||||
|
||||
const curType = types[props.reaction];
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`${curType[0]} ${props.reactedClass}`}
|
||||
onClick={this.handleReactionClick}
|
||||
data-category={curType[1]}
|
||||
>
|
||||
<img
|
||||
src={curType[2]}
|
||||
data-category={curType[1]}
|
||||
alt={`${curType[1]} reaction`}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
render() {
|
||||
const { resource: article } = this.props;
|
||||
let heartReactedClass = '';
|
||||
let unicornReactedClass = '';
|
||||
let bookmarkReactedClass = '';
|
||||
const { state } = this;
|
||||
state.userReactions.forEach(reaction => {
|
||||
if (
|
||||
reaction.category === 'like' ||
|
||||
state.optimisticUserReaction === 'like'
|
||||
) {
|
||||
heartReactedClass = 'active';
|
||||
}
|
||||
if (
|
||||
reaction.category === 'unicorn' ||
|
||||
state.optimisticUserReaction === 'unicorn'
|
||||
) {
|
||||
unicornReactedClass = 'active';
|
||||
}
|
||||
if (
|
||||
reaction.category === 'readinglist' ||
|
||||
state.optimisticUserReaction === 'readinglist'
|
||||
) {
|
||||
bookmarkReactedClass = 'active';
|
||||
}
|
||||
});
|
||||
let coverImage = '';
|
||||
if (article.cover_image) {
|
||||
coverImage = (
|
||||
<section>
|
||||
<div
|
||||
className="image image-final"
|
||||
style={{ backgroundImage: `url(${article.cover_image}` }}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="activechatchannel__activeArticle">
|
||||
<div className="activechatchannel__activeArticleDetails">
|
||||
<a href={article.path} target="_blank" rel="noopener noreferrer">
|
||||
<span className="activechatchannel__activeArticleDetailsPath">
|
||||
{article.path}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className="container">
|
||||
{coverImage}
|
||||
<div className="title">
|
||||
<h1>{article.title}</h1>
|
||||
<h3>
|
||||
<a
|
||||
href={`/${article.user.username}`}
|
||||
className="author"
|
||||
data-content={`/users/${article.user.id}`}
|
||||
>
|
||||
<img
|
||||
className="profile-pic"
|
||||
src={article.user.profile_image_90}
|
||||
alt={article.user.username}
|
||||
/>
|
||||
<span>
|
||||
{article.user.name}
|
||||
{' '}
|
||||
</span>
|
||||
<span className="published-at">
|
||||
{' '}
|
||||
|
|
||||
{' '}
|
||||
{article.readable_publish_date}
|
||||
</span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div className="body">
|
||||
{/* eslint-disable-next-line react/no-danger */}
|
||||
<div dangerouslySetInnerHTML={{ __html: article.body_html }} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="activechatchannel__activeArticleActions">
|
||||
<this.actionButton
|
||||
reaction="heart"
|
||||
reactedClass={heartReactedClass}
|
||||
/>
|
||||
<this.actionButton
|
||||
reaction="unicorn"
|
||||
reactedClass={unicornReactedClass}
|
||||
/>
|
||||
<this.actionButton
|
||||
reaction="readinglist"
|
||||
reactedClass={bookmarkReactedClass}
|
||||
/>
|
||||
</div>
|
||||
<iframe src={article.path} title={article.title} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,12 +111,12 @@ class ChannelDetails extends Component {
|
|||
className="channeldetails__userprofileimage"
|
||||
src={user.profile_image}
|
||||
alt={`${user.username} profile`}
|
||||
data-content={`users/${user.id}`}
|
||||
data-content='sidecar-user'
|
||||
/>
|
||||
<a
|
||||
href={`/${user.username}`}
|
||||
style={{ color: user.darker_color, padding: '3px 0px' }}
|
||||
data-content={`users/by_username?url=${user.username}`}
|
||||
data-content='sidecar-user'
|
||||
>
|
||||
{user.name}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -681,7 +681,8 @@ export default class Chat extends Component {
|
|||
}
|
||||
|
||||
const { target } = e;
|
||||
if (target.dataset.content) {
|
||||
const content = target.dataset.content || target.parentElement.dataset.content
|
||||
if (content) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
|
|
@ -695,24 +696,14 @@ export default class Chat extends Component {
|
|||
this.setActiveContent,
|
||||
null,
|
||||
);
|
||||
} else if (target.dataset.content.startsWith('users/')) {
|
||||
this.setActiveContentState(activeChannelId, {
|
||||
type_of: 'loading-user',
|
||||
});
|
||||
getContent(
|
||||
`/api/${target.dataset.content}`,
|
||||
this.setActiveContent,
|
||||
null,
|
||||
);
|
||||
} else if (target.dataset.content.startsWith('articles/')) {
|
||||
} else if (content.startsWith('sidecar') || content.startsWith('article')) { // article is legacy which can be removed shortly
|
||||
this.setActiveContentState(activeChannelId, {
|
||||
type_of: 'loading-post',
|
||||
});
|
||||
getContent(
|
||||
`/api/${target.dataset.content}`,
|
||||
this.setActiveContent,
|
||||
null,
|
||||
);
|
||||
this.setActiveContent({
|
||||
path: target.href || target.parentElement.href,
|
||||
type_of: 'article'
|
||||
})
|
||||
} else if (target.dataset.content === 'exit') {
|
||||
this.setActiveContentState(activeChannelId, null);
|
||||
}
|
||||
|
|
@ -1202,7 +1193,7 @@ export default class Chat extends Component {
|
|||
<a
|
||||
href={`/${activeChannel.channel_username}`}
|
||||
onClick={this.triggerActiveContent}
|
||||
data-content={`users/by_username?url=${activeChannel.channel_username}`}
|
||||
data-content='sidecar-user'
|
||||
>
|
||||
{activeChannel.channel_modified_slug}
|
||||
</a>
|
||||
|
|
@ -1231,18 +1222,18 @@ export default class Chat extends Component {
|
|||
|
||||
const dataContent =
|
||||
activeChannel.channel_type === 'direct'
|
||||
? `users/by_username?url=${activeChannel.channel_username}`
|
||||
? 'sidecar-user'
|
||||
: `chat_channels/${activeChannelId}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
<a
|
||||
className="activechatchannel__channelconfig"
|
||||
onClick={this.triggerActiveContent}
|
||||
onKeyUp={e => {
|
||||
if (e.keyCode === 13) this.triggerActiveContent(e);
|
||||
}}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
href={`/${activeChannel.channel_username}`}
|
||||
data-content={dataContent}
|
||||
>
|
||||
<img
|
||||
|
|
@ -1250,7 +1241,7 @@ export default class Chat extends Component {
|
|||
alt="channel config"
|
||||
data-content={dataContent}
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const Message = ({
|
|||
href={`/${user}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
data-content={`users/${userID}`}
|
||||
data-content='sidecar-user'
|
||||
onClick={onContentTrigger}
|
||||
>
|
||||
<img
|
||||
|
|
@ -47,7 +47,7 @@ const Message = ({
|
|||
className="chatmessagebody__profileimage"
|
||||
src={profileImageUrl}
|
||||
alt={`${user} profile`}
|
||||
data-content={`users/${userID}`}
|
||||
data-content='sidecar-user'
|
||||
onClick={onContentTrigger}
|
||||
/>
|
||||
</a>
|
||||
|
|
@ -65,7 +65,7 @@ const Message = ({
|
|||
href={`/${user}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
data-content={`users/${userID}`}
|
||||
data-content='sidecar-user'
|
||||
onClick={onContentTrigger}
|
||||
>
|
||||
{user}
|
||||
|
|
|
|||
|
|
@ -50,18 +50,49 @@ class Message < ApplicationRecord
|
|||
|
||||
def append_rich_links(html)
|
||||
doc = Nokogiri::HTML(html)
|
||||
rich_style = "border: 1px solid #0a0a0a; border-radius: 3px; padding: 8px;"
|
||||
doc.css("a").each do |anchor|
|
||||
if (article = rich_link_article(anchor))
|
||||
html += "<a style='color: #0a0a0a' href='#{article.path}'
|
||||
target='_blank' data-content='articles/#{article.id}'>
|
||||
<h1 style='#{rich_style}' data-content='articles/#{article.id}'>
|
||||
#{article.title}</h1></a>".html_safe
|
||||
if article = rich_link_article(anchor)
|
||||
html += "<a href='#{article.path}'
|
||||
class='chatchannels__richlink'
|
||||
target='_blank' data-content='sidecar-article'>
|
||||
#{"<div class='chatchannels__richlinkmainimage' style='background-image:url(" + cl_path(article.main_image) + ")' data-content='sidecar-article' ></div>" if article.main_image.present?}
|
||||
<h1 data-content='sidecar-article'>#{article.title}</h1>
|
||||
<h4 data-content='sidecar-article'><img src='#{ProfileImage.new(article.cached_user).get(90)}' /> #{article.cached_user.name}・#{article.readable_publish_date}</h4>
|
||||
</a>".html_safe
|
||||
elsif tag = rich_link_tag(anchor)
|
||||
html += "<a href='/t/#{tag.name}'
|
||||
class='chatchannels__richlink'
|
||||
target='_blank' data-content='sidecar-tag'>
|
||||
<h1 data-content='sidecar-tag'>
|
||||
#{"<img src='" + cl_path(tag.badge.badge_image_url) + "' data-content='sidecar-tag' style='transform:rotate(-5deg)' />" if tag.badge_id.present?}
|
||||
##{tag.name}
|
||||
</h1>
|
||||
</a>".html_safe
|
||||
elsif user = rich_user_link(anchor)
|
||||
html += "<a href='#{user.path}'
|
||||
class='chatchannels__richlink'
|
||||
target='_blank' data-content='sidecar-user'>
|
||||
<h1 data-content='sidecar-user'>
|
||||
<img src='#{ProfileImage.new(user).get(90)}' data-content='sidecar-user' class='chatchannels__richlinkprofilepic' />
|
||||
#{user.name}
|
||||
</h1>
|
||||
</a>".html_safe
|
||||
end
|
||||
end
|
||||
html
|
||||
end
|
||||
|
||||
def cl_path(img_src)
|
||||
ActionController::Base.helpers.
|
||||
cl_image_path(img_src,
|
||||
type: "fetch",
|
||||
width: 725,
|
||||
crop: "limit",
|
||||
flags: "progressive",
|
||||
fetch_format: "auto",
|
||||
sign_url: true)
|
||||
end
|
||||
|
||||
def determine_user_validity
|
||||
return unless chat_channel
|
||||
|
||||
|
|
@ -83,6 +114,14 @@ class Message < ApplicationRecord
|
|||
Article.find_by(slug: link["href"].split("/")[4].split("?")[0]) if link["href"].include?("//#{ApplicationConfig['APP_DOMAIN']}/") && link["href"].split("/")[4]
|
||||
end
|
||||
|
||||
def rich_link_tag(link)
|
||||
Tag.find_by(name: link["href"].split("/t/")[1].split("/")[0]) if link["href"].include?("//#{ApplicationConfig['APP_DOMAIN']}/t/")
|
||||
end
|
||||
|
||||
def rich_user_link(link)
|
||||
User.find_by(username: link["href"].split("/")[3].split("/")[0]) if link["href"].include?("//#{ApplicationConfig['APP_DOMAIN']}/")
|
||||
end
|
||||
|
||||
def send_email_if_appropriate
|
||||
recipient = direct_receiver
|
||||
return if !chat_channel.direct? ||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,9 @@
|
|||
if (localStorage.getItem('config_minimize_newest_listings') === 'yes') {
|
||||
document.getElementsByTagName("body")[0].classList.add("config_minimize_newest_listings");
|
||||
}
|
||||
if (window.frameElement) {
|
||||
document.getElementsByTagName("body")[0].classList.add("hidden-shell");
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ require "rails_helper"
|
|||
RSpec.describe Message, type: :model do
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:tag) { create(:tag) }
|
||||
let(:chat_channel) { create(:chat_channel) }
|
||||
let(:message) { create(:message, user: user) }
|
||||
|
||||
|
|
@ -40,12 +41,28 @@ RSpec.describe Message, type: :model do
|
|||
let_it_be(:article) { create(:article) }
|
||||
|
||||
describe "#message_html" do
|
||||
it "creates rich link with proper link" do
|
||||
it "creates rich link with proper link for article" do
|
||||
message.message_markdown = "hello http://#{ApplicationConfig['APP_DOMAIN']}#{article.path}"
|
||||
message.validate!
|
||||
|
||||
expect(message.message_html).to include(article.title)
|
||||
expect(message.message_html).to include("data-content")
|
||||
expect(message.message_html).to include("sidecar-article")
|
||||
end
|
||||
|
||||
it "creates rich link with proper link for user" do
|
||||
message.message_markdown = "hello http://#{ApplicationConfig['APP_DOMAIN']}#{user.path}"
|
||||
message.validate!
|
||||
|
||||
expect(message.message_html).to include(user.name)
|
||||
expect(message.message_html).to include("sidecar-user")
|
||||
end
|
||||
|
||||
it "creates rich link with proper link for tag" do
|
||||
message.message_markdown = "hello http://#{ApplicationConfig['APP_DOMAIN']}/t/#{tag.name}"
|
||||
message.validate!
|
||||
|
||||
expect(message.message_html).to include(tag.name)
|
||||
expect(message.message_html).to include("sidecar-tag")
|
||||
end
|
||||
|
||||
it "creates rich link with non-rich link" do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue