[deploy] Small design and functionality touches to /connect (#7234)

* Small design and functionality touches to /connect

* Small fixes

* Fix test

* Fix test
This commit is contained in:
Ben Halpern 2020-04-11 10:56:52 -04:00 committed by GitHub
parent 34848a4209
commit b0a95cdcd7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 199 additions and 310 deletions

View file

@ -55,6 +55,10 @@ function fetchNotificationsCount() {
document.getElementById('notifications-link').href,
'force',
);
InstantClick.preload(
document.getElementById('connect-link').href,
'force',
);
}, 30);
}
}

View file

@ -9,7 +9,7 @@
}
.live-chat {
height: calc(100vh - 80px);
height: calc(100vh - 70px);
&.live-chat--iossafari {
height: calc(100vh - 190px);
@ -18,14 +18,14 @@
overflow-y: hidden;
@media screen and (min-width: 400px) {
height: calc(100vh - 80px);
height: calc(100vh - 70px);
}
}
//chat
.chat {
display: flex;
height: calc(100vh - 91px);
height: calc(100vh - 80px);
&.chat--iossafari {
height: calc(100vh - 201px);
@ -34,7 +34,7 @@
padding-right: 3px;
@media screen and (min-width: 400px) {
height: calc(100vh - 91px);
height: calc(100vh - 80px);
}
}
@ -76,11 +76,24 @@
height: inherit;
position: relative;
overflow: hidden;
.chat__channelssearchtoggle {
background: transparent;
border: 0px;
margin-left: 2px;
svg {
vertical-align: -5px;
}
}
input {
outline: 0;
}
}
.chat__channels--expanded {
width: 160px;
min-width: 160px;
width: 219px;
min-width: 183px;
@media screen and (min-width: 1000px) {
width: 200px;
@ -149,39 +162,16 @@
.chat__channeltypefilter {
padding: 4px 0px 0px;
width: calc(100% - 13px);
width: calc(100% - 5px);
}
.chat__channeltypefilterbutton {
background: transparent;
border: 0px;
font-size: 15px;
font-weight: bold;
padding: 5px 15px;
display: block;
width: 100%;
box-sizing: border-box;
border-bottom: 1px solid darken($light-medium-gray, 5%);
margin: 1px;
@media screen and (min-width: 1300px) {
width: 33%;
display: inline-block;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
margin: 5px 4px;
}
}
.chat__channeltypefilterbutton--active {
color: var(--link-branded-color);
@media screen and (min-width: 1300px) {
border: 1px solid darken($light-medium-gray, 5%);
}
}
.chat__channeltypefilterbutton--inactive {
@include themeable(color, theme-secondary-color, lighten($medium-gray, 5%));
}
.chat_chatconfig {
position: absolute;
@ -220,185 +210,20 @@
}
}
.chat__videocall {
height: calc(75vw - 60px);
width: calc(100% - 60px);
position: fixed;
background: $black;
left: 0px;
top: 0px;
text-align: center;
cursor: grab;
border-radius: 12px;
z-index: 10000;
@media screen and (min-width: 640px) {
width: 640px;
height: 480px;
left: 250px;
top: 150px;
}
&:active {
cursor: grabbing;
}
video {
border-radius: 12px;
width: 100%;
height: 100%;
}
.chat__remotevideoscreen-2 {
video {
margin-top: 20%;
width: 45%;
margin-left: 1%;
margin-right: 1%;
}
}
.chat__remotevideoscreen-3,
.chat__remotevideoscreen-4 {
video {
width: 45%;
margin-top: 1%;
margin-left: 1%;
margin-right: 1%;
}
}
.chat__remotevideoscreen-5,
.chat__remotevideoscreen-6 {
video {
width: 30%;
margin-top: 3%;
margin-left: 1%;
margin-right: 1%;
}
}
.chat__remotevideoscreen-7,
.chat__remotevideoscreen-8 {
video {
width: 24%;
}
}
.chat__localvideoscren {
height: 120px;
width: 160px;
position: absolute;
right: 15px;
bottom: 15px;
border-radius: 5px;
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.4);
video {
border-radius: 5px;
}
}
}
.chatNonChatView {
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
padding: 80px 7%;
z-index: 10;
background: var(--body-bg);
}
.chatNonChatView_exitbutton {
position: absolute;
left: 4%;
top: 60px;
font-size: 25px;
background: transparent;
border: 0px;
font-size: 1.8em;
color: var(--body-color);
}
.chatNonChatView_contentblock {
border: 1px solid $medium-gray;
padding: 20px;
margin-bottom: 10px;
max-width: 900px;
@include themeable(box-shadow, theme-container-box-shadow, $bold-shadow);
h2 {
margin: 5px auto 5px;
}
button {
font-size: 1.6em;
background: white;
border: 4px solid $black;
padding: 4px 12px;
border-radius: 3px;
margin: 20px 12px;
}
}
.chat__videocallexitbutton {
border: 0px;
font-size: 20px;
color: $medium-gray;
padding: 1px 6px 2px;
border-radius: 6px;
background: rgba(0, 0, 0, 0.1);
position: absolute;
left: 3px;
top: 3px;
}
.chat__videocallcontrolbutton {
border: 0px;
font-size: 14px;
color: $light-medium-gray;
padding: 3px 0px 4px;
border-radius: 6px;
background: rgba(0, 0, 0, 0.7);
position: absolute;
bottom: 10px;
left: 10px;
width: 125px;
&.chat__videocallcontrolbutton--videoonoff {
left: 140px;
}
}
.chat__videocalltrackdiv--audio {
display: none;
}
.chat__videocalltrackdiv--video {
display: inline;
}
.activechatchannel {
height: inherit;
max-height: inherit;
display: flex;
flex-direction: row;
justify-content: space-between;
border-radius: 5px;
@include themeable(border, theme-container-border, 1px solid $outline-color);
@include themeable(box-shadow, theme-container-box-shadow, $bold-shadow);
background: var(--card-bg);
flex-flow: row nowrap;
}
.activechatchannel__conversation {
position: relative;
@include themeable(
border-top,
theme-container-border,
1px solid $outline-color
);
order: 1;
display: flex;
flex-direction: column;
@ -406,7 +231,7 @@
width: 100%;
min-width: 50%;
@media screen and (min-width: 1440px) {
min-width: 55%;
min-width: 35%;
}
@media screen and (max-width: 426px) {
overflow-x: hidden;
@ -421,12 +246,7 @@
);
padding: 10px 12px;
font-weight: bold;
font-size: 14px;
@include themeable(
background,
theme-container-accent-background,
$light-gray
);
font-size: 17px;
position: relative;
a {
@ -437,10 +257,9 @@
height: 17px;
position: absolute;
right: 15px;
top: 10px;
top: 12px;
cursor: pointer;
@include themeable(filter, theme-social-icon-invert, invert(0));
img {
height: 100%;
}
@ -488,20 +307,11 @@
);
@include themeable(background, theme-container-accent-background, #ededed);
width: 100%;
height: 60px;
height: 63px;
position: relative;
}
.activechatchannel__incomingcall {
border: 2px solid $green;
box-shadow: 6px 6px 0px $green;
padding: 20px;
margin: 10px;
background: lighten($green, 28%);
font-size: 28px;
font-weight: bold;
cursor: pointer;
animation: pulser 0.5s linear infinite;
@media screen and (min-height: 1000px) {
height: 85px;
}
}
.activechatchannel__activecontent {
@ -540,16 +350,14 @@
@media screen and (min-width: 1300px) {
margin-left: initial;
padding: 18px;
max-width: 620px;
width: 750px;
max-width: 750px;
}
}
.chat--video-visible {
.activechatchannel__conversation {
min-width: 25%;
@media screen and (min-width: 1440px) {
min-width: 45%;
}
min-width: 20%;
}
.activechatchannel__activecontent {
&.activechatchannel__activecontent--video {
@ -576,16 +384,20 @@
width: 400px;
min-width: 400px;
}
@media screen and (min-width: 1300px) {
width: 500px;
min-width: 500px;
@media screen and (min-width: 1500px) {
width: 750px;
min-width: 750px;
}
}
}
.chat--content-visible .activechatchannel__conversation {
max-width: 52%;
}
.chat--content-visible .activechatchannel__activecontent.activechatchannel__activecontent--video {
width: 280px;
min-width: 280px;
width: 180px;
min-width: 180px;
}
.live-chat-wrapper .activechatchannel__activecontent {
@ -715,16 +527,15 @@
width: calc(95% - 2px);
border: none;
background: transparent;
padding: 3px 0px;
margin-bottom: -5px 0;
padding: 2px 0px;
margin-bottom: -12px;
box-sizing: border-box;
color: var(--body-color);
&:hover {
.chatchanneltab--inactive {
border: 1px solid $outline-color;
box-shadow: $bold-shadow;
}
display: block;
position: relative;
.crayons-indicator {
vertical-align: -1px;
margin-right: 4px;
}
}
@ -736,31 +547,28 @@
text-align: start;
font-size: 11px;
font-weight: 500;
padding: 6px 4px;
border: 1px solid transparent;
padding: 5px 4px;
border-radius: 3px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
@media screen and (min-width: 550px) {
font-size: 13px;
padding: 8px;
}
border: 1px solid transparent;
font-size: 13px;
padding: 8px;
}
.chatchanneltab--active {
background: var(--card-bg);
border: 1px solid $dark-purple;
box-shadow: 3px 3px 0px $dark-purple;
border: 1px solid var(--base-30);
}
.chatchanneltab--new {
background: lighten($yellow, 10%);
color: $black;
border: 1px solid var(--accent-brand);
color: var(--accent-brand-darker);
}
.chatchanneltab--video {
background: lighten($green, 10%);
animation: small-pulser 0.5s linear infinite;
@ -771,19 +579,14 @@
min-height: 0.3em;
min-width: 0.3em;
border-radius: 100px;
margin-right: 3px;
margin-right: 5px;
border: 1px solid var(--base-30);
img {
height: calc(20px + 0.3vw);
width: calc(20px + 0.3vw);
vertical-align: calc(-7px - 0.14vw);
border: 2px solid transparent;
&.chatchanneltabindicatordirectimage {
border: 2px solid $light-medium-gray;
border-radius: 100px;
background: white;
}
height: calc(22px + 0.3vw);
width: calc(22px + 0.3vw);
vertical-align: calc(-6px - 0.14vw);
border-radius: 100px;
}
.chatchanneltabgroupicon {
@ -793,16 +596,7 @@
}
.chatchanneltabindicator--new {
background: $yellow;
img.chatchanneltabindicatordirectimage {
border: 2px solid $black;
}
}
.chatchanneltabindicator--phone {
background: $green;
padding: 0px 8px;
border: 1px solid var(--accent-brand);
}
.chatchannels__channelslist {
@ -885,25 +679,24 @@
padding: 3px 10px;
display: flex;
max-width: 100%;
&:hover {
@include themeable(
background,
theme-container-background-hover,
$light-gray
);
.chatmessagebody__username--link {
@media not all and (pointer: coarse) {
&:hover {
@include themeable(
background,
theme-container-background-hover,
transparent
$light-gray
);
.chatmessagebody__username--link {
@include themeable(
background,
theme-container-background-hover,
transparent
);
}
}
}
}
.chatmessage__profileimage {
}
.chatmessage__body {
flex-grow: 1;
@ -1021,20 +814,23 @@
.messagecomposer__input {
border-radius: 3px;
font-size: 16px;
margin: 4px 2px 4px 6px;
margin: 4px 4px 4px 6px;
padding: 4px;
resize: none;
flex-grow: 1;
height: 42px;
height: 44px;
background: var(--card-bg);
@include themeable(border, theme-border, 1px solid $outline-color);
color: var(--body-color);
@media screen and (min-height: 1000px) {
height: 66px;
}
}
.messagecomposer__submit {
margin: 4px 6px 4px 0;
border-radius: 3px;
background: $dark-purple;
background: var(--accent-brand);
font-weight: 600px;
font-family: $helvetica-condensed;
color: white;
@ -1159,6 +955,11 @@
padding: 0px;
margin-bottom: 6px;
background: var(--card-bg);
&.chatchannels__richlink--base {
h1 {
margin: 18px auto;
}
}
.chatchannels__richlinkmainimage {
position: relative;
width: 100%;
@ -1261,7 +1062,7 @@
background: white;
padding: 0px 5px;
border: 1px solid var(--header-bg);
border-left: 3px solid #4e57ef;
border-left: 3px solid var(--accent-brand);
height: 50px;
overflow: hidden;
p {
@ -1274,13 +1075,13 @@
user-select: none;
cursor: pointer;
padding: 2px 5px;
color: #4e57ef;
color: var(--accent-brand);
font-weight: bold;
}
.editHead {
position: absolute;
color: #4e57ef;
color: var(--accent-brand);
font-size: 14px;
font-weight: bold;
}

View file

@ -109,7 +109,7 @@
height: 100%;
display: flex;
align-items: center;
z-index: 20;
&__trigger {
all: unset;
padding: $su-1;

View file

@ -20,7 +20,7 @@ class ChatChannelsController < ApplicationController
end
def show
@chat_messages = @chat_channel.messages.includes(:user).order("created_at DESC").offset(params[:message_offset]).limit(150)
@chat_messages = @chat_channel.messages.includes(:user).order("created_at DESC").offset(params[:message_offset]).limit(50)
end
def create

View file

@ -15,15 +15,30 @@ exports[`<Chat /> should load chat 1`] = `
>
&lt;
</button>
<input
onKeyUp={[Function]}
placeholder="Filter"
/>
<div
class="chat__channeltypefilter"
>
<button
class="chat__channeltypefilterbutton chat__channeltypefilterbutton--active"
class="chat__channelssearchtoggle"
onClick={[Function]}
>
<svg
height="17"
viewBox="0 0 24 24"
width="17"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 0h24v24H0z"
fill="none"
/>
<path
d="M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z"
/>
</svg>
</button>
<button
class="chat__channeltypefilterbutton crayons-indicator crayons-indicator--accent"
data-channel-type="all"
onClick={[Function]}
type="button"
@ -31,7 +46,7 @@ exports[`<Chat /> should load chat 1`] = `
all
</button>
<button
class="chat__channeltypefilterbutton chat__channeltypefilterbutton--inactive"
class="chat__channeltypefilterbutton crayons-indicator crayons-indicator--"
data-channel-type="direct"
onClick={[Function]}
type="button"
@ -39,7 +54,7 @@ exports[`<Chat /> should load chat 1`] = `
direct
</button>
<button
class="chat__channeltypefilterbutton chat__channeltypefilterbutton--inactive"
class="chat__channeltypefilterbutton crayons-indicator crayons-indicator--"
data-channel-type="invite_only"
onClick={[Function]}
type="button"

View file

@ -53,7 +53,7 @@ const Channels = ({
}
/>
</span>
{channel.channel_name}
{isUnopened ? <span class="crayons-indicator crayons-indicator--accent crayons-indicator--bullet"></span> : ''}{channel.channel_name}
</span>
</button>
);

View file

@ -74,10 +74,12 @@ export default class Chat extends Component {
startEditing: false,
activeEditMessage: {},
markdownEdited: false,
searchShowing: false,
channelUsers: [],
showMemberlist: false,
memberFilterQuery: null,
};
getAllMessages(chatOptions.activeChannelId, 0, this.receiveAllMessages);
}
componentDidMount() {
@ -89,6 +91,7 @@ export default class Chat extends Component {
isMobileDevice,
channelPaginationNum,
currentUserId,
messageOffset,
} = this.state;
this.setupChannels(chatChannels);
@ -935,8 +938,8 @@ export default class Chat extends Component {
<button
data-channel-type={type}
onClick={this.triggerChannelTypeFilter}
className={`chat__channeltypefilterbutton chat__channeltypefilterbutton--${
type === active ? 'active' : 'inactive'
className={`chat__channeltypefilterbutton crayons-indicator crayons-indicator--${
type === active ? 'accent' : ''
}`}
type="button"
>
@ -944,6 +947,15 @@ export default class Chat extends Component {
</button>
);
toggleSearchShowing = () => {
if (!this.state.searchShowing) {
setTimeout(function(){
document.getElementById("chatchannelsearchbar").focus()
},100)
}
this.setState({searchShowing: !this.state.searchShowing})
}
renderChatChannels = () => {
const { state } = this;
if (state.showChannelsList) {
@ -993,9 +1005,10 @@ export default class Chat extends Component {
>
{'<'}
</button>
<input placeholder="Filter" onKeyUp={this.debouncedChannelFilter} />
{state.searchShowing ? <input placeholder="Search Channels" onKeyUp={this.debouncedChannelFilter} id="chatchannelsearchbar" className="crayons-textfield" /> : ''}
{invitesButton}
<div className="chat__channeltypefilter">
<button className="chat__channelssearchtoggle" onClick={this.toggleSearchShowing}><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="17" height="17"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z"/></svg></button>
{this.renderChannelFilterButton(
'all',
'all',

View file

@ -51,7 +51,7 @@ connect(root.dataset.token, { name: 'room-name', audio: true, type: roomType, vi
createLocalVideoTrack().then(track => {
localMediaContainer.appendChild(track.attach());
document.getElementById('video-controls').classList.add('showing');
});

View file

@ -40,6 +40,7 @@ class Message < ApplicationRecord
def evaluate_markdown
html = MarkdownParser.new(message_markdown).evaluate_markdown
html = target_blank_links(html)
html = append_rich_links(html)
html = wrap_mentions_with_links(html)
html = handle_call(html)
@ -90,6 +91,13 @@ class Message < ApplicationRecord
end
end
def target_blank_links(html)
return html if html.blank?
html = html.gsub("<a href", "<a target='_blank' rel='noopener nofollow' href")
html
end
def append_rich_links(html)
doc = Nokogiri::HTML(html)
doc.css("a").each do |anchor|
@ -119,6 +127,14 @@ class Message < ApplicationRecord
#{user.name}
</h1>
</a>".html_safe
elsif anchor["href"].include?("https://www.figma.com/file/") # Proof of concept
html += "<a href='https://www.figma.com/embed?embed_host=astra&url=#{anchor['href']}' class='chatchannels__richlink chatchannels__richlink--base' data-content='sidecar-embeddable' target='_blank'>
<h1 data-content='sidecar-embeddable'>Figma File</h1>
</a>".html_safe
elsif anchor["href"].starts_with?("https://docs.google.com/") # Proof of concept
html += "<a href='#{anchor['href']}' class='chatchannels__richlink chatchannels__richlink--base' data-content='sidecar-embeddable' target='_blank'>
<h1 data-content='sidecar-embeddable'>Google Docs</h1>
</a>".html_safe
end
end
html
@ -128,9 +144,9 @@ class Message < ApplicationRecord
return html if html.to_s.exclude?("<p>/call</p>")
"<a href='/video_chats/#{chat_channel_id}'
class='chatchannels__richlink'
class='chatchannels__richlink chatchannels__richlink--base'
target='_blank' data-content='sidecar-video'>
<h1 data-content='sidecar-video' style='margin: 18px auto;'>
<h1 data-content='sidecar-video'>
Let's video chat 😄
</h1>
</a>".html_safe

View file

@ -19,7 +19,6 @@
<div class="chat__channels chat__channels--expanded chat__channels--placeholder">
<button class="chat__channelstogglebutt" onClick={this.toggleExpand}>&lt;</button>
<button class="chat__channelstogglebutt chat__channelstogglebutt--placeholderunexpanded">&gt;</button>
<input placeholder='Filter' onKeyUp={this.debouncedChannelFilter} />
</div>
<div class="chat__activechat">
<div class="activechatchannel">

View file

@ -6,7 +6,6 @@
.video-chat-wrapper {
position: relative;
height: 100vh;
background: #171717;
overflow: scroll;
}
.individual-video video {
@ -18,10 +17,14 @@
.individual-video {
overflow: hidden;
}
.individual-video:last-child {
margin-bottom: 100px;
}
#local-media video {
width: 150px;
position: absolute;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 5;
@ -54,10 +57,16 @@
margin: 8px;
}
.video-controls {
position: absolute;
position: fixed;
bottom: 15px;
left: 5px;
width: calc(100% - 175px);
z-index: 10;
display: none;
}
.showing {
display: block;
}
.video-controls button {
@ -95,13 +104,24 @@
color: white;
margin-top: 30px;
}
@media screen and (max-width: 280px) {
.video-controls {
width: 92%;
bottom: 5px;
}
#local-media video {
width: 90%;
bottom: 85px;
}
}
</style>
<div class="video-chat-wrapper" id="videochat" data-token="<%= @token %>" data-channel="<%= params[:id] %>" data-user="<%= @username %>">
<div id="room-type" data-type="<%= @video_type %>"></div>
<div id="remote-media"></div>
<div id="local-media"></div>
<div class="video-controls">
<div id="video-controls" class="video-controls">
<button id="mute-toggle" data-muted="false" class="crayons-btn crayons-btn--secondary">Mute</button>
<button id="videohide-toggle" data-hidden="false" class="crayons-btn crayons-btn--secondary">Hide</button>
</div>

View file

@ -50,6 +50,13 @@ RSpec.describe Message, type: :model do
expect(message.message_html).to include("sidecar-article")
end
it "creates target blank link" do
message.message_markdown = "hello http://#{ApplicationConfig['APP_DOMAIN']}#{user.path}"
message.validate!
expect(message.message_html).to include("<a target=\"_blank\"")
end
it "creates rich link with proper link for user" do
message.message_markdown = "hello http://#{ApplicationConfig['APP_DOMAIN']}#{user.path}"
message.validate!
@ -58,6 +65,20 @@ RSpec.describe Message, type: :model do
expect(message.message_html).to include("sidecar-user")
end
it "creates rich call link" do
message.message_markdown = "/call"
message.validate!
expect(message.message_html).to include("sidecar-video")
end
it "creates rich embeddable link" do
message.message_markdown = "https://docs.google.com/ https://www.figma.com/file/"
message.validate!
expect(message.message_html).to include("chatchannels__richlink--base")
end
it "creates rich link with proper link for tag" do
message.message_markdown = "hello http://#{ApplicationConfig['APP_DOMAIN']}/t/#{tag.name}"
message.validate!