Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.2.0: Tips #351

Merged
merged 3 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading