Skip to content
This repository has been archived by the owner on Oct 11, 2022. It is now read-only.

Commit

Permalink
Merge pull request #2979 from withspectrum/2.2.8
Browse files Browse the repository at this point in the history
2.2.8
  • Loading branch information
brianlovin authored Apr 30, 2018
2 parents 7f3cca1 + 760517e commit 4286a58
Show file tree
Hide file tree
Showing 111 changed files with 3,258 additions and 1,793 deletions.
1 change: 0 additions & 1 deletion api/loaders/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
import { getChannelsSettings } from '../models/channelSettings';
import createLoader from './create-loader';
import { getPendingUsersInChannels } from '../models/usersChannels';
import type { Loader } from './types';

export const __createChannelLoader = createLoader(channels =>
getChannels(channels)
Expand Down
2 changes: 2 additions & 0 deletions api/loaders/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
} from './directMessageThread';
import { __createReactionLoader } from './reaction';
import { __createStripeCustomersLoader } from './stripe';
import { __createMessageLoader } from './message';
import type { DataLoaderOptions } from './types';

// Create all the necessary loaders to be attached to the GraphQL context for each request
Expand Down Expand Up @@ -70,6 +71,7 @@ const createLoaders = (options?: DataLoaderOptions) => ({
directMessageThread: __createDirectMessageThreadLoader(options),
directMessageParticipants: __createDirectMessageParticipantsLoader(options),
directMessageSnippet: __createDirectMessageSnippetLoader(options),
message: __createMessageLoader(options),
messageReaction: __createReactionLoader(options),
});

Expand Down
14 changes: 14 additions & 0 deletions api/loaders/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @flow
import { getManyMessages } from '../models/message';
import createLoader from './create-loader';
import type { Loader } from './types';

export const __createMessageLoader = createLoader((messages: string[]) =>
getManyMessages(messages)
);

export default () => {
throw new Error(
'⚠️ Do not import loaders directly, get them from the GraphQL context instead! ⚠️'
);
};
1 change: 0 additions & 1 deletion api/loaders/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { getUsersPermissionsInChannels } from '../models/usersChannels';
import { getThreadsNotificationStatusForUsers } from '../models/usersThreads';
import createLoader from './create-loader';
import type { Loader } from './types';

export const __createUserLoader = createLoader(users => getUsers(users), 'id');

Expand Down
20 changes: 16 additions & 4 deletions api/models/communitySettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ export const getCommunitiesSettings = (
.getAll(...communityIds, { index: 'communityId' })
.run()
.then(data => {
if (!data || data.length === 0)
if (!data || data.length === 0) {
return Array.from({ length: communityIds.length }, (_, index) => ({
...defaultSettings,
communityId: communityIds[index],
}));
}

if (data.length === communityIds.length) {
return data.map(
Expand All @@ -50,12 +51,23 @@ export const getCommunitiesSettings = (
}

if (data.length < communityIds.length) {
return communityIds.map(community => {
const record = data.find(o => o.communityId === community);
return communityIds.map(communityId => {
const record = data.find(o => o.communityId === communityId);
if (record) return record;
return {
...defaultSettings,
communityId,
};
});
}

if (data.length > communityIds.length) {
return communityIds.map(communityId => {
const record = data.find(o => o.communityId === communityId);
if (record) return record;
return {
...defaultSettings,
communityId: community,
communityId,
};
});
}
Expand Down
11 changes: 10 additions & 1 deletion api/models/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { createChangefeed } from 'shared/changefeed-utils';
import { setThreadLastActive } from './thread';

export type MessageTypes = 'text' | 'media';
// TODO: Fix this
export type Message = Object;

export const getMessage = (messageId: string): Promise<Message> => {
Expand All @@ -25,6 +24,16 @@ export const getMessage = (messageId: string): Promise<Message> => {
});
};

export const getManyMessages = (messageIds: string[]): Promise<Message[]> => {
return db
.table('messages')
.getAll(...messageIds)
.run()
.then(messages => {
return messages.filter(message => message && !message.deletedAt);
});
};

type BackwardsPaginationOptions = { last?: number, before?: number | Date };

const getBackwardsMessages = (
Expand Down
9 changes: 8 additions & 1 deletion api/mutations/message/addMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { EditorState } from 'draft-js';
import type { GraphQLContext } from '../../';
import UserError from '../../utils/UserError';
import { uploadImage } from '../../utils/file-storage';
import { storeMessage } from '../../models/message';
import { storeMessage, getMessage } from '../../models/message';
import { setDirectMessageThreadLastActive } from '../../models/directMessageThread';
import { setUserLastSeenInDirectMessageThread } from '../../models/usersDirectMessageThreads';
import { createMemberInChannel } from '../../models/usersChannels';
Expand All @@ -25,6 +25,7 @@ type AddMessageInput = {
content: {
body: string,
},
parentId?: string,
file?: FileUpload,
},
};
Expand Down Expand Up @@ -86,6 +87,12 @@ export default async (
}
}

if (message.parentId) {
const parent = await getMessage(message.parentId);
if (parent.threadId !== message.threadId)
throw new UserError('You can only quote messages from the same thread.');
}

// construct the shape of the object to be stored in the db
let messageForDb = Object.assign({}, message);
if (message.file && message.messageType === 'media') {
Expand Down
1 change: 0 additions & 1 deletion api/queries/community/brandedLogin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// @flow
import type { DBCommunity } from 'shared/types';
import { getCommunitySettings } from '../../models/communitySettings';
import type { GraphQLContext } from '../../';

export default async (
Expand Down
2 changes: 2 additions & 0 deletions api/queries/message/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sender from './sender';
import author from './author';
import thread from './thread';
import reactions from './reactions';
import parent from './parent';

module.exports = {
Query: {
Expand All @@ -16,5 +17,6 @@ module.exports = {
sender, // deprecated
thread,
reactions,
parent,
},
};
12 changes: 12 additions & 0 deletions api/queries/message/parent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @flow
import type { DBMessage } from 'shared/types';
import type { GraphQLContext } from '../../';

export default (
{ parentId }: DBMessage,
_: void,
{ loaders }: GraphQLContext
) => {
if (!parentId) return null;
return loaders.message.load(parentId);
};
4 changes: 3 additions & 1 deletion api/queries/message/rootMessage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// @flow
import { getMessage } from '../../models/message';
import type { GraphQLContext } from '../../';

export default (_: any, { id }: { id: string }) => getMessage(id);
export default (_: any, { id }: { id: string }, { loaders }: GraphQLContext) =>
loaders.message.load(id);
2 changes: 2 additions & 0 deletions api/types/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const Message = /* GraphQL */ `
author: ThreadParticipant! @cost(complexity: 2)
reactions: ReactionData @cost(complexity: 1)
messageType: MessageTypes!
parent: Message
sender: User! @deprecated(reason:"Use Message.author field instead")
}
Expand All @@ -41,6 +42,7 @@ const Message = /* GraphQL */ `
threadType: ThreadTypes!
messageType: MessageTypes!
content: MessageContentInput!
parentId: String
file: Upload
}
Expand Down
23 changes: 22 additions & 1 deletion athena/queues/new-message-in-thread/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ import {
import { getThreadNotificationUsers } from '../../models/usersThreads';
import { getUserPermissionsInChannel } from '../../models/usersChannels';
import { getUserPermissionsInCommunity } from '../../models/usersCommunities';
import { getUserById } from '../../models/user';
import { getMessageById } from '../../models/message';
import { sendMentionNotificationQueue } from 'shared/bull/queues';
import type { MessageNotificationJobData, Job } from 'shared/bull/types';
import type { DBMessage } from 'shared/types';

export default async (job: Job<MessageNotificationJobData>) => {
const { message: incomingMessage } = job.data;
Expand Down Expand Up @@ -97,7 +100,25 @@ export default async (job: Job<MessageNotificationJobData>) => {
: incomingMessage.content.body;

// get mentions in the message
const mentions = getMentions(body);
let mentions = getMentions(body);
// If the message quoted another message, send a mention notification to the author
// of the quoted message
if (typeof incomingMessage.parentId === 'string') {
// $FlowIssue
const parent = await getMessageById(incomingMessage.parentId);
// eslint-disable-next-line
(parent: DBMessage);
if (parent) {
const parentAuthor = await getUserById(parent.senderId);
if (
parentAuthor &&
parentAuthor.username &&
mentions.indexOf(parentAuthor.username) < 0
) {
mentions.push(parentAuthor.username);
}
}
}
if (mentions && mentions.length > 0) {
mentions.forEach(username => {
sendMentionNotificationQueue.add({
Expand Down
12 changes: 12 additions & 0 deletions config-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const WriteFilePlugin = require('write-file-webpack-plugin');
const { ReactLoadablePlugin } = require('react-loadable/webpack');
const OfflinePlugin = require('offline-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const BundleBuddyWebpackPlugin = require('bundle-buddy-webpack-plugin');

// Recursively walk a folder and get all file paths
function walkFolder(currentDirPath, callback) {
Expand Down Expand Up @@ -147,6 +148,9 @@ module.exports = function override(config, env) {
})
);
}
if (process.env.BUNDLE_BUDDY === 'true') {
config.plugins.push(new BundleBuddyWebpackPlugin());
}
if (process.env.NODE_ENV === 'development') {
config.plugins.push(
WriteFilePlugin({
Expand All @@ -155,5 +159,13 @@ module.exports = function override(config, env) {
})
);
}
config.plugins.push(
new webpack.optimize.CommonsChunkPlugin({
minChunks: 3,
name: 'main',
async: 'commons',
children: true,
})
);
return rewireStyledComponents(config, env, { ssr: true });
};
25 changes: 25 additions & 0 deletions cypress/integration/messages_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import data from '../../shared/testing/data';

const thread = data.threads[0];
const community = data.communities.find(
community => community.id === thread.communityId
);
const author = data.users.find(user => user.id === thread.creatorId);

describe('/messages/new', () => {
beforeEach(() => {
cy.auth(author.id);
cy.visit('/messages/new');
});

it('should allow to continue composing message incase of crash or reload', () => {
const newMessage = 'Persist New Message';
cy.get('[contenteditable="true"]').type(newMessage);
cy.get('[contenteditable="true"]').contains(newMessage);

cy.wait(2000);
// Reload page(incase page closed or crashed ,reload should have same effect)
cy.reload();
cy.get('[contenteditable="true"]').contains(newMessage);
});
});
45 changes: 45 additions & 0 deletions cypress/integration/thread/chat_input_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,51 @@ describe('chat input', () => {
cy.get('[contenteditable="true"]').type('');
cy.contains(newMessage);
});

it('should allow chat input to be maintained', () => {
const newMessage = 'Persist New Message';
cy.get('[data-cy="thread-view"]').should('be.visible');
cy.get('[contenteditable="true"]').type(newMessage);
cy.get('[contenteditable="true"]').contains(newMessage);
cy.get('[data-cy="message-group"]').should('be.visible');
cy.wait(1000);
// Reload page(incase page closed or crashed ,reload should have same effect)
cy.reload();
cy.get('[contenteditable="true"]').contains(newMessage);
});
});

describe('message attachments', () => {
beforeEach(() => {
cy.auth(memberInChannelUser.id);
cy.visit(`/thread/${publicThread.id}`);
});

it('should allow quoting a message', () => {
// Quote a message
cy.get('[data-cy="staged-quoted-message"]').should('not.be.visible');
cy
.get('[data-cy="message"]')
.first()
.should('be.visible')
.click();
cy
.get('[data-cy="reply-to-message"]')
.first()
.should('be.visible')
.click();
cy
.get('[data-cy="reply-to-message"]')
.first()
.should('not.be.visible');
cy.get('[data-cy="staged-quoted-message"]').should('be.visible');
// Remove quoted message again
cy
.get('[data-cy="remove-staged-quoted-message"]')
.should('be.visible')
.click();
cy.get('[data-cy="staged-quoted-message"]').should('not.be.visible');
});
});

// NOTE(@mxstbr): This fails in CI, but not locally for some reason
Expand Down
17 changes: 17 additions & 0 deletions cypress/integration/thread_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,21 @@ describe('/new/thread', () => {
cy.contains(title);
cy.contains(body);
});

it('should allow to continue composing thread incase of crash or reload', () => {
const title = 'Persist Title';
const body = 'with some persisting content';
cy.get('[data-cy="rich-text-editor"]').should('be.visible');
cy.get('[data-cy="composer-community-selector"]').should('be.visible');
cy.get('[data-cy="composer-channel-selector"]').should('be.visible');
// Type title and body
cy.get('[data-cy="composer-title-input"]').type(title);
cy.get('[contenteditable="true"]').type(body);
/////need time as our localstorage is not set
cy.wait(1000);
cy.reload();

cy.get('[data-cy="composer-title-input"]').contains(title);
cy.get('[contenteditable="true"]').contains(body);
});
});
2 changes: 1 addition & 1 deletion docs/api/graphql/fragments.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const getStory = gql`
...frequencyInfo
}
}
${userInfoFragment}
${storyInfoFragment}
${frequencyInfoFragment}
`;
```
Loading

0 comments on commit 4286a58

Please sign in to comment.