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

2.4.28 #3819

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
0890960
Start rework of channel list item
brianlovin Aug 13, 2018
6d48a9a
Merge branch 'alpha' of https://github.com/withspectrum/spectrum into…
brianlovin Aug 13, 2018
8c97bf5
Actually fix overflows, get hacky with layout
brianlovin Aug 13, 2018
8ef4915
Join channel when toggling notifications on as a non-member
brianlovin Aug 13, 2018
3986bad
Eslint
brianlovin Aug 13, 2018
65aa050
Add owner channel list item
brianlovin Aug 13, 2018
6c954c6
Match styles temporarily to user list item
brianlovin Aug 13, 2018
540c32e
Attempt to refactor channel list item to be reusable in any context
brianlovin Aug 13, 2018
c318c80
Eslint
brianlovin Aug 14, 2018
10fa326
More channel list item cleanup
brianlovin Aug 14, 2018
f048d8b
WIP: Assign each thread a score
mxstbr Aug 18, 2018
33e35ca
Add calculateThreadScore jobs to the queue from mutations
mxstbr Aug 18, 2018
c47cafc
Add calculate thread score job if threadScoreUPdatedAt is older than …
mxstbr Aug 18, 2018
34a1443
Store thread.scoreUpdatedAt
mxstbr Aug 18, 2018
dc6bd72
Fix ms in a day
mxstbr Aug 18, 2018
d7beed4
Fix flow types
mxstbr Aug 18, 2018
2578572
Aggressively decay based on lastActive
mxstbr Aug 18, 2018
1bd214a
Fix typo
mxstbr Aug 18, 2018
b119225
Add sort arg to Community.threadConnection
mxstbr Aug 18, 2018
cc4c6b4
Run calculateThreadScore once for each thread
mxstbr Aug 18, 2018
d01c9ae
Rework thread trending score algorithm with exponential explosion
mxstbr Aug 20, 2018
b073fd6
Remove not-working migration
mxstbr Aug 20, 2018
4f3ed40
Top => trending
mxstbr Aug 20, 2018
ea79472
Make sure it is distinct participants
mxstbr Aug 20, 2018
c3f3d17
threadConnection sort arg lowercase
mxstbr Aug 21, 2018
eea3984
Make sure new threads get scores if they dont have any
mxstbr Aug 21, 2018
1e688f9
Merge branch 'alpha' of github.com:withspectrum/spectrum into fix-lis…
brianlovin Aug 22, 2018
2698e94
Re-add channel sidebar on channel view
brianlovin Aug 22, 2018
7a4bfb1
Re add notification channel item
brianlovin Aug 22, 2018
f61af4b
Fix e2e tests
brianlovin Aug 22, 2018
2acede7
Merge pull request #3766 from withspectrum/fix-list-items-overflows
mxstbr Aug 22, 2018
ea720e3
Add context to team badge on hover
mxstbr Aug 22, 2018
a6b68bf
Update to draft-js-markdown-plugin@3.0.3
mxstbr Aug 22, 2018
392a1c6
Fix perpetually being signed out AGAIN
mxstbr Aug 22, 2018
98776bd
Merge pull request #3817 from withspectrum/update-markdown-3.0.3
brianlovin Aug 22, 2018
8f17cca
Expire 10 years from now
mxstbr Aug 22, 2018
263a50e
Merge pull request #3818 from withspectrum/fix-perpetually-being-sign…
mxstbr Aug 22, 2018
080caed
Merge pull request #3798 from withspectrum/new-frecency-feeds
mxstbr Aug 22, 2018
cd4e732
threadConnection(sort: new) => threadConnection(sort: latest)
mxstbr Aug 22, 2018
37a2440
maybe fix flaky e2e test
mxstbr Aug 22, 2018
2609889
Merge pull request #3816 from withspectrum/add-context-to-team-badge
brianlovin Aug 22, 2018
26806ab
Merge branch 'alpha' of github.com:withspectrum/spectrum into 2.4.28
brianlovin Aug 22, 2018
6592594
2.4.28
brianlovin Aug 22, 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
29 changes: 21 additions & 8 deletions api/models/thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,27 @@ export const getThreadsByChannel = (channelId: string, options: PaginationOption
};

// prettier-ignore
export const getThreadsByChannels = (channelIds: Array<string>, options: PaginationOptions): Promise<Array<DBThread>> => {
const { first, after } = options

type GetThreadsByChannelPaginationOptions = {
first: number,
after: number,
sort: 'latest' | 'trending'
};

export const getThreadsByChannels = (
channelIds: Array<string>,
options: GetThreadsByChannelPaginationOptions
): Promise<Array<DBThread>> => {
const { first, after, sort = 'latest' } = options;

let order = [db.desc('lastActive'), db.desc('createdAt')];
// If we want the top threads, first sort by the score and then lastActive
if (sort === 'trending') order.unshift(db.desc('score'));

return db
.table('threads')
.getAll(...channelIds, { index: 'channelId' })
.filter(thread => db.not(thread.hasFields('deletedAt')))
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
.orderBy(...order)
.skip(after || 0)
.limit(first || 999999)
.run();
Expand Down Expand Up @@ -447,10 +460,10 @@ export const setThreadLock = (threadId: string, value: boolean, userId: string,
.run()
.then(async () => {
const thread = await getThreadById(threadId)
const event = value
? byModerator
? events.THREAD_LOCKED_BY_MODERATOR

const event = value
? byModerator
? events.THREAD_LOCKED_BY_MODERATOR
: events.THREAD_LOCKED
: byModerator
? events.THREAD_UNLOCKED_BY_MODERATOR
Expand Down
19 changes: 16 additions & 3 deletions api/models/usersChannels.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ const createMemberInDefaultChannels = (communityId: string, userId: string): Pro
};

// prettier-ignore
const toggleUserChannelNotifications = (userId: string, channelId: string, value: boolean): Promise<DBChannel> => {
const toggleUserChannelNotifications = async (userId: string, channelId: string, value: boolean): Promise<DBChannel> => {
const event = value ? events.CHANNEL_NOTIFICATIONS_ENABLED : events.CHANNEL_NOTIFICATIONS_DISABLED

trackQueue.add({
Expand All @@ -474,12 +474,25 @@ const toggleUserChannelNotifications = (userId: string, channelId: string, value
context: { channelId }
})

return db
const permissions = await db
.table('usersChannels')
.getAll(channelId, { index: 'channelId' })
.filter({ userId })
.update({ receiveNotifications: value })
.run();

// permissions exist, this user is trying to toggle notifications for a channel where they
// are already a member
if (permissions && permissions.length > 0) {
return db
.table('usersChannels')
.getAll(channelId, { index: 'channelId' })
.filter({ userId })
.update({ receiveNotifications: value })
.run();
}

// if permissions don't exist, create a usersChannel relationship with notifications on
return createMemberInChannel(channelId, userId, false)
};

const removeUsersChannelMemberships = async (userId: string) => {
Expand Down
6 changes: 4 additions & 2 deletions api/mutations/channel/toggleChannelNotifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export default requireAuth(async (_: any, args: Input, ctx: GraphQLContext) => {
loaders.userPermissionsInChannel.load([user.id, channelId]),
]);

if (!permissions || permissions.isBlocked || !permissions.isMember) {
// only reject if the user is blocked in the channel
if (permissions && permissions.isBlocked) {
let event = !permissions
? events.CHANNEL_NOTIFICATIONS_ENABLED_FAILED
: permissions.receiveNotifications
Expand All @@ -37,7 +38,8 @@ export default requireAuth(async (_: any, args: Input, ctx: GraphQLContext) => {
return new UserError("You don't have permission to do that.");
}

const value = !permissions.receiveNotifications;
// if the user hasn't joined the channel, they are trying to join it now
const value = permissions ? !permissions.receiveNotifications : true;

return toggleUserChannelNotifications(user.id, channelId, value).then(
() => channel
Expand Down
10 changes: 9 additions & 1 deletion api/mutations/message/addMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { trackUserThreadLastSeenQueue } from 'shared/bull/queues';
import type { FileUpload } from 'shared/types';
import { events } from 'shared/analytics';
import { isAuthedResolver as requireAuth } from '../../utils/permissions';
import { trackQueue } from 'shared/bull/queues';
import { trackQueue, calculateThreadScoreQueue } from 'shared/bull/queues';

type Input = {
message: {
Expand Down Expand Up @@ -344,6 +344,14 @@ export default requireAuth(async (_: any, args: Input, ctx: GraphQLContext) => {
timestamp: Date.now(),
});

calculateThreadScoreQueue.add(
{
threadId: message.threadId,
},
{
jobId: message.threadId,
}
);
return {
...dbMessage,
contextPermissions,
Expand Down
12 changes: 11 additions & 1 deletion api/mutations/message/deleteMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { getUserPermissionsInChannel } from '../../models/usersChannels';
import { getUserPermissionsInCommunity } from '../../models/usersCommunities';
import { events } from 'shared/analytics';
import { isAuthedResolver as requireAuth } from '../../utils/permissions';
import { trackQueue } from 'shared/bull/queues';
import { trackQueue, calculateThreadScoreQueue } from 'shared/bull/queues';

type Input = {
id: string,
Expand Down Expand Up @@ -116,5 +116,15 @@ export default requireAuth(async (_: any, args: Input, ctx: GraphQLContext) => {
message.senderId
);
})
.then(() => {
return calculateThreadScoreQueue.add(
{
threadId: message.threadId,
},
{
jobId: message.threadId,
}
);
})
.then(() => true);
});
15 changes: 12 additions & 3 deletions api/mutations/thread/addThreadReaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { GraphQLContext } from '../../';
import { addThreadReaction } from '../../models/threadReaction';
import { getThreadById } from '../../models/thread';
import { isAuthedResolver as requireAuth } from '../../utils/permissions';
import { calculateThreadScoreQueue } from 'shared/bull/queues';

type Input = {
input: {
Expand All @@ -13,8 +14,16 @@ type Input = {

export default requireAuth(
async (_: any, args: Input, { user, loaders }: GraphQLContext) => {
return await addThreadReaction(args.input, user.id).then(
async () => await getThreadById(args.input.threadId)
);
return await addThreadReaction(args.input, user.id).then(() => {
calculateThreadScoreQueue.add(
{
threadId: args.input.threadId,
},
{
jobId: args.input.threadId,
}
);
return getThreadById(args.input.threadId);
});
}
);
15 changes: 12 additions & 3 deletions api/mutations/thread/removeThreadReaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { GraphQLContext } from '../../';
import { removeThreadReaction } from '../../models/threadReaction';
import { getThreadById } from '../../models/thread';
import { isAuthedResolver as requireAuth } from '../../utils/permissions';
import { calculateThreadScoreQueue } from 'shared/bull/queues';

type Input = {
input: {
Expand All @@ -12,8 +13,16 @@ type Input = {

export default requireAuth(
async (_: any, args: Input, { user, loaders }: GraphQLContext) => {
return await removeThreadReaction(args.input.threadId, user.id).then(
async () => await getThreadById(args.input.threadId)
);
return await removeThreadReaction(args.input.threadId, user.id).then(() => {
calculateThreadScoreQueue.add(
{
threadId: args.input.threadId,
},
{
jobId: args.input.threadId,
}
);
return getThreadById(args.input.threadId);
});
}
);
14 changes: 10 additions & 4 deletions api/queries/community/threadConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ import {
import { getThreadsByChannels } from '../../models/thread';
import { canViewCommunity } from '../../utils/permissions';

export type CommunityThreadConnectionPaginationOptions = {
after: string,
first: number,
sort: 'latest' | 'trending',
};

// prettier-ignore
export default async (root: DBCommunity, args: PaginationOptions, ctx: GraphQLContext) => {
const { first = 10, after } = args
export default async (root: DBCommunity, args: CommunityThreadConnectionPaginationOptions, ctx: GraphQLContext) => {
const { first = 10, after, sort = 'latest' } = args
const { user, loaders } = ctx
const { id } = root

Expand All @@ -29,7 +35,7 @@ export default async (root: DBCommunity, args: PaginationOptions, ctx: GraphQLCo
// Get the index from the encoded cursor, asdf234gsdf-2 => ["-2", "2"]
const lastDigits = cursor.match(/-(\d+)$/);
const lastThreadIndex =
lastDigits && lastDigits.length > 0 && parseInt(lastDigits[1], 10);
lastDigits && lastDigits.length > 0 && parseInt(lastDigits[1], 10) || 0;
const currentUser = user;

// if the user is signed in, only return stories for the channels
Expand All @@ -44,10 +50,10 @@ export default async (root: DBCommunity, args: PaginationOptions, ctx: GraphQLCo
channels = await getPublicChannelsByCommunity(id);
}

// $FlowFixMe
const threads = await getThreadsByChannels(channels, {
first,
after: lastThreadIndex,
sort,
});

return {
Expand Down
18 changes: 18 additions & 0 deletions api/queries/thread/rootThread.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @flow
import { calculateThreadScoreQueue } from 'shared/bull/queues';
import type { GraphQLContext } from '../../';

export default async (
Expand Down Expand Up @@ -50,6 +51,23 @@ export default async (
(!communityPermissions || !communityPermissions.isMember)
)
return null;

// If the threads score hasn't been updated in the past
// 24 hours add a new job to the queue to update it
if (
(!thread.score && !thread.scoreUpdatedAt) ||
(thread.scoreUpdatedAt &&
Date.now() > new Date(thread.scoreUpdatedAt).getTime() + 86400000)
) {
calculateThreadScoreQueue.add(
{
threadId: thread.id,
},
{
jobId: thread.id,
}
);
}
return thread;
}
};
5 changes: 5 additions & 0 deletions api/routes/auth/create-signin-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ export const createSigninRoutes = (
// to the old URL the next time around
// $FlowIssue
req.session.redirectUrl = undefined;
res.cookie('_now_no_cache', '1', {
maxAge: 315569260000, // 10 years
sameSite: 'lax',
secure: false,
});
return res.redirect(redirectUrl.href);
},
],
Expand Down
6 changes: 6 additions & 0 deletions api/types/Community.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ const Community = /* GraphQL */ `
message: String
}

enum CommunityThreadConnectionSort {
latest
trending
}

type Community {
id: ID!
createdAt: Date
Expand All @@ -153,6 +158,7 @@ const Community = /* GraphQL */ `
threadConnection(
first: Int = 10
after: String
sort: CommunityThreadConnectionSort = latest
): CommunityThreadsConnection @cost(complexity: 2, multiplier: "first")
metaData: CommunityMetaData @cost(complexity: 10)
invoices: [Invoice] @cost(complexity: 1)
Expand Down
Loading