feat: presence and count improvements

This commit is contained in:
Wyatt Johnson
2018-09-21 13:16:21 -06:00
parent 6b814cee0d
commit ebe43a28e6
5 changed files with 125 additions and 49 deletions
@@ -4,12 +4,12 @@ import Context from "talk-server/graph/tenant/context";
import {
AssetToCommentsArgs,
CommentToRepliesArgs,
GQLActionPresence,
GQLCOMMENT_SORT,
} from "talk-server/graph/tenant/schema/__generated__/types";
import {
ACTION_ITEM_TYPE,
ActionCounts,
retrieveManyAuthoredActionCounts,
retrieveManyAuthoredActionPresence,
} from "talk-server/models/actions";
import {
retrieveCommentAssetConnection,
@@ -21,9 +21,9 @@ export default (ctx: Context) => ({
comment: new DataLoader((ids: string[]) =>
retrieveManyComments(ctx.mongo, ctx.tenant.id, ids)
),
retrieveAuthoredActionCounts: new DataLoader<string, ActionCounts>(
retrieveAuthoredActionPresence: new DataLoader<string, GQLActionPresence>(
(itemIDs: string[]) =>
retrieveManyAuthoredActionCounts(
retrieveManyAuthoredActionPresence(
ctx.mongo,
ctx.tenant.id,
// This should only ever be accessed when a user is logged in.
@@ -19,8 +19,8 @@ const Comment: GQLCommentTypeResolver<Comment> = {
replies: (comment, input, ctx) =>
ctx.loaders.Comments.forParent(comment.asset_id, comment.id, input),
actionCounts: comment => decodeActionCounts(comment.action_counts),
authoredActionCounts: (comment, input, ctx) =>
ctx.loaders.Comments.retrieveAuthoredActionCounts.load(comment.id),
authoredActionPresence: (comment, input, ctx) =>
ctx.loaders.Comments.retrieveAuthoredActionPresence.load(comment.id),
};
export default Comment;
@@ -112,11 +112,25 @@ enum COMMENT_FLAG_REASON {
COMMENT_DETECTED_SUSPECT_WORD
}
"""
ReactionActionCounts stores all the counts for the counts for the reaction
action on a given item.
"""
type ReactionActionCounts {
"""
total is the total number of reactions against a given item.
"""
total: Int!
}
"""
DontAgreeActionCounts stores all the counts for the counts for the dontAgree
action on a given item.
"""
type DontAgreeActionCounts {
"""
total is the total number of dontAgree actions against a given item.
"""
total: Int!
}
@@ -132,15 +146,65 @@ type FlagReasonActionCounts {
COMMENT_DETECTED_SUSPECT_WORD: Int!
}
"""
FlagActionCounts stores all the counts for the counts for the flag action on a
given item and the reason counts.
"""
type FlagActionCounts {
"""
total is the total number of flags against a given item.
"""
total: Int!
"""
reasons stores the counts for the various reasons that an item could be
flagged for.
"""
reasons: FlagReasonActionCounts!
}
"""
ActionCounts returns the counts of each action for an item.
"""
type ActionCounts {
"""
reaction returns the counts for the reaction action on an item.
"""
reaction: ReactionActionCounts!
dontagree: DontAgreeActionCounts!
flag: FlagActionCounts!
"""
dontAgree returns the counts for the dontAgree action on an item. This edge is
restricted to administrators and moderators.
"""
dontAgree: DontAgreeActionCounts! @auth(roles: [ADMIN, MODERATOR])
"""
flag returns the counts for the flag action on an item. This edge is
restricted to administrators and moderators.
"""
flag: FlagActionCounts! @auth(roles: [ADMIN, MODERATOR])
}
"""
ActionPresence returns whether or not a given item has one of the following
actions on it. This is typically used to determine if a given user has left
one of the following actions.
"""
type ActionPresence {
"""
reaction is true when a reaction action was left on an item.
"""
reaction: Boolean!
"""
dontAgree is true when a dontAgree action was left on an item.
"""
dontAgree: Boolean!
"""
flag is true when a flag action was left on an item.
"""
flag: Boolean!
}
################################################################################
@@ -769,10 +833,10 @@ type Comment {
actionCounts: ActionCounts!
"""
authoredActionCounts stores the counts of all the actions for the Comment as
it is written by the current User.
authoredActionPresence stores the presense information for all the actions
left by the current User on this Comment.
"""
authoredActionCounts: ActionCounts! @auth
authoredActionPresence: ActionPresence! @auth
}
type PageInfo {
@@ -940,7 +1004,7 @@ type Query {
"""
me is the current logged in User.
"""
me: User @auth
me: User
"""
settings is the Settings for a given Tenant.
@@ -12,10 +12,10 @@ Object {
exports[`#decodeActionCounts parses the action counts correctly 2`] = `
Object {
"DONT_AGREE": Object {
"dontAgree": Object {
"total": 1,
},
"FLAG": Object {
"flag": Object {
"reasons": Object {
"COMMENT_DETECTED_BANNED_WORD": 1,
"COMMENT_DETECTED_BODY_COUNT": 1,
@@ -29,7 +29,7 @@ Object {
},
"total": 2,
},
"REACTION": Object {
"reaction": Object {
"total": 3,
},
}
+46 -34
View File
@@ -4,7 +4,11 @@ import { Db } from "mongodb";
import uuid from "uuid";
import { Omit, Sub } from "talk-common/types";
import { GQLCOMMENT_FLAG_REASON } from "talk-server/graph/tenant/schema/__generated__/types";
import {
GQLActionCounts,
GQLActionPresence,
GQLCOMMENT_FLAG_REASON,
} from "talk-server/graph/tenant/schema/__generated__/types";
import { FilterQuery } from "talk-server/models/query";
import { TenantResource } from "talk-server/models/tenant";
@@ -37,14 +41,6 @@ export interface ActionCountGroup {
total: number;
}
export interface ActionCounts {
[ACTION_TYPE.REACTION]: ActionCountGroup;
[ACTION_TYPE.DONT_AGREE]: ActionCountGroup;
[ACTION_TYPE.FLAG]: ActionCountGroup & {
reasons: Record<GQLCOMMENT_FLAG_REASON, number>;
};
}
export enum ACTION_ITEM_TYPE {
COMMENTS = "COMMENTS",
}
@@ -194,35 +190,49 @@ export async function createActions(
}
/**
* retrieveManyAuthoredActionCounts returns the action counts for a specific
* retrieveManyAuthoredActionPresence returns the action presence for a specific
* user.
*/
export async function retrieveManyAuthoredActionCounts(
export async function retrieveManyAuthoredActionPresence(
mongo: Db,
tenantID: string,
userID: string | null,
itemType: ACTION_ITEM_TYPE,
itemIDs: string[]
) {
const cursor = await collection(mongo).find({
tenant_id: tenantID,
user_id: userID,
item_type: itemType,
item_id: { $in: itemIDs },
});
): Promise<GQLActionPresence[]> {
const cursor = await collection(mongo).find(
{
tenant_id: tenantID,
user_id: userID,
item_type: itemType,
item_id: { $in: itemIDs },
},
{
// We only need the item_id and action_type from the database.
projection: {
item_id: 1,
action_type: 1,
},
}
);
const actions = await cursor.toArray();
// For each of the actions returned by the query, group the actions by the
// item id. Then compute the action counts for each of these action groups,
// which can be used to determine existence of actions.
// item id. Then compute the action presence for each of the actions.
return itemIDs
.map(itemID => actions.filter(action => action.item_id === itemID))
.map(itemActions =>
itemActions.reduce(
(actionCounts, { action_type, reason }) =>
incrementActionCounts(actionCounts, action_type, reason),
createEmptyActionCounts()
(actionPresence, { action_type }) => ({
...actionPresence,
[action_type.toLowerCase()]: true,
}),
{
reaction: false,
dontAgree: false,
flag: false,
}
)
);
}
@@ -385,16 +395,18 @@ function decodeActionCountKey(key: string): DecodedActionCountKey {
/**
* createEmptyActionCounts creates a default/empty set of action counts.
*/
function createEmptyActionCounts(): ActionCounts {
function createEmptyActionCounts(): GQLActionCounts {
return {
[ACTION_TYPE.REACTION]: {
reaction: {
total: 0,
},
[ACTION_TYPE.DONT_AGREE]: {
dontAgree: {
total: 0,
},
[ACTION_TYPE.FLAG]: {
flag: {
total: 0,
// Note that this isn't type checked.. We force it because TS can't
// understand the reduce.
reasons: Object.keys(GQLCOMMENT_FLAG_REASON).reduce(
(reasons, reason) => ({
...reasons,
@@ -414,9 +426,9 @@ function createEmptyActionCounts(): ActionCounts {
*/
export function decodeActionCounts(
encodedActionCounts: EncodedActionCounts
): ActionCounts {
): GQLActionCounts {
// Default all the action counts to zero.
const actionCounts: ActionCounts = createEmptyActionCounts();
const actionCounts: GQLActionCounts = createEmptyActionCounts();
// Loop over all the encoded action counts to extract each of the action
// counts as they are encoded.
@@ -432,26 +444,26 @@ export function decodeActionCounts(
}
function incrementActionCounts(
actionCounts: ActionCounts,
actionCounts: GQLActionCounts,
actionType: ACTION_TYPE,
reason: GQLCOMMENT_FLAG_REASON | undefined,
count: number = 1
) {
switch (actionType) {
case ACTION_TYPE.REACTION:
actionCounts[ACTION_TYPE.REACTION].total += count;
actionCounts.reaction.total += count;
break;
case ACTION_TYPE.DONT_AGREE:
actionCounts[ACTION_TYPE.DONT_AGREE].total += count;
actionCounts.dontAgree.total += count;
break;
case ACTION_TYPE.FLAG:
// When we have a reason, we are incrementing for that particular reason
// rather than incrementing for the total. If we don't have a reason, we
// just got the updated reason.
if (reason) {
actionCounts[ACTION_TYPE.FLAG].reasons[reason] += count;
actionCounts.flag.reasons[reason] += count;
} else {
actionCounts[ACTION_TYPE.FLAG].total += count;
actionCounts.flag.total += count;
}
break;
default: