Skip to content
This repository was archived by the owner on Oct 11, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7843867
Add new thread route, redirect old route to new route in frontend
mxstbr Aug 13, 2018
fb78373
Add support custom slugs in thread URLs
mxstbr Aug 13, 2018
83ded30
Redirect thread URL without slug to URL with slug
mxstbr Aug 13, 2018
f92be6b
Add canonical link meta tag to threads
mxstbr Aug 13, 2018
19372fe
Fix flow
mxstbr Aug 13, 2018
7c88988
Move links over to new thread url
mxstbr Aug 20, 2018
22b9fe0
Merge branch 'alpha' into nicer-thread-slugs
mxstbr Aug 20, 2018
3edf855
Maybe fix e2e tests
mxstbr Aug 20, 2018
93e5dad
Render thread view at old route if non-existant
mxstbr Aug 21, 2018
f40039c
Persist search for message selection in thread redirect
mxstbr Aug 22, 2018
1760c76
Fix last e2e test
mxstbr Aug 22, 2018
8e78580
Merge branch 'alpha' into nicer-thread-slugs
mxstbr Aug 22, 2018
5656d4a
Fix typo
mxstbr Aug 22, 2018
02e749e
Merge branch 'alpha' into nicer-thread-slugs
mxstbr Aug 22, 2018
0327023
Switch to better thread slugs structure
mxstbr Aug 24, 2018
ff5378a
Remove console.log
mxstbr Aug 24, 2018
286b610
Revert "Switch to better thread slugs structure"
mxstbr Aug 29, 2018
c6310c1
Merge branch 'alpha' into nicer-thread-slugs
mxstbr Aug 29, 2018
3c8886e
Merge branch 'alpha' into nicer-thread-slugs
mxstbr Nov 5, 2018
3bfa44d
Fix nested thread slugs
mxstbr Nov 5, 2018
fe15249
Fix "Copy link to message" functionality
mxstbr Nov 5, 2018
8fb5c88
Fix timestamp link on DM messages
mxstbr Nov 5, 2018
75de274
Merge branch 'alpha' into nicer-thread-slugs
mxstbr Nov 19, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions cypress/integration/thread_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,7 @@ describe('Thread View', () => {
expect($p).to.have.length(3);
});
// the url should contain the message query param
cy.url().should(
'eq',
`http://localhost:3000/thread/${thread.id}?m=MTQ4MzIyNTE5OTk5OQ==`
);
cy.url().should('contain', `${thread.id}?m=MTQ4MzIyNTE5OTk5OQ==`);
});
});

Expand Down Expand Up @@ -204,10 +201,7 @@ describe('Thread View', () => {
expect($p).to.have.length(3);
});
// the url should contain the message query param
cy.url().should(
'eq',
`http://localhost:3000/thread/${thread.id}?m=MTQ4MzIyNTE5OTk5OQ==`
);
cy.url().should('contain', `?m=MTQ4MzIyNTE5OTk5OQ==`);
});
});

Expand Down Expand Up @@ -240,7 +234,7 @@ describe('Thread View', () => {
// the message should not be selected
cy.get('[data-cy="message-selected"]').should('not.be.visible');
// the url should not have changed
cy.url().should('eq', `http://localhost:3000/thread/${thread.id}`);
cy.url().should('not.contain', `?m`);

// unlike the message from the message action bar
cy.get('[data-cy="unlike-action"]')
Expand All @@ -254,7 +248,7 @@ describe('Thread View', () => {
expect($p).to.have.length(3);
});
// the url should not have changed
cy.url().should('eq', `http://localhost:3000/thread/${thread.id}`);
cy.url().should('not.contain', `?m`);
});

it('should unlike a message from the inline reaction', () => {
Expand All @@ -274,7 +268,7 @@ describe('Thread View', () => {
// no inline like buttons should be visible
cy.get('[data-cy="inline-unlike-action"]').should('not.be.visible');
// the url should not have changed
cy.url().should('eq', `http://localhost:3000/thread/${thread.id}`);
cy.url().should('not.contain', `?m`);
});

it('should not allow user to like their own message from inline reaction', () => {
Expand Down
19 changes: 17 additions & 2 deletions src/components/message/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import {
LikeAction,
EditedIndicator,
} from './style';
import getThreadLink from 'src/helpers/get-thread-link';
import type { GetThreadType } from 'shared/graphql/queries/thread/getThread';

type Props = {|
me: boolean,
Expand All @@ -49,6 +51,7 @@ type Props = {|
canModerateMessage: boolean,
threadType: 'directMessageThread' | 'story',
threadId: string,
thread: GetThreadType,
toggleReaction: Function,
location: Location,
history: History,
Expand Down Expand Up @@ -154,12 +157,18 @@ class Message extends React.Component<Props, State> {
toggleReaction,
threadId,
threadType,
thread,
} = this.props;
const { isEditing } = this.state;

const canEditMessage = me && message.messageType !== 'media';
const selectedMessageId = btoa(new Date(message.timestamp).getTime() - 1);
const messageUrl = `/thread/${threadId}?m=${selectedMessageId}`;
const messageUrl =
threadType === 'story' && thread
? `/${getThreadLink(thread)}?m=${selectedMessageId}`
: threadType === 'directMessageThread'
? `/messages/${threadId}?m=${selectedMessageId}`
: `/thread/${threadId}?m=${selectedMessageId}`;

return (
<MessagesContext.Consumer>
Expand Down Expand Up @@ -359,7 +368,13 @@ class Message extends React.Component<Props, State> {
background: 'none',
borderLeft: '1px solid #DFE7EF',
}}
data-clipboard-text={`${CLIENT_URL}/thread/${threadId}?m=${selectedMessageId}`}
data-clipboard-text={
thread
? `${CLIENT_URL}/${getThreadLink(
thread
)}?m=${selectedMessageId}`
: `${CLIENT_URL}/thread/${threadId}?m=${selectedMessageId}`
}
onSuccess={() =>
this.props.dispatch(
addToastWithTimeout(
Expand Down
2 changes: 2 additions & 0 deletions src/components/messageGroup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ class Messages extends React.Component<Props, State> {
threadId,
isModerator,
lastSeen,
thread,
} = this.props;

let hasInjectedUnseenRobo;
Expand Down Expand Up @@ -251,6 +252,7 @@ class Messages extends React.Component<Props, State> {
canModerateMessage={canModerateMessage}
threadType={threadType}
threadId={threadId}
thread={thread}
/>
</MessagesContext.Provider>
</ErrorBoundary>
Expand Down
11 changes: 11 additions & 0 deletions src/helpers/get-thread-link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @flow
import slugg from 'slugg';
import type { ThreadInfoType } from 'shared/graphql/fragments/thread/threadInfo';

const getThreadLink = (thread: ThreadInfoType) => {
return `${thread.community.slug}/${thread.channel.slug}/${slugg(
thread.content.title
)}~${thread.id}`;
};

export default getThreadLink;
13 changes: 12 additions & 1 deletion src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Navbar from './views/navbar';
import Status from './views/status';
import Login from './views/login';
import DirectMessages from './views/directMessages';
import RedirectOldThreadRoute from './views/thread/redirect-old-route';
import { FullscreenThreadView } from './views/thread';

/* prettier-ignore */
Expand Down Expand Up @@ -238,7 +239,7 @@ class Routes extends React.Component<Props> {
<Route path="/messages" component={MessagesFallback} />
<Route
path="/thread/:threadId"
component={FullscreenThreadView}
component={RedirectOldThreadRoute}
/>
<Route path="/thread" render={() => <Redirect to="/" />} />
<Route exact path="/users" render={() => <Redirect to="/" />} />
Expand Down Expand Up @@ -305,6 +306,16 @@ class Routes extends React.Component<Props> {
path="/:communitySlug/login"
component={CommunityLoginFallback}
/>
<Route
// NOTE(@mxstbr): This custom path regexp matches threadId correctly in all cases, no matter if we prepend it with a custom slug or not.
// Imagine our threadId is "id-123-id" (similar in shape to an actual UUID)
// - /id-123-id => id-123-id, easy start that works
// - /some-custom-slug~id-123-id => id-123-id, custom slug also works
// - /~id-123-id => id-123-id => id-123-id, empty custom slug also works
// - /some~custom~slug~id-123-id => id-123-id, custom slug with delimiter char in it (~) also works! :tada:
path="/:communitySlug/:channelSlug/(.*~)?:threadId"
component={FullscreenThreadView}
/>
<Route
path="/:communitySlug/:channelSlug"
component={ChannelView}
Expand Down
17 changes: 9 additions & 8 deletions src/views/thread/components/actionBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { GetThreadType } from 'shared/graphql/queries/thread/getThread';
import toggleThreadNotificationsMutation from 'shared/graphql/mutations/thread/toggleThreadNotifications';
import OutsideClickHandler from 'src/components/outsideClickHandler';
import { track, events, transformations } from 'src/helpers/analytics';
import getThreadLink from 'src/helpers/get-thread-link';
import type { Dispatch } from 'redux';

import {
Expand Down Expand Up @@ -287,7 +288,7 @@ class ActionBar extends React.Component<Props, State> {
<a
href={`https://www.facebook.com/sharer/sharer.php?t=${encodeURIComponent(
thread.content.title
)}&u=https://spectrum.chat/thread/${thread.id}`}
)}&u=https://spectrum.chat/${getThreadLink(thread)}`}
target="_blank"
rel="noopener noreferrer"
>
Expand All @@ -308,9 +309,9 @@ class ActionBar extends React.Component<Props, State> {
data-cy="thread-tweet-button"
>
<a
href={`https://twitter.com/share?url=https://spectrum.chat/thread/${
thread.id
}&text=${encodeURIComponent(
href={`https://twitter.com/share?url=https://spectrum.chat/${getThreadLink(
thread
)}&text=${encodeURIComponent(
thread.content.title
)} on @withspectrum`}
target="_blank"
Expand All @@ -328,7 +329,7 @@ class ActionBar extends React.Component<Props, State> {

<Clipboard
style={{ background: 'none' }}
data-clipboard-text={`${CLIENT_URL}/thread/${thread.id}`}
data-clipboard-text={`${CLIENT_URL}/${getThreadLink(thread)}`}
onSuccess={() =>
this.props.dispatch(
addToastWithTimeout('success', 'Copied to clipboard')
Expand Down Expand Up @@ -357,9 +358,9 @@ class ActionBar extends React.Component<Props, State> {
<ShareButtons>
<Clipboard
style={{ background: 'none' }}
data-clipboard-text={`https://spectrum.chat/thread/${
thread.id
}`}
data-clipboard-text={`https://spectrum.chat/${getThreadLink(
thread
)}`}
onSuccess={() =>
this.props.dispatch(
addToastWithTimeout('success', 'Copied to clipboard')
Expand Down
21 changes: 15 additions & 6 deletions src/views/thread/components/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from '../style';
import getThreadMessages from 'shared/graphql/queries/thread/getThreadMessageConnection';
import { ErrorBoundary } from 'src/components/error';
import getThreadLink from 'src/helpers/get-thread-link';
import type { GetThreadMessageConnectionType } from 'shared/graphql/queries/thread/getThreadMessageConnection';
import type { GetThreadType } from 'shared/graphql/queries/thread/getThread';

Expand Down Expand Up @@ -184,7 +185,9 @@ class MessagesWithData extends React.Component<Props, State> {
<A
href={`https://twitter.com/share?text=${encodeURIComponent(
threadTitle
)} on @withspectrum&url=https://spectrum.chat/thread/${threadId}`}
)} on @withspectrum&url=https://spectrum.chat/${getThreadLink(
this.props.data.thread
)}`}
target="_blank"
rel="noopener noreferrer"
>
Expand All @@ -193,9 +196,9 @@ class MessagesWithData extends React.Component<Props, State> {
</Button>
</A>
<A
href={`https://www.facebook.com/sharer/sharer.php?u=https://spectrum.chat/thread/${threadId}&t=${encodeURIComponent(
threadTitle
)}`}
href={`https://www.facebook.com/sharer/sharer.php?u=https://spectrum.chat/${getThreadLink(
this.props.data.thread
)}&t=${encodeURIComponent(threadTitle)}`}
target="_blank"
rel="noopener noreferrer"
>
Expand Down Expand Up @@ -276,7 +279,10 @@ class MessagesWithData extends React.Component<Props, State> {
href={`${location.pathname}?msgsbefore=${prevCursor}`}
/>
)}
<link rel="canonical" href={`/thread/${data.thread.id}`} />
<link
rel="canonical"
href={'https://spectrum.chat/' + getThreadLink(thread)}
/>
</Head>
</div>
)}
Expand All @@ -288,7 +294,10 @@ class MessagesWithData extends React.Component<Props, State> {
href={`${location.pathname}?msgsafter=${nextCursor}`}
/>
)}
<link rel="canonical" href={`/thread/${data.thread.id}`} />
<link
rel="canonical"
href={'https://spectrum.chat/' + getThreadLink(thread)}
/>
</Head>
)}
<InfiniteList
Expand Down
14 changes: 9 additions & 5 deletions src/views/thread/components/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { connect } from 'react-redux';
import compose from 'recompose/compose';
import { CLIENT_URL } from 'src/api/constants';
import Icon from 'src/components/icons';
import getThreadLink from 'src/helpers/get-thread-link';
import type { Dispatch } from 'redux';
import {
SidebarSection,
Expand Down Expand Up @@ -81,11 +82,14 @@ class Sidebar extends React.Component<Props> {
.sort((a, b) => b.messageCount - a.messageCount)
.slice(0, 5);

const loginUrl = thread
? thread.community.brandedLogin.isEnabled
? `/${thread.community.slug}/login?r=${CLIENT_URL}/thread/${thread.id}`
: `/login?r=${CLIENT_URL}/thread/${thread.id}`
: '/login';
const loginUrl =
thread && thread.community
? thread.community.brandedLogin.isEnabled
? `/${thread.community.slug}/login?r=${CLIENT_URL}/${getThreadLink(
thread
)}`
: `/login?r=${CLIENT_URL}/${getThreadLink(thread)}`
: '/login';

return (
<ThreadSidebarView>
Expand Down
12 changes: 7 additions & 5 deletions src/views/thread/components/threadCommunityBanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { GetThreadType } from 'shared/graphql/queries/thread/getThread';
import { addToastWithTimeout } from '../../../actions/toasts';
import { CommunityAvatar } from '../../../components/avatar';
import { CLIENT_URL } from 'src/api/constants';
import getThreadLink from 'src/helpers/get-thread-link';
import type { Dispatch } from 'redux';
import {
CommunityHeader,
Expand Down Expand Up @@ -110,8 +111,8 @@ class ThreadCommunityBanner extends React.Component<Props, State> {
const { isLoading } = this.state;

const loginUrl = community.brandedLogin.isEnabled
? `/${community.slug}/login?r=${CLIENT_URL}/thread/${id}`
: `/login?r=${CLIENT_URL}/${community.slug}/thread/${id}`;
? `/${community.slug}/login?r=${CLIENT_URL}/${getThreadLink(thread)}`
: `/login?r=${CLIENT_URL}/${getThreadLink(thread)}`;

const createdAt = new Date(thread.createdAt).getTime();
const timestamp = convertTimestampToDate(createdAt);
Expand All @@ -137,7 +138,7 @@ class ThreadCommunityBanner extends React.Component<Props, State> {
{channel.name}
</Link>
</ChannelHoverProfile>
<Link to={`/thread/${id}`}>
<Link to={'/' + getThreadLink(thread)}>
&nbsp;
{`· ${timestamp}`}
</Link>
Expand Down Expand Up @@ -165,9 +166,10 @@ class ThreadCommunityBanner extends React.Component<Props, State> {
);
}
}
const map = state => ({ currentUser: state.users.currentUser });

const map = (state: *): * => ({ currentUser: state.users.currentUser });
// $FlowIssue
export default compose(
// $FlowIssue
connect(map),
toggleChannelSubscriptionMutation
)(ThreadCommunityBanner);
3 changes: 2 additions & 1 deletion src/views/thread/components/threadDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
ThreadSubtitle,
} from '../style';
import { track, events, transformations } from 'src/helpers/analytics';
import getThreadLink from 'src/helpers/get-thread-link';
import type { Dispatch } from 'redux';
import { ErrorBoundary } from 'src/components/error';

Expand Down Expand Up @@ -384,7 +385,7 @@ class ThreadDetailPure extends React.Component<Props, State> {
</Link>
</ChannelHoverProfile>
<span>&nbsp;·&nbsp;</span>
<Link to={`/thread/${thread.id}`}>
<Link to={'/' + getThreadLink(thread)}>
{timestamp}
{thread.modifiedAt && (
<React.Fragment>
Expand Down
Loading