Skip to content

Commit

Permalink
4.2.0: Tips (#351)
Browse files Browse the repository at this point in the history
* 4.2.0: Tips

* Add missing env to integration tests

* COMMENT_TIP_TIERS env: use single quotes
  • Loading branch information
Lezek123 authored Dec 16, 2024
1 parent 353e9da commit 8e4ccbd
Show file tree
Hide file tree
Showing 19 changed files with 175 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ VIDEO_RELEVANCE_VIEWS_TICK=50
# Default channel weight/bias
# ]
RELEVANCE_WEIGHTS="[1, 0.03, 0.3, 0.5, [7,3], 1]"
MAX_CACHED_ENTITIES=1000
COMMENT_TIP_TIERS='{"SILVER": 100, "GOLD": 500, "DIAMOND": 1000}'
MAX_CACHED_ENTITIES=5000
APP_PRIVATE_KEY=this-is-not-so-secret-change-it
SESSION_EXPIRY_AFTER_INACTIVITY_MINUTES=60
SESSION_MAX_DURATION_HOURS=720
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
# 4.2.0

## Affected components:
- Migrations:
- **(A) `1733920148217-Data.js`**
- views migration
- GraphQL schema:
- **(M) `Comment`**: added `tipTier`, `tipAmount` and `sortPriority` fields
- **(M) `OperatorPermission`**: added `SET_TIP_TIERS` variant
- **(A) `CommentTipTier`**: new enum
- GraphQL server:
- **(A) `tipTiers`** (query)
- **(A) `setTipTierAmounts`** (mutation)
- Processor:
- **(M) `Members.MemberRemarked`** (event handler)
- Config:
- **(A) `COMMENT_TIP_TIERS`**
- Dockerfile

## Changes
- Added support for [Atlas tipping functionality](/~https://github.com/Joystream/atlas/issues/6291):
- added `COMMENT_TIP_TIERS` config variable and corresponding `tipTiers` query and `setTipTierAmounts` mutation which allow configuring the minimum amounts of JOY tokens required to obtain each tier (SILVER / GOLD / DIAMOND) when adding a video comment with a tip.
- modified `MemberRemarked` event handler: `processCreateCommentMessage` now takes `payment` parameter and assigns a `tipTier`, `tipAmount` and `sortPriority` to a comment based on the amount of JOY paid to channel reward account.
- updated GraphQL schema (`Comment`, `CommentTipTier`) and migrations to support new `Comment` fields.

## Bug Fixes:
- Dockerfile: added missing `entrypoints` and `opentelemetry` directories.

# 4.1.1

## Affected components:
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ ADD db db
ADD assets assets
ADD schema schema
ADD scripts scripts
ADD entrypoints entrypoints
ADD opentelemetry opentelemetry
# TODO: use shorter PROMETHEUS_PORT
ENV PROCESSOR_PROMETHEUS_PORT 3000
EXPOSE 3000
Expand Down
3 changes: 3 additions & 0 deletions db/generateViewsMigration.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ module.exports = class ${className} {
const viewDefinitions = getViewDefinitions(db);
for (const [tableName, viewConditions] of Object.entries(viewDefinitions)) {
if (Array.isArray(viewConditions)) {
await db.query(\`
DROP VIEW IF EXISTS "\${tableName}" CASCADE
\`)
await db.query(\`
CREATE OR REPLACE VIEW "\${tableName}" AS
SELECT *
Expand Down
15 changes: 15 additions & 0 deletions db/migrations/1733920148217-Data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = class Data1733920148217 {
name = 'Data1733920148217'

async up(db) {
await db.query(`ALTER TABLE "admin"."comment" ADD "tip_tier" character varying(7)`)
await db.query(`ALTER TABLE "admin"."comment" ADD "tip_amount" numeric NOT NULL DEFAULT 0`)
await db.query(`ALTER TABLE "admin"."comment" ADD "sort_priority" integer NOT NULL DEFAULT 0`)
}

async down(db) {
await db.query(`ALTER TABLE "admin"."comment" DROP COLUMN "tip_tier"`)
await db.query(`ALTER TABLE "admin"."comment" DROP COLUMN "tip_amount"`)
await db.query(`ALTER TABLE "admin"."comment" DROP COLUMN "sort_priority"`)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

const { getViewDefinitions } = require('../viewDefinitions')

module.exports = class Views1730976542053 {
name = 'Views1730976542053'
module.exports = class Views1733921114970 {
name = 'Views1733921114970'

async up(db) {
// these two queries will be invoked and the cleaned up by the squid itself
Expand All @@ -15,6 +15,9 @@ module.exports = class Views1730976542053 {
const viewDefinitions = getViewDefinitions(db);
for (const [tableName, viewConditions] of Object.entries(viewDefinitions)) {
if (Array.isArray(viewConditions)) {
await db.query(`
DROP VIEW IF EXISTS "${tableName}" CASCADE
`)
await db.query(`
CREATE OR REPLACE VIEW "${tableName}" AS
SELECT *
Expand Down
2 changes: 1 addition & 1 deletion entrypoints/auth-server.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh

set -e

Expand Down
2 changes: 1 addition & 1 deletion entrypoints/graphql-server.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh

set -e

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "orion",
"version": "4.1.1",
"version": "4.2.0",
"engines": {
"node": ">=16"
},
Expand Down
1 change: 1 addition & 0 deletions schema/auth.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum OperatorPermission {
SET_PUBLIC_FEED_VIDEOS
SET_FEATURED_CRTS
SET_CRT_MARKETCAP_MIN_VOLUME
SET_TIP_TIERS
}

type User @entity @schema(name: "admin") {
Expand Down
15 changes: 15 additions & 0 deletions schema/videoComments.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ type CommentReactionsCountByReactionId {
count: Int!
}

enum CommentTipTier {
SILVER
GOLD
DIAMOND
}

type Comment @entity @schema(name: "admin") {
"METAPROTOCOL-{network}-{blockNumber}-{indexInBlock}"
id: ID!
Expand Down Expand Up @@ -73,4 +79,13 @@ type Comment @entity @schema(name: "admin") {

"Whether a comment has been excluded/hidden (by the gateway operator)"
isExcluded: Boolean!

"Tier received for adding a tip to the comment (if any)"
tipTier: CommentTipTier

"Tip included when adding the comment (in HAPI)"
tipAmount: BigInt!

"Base sort priority of the comment (can be increased by a tip)"
sortPriority: Int!
}
47 changes: 46 additions & 1 deletion src/mappings/content/commentsAndReactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { isSet } from '@joystream/metadata-protobuf/utils'
import { assertNotNull, SubstrateBlock } from '@subsquid/substrate-processor'
import {
BannedMember,
Channel,
ChannelRecipient,
Comment,
CommentCreatedEventData,
Expand All @@ -27,6 +28,7 @@ import {
CommentReply,
CommentStatus,
CommentTextUpdatedEventData,
CommentTipTier,
Event,
MemberRecipient,
MetaprotocolTransactionResult,
Expand Down Expand Up @@ -56,6 +58,7 @@ import {
import { getAccountForMember, getChannelOwnerMemberByChannelId, memberHandleById } from './utils'
import { addNotification } from '../../utils/notification'
import { parseVideoTitle } from '../../utils/notification/helpers'
import { HAPI_TO_JOY_RATE } from '../../utils/joystreamPrice'

function parseVideoReaction(reaction: ReactVideo.Reaction): VideoReactionOptions {
const protobufReactionToGraphqlReaction = {
Expand Down Expand Up @@ -418,13 +421,43 @@ export async function processReactCommentMessage(
return new MetaprotocolTransactionResultOK()
}

async function commentTipTierParams(
overlay: EntityManagerOverlay,
tipAmountHapi: bigint
): Promise<Partial<Comment>> {
const tipTiers = await config.get(ConfigVariable.CommentTipTiers, overlay.getEm())
if (tipAmountHapi >= BigInt(tipTiers.DIAMOND) * BigInt(HAPI_TO_JOY_RATE)) {
return {
tipTier: CommentTipTier.DIAMOND,
sortPriority: 1000,
}
}
if (tipAmountHapi >= BigInt(tipTiers.GOLD) * BigInt(HAPI_TO_JOY_RATE)) {
return {
tipTier: CommentTipTier.GOLD,
sortPriority: 100,
}
}
if (tipAmountHapi >= BigInt(tipTiers.SILVER) * BigInt(HAPI_TO_JOY_RATE)) {
return {
tipTier: CommentTipTier.SILVER,
sortPriority: 10,
}
}
return {
tipTier: null,
sortPriority: 0,
}
}

export async function processCreateCommentMessage(
overlay: EntityManagerOverlay,
block: SubstrateBlock,
indexInBlock: number,
txHash: string | undefined,
memberId: string,
message: DecodedMetadataObject<ICreateComment>
message: DecodedMetadataObject<ICreateComment>,
payment?: [string, bigint]
): Promise<MetaprotocolTransactionResult> {
const { videoId, parentCommentId, body } = message

Expand All @@ -437,6 +470,7 @@ export async function processCreateCommentMessage(
}

const channelId = assertNotNull(video.channelId)
const channel = await overlay.getRepository(Channel).getByIdOrFail(channelId)
const bannedMembers = await overlay
.getRepository(BannedMember)
.getManyByRelation('channelId', channelId)
Expand Down Expand Up @@ -473,6 +507,15 @@ export async function processCreateCommentMessage(
)
}

let tipAmount = BigInt(0)
if (payment) {
const [tipDestination, tip] = payment
if (tipDestination === channel.rewardAccount) {
tipAmount = tip
}
}
const tipTierParams = await commentTipTierParams(overlay, tipAmount)

// add new comment
const comment = overlay.getRepository(Comment).new({
// TODO: Re-think backward compatibility
Expand All @@ -488,6 +531,8 @@ export async function processCreateCommentMessage(
reactionsAndRepliesCount: 0,
isEdited: false,
isExcluded: false,
tipAmount,
...tipTierParams,
})

// schedule comment counters update
Expand Down
3 changes: 2 additions & 1 deletion src/mappings/membership/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ export async function processMemberRemark(
indexInBlock,
txHash,
memberId,
decodedMessage.createComment
decodedMessage.createComment,
payment
)
}

Expand Down
18 changes: 18 additions & 0 deletions src/server-extension/resolvers/AdminResolver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { model } from '../model'
import {
AppActionSignatureInput,
AppRootDomain,
CommentTipTiers,
ChannelWeight,
CrtMarketCapMinVolume,
ExcludableContentType,
Expand Down Expand Up @@ -66,6 +67,7 @@ import {
SetRootDomainInput,
SetSupportedCategoriesInput,
SetSupportedCategoriesResult,
SetTipTierAmountsInput,
SetVideoHeroInput,
SetVideoHeroResult,
SetVideoViewPerUserTimeLimitInput,
Expand Down Expand Up @@ -165,6 +167,22 @@ export class AdminResolver {
return { isApplied: true }
}

@UseMiddleware(OperatorOnly(OperatorPermission.SET_TIP_TIERS))
@Mutation(() => CommentTipTiers)
async setTipTierAmounts(@Args() args: SetTipTierAmountsInput): Promise<CommentTipTiers> {
const em = await this.em()
const tipTiers = await config.get(ConfigVariable.CommentTipTiers, em)
const newTipTiers = { ...tipTiers, ...args }
await config.set(ConfigVariable.CommentTipTiers, newTipTiers, em)
return newTipTiers
}

@Query(() => CommentTipTiers)
async tipTiers(): Promise<CommentTipTiers> {
const em = await this.em()
return config.get(ConfigVariable.CommentTipTiers, em)
}

@UseMiddleware(OperatorOnly())
@Mutation(() => Int)
async setMaxAttemptsOnMailDelivery(
Expand Down
24 changes: 24 additions & 0 deletions src/server-extension/resolvers/AdminResolver/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ export class SetVideoWeightsInput {
defaultChannelWeight!: number
}

@ArgsType()
export class SetTipTierAmountsInput {
@Field(() => Int, { nullable: true })
SILVER?: number

@Field(() => Int, { nullable: true })
GOLD?: number

@Field(() => Int, { nullable: true })
DIAMOND?: number
}

@ObjectType()
export class CommentTipTiers {
@Field(() => Int, { nullable: false })
SILVER!: number

@Field(() => Int, { nullable: false })
GOLD!: number

@Field(() => Int, { nullable: false })
DIAMOND!: number
}

@ArgsType()
export class SetMaxAttemptsOnMailDeliveryInput {
@Field(() => Int, { nullable: false })
Expand Down
1 change: 1 addition & 0 deletions src/tests/integration/.env
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ VIDEO_RELEVANCE_VIEWS_TICK=50
# [joystream creation weight, YT creation weight]
# ]
RELEVANCE_WEIGHTS="[1, 0.03, 0.3, 0.5, [7,3]]"
COMMENT_TIP_TIERS='{"SILVER": 100, "GOLD": 500, "DIAMOND": 1000}'
MAX_CACHED_ENTITIES=1000
APP_PRIVATE_KEY=this-is-not-so-secret-change-it
SESSION_EXPIRY_AFTER_INACTIVITY_MINUTES=60
Expand Down
6 changes: 5 additions & 1 deletion src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EntityManager } from 'typeorm'
import { GatewayConfig } from '../model'
import { CommentTipTier, GatewayConfig } from '../model'
import { withHiddenEntities } from './sql'

export enum ConfigVariable {
Expand All @@ -25,6 +25,7 @@ export enum ConfigVariable {
AppAssetStorage = 'APP_ASSET_STORAGE',
AppNameAlt = 'APP_NAME_ALT',
NotificationAssetRoot = 'NOTIFICATION_ASSET_ROOT',
CommentTipTiers = 'COMMENT_TIP_TIERS',
}

const boolType = {
Expand All @@ -47,6 +48,8 @@ const jsonType = <T>() => ({
deserialize: (v: string) => JSON.parse(v) as T,
})

export type CommentTipTiers = { [key in CommentTipTier]: number }

export const configVariables = {
[ConfigVariable.SupportNoCategoryVideo]: boolType,
[ConfigVariable.SupportNewCategories]: boolType,
Expand All @@ -71,6 +74,7 @@ export const configVariables = {
[ConfigVariable.AppAssetStorage]: stringType,
[ConfigVariable.AppNameAlt]: stringType,
[ConfigVariable.NotificationAssetRoot]: stringType,
[ConfigVariable.CommentTipTiers]: jsonType<CommentTipTiers>(),
} as const

type TypeOf<C extends ConfigVariable> = ReturnType<(typeof configVariables)[C]['deserialize']>
Expand Down
Loading

0 comments on commit 8e4ccbd

Please sign in to comment.