REF: React eslint (#2432)
This commit is contained in:
parent
c6ee9fe3b4
commit
bf68782374
34 changed files with 586 additions and 372 deletions
1
.eslintignore
Normal file
1
.eslintignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
package.json
|
||||
|
|
@ -2,34 +2,54 @@ import { h } from 'preact';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
const BodyPreview = ({ previewHTML, version, articleState }) => (
|
||||
<div className="container" style={{marginTop: "10px",minHeight:"508px", overflow:"hidden", boxShadow: "0px 0px 0px #fff",border:"0px"}}>
|
||||
<div
|
||||
className="container"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
minHeight: '508px',
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0px 0px 0px #fff',
|
||||
border: '0px',
|
||||
}}
|
||||
>
|
||||
{titleArea(version, articleState)}
|
||||
<div className="body" dangerouslySetInnerHTML={{__html: previewHTML}} style={{width: "90%"}}>
|
||||
</div>
|
||||
<div
|
||||
className="body"
|
||||
dangerouslySetInnerHTML={{ __html: previewHTML }}
|
||||
style={{ width: '90%' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
function titleArea(version, articleState) {
|
||||
if(version === 'help'){
|
||||
//possibly something different here in future.
|
||||
return ''
|
||||
} else {
|
||||
const tags = articleState.tagList.split(", ").map(tag => {
|
||||
return <span><div class="tag">{tag}</div> </span>
|
||||
})
|
||||
return <div class="title" style={{width: "90%", maxWidth:"1000px"}}>
|
||||
<h1>{articleState.title}</h1>
|
||||
<h3>
|
||||
<img class="profile-pic" src={window.currentUser.profile_image_90} alt="image" />
|
||||
<span>{window.currentUser.name}</span>
|
||||
</h3>
|
||||
<div class="tags">
|
||||
{tags}
|
||||
</div>
|
||||
</div>
|
||||
if (version === 'help') {
|
||||
// possibly something different here in future.
|
||||
return '';
|
||||
}
|
||||
const tags = articleState.tagList.split(', ').map(tag => {
|
||||
return (
|
||||
<span>
|
||||
<div className="tag">{tag}</div>
|
||||
{' '}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div className="title" style={{ width: '90%', maxWidth: '1000px' }}>
|
||||
<h1>{articleState.title}</h1>
|
||||
<h3>
|
||||
<img
|
||||
className="profile-pic"
|
||||
src={window.currentUser.profile_image_90}
|
||||
alt="image"
|
||||
/>
|
||||
|
||||
<span>{window.currentUser.name}</span>
|
||||
</h3>
|
||||
<div className="tags">{tags}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
BodyPreview.propTypes = {
|
||||
previewHTML: PropTypes.string.isRequired,
|
||||
|
|
@ -37,8 +57,3 @@ BodyPreview.propTypes = {
|
|||
};
|
||||
|
||||
export default BodyPreview;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,19 @@ import { h } from 'preact';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
const Errors = ({ errorsList }) => (
|
||||
<div className='articleform__errors'>
|
||||
<div className="articleform__errors">
|
||||
<h2>😱 Heads up:</h2>
|
||||
<ul>{Object.keys(errorsList).map((key) => {
|
||||
return <li>{key}: {errorsList[key]}</li>
|
||||
})}</ul>
|
||||
<ul>
|
||||
{Object.keys(errorsList).map(key => {
|
||||
return (
|
||||
<li>
|
||||
{key}
|
||||
:
|
||||
{errorsList[key]}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { h, Component } from 'preact';
|
||||
import PropTypes from 'prop-types';
|
||||
import { generateMainImage } from '../actions'
|
||||
|
||||
|
||||
import { generateMainImage } from '../actions';
|
||||
|
||||
// const ImageManagement = ({ onExit }) => (
|
||||
export default class MoreConfig extends Component {
|
||||
|
|
@ -14,50 +12,91 @@ export default class MoreConfig extends Component {
|
|||
handleSeriesButtonClick = e => {
|
||||
e.preventDefault();
|
||||
this.props.onConfigChange(e);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onExit, passedData, onSaveDraft } = this.props;
|
||||
let publishedField = '';
|
||||
let seriesTip = <small>Will this post be part of a series? Give the series a unique name. (Series visible once it has multiple posts)</small>
|
||||
let seriesTip = (
|
||||
<small>
|
||||
Will this post be part of a series? Give the series a unique name.
|
||||
(Series visible once it has multiple posts)
|
||||
</small>
|
||||
);
|
||||
if (passedData.allSeries.length > 0) {
|
||||
const seriesNames = passedData.allSeries.map( name => {
|
||||
return <button name='series' onClick={this.props.onConfigChange} value={name}>{name}</button>
|
||||
})
|
||||
seriesTip = <small>Existing series: {seriesNames}</small>
|
||||
const seriesNames = passedData.allSeries.map(name => {
|
||||
return (
|
||||
<button
|
||||
name="series"
|
||||
onClick={this.props.onConfigChange}
|
||||
value={name}
|
||||
>
|
||||
{name}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
seriesTip = (
|
||||
<small>
|
||||
Existing series:
|
||||
{seriesNames}
|
||||
</small>
|
||||
);
|
||||
}
|
||||
if (passedData.published) {
|
||||
publishedField = <div>
|
||||
<h4>Danger Zone</h4>
|
||||
<button onClick={onSaveDraft}>Unpublish Post</button>
|
||||
</div>
|
||||
|
||||
publishedField = (
|
||||
<div>
|
||||
<h4>Danger Zone</h4>
|
||||
<button onClick={onSaveDraft}>Unpublish Post</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div
|
||||
className="articleform__overlay"
|
||||
>
|
||||
<h3>Additional Config/Settings</h3>
|
||||
<button
|
||||
class="articleform__exitbutton"
|
||||
data-content="exit"
|
||||
onClick={onExit}
|
||||
>×</button>
|
||||
<div>
|
||||
<label>Canonical URL</label>
|
||||
<input type="text" value={passedData.canonicalUrl} name="canonicalUrl" onKeyUp={this.props.onConfigChange}/>
|
||||
</div>
|
||||
<small>Change meta tag <code>canonical_url</code> if this post was first published elsewhere (like your own blog)</small>
|
||||
<div>
|
||||
<label>Series Name</label>
|
||||
<input type="text" value={passedData.series} name="series" onKeyUp={this.props.onConfigChange}/>
|
||||
</div>
|
||||
{seriesTip}
|
||||
<div><button class="articleform__donebutton" onClick={onExit}>Done</button></div>
|
||||
{publishedField}
|
||||
</div>
|
||||
return (
|
||||
<div className="articleform__overlay">
|
||||
<h3>Additional Config/Settings</h3>
|
||||
<button
|
||||
className="articleform__exitbutton"
|
||||
data-content="exit"
|
||||
onClick={onExit}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
<div>
|
||||
<label>Canonical URL</label>
|
||||
<input
|
||||
type="text"
|
||||
value={passedData.canonicalUrl}
|
||||
name="canonicalUrl"
|
||||
onKeyUp={this.props.onConfigChange}
|
||||
/>
|
||||
</div>
|
||||
<small>
|
||||
Change meta tag
|
||||
<code>canonical_url</code>
|
||||
{' '}
|
||||
if this post was first published elsewhere
|
||||
(like your own blog)
|
||||
</small>
|
||||
<div>
|
||||
<label>Series Name</label>
|
||||
<input
|
||||
type="text"
|
||||
value={passedData.series}
|
||||
name="series"
|
||||
onKeyUp={this.props.onConfigChange}
|
||||
/>
|
||||
</div>
|
||||
{seriesTip}
|
||||
<div>
|
||||
<button className="articleform__donebutton" onClick={onExit}>
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
{publishedField}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MoreConfig.propTypes = {
|
||||
onExit: PropTypes.func.isRequired,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,13 @@ import { h } from 'preact';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
const Notice = ({ published }) => (
|
||||
<div className={'articleform__notice articleform__notice--'+(published ? "publishing" : "draft")}>{published ? "Publishing..." : "Saving Draft..."}</div>
|
||||
<div
|
||||
className={`articleform__notice articleform__notice--${
|
||||
published ? 'publishing' : 'draft'
|
||||
}`}
|
||||
>
|
||||
{published ? 'Publishing...' : 'Saving Draft...'}
|
||||
</div>
|
||||
);
|
||||
|
||||
Notice.propTypes = {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,17 @@ import { h } from 'preact';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
const OrgSettings = ({ organization, postUnderOrg, onToggle }) => (
|
||||
<div
|
||||
className='articleform__orgsettings'
|
||||
onClick={onToggle}
|
||||
>
|
||||
<img src={organization.profile_image_90} style={{opacity: postUnderOrg ? '1' : '0.7' }} /> {organization.name} <button class={postUnderOrg ? 'yes' : 'no'}>{postUnderOrg ? '✅ YES' : '◻️ NO'}</button>
|
||||
<div className="articleform__orgsettings" onClick={onToggle}>
|
||||
<img
|
||||
src={organization.profile_image_90}
|
||||
style={{ opacity: postUnderOrg ? '1' : '0.7' }}
|
||||
/>
|
||||
{' '}
|
||||
{organization.name}
|
||||
{' '}
|
||||
<button className={postUnderOrg ? 'yes' : 'no'}>
|
||||
{postUnderOrg ? '✅ YES' : '◻️ NO'}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class Tags extends Component {
|
|||
};
|
||||
|
||||
handleInput = e => {
|
||||
let value = e.target.value;
|
||||
let { value } = e.target;
|
||||
// If we start typing immediately after a comma, add a space
|
||||
// before what we typed.
|
||||
// e.g. If value = "javascript," and we type a "p",
|
||||
|
|
|
|||
|
|
@ -199,16 +199,19 @@ describe('<ChannelDetails />', () => {
|
|||
let inviteAttrAns;
|
||||
const included = (list, el) => {
|
||||
const keys = Object.keys(list);
|
||||
for (var key of keys) {
|
||||
for (const key of keys) {
|
||||
if (list[key].id === el.id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
for (let i = 0; i < searchedusersdivs.length; i += 1) {
|
||||
if (!included(
|
||||
if (
|
||||
!included(
|
||||
moddetails.pending_users_select_fields,
|
||||
searchedusers.searchedUsers[i])) {
|
||||
searchedusers.searchedUsers[i],
|
||||
)
|
||||
) {
|
||||
expect(
|
||||
searchedusersdivs
|
||||
.at(i)
|
||||
|
|
@ -226,7 +229,9 @@ describe('<ChannelDetails />', () => {
|
|||
}`,
|
||||
);
|
||||
|
||||
if (included(moddetails.channel_users, searchedusers.searchedUsers[i])) {
|
||||
if (
|
||||
included(moddetails.channel_users, searchedusers.searchedUsers[i])
|
||||
) {
|
||||
inviteMessage = `is already in ${moddetails.channel_name}`;
|
||||
inviteAttr = 'className';
|
||||
inviteAttrAns = 'channel__member';
|
||||
|
|
@ -247,8 +252,8 @@ describe('<ChannelDetails />', () => {
|
|||
.childAt(2)
|
||||
.attr(inviteAttr),
|
||||
).toEqual(inviteAttrAns);
|
||||
}
|
||||
}
|
||||
}
|
||||
const tree = render(context);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { h } from 'preact';
|
||||
import render from 'preact-render-to-json';
|
||||
import { shallow } from 'preact-render-spy';
|
||||
import { deep } from 'preact-render-spy';
|
||||
import View from '../view';
|
||||
|
||||
let exited = false;
|
||||
|
|
@ -43,12 +43,12 @@ describe('<View />', () => {
|
|||
});
|
||||
|
||||
it('should render and test snapshot (with channel)', () => {
|
||||
const tree = shallow(getView(sampleChannel));
|
||||
const tree = deep(getView(sampleChannel), { depth: 2 });
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should have the proper attributes and text values (no channel provided)', () => {
|
||||
const context = shallow(getView([]));
|
||||
const context = deep(getView([]), { depth: 2 });
|
||||
expect(context.find('.chatNonChatView').exists()).toEqual(true);
|
||||
expect(context.find('.container').exists()).toEqual(true);
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ describe('<View />', () => {
|
|||
});
|
||||
|
||||
it('should have the proper attributes and text values (with channel provided)', () => {
|
||||
const context = shallow(getView(sampleChannel));
|
||||
const context = deep(getView(sampleChannel), { depth: 2 });
|
||||
expect(context.find('.chatNonChatView_contentblock').exists()).toEqual(
|
||||
true,
|
||||
);
|
||||
|
|
@ -103,14 +103,14 @@ describe('<View />', () => {
|
|||
});
|
||||
|
||||
it('should trigger exit', () => {
|
||||
const context = shallow(getView([]));
|
||||
const context = deep(getView([]), { depth: 2 });
|
||||
context.find('.chatNonChatView_exitbutton').simulate('click');
|
||||
expect(exited).toEqual(true);
|
||||
exited = false;
|
||||
});
|
||||
|
||||
it('should trigger accept', () => {
|
||||
const context = shallow(getView(sampleChannel));
|
||||
const context = deep(getView(sampleChannel), { depth: 2 });
|
||||
context
|
||||
.find('.cta')
|
||||
.at(0)
|
||||
|
|
@ -124,7 +124,7 @@ describe('<View />', () => {
|
|||
});
|
||||
|
||||
it('should trigger decline', () => {
|
||||
const context = shallow(getView(sampleChannel));
|
||||
const context = deep(getView(sampleChannel), { depth: 2 });
|
||||
context
|
||||
.find('.cta')
|
||||
.at(1)
|
||||
|
|
|
|||
|
|
@ -72,18 +72,31 @@ export function conductModeration(
|
|||
.catch(failureCb);
|
||||
}
|
||||
|
||||
export function getChannels(query,retrievalID, props, paginationNumber, additionalFilters, successCb, failureCb) {
|
||||
export function getChannels(
|
||||
query,
|
||||
retrievalID,
|
||||
props,
|
||||
paginationNumber,
|
||||
additionalFilters,
|
||||
successCb,
|
||||
failureCb,
|
||||
) {
|
||||
const client = algoliasearch(props.algoliaId, props.algoliaKey);
|
||||
const index = client.initIndex(props.algoliaIndex);
|
||||
let filters = {...{
|
||||
hitsPerPage: 30 + paginationNumber,
|
||||
page: paginationNumber
|
||||
}, ...additionalFilters};
|
||||
index.search(query, filters)
|
||||
.then(function(content) {
|
||||
let channels = content.hits
|
||||
if (retrievalID === null || content.hits.filter(e => e.id === retrievalID).length === 1) {
|
||||
successCb(channels, query)
|
||||
const filters = {
|
||||
...{
|
||||
hitsPerPage: 30 + paginationNumber,
|
||||
page: paginationNumber,
|
||||
},
|
||||
...additionalFilters,
|
||||
};
|
||||
index.search(query, filters).then(function(content) {
|
||||
const channels = content.hits;
|
||||
if (
|
||||
retrievalID === null ||
|
||||
content.hits.filter(e => e.id === retrievalID).length === 1
|
||||
) {
|
||||
successCb(channels, query);
|
||||
} else {
|
||||
index.getObjects([`${retrievalID}`], function(err, content) {
|
||||
channels.unshift(content.results[0]);
|
||||
|
|
@ -102,7 +115,7 @@ export function sendKeys(subscription, successCb, failureCb) {
|
|||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
subscription: subscription
|
||||
subscription,
|
||||
}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
|
|
@ -153,10 +166,10 @@ export function getChannelInvites(successCb, failureCb) {
|
|||
.then(response => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
};
|
||||
}
|
||||
|
||||
export function sendChannelInviteAction(id, action, successCb, failureCb) {
|
||||
fetch('/chat_channel_memberships/'+id, {
|
||||
fetch(`/chat_channel_memberships/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -166,11 +179,11 @@ export function sendChannelInviteAction(id, action, successCb, failureCb) {
|
|||
body: JSON.stringify({
|
||||
chat_channel_membership: {
|
||||
user_action: action,
|
||||
}
|
||||
},
|
||||
}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class ChannelDetails extends Component {
|
|||
};
|
||||
|
||||
userInList = (list, user) => {
|
||||
const keys = Object.keys(list)
|
||||
const keys = Object.keys(list);
|
||||
for (const key of keys) {
|
||||
if (user.id === list[key].id) {
|
||||
return true;
|
||||
|
|
@ -144,7 +144,7 @@ class ChannelDetails extends Component {
|
|||
if (this.userInList(channel.channel_users, user)) {
|
||||
invite = (
|
||||
<span className="channel__member">
|
||||
is already in
|
||||
is already in
|
||||
{' '}
|
||||
<em>{channel.channel_name}</em>
|
||||
</span>
|
||||
|
|
@ -153,11 +153,11 @@ class ChannelDetails extends Component {
|
|||
return (
|
||||
<div className="channeldetails__searchedusers">
|
||||
<a href={user.path} target="_blank" rel="noopener noreferrer">
|
||||
<img src={user.user.profile_image_90} alt="profile_image"/>
|
||||
<img src={user.user.profile_image_90} alt="profile_image" />
|
||||
@
|
||||
{user.user.username}
|
||||
{' '}
|
||||
-
|
||||
-
|
||||
{' '}
|
||||
{user.title}
|
||||
</a>
|
||||
|
|
@ -178,7 +178,7 @@ class ChannelDetails extends Component {
|
|||
@
|
||||
{user.username}
|
||||
{' '}
|
||||
-
|
||||
-
|
||||
{' '}
|
||||
{user.name}
|
||||
</a>
|
||||
|
|
@ -188,9 +188,7 @@ class ChannelDetails extends Component {
|
|||
<div className="channeldetails__inviteusers">
|
||||
<h2>Invite Members</h2>
|
||||
<input onKeyUp={this.triggerUserSearch} placeholder="Find users" />
|
||||
<div className="channeldetails__searchresults">
|
||||
{searchedUsers}
|
||||
</div>
|
||||
<div className="channeldetails__searchresults">{searchedUsers}</div>
|
||||
<h2>Pending Invites:</h2>
|
||||
{pendingInvites}
|
||||
<div style={{ marginTop: '10px' }}>
|
||||
|
|
@ -212,12 +210,11 @@ class ChannelDetails extends Component {
|
|||
</h3>
|
||||
<h4>It may not be immediately in the sidebar</h4>
|
||||
<p>
|
||||
Contact the admins at
|
||||
Contact the admins at
|
||||
{' '}
|
||||
<a href="mailto:yo@dev.to">yo@dev.to</a>
|
||||
{' '}
|
||||
if
|
||||
this was a mistake
|
||||
if this was a mistake
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,30 +2,30 @@ import { h, Component } from 'preact';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
export default class CodeEditor extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
import('codemirror')
|
||||
.then(CodeMirror => {
|
||||
const editor = document.getElementById("codeeditor");
|
||||
import('codemirror').then(CodeMirror => {
|
||||
const editor = document.getElementById('codeeditor');
|
||||
const myCodeMirror = CodeMirror(editor, {
|
||||
mode: "javascript",
|
||||
theme: "material",
|
||||
mode: 'javascript',
|
||||
theme: 'material',
|
||||
autofocus: true,
|
||||
});
|
||||
myCodeMirror.setSize("100%", "100%");
|
||||
//Initial trigger:
|
||||
const channel = window.pusher.channel(`presence-channel-${this.props.activeChannelId}`)
|
||||
myCodeMirror.setSize('100%', '100%');
|
||||
// Initial trigger:
|
||||
const channel = window.pusher.channel(
|
||||
`presence-channel-${this.props.activeChannelId}`,
|
||||
);
|
||||
channel.trigger('client-livecode', {
|
||||
context: 'initializing-live-code-channel',
|
||||
channel: `presence-channel-${this.props.activeChannelId}`
|
||||
channel: `presence-channel-${this.props.activeChannelId}`,
|
||||
});
|
||||
//Coding trigger:
|
||||
// Coding trigger:
|
||||
myCodeMirror.on('keyup', cm => {
|
||||
channel.trigger('client-livecode', {
|
||||
keyPressed: true,
|
||||
value: cm.getValue(),
|
||||
cursorPos: cm.getCursor(),
|
||||
});
|
||||
channel.trigger('client-livecode', {
|
||||
keyPressed: true,
|
||||
value: cm.getValue(),
|
||||
cursorPos: cm.getCursor(),
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -35,11 +35,10 @@ export default class CodeEditor extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
|
||||
return <div id="codeeditor" className="chatcodeeditor">
|
||||
<div className="chatcodeeditor__header">Experimental (WIP)</div>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div id="codeeditor" className="chatcodeeditor">
|
||||
<div className="chatcodeeditor__header">Experimental (WIP)</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default class Chat extends Component {
|
|||
static propTypes = {
|
||||
handleKeyDown: PropTypes.func.isRequired,
|
||||
handleSubmitOnClick: PropTypes.func.isRequired,
|
||||
activeChannelId: PropTypes.number
|
||||
activeChannelId: PropTypes.number,
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
|
|
|
|||
|
|
@ -12,109 +12,128 @@ export default class Video extends Component {
|
|||
pageY: null,
|
||||
token: null,
|
||||
room: null,
|
||||
participants: []
|
||||
}
|
||||
participants: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
getTwilioToken('private-video-channel-'+this.props.activeChannelId, this.setupCallChannel)
|
||||
getTwilioToken(
|
||||
`private-video-channel-${this.props.activeChannelId}`,
|
||||
this.setupCallChannel,
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.state.room.disconnect();
|
||||
}
|
||||
|
||||
setupCallChannel = (response) => {
|
||||
setupCallChannel = response => {
|
||||
const component = this;
|
||||
import('twilio-video')
|
||||
.then(({ connect, createLocalVideoTrack }) => {
|
||||
connect(response.token,
|
||||
import('twilio-video').then(({ connect, createLocalVideoTrack }) => {
|
||||
connect(
|
||||
response.token,
|
||||
{
|
||||
name:`private-video-channel-${this.props.activeChannelId}`,
|
||||
name: `private-video-channel-${this.props.activeChannelId}`,
|
||||
audio: true,
|
||||
type: 'peer-to-peer',
|
||||
video: { width: 640 }
|
||||
}).then(function(room) {
|
||||
component.setState({token: response.token, room: room})
|
||||
createLocalVideoTrack().then(track => {
|
||||
let localMediaContainer = document.getElementById('videolocalscreen');
|
||||
localMediaContainer.appendChild(track.attach());
|
||||
});
|
||||
let roomParticipants = []
|
||||
room.participants.forEach(participant => {
|
||||
component.triggerRemoteJoin(participant);
|
||||
roomParticipants.push(participant);
|
||||
});
|
||||
component.setState({participants: roomParticipants})
|
||||
room.on('participantConnected', function(participant) {
|
||||
component.triggerRemoteJoin(participant);
|
||||
let roomParticipants = []
|
||||
video: { width: 640 },
|
||||
},
|
||||
).then(
|
||||
function(room) {
|
||||
component.setState({ token: response.token, room });
|
||||
createLocalVideoTrack().then(track => {
|
||||
const localMediaContainer = document.getElementById(
|
||||
'videolocalscreen',
|
||||
);
|
||||
localMediaContainer.appendChild(track.attach());
|
||||
});
|
||||
const roomParticipants = [];
|
||||
room.participants.forEach(participant => {
|
||||
component.triggerRemoteJoin(participant);
|
||||
roomParticipants.push(participant);
|
||||
});
|
||||
component.setState({participants: roomParticipants})
|
||||
room.on('participantDisconnected', function(participant) {
|
||||
component.props.onExit()
|
||||
component.setState({ participants: roomParticipants });
|
||||
room.on('participantConnected', function(participant) {
|
||||
component.triggerRemoteJoin(participant);
|
||||
const roomParticipants = [];
|
||||
room.participants.forEach(participant => {
|
||||
roomParticipants.push(participant);
|
||||
});
|
||||
component.setState({ participants: roomParticipants });
|
||||
room.on('participantDisconnected', function(participant) {
|
||||
component.props.onExit();
|
||||
});
|
||||
});
|
||||
})
|
||||
}, function(error) {
|
||||
document.getElementById('videoremotescreen').innerHTML = "";
|
||||
console.error('Unable to connect to Room: ' + error.message);
|
||||
});
|
||||
},
|
||||
function(error) {
|
||||
document.getElementById('videoremotescreen').innerHTML = '';
|
||||
console.error(`Unable to connect to Room: ${error.message}`);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
triggerRemoteJoin = (participant) => {
|
||||
triggerRemoteJoin = participant => {
|
||||
// document.getElementById('videoremotescreen').innerHTML = ""
|
||||
participant.on('trackAdded', track => {
|
||||
document.getElementById('videoremotescreen').appendChild(track.attach());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleDrag = e => {
|
||||
if (!this.state.pageX) {
|
||||
this.setState({pageX:e.pageX,
|
||||
this.setState({
|
||||
pageX: e.pageX,
|
||||
pageY: e.pageY,
|
||||
offsetDiffX: e.pageX - e.target.offsetLeft,
|
||||
offsetDiffY: e.pageY - e.target.offsetTop,
|
||||
})
|
||||
} else if (e.pageX != 0){
|
||||
});
|
||||
} else if (e.pageX != 0) {
|
||||
this.setState({
|
||||
leftPx: this.state.pageX + e.pageX - this.state.pageX - this.state.offsetDiffX,
|
||||
topPx: this.state.pageY + e.pageY - this.state.pageY - this.state.offsetDiffY
|
||||
})
|
||||
leftPx:
|
||||
this.state.pageX +
|
||||
e.pageX -
|
||||
this.state.pageX -
|
||||
this.state.offsetDiffX,
|
||||
topPx:
|
||||
this.state.pageY +
|
||||
e.pageY -
|
||||
this.state.pageY -
|
||||
this.state.offsetDiffY,
|
||||
});
|
||||
} else if (e.pageX === 0) {
|
||||
this.setState({pageX:null,
|
||||
this.setState({
|
||||
pageX: null,
|
||||
pageY: null,
|
||||
offsetDiffX: null,
|
||||
offsetDiffY: null,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className="chat__videocall"
|
||||
id="chat__videocall"
|
||||
draggable="true"
|
||||
onDrag={this.handleDrag}
|
||||
style={{left:this.state.leftPx+'px', top: this.state.topPx+'px'}}
|
||||
>
|
||||
<div id="videoremotescreen" class={'chat__remotevideoscreen-'+this.state.participants.length}></div>
|
||||
<div className="chat__localvideoscren" id="videolocalscreen"></div>
|
||||
<button className="chat__videocallexitbutton" onClick={this.props.onExit}>
|
||||
id="chat__videocall"
|
||||
draggable="true"
|
||||
onDrag={this.handleDrag}
|
||||
style={{ left: `${this.state.leftPx}px`, top: `${this.state.topPx}px` }}
|
||||
>
|
||||
<div
|
||||
id="videoremotescreen"
|
||||
className={`chat__remotevideoscreen-${
|
||||
this.state.participants.length
|
||||
}`}
|
||||
/>
|
||||
<div className="chat__localvideoscren" id="videolocalscreen" />
|
||||
<button
|
||||
className="chat__videocallexitbutton"
|
||||
onClick={this.props.onExit}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,49 @@
|
|||
|
||||
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
export default class View extends Component {
|
||||
channel = (props) => {
|
||||
return (
|
||||
<div className="chatNonChatView_contentblock">
|
||||
<h2>{props.channel.channel_name}</h2>
|
||||
<div>
|
||||
<em>{props.channel.description}</em>
|
||||
</div>
|
||||
<button
|
||||
className="cta"
|
||||
onClick={this.props.onAcceptInvitation}
|
||||
data-content={props.channel.membership_id}
|
||||
>
|
||||
Accept
|
||||
</button>
|
||||
<button
|
||||
className="cta"
|
||||
onClick={this.props.onDeclineInvitation}
|
||||
data-content={props.channel.membership_id}
|
||||
>
|
||||
Decline
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const channels = this.props.channels.map((channel) => {
|
||||
return <div className='chatNonChatView_contentblock'>
|
||||
<h2>{channel.channel_name}</h2>
|
||||
<div><em>{channel.description}</em></div>
|
||||
<button className='cta' onClick={this.props.onAcceptInvitation} data-content={channel.membership_id}>Accept</button>
|
||||
<button className='cta' onClick={this.props.onDeclineInvitation} data-content={channel.membership_id}>Decline</button>
|
||||
</div>
|
||||
const channels = this.props.channels.map(channel => {
|
||||
return <this.channel channel={channel} />
|
||||
});
|
||||
return <div className="chatNonChatView">
|
||||
<div className="container">
|
||||
<button
|
||||
class="chatNonChatView_exitbutton"
|
||||
data-content="exit"
|
||||
onClick={this.props.onViewExit}
|
||||
>×</button>
|
||||
<h1>Channel Invitations 🤗</h1>
|
||||
{channels}
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div className="chatNonChatView">
|
||||
<div className="container">
|
||||
<button
|
||||
className="chatNonChatView_exitbutton"
|
||||
data-content="exit"
|
||||
onClick={this.props.onViewExit}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
<h1>Channel Invitations 🤗</h1>
|
||||
{channels}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ function isUserSignedIn() {
|
|||
function renderPage() {
|
||||
import('../src/Onboarding')
|
||||
.then(({ default: Onboarding }) => {
|
||||
let waitingForOnboarding = setInterval(function(){
|
||||
if (document.getElementById("main-head-stylesheet")) {
|
||||
render(<Onboarding />, document.getElementById('top-bar'))
|
||||
const waitingForOnboarding = setInterval(function() {
|
||||
if (document.getElementById('main-head-stylesheet')) {
|
||||
render(<Onboarding />, document.getElementById('top-bar'));
|
||||
clearInterval(waitingForOnboarding);
|
||||
}
|
||||
}, 3)
|
||||
}, 3);
|
||||
})
|
||||
.catch(error => {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@ HTMLDocument.prototype.ready = new Promise(resolve => {
|
|||
return null;
|
||||
});
|
||||
|
||||
document.ready.then(function(){
|
||||
document.ready.then(function() {
|
||||
loadForm();
|
||||
window.InstantClick.on('change', () => {
|
||||
if (document.getElementById('article-form')){
|
||||
if (document.getElementById('article-form')) {
|
||||
loadForm();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function loadForm(){
|
||||
function loadForm() {
|
||||
getUserDataAndCsrfToken().then(({ currentUser, csrfToken }) => {
|
||||
window.currentUser = currentUser;
|
||||
window.csrfToken = csrfToken;
|
||||
|
|
@ -32,5 +32,5 @@ function loadForm(){
|
|||
root,
|
||||
root.firstElementChild,
|
||||
);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { GithubRepos } from '../githubRepos/githubRepos';
|
|||
|
||||
function loadElement() {
|
||||
const root = document.getElementById('github-repos-container');
|
||||
if (root){
|
||||
if (root) {
|
||||
render(<GithubRepos />, root, root.firstElementChild);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,9 @@ class SidebarWidget extends Component {
|
|||
return (
|
||||
<div className="widget" id="widget-00001">
|
||||
<div className="widget-suggested-follows-container">
|
||||
<header><h4>who to follow</h4></header>
|
||||
<header>
|
||||
<h4>who to follow</h4>
|
||||
</header>
|
||||
<div className="widget-body">{users}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -222,8 +222,8 @@ class Onboarding extends Component {
|
|||
const newCheckedUsers = checkedUsers.slice();
|
||||
const index = checkedUsers.indexOf(user);
|
||||
|
||||
if(index > -1){
|
||||
newCheckedUsers.splice(index,1);
|
||||
if (index > -1) {
|
||||
newCheckedUsers.splice(index, 1);
|
||||
} else {
|
||||
newCheckedUsers.push(user);
|
||||
}
|
||||
|
|
@ -259,12 +259,7 @@ class Onboarding extends Component {
|
|||
}
|
||||
|
||||
handleNextButton() {
|
||||
const {
|
||||
users,
|
||||
articles,
|
||||
checkedUsers,
|
||||
profileInfo,
|
||||
} = this.state;
|
||||
const { users, articles, checkedUsers, profileInfo } = this.state;
|
||||
let { pageNumber } = this.state;
|
||||
if (pageNumber === 2 && users.length === 0 && articles.length === 0) {
|
||||
this.getUsersToFollow();
|
||||
|
|
|
|||
|
|
@ -30,12 +30,14 @@ const OnboardingArticle = ({ article, isSaved, onSaveArticle }) => (
|
|||
<img
|
||||
src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/reactions-stack-4bb9c1e4b3e71b7aa135d6f9a5ef29a6494141da882edd4fa971a77abe13dbe7.png"
|
||||
alt="Reactions"
|
||||
/>{' '}
|
||||
/>
|
||||
{' '}
|
||||
{article.positive_reactions_count}
|
||||
<img
|
||||
src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/comments-bubble-7448082accd39cfe9db9b977f38fa6e8f8d26dc43e142c5d160400d6f952ee47.png"
|
||||
alt="Comments"
|
||||
/>{' '}
|
||||
/>
|
||||
{' '}
|
||||
{article.comments_count}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,8 +27,11 @@ class OnboardingArticles extends Component {
|
|||
return (
|
||||
<div className="onboarding-user-container">
|
||||
<div className="onboarding-user-cta">
|
||||
When you see an interesting post, you can{' '}
|
||||
<strong className="purple">SAVE</strong> it. To get started, here are
|
||||
When you see an interesting post, you can
|
||||
{' '}
|
||||
<strong className="purple">SAVE</strong>
|
||||
{' '}
|
||||
it. To get started, here are
|
||||
pre-selected suggestions.
|
||||
</div>
|
||||
<div className="onboarding-user-list">
|
||||
|
|
|
|||
|
|
@ -1,24 +1,58 @@
|
|||
import { h, render, Component } from 'preact';
|
||||
|
||||
// page 1
|
||||
const OnboardingProfile = ({onChange}) => {
|
||||
const OnboardingProfile = ({ onChange }) => {
|
||||
return (
|
||||
<div class="onboarding-profile-page">
|
||||
<div className="onboarding-profile-page">
|
||||
<div className="onboarding-user-cta">
|
||||
Tell the us a bit about yourself ❤️
|
||||
</div>
|
||||
<div class="onboarding-profile-question">What's your quick bio?</div>
|
||||
<input name="summary" placeholder="e.g. I'm a passionate hacker wizard unicorn ninja" maxLength="120" onChange={onChange} />
|
||||
<div class="onboarding-profile-question">Where are you located?</div>
|
||||
<input name="location" placeholder="e.g. New York City" maxLength="60" onChange={onChange} />
|
||||
<div class="onboarding-profile-question">What is your title?</div>
|
||||
<input name="employment_title" placeholder="e.g. Frontend developer" maxLength="60" onChange={onChange} />
|
||||
<div class="onboarding-profile-question">Where do you work?</div>
|
||||
<input name="employer_name" placeholder="e.g. Google" maxLength="60" onChange={onChange} />
|
||||
<div class="onboarding-profile-question">What are your core skills/languages?</div>
|
||||
<input name="mostly_work_with" placeholder="e.g. JavaScript and MongoDB" maxLength="200" onChange={onChange} />
|
||||
<div class="onboarding-profile-question">What are you currently learning/playing with?</div>
|
||||
<input name="currently_learning" placeholder="e.g. Rust and Docker" maxLength="200" onChange={onChange} />
|
||||
<div className="onboarding-profile-question">What's your quick bio?</div>
|
||||
<input
|
||||
name="summary"
|
||||
placeholder="e.g. I'm a passionate hacker wizard unicorn ninja"
|
||||
maxLength="120"
|
||||
onChange={onChange}
|
||||
/>
|
||||
<div className="onboarding-profile-question">Where are you located?</div>
|
||||
<input
|
||||
name="location"
|
||||
placeholder="e.g. New York City"
|
||||
maxLength="60"
|
||||
onChange={onChange}
|
||||
/>
|
||||
<div className="onboarding-profile-question">What is your title?</div>
|
||||
<input
|
||||
name="employment_title"
|
||||
placeholder="e.g. Frontend developer"
|
||||
maxLength="60"
|
||||
onChange={onChange}
|
||||
/>
|
||||
<div className="onboarding-profile-question">Where do you work?</div>
|
||||
<input
|
||||
name="employer_name"
|
||||
placeholder="e.g. Google"
|
||||
maxLength="60"
|
||||
onChange={onChange}
|
||||
/>
|
||||
<div className="onboarding-profile-question">
|
||||
What are your core skills/languages?
|
||||
</div>
|
||||
<input
|
||||
name="mostly_work_with"
|
||||
placeholder="e.g. JavaScript and MongoDB"
|
||||
maxLength="200"
|
||||
onChange={onChange}
|
||||
/>
|
||||
<div className="onboarding-profile-question">
|
||||
What are you currently learning/playing with?
|
||||
</div>
|
||||
<input
|
||||
name="currently_learning"
|
||||
placeholder="e.g. Rust and Docker"
|
||||
maxLength="200"
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,17 +12,28 @@ class OnboardingSingleTag extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const backgroundColor = this.props.tag.following ? this.props.tag.bg_color_hex : ''
|
||||
const textroundColor = this.props.tag.following ? this.props.tag.text_color_hex : ''
|
||||
const backgroundColor = this.props.tag.following
|
||||
? this.props.tag.bg_color_hex
|
||||
: '';
|
||||
const textroundColor = this.props.tag.following
|
||||
? this.props.tag.text_color_hex
|
||||
: '';
|
||||
return (
|
||||
<div className={`onboarding-tag-container${this.props.tag.following ? ' followed-tag' : ''}`} id={`onboarding-tag-container-${this.props.tag.name}`} style={`background: ${backgroundColor}`}>
|
||||
<div
|
||||
className={`onboarding-tag-container${
|
||||
this.props.tag.following ? ' followed-tag' : ''
|
||||
}`}
|
||||
id={`onboarding-tag-container-${this.props.tag.name}`}
|
||||
style={`background: ${backgroundColor}`}
|
||||
>
|
||||
<a
|
||||
className="onboarding-tag-link"
|
||||
href="#"
|
||||
style={`color:${textroundColor}`}
|
||||
onClick={this.onClick}
|
||||
>
|
||||
#{this.props.tag.name}
|
||||
#
|
||||
{this.props.tag.name}
|
||||
</a>
|
||||
<a
|
||||
className="onboarding-tag-link-follow"
|
||||
|
|
|
|||
|
|
@ -6,21 +6,22 @@ class OnboardingTags extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const tags = this.props.allTags.map((tag) => {
|
||||
const tags = this.props.allTags.map(tag => {
|
||||
return (
|
||||
<OnboardingSingleTag key={tag.id} tag={tag} onTagClick={this.props.handleFollowTag.bind(this, tag)} />
|
||||
<OnboardingSingleTag
|
||||
key={tag.id}
|
||||
tag={tag}
|
||||
onTagClick={this.props.handleFollowTag.bind(this, tag)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="tags-slide">
|
||||
<div className="onboarding-user-cta">
|
||||
Personalize your home feed
|
||||
</div>
|
||||
<div className="tags-col-container">
|
||||
{tags}
|
||||
</div>
|
||||
<div className="onboarding-user-cta">Personalize your home feed</div>
|
||||
<div className="tags-col-container">{tags}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,19 +12,23 @@ class OnboardingUsers extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const followList = this.props.users.map((user) => {
|
||||
const followList = this.props.users.map(user => {
|
||||
return (
|
||||
<div className="onboarding-user-list-row" key={user.id} >
|
||||
<div className="onboarding-user-list-row" key={user.id}>
|
||||
<div className="onboarding-user-list-key">
|
||||
<img
|
||||
src={user.profile_image_url}
|
||||
alt={user.name}
|
||||
/>
|
||||
<img src={user.profile_image_url} alt={user.name} />
|
||||
<div>{user.name}</div>
|
||||
<div className="onboarding-user-list-row__summary"><em>{user.summary}</em></div>
|
||||
<div className="onboarding-user-list-row__summary">
|
||||
<em>{user.summary}</em>
|
||||
</div>
|
||||
</div>
|
||||
<div className="onboarding-user-list-checkbox">
|
||||
<button onClick={this.props.handleCheckUser.bind(this, user)} className={this.props.checkedUsers.indexOf(user) > -1 ? 'checked' : ''}>
|
||||
<button
|
||||
onClick={this.props.handleCheckUser.bind(this, user)}
|
||||
className={
|
||||
this.props.checkedUsers.indexOf(user) > -1 ? 'checked' : ''
|
||||
}
|
||||
>
|
||||
{this.props.checkedUsers.indexOf(user) > -1 ? '✓' : '+'}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -33,11 +37,7 @@ class OnboardingUsers extends Component {
|
|||
});
|
||||
const renderLoadingOrList = () => {
|
||||
if (this.props.users.length === 0) {
|
||||
return (
|
||||
<div className="onboarding-user-loading">
|
||||
Loading...
|
||||
</div>
|
||||
);
|
||||
return <div className="onboarding-user-loading">Loading...</div>;
|
||||
}
|
||||
return followList;
|
||||
};
|
||||
|
|
@ -49,12 +49,20 @@ class OnboardingUsers extends Component {
|
|||
</div>
|
||||
<div className="onboarding-user-list">
|
||||
<div className="onboarding-user-list-header">
|
||||
<div className="onboarding-user-list-key">
|
||||
Follow All
|
||||
</div>
|
||||
<div className="onboarding-user-list-key">Follow All</div>
|
||||
<div className="onboarding-user-list-checkbox">
|
||||
<button id="onboarding-user-follow-all-btn" onClick={this.handleAllClick} className={this.props.checkedUsers.length === this.props.users.length ? 'checked' : ''}>
|
||||
{this.props.checkedUsers.length === this.props.users.length ? '✓' : '+'}
|
||||
<button
|
||||
id="onboarding-user-follow-all-btn"
|
||||
onClick={this.handleAllClick}
|
||||
className={
|
||||
this.props.checkedUsers.length === this.props.users.length
|
||||
? 'checked'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{this.props.checkedUsers.length === this.props.users.length
|
||||
? '✓'
|
||||
: '+'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -72,5 +80,4 @@ OnboardingUsers.propTypes = {
|
|||
handleCheckAllUsers: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
export default OnboardingUsers;
|
||||
|
|
|
|||
|
|
@ -3,16 +3,22 @@ import { h, render, Component } from 'preact';
|
|||
// page 1
|
||||
const OnboardingWelcome = () => {
|
||||
const messages = [
|
||||
"Thank you for joining the DEV Community.",
|
||||
"Keep up with the people and software trends you care about. ❤️",
|
||||
'Thank you for joining the DEV Community.',
|
||||
'Keep up with the people and software trends you care about. ❤️',
|
||||
];
|
||||
|
||||
const specialMessage = "Let's get started!";
|
||||
|
||||
return (
|
||||
<div class="onboarding-initial-welcome">
|
||||
{messages.map(item => (<p>{item}</p>))}
|
||||
<p><em><strong className="green">{specialMessage}</strong></em></p>
|
||||
<div className="onboarding-initial-welcome">
|
||||
{messages.map(item => (
|
||||
<p>{item}</p>
|
||||
))}
|
||||
<p>
|
||||
<em>
|
||||
<strong className="green">{specialMessage}</strong>
|
||||
</em>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { h } from 'preact';
|
||||
|
||||
const OnboardingWelcomeThread = () => {
|
||||
const wrapInYellow = message => (
|
||||
<strong className="yellow">{message}</strong>
|
||||
);
|
||||
const wrapInYellow = message => <strong className="yellow">{message}</strong>;
|
||||
|
||||
return (
|
||||
<div class="onboarding-final-message">
|
||||
<div className="onboarding-final-message">
|
||||
<p>Software is driven by community.</p>
|
||||
<p>Don't hesitate to find a discussion and jump right in.</p>
|
||||
<p><em class="green">Everyone is welcome!</em></p>
|
||||
<p>
|
||||
<em className="green">Everyone is welcome!</em>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ export class Search extends Component {
|
|||
}
|
||||
|
||||
enableSearchPageChecker = true;
|
||||
|
||||
globalSearchKeyListener;
|
||||
|
||||
enableSearchPageListener = () => {
|
||||
this.enableSearchPageChecker = true;
|
||||
};
|
||||
|
|
@ -76,7 +78,10 @@ export class Search extends Component {
|
|||
}
|
||||
|
||||
search = event => {
|
||||
const { keyCode, target: { value } } = event;
|
||||
const {
|
||||
keyCode,
|
||||
target: { value },
|
||||
} = event;
|
||||
|
||||
this.enableSearchPageChecker = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { h } from 'preact';
|
||||
|
||||
const UnopenedChannelNotice = () => {
|
||||
return (
|
||||
<div style={{position: 'absolute'}}>
|
||||
</div>
|
||||
);
|
||||
return <div style={{ position: 'absolute' }} />;
|
||||
};
|
||||
|
||||
export default UnopenedChannelNotice;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { h } from 'preact';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { globalModalDecorator } from '../__stories__/story-decorators';
|
||||
import { globalModalDecorator } from './story-decorators';
|
||||
import OnboardingWelcome from '../OnboardingWelcome';
|
||||
|
||||
storiesOf('OnboardingWelcome', module)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { h } from 'preact';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { globalModalDecorator } from '../__stories__/story-decorators';
|
||||
import { globalModalDecorator } from './story-decorators';
|
||||
import OnboardingWelcomeThread from '../OnboardingWelcomeThread';
|
||||
|
||||
storiesOf('OnboardingWelcomeThread', module)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import { h, render, Component } from 'preact';
|
||||
import setupPusher from './pusher';
|
||||
|
||||
|
||||
|
||||
class UnopenedChannelNotice extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const unopenedChannels = this.props.unopenedChannels;
|
||||
const visible = unopenedChannels.length > 0 ? true : false;
|
||||
const { unopenedChannels } = this.props;
|
||||
const visible = unopenedChannels.length > 0;
|
||||
this.state = {
|
||||
visible: visible,
|
||||
unopenedChannels }
|
||||
visible,
|
||||
unopenedChannels,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -19,79 +18,95 @@ class UnopenedChannelNotice extends Component {
|
|||
messageCreated: this.receiveNewMessage,
|
||||
});
|
||||
const component = this;
|
||||
document.getElementById("connect-link").onclick = function(){
|
||||
//Hack, should probably be its own component in future
|
||||
document.getElementById("connect-number").classList.remove("showing");
|
||||
component.setState({visible: false});
|
||||
}
|
||||
document.getElementById('connect-link').onclick = function() {
|
||||
// Hack, should probably be its own component in future
|
||||
document.getElementById('connect-number').classList.remove('showing');
|
||||
component.setState({ visible: false });
|
||||
};
|
||||
}
|
||||
|
||||
receiveNewMessage = e => {
|
||||
if (location.pathname.startsWith("/connect")) {
|
||||
return
|
||||
if (location.pathname.startsWith('/connect')) {
|
||||
return;
|
||||
}
|
||||
let channels = this.state.unopenedChannels;
|
||||
const newObj = {adjusted_slug: e.chat_channel_adjusted_slug}
|
||||
if(channels.filter(obj => obj.adjusted_slug === newObj.adjusted_slug).length === 0 &&
|
||||
newObj.adjusted_slug != `@${window.currentUser.username}`) {
|
||||
const channels = this.state.unopenedChannels;
|
||||
const newObj = { adjusted_slug: e.chat_channel_adjusted_slug };
|
||||
if (
|
||||
channels.filter(obj => obj.adjusted_slug === newObj.adjusted_slug)
|
||||
.length === 0 &&
|
||||
newObj.adjusted_slug != `@${window.currentUser.username}`
|
||||
) {
|
||||
channels.push(newObj);
|
||||
}
|
||||
this.setState({
|
||||
visible: (channels.length > 0 && e.user_id != window.currentUser.id),
|
||||
unopenedChannels: channels
|
||||
})
|
||||
visible: channels.length > 0 && e.user_id != window.currentUser.id,
|
||||
unopenedChannels: channels,
|
||||
});
|
||||
|
||||
const number = document.getElementById("connect-number")
|
||||
number.classList.add("showing")
|
||||
number.innerHTML = channels.length
|
||||
const number = document.getElementById('connect-number');
|
||||
number.classList.add('showing');
|
||||
number.innerHTML = channels.length;
|
||||
const component = this;
|
||||
if (channels.length === 0) {
|
||||
number.classList.remove("showing")
|
||||
number.classList.remove('showing');
|
||||
} else {
|
||||
document.getElementById("connect-link").href = `/connect/${channels[0].adjusted_slug}`
|
||||
document.getElementById('connect-link').href = `/connect/${
|
||||
channels[0].adjusted_slug
|
||||
}`;
|
||||
}
|
||||
setTimeout(function(){
|
||||
component.setState({visible: false});
|
||||
}, 7500)
|
||||
}
|
||||
setTimeout(function() {
|
||||
component.setState({ visible: false });
|
||||
}, 7500);
|
||||
};
|
||||
|
||||
handleClick = e => {
|
||||
document.getElementById("connect-number").classList.remove("showing");
|
||||
this.setState({visible: false})
|
||||
}
|
||||
document.getElementById('connect-number').classList.remove('showing');
|
||||
this.setState({ visible: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.visible) {
|
||||
const channels = this.state.unopenedChannels.map(channel => {
|
||||
return <a
|
||||
href={"/connect/"+channel.adjusted_slug}
|
||||
style={{
|
||||
background: "#66e2d5",
|
||||
color: "black",
|
||||
border: "1px solid black",
|
||||
padding: "2px 7px",
|
||||
display: "inline-block",
|
||||
margin: "3px 6px",
|
||||
borderRadius: "3px"}}>{channel.adjusted_slug}</a>
|
||||
return (
|
||||
<a
|
||||
href={`/connect/${channel.adjusted_slug}`}
|
||||
style={{
|
||||
background: '#66e2d5',
|
||||
color: 'black',
|
||||
border: '1px solid black',
|
||||
padding: '2px 7px',
|
||||
display: 'inline-block',
|
||||
margin: '3px 6px',
|
||||
borderRadius: '3px',
|
||||
}}
|
||||
>
|
||||
{channel.adjusted_slug}
|
||||
</a>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<a
|
||||
onClick={this.handleClick}
|
||||
href={"/connect/"+this.state.unopenedChannels[0].adjusted_slug}
|
||||
href={`/connect/${this.state.unopenedChannels[0].adjusted_slug}`}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
zIndex: '200',
|
||||
top: '44px',
|
||||
right: 0,
|
||||
left: 0,
|
||||
background: '#66e2d5',
|
||||
borderBottom: '1px solid black',
|
||||
color: 'black',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
fontSize: '17px',
|
||||
opacity: '0.94',
|
||||
padding: '19px 5px 14px'}}>
|
||||
New Message from {channels}
|
||||
position: 'fixed',
|
||||
zIndex: '200',
|
||||
top: '44px',
|
||||
right: 0,
|
||||
left: 0,
|
||||
background: '#66e2d5',
|
||||
borderBottom: '1px solid black',
|
||||
color: 'black',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
fontSize: '17px',
|
||||
opacity: '0.94',
|
||||
padding: '19px 5px 14px',
|
||||
}}
|
||||
>
|
||||
New Message from
|
||||
{' '}
|
||||
{channels}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
@ -99,9 +114,15 @@ class UnopenedChannelNotice extends Component {
|
|||
}
|
||||
|
||||
export default function getUnopenedChannels(user, successCb) {
|
||||
render(<UnopenedChannelNotice unopenedChannels={[]} pusherKey={document.body.dataset.pusherKey} />, document.getElementById('message-notice'));
|
||||
if (location.pathname.startsWith("/connect")) {
|
||||
return
|
||||
render(
|
||||
<UnopenedChannelNotice
|
||||
unopenedChannels={[]}
|
||||
pusherKey={document.body.dataset.pusherKey}
|
||||
/>,
|
||||
document.getElementById('message-notice'),
|
||||
);
|
||||
if (location.pathname.startsWith('/connect')) {
|
||||
return;
|
||||
}
|
||||
fetch('/chat_channels?state=unopened', {
|
||||
method: 'GET',
|
||||
|
|
@ -109,13 +130,15 @@ export default function getUnopenedChannels(user, successCb) {
|
|||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
const number = document.getElementById("connect-number")
|
||||
const number = document.getElementById('connect-number');
|
||||
if (json.length > 0) {
|
||||
number.classList.add("showing")
|
||||
number.innerHTML = json.length
|
||||
document.getElementById("connect-link").href = `/connect/${json[0].adjusted_slug}` // Jump the user directly to the channel where appropriate
|
||||
number.classList.add('showing');
|
||||
number.innerHTML = json.length;
|
||||
document.getElementById('connect-link').href = `/connect/${
|
||||
json[0].adjusted_slug
|
||||
}`; // Jump the user directly to the channel where appropriate
|
||||
} else {
|
||||
number.classList.remove("showing")
|
||||
number.classList.remove('showing');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"git add"
|
||||
],
|
||||
"{app,spec}/**/*.rb": [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue