mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 01:46:07 +00:00
first try of x-cross tx per link
This commit is contained in:
parent
f06855e305
commit
f1f46b2d80
@ -7,6 +7,7 @@ export const INALIENABLE_RIGHTS = [
|
||||
RIGHTS.SEND_RESET_PASSWORD_EMAIL,
|
||||
RIGHTS.SET_PASSWORD,
|
||||
RIGHTS.QUERY_TRANSACTION_LINK,
|
||||
RIGHTS.QUERY_REDEEM_JWT,
|
||||
RIGHTS.QUERY_OPT_IN,
|
||||
RIGHTS.CHECK_USERNAME,
|
||||
RIGHTS.PROJECT_BRANDING_BANNER,
|
||||
|
||||
@ -6,6 +6,7 @@ export enum RIGHTS {
|
||||
SEND_RESET_PASSWORD_EMAIL = 'SEND_RESET_PASSWORD_EMAIL',
|
||||
SET_PASSWORD = 'SET_PASSWORD',
|
||||
QUERY_TRANSACTION_LINK = 'QUERY_TRANSACTION_LINK',
|
||||
QUERY_REDEEM_JWT = 'QUERY_REDEEM_JWT',
|
||||
QUERY_OPT_IN = 'QUERY_OPT_IN',
|
||||
CHECK_USERNAME = 'CHECK_USERNAME',
|
||||
PROJECT_BRANDING_BANNER = 'PROJECT_BRANDING_BANNER',
|
||||
|
||||
37
backend/src/auth/jwt/JWT.ts
Normal file
37
backend/src/auth/jwt/JWT.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { SignJWT, jwtVerify } from 'jose'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { JwtPayloadType } from './payloadtypes/JwtPayloadType'
|
||||
|
||||
export const decode = async (token: string, signkey: Buffer): Promise<JwtPayloadType | null> => {
|
||||
if (!token) throw new LogError('401 Unauthorized')
|
||||
|
||||
try {
|
||||
const secret = new TextEncoder().encode(signkey.toString())
|
||||
const { payload } = await jwtVerify(token, secret, {
|
||||
issuer: 'urn:gradido:issuer',
|
||||
audience: 'urn:gradido:audience',
|
||||
})
|
||||
return payload as unknown as JwtPayloadType
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const encode = async (payload: JwtPayloadType, signkey: Buffer): Promise<string> => {
|
||||
const secret = new TextEncoder().encode(signkey.toString())
|
||||
const token = await new SignJWT({ payload, 'urn:gradido:claim': true })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt()
|
||||
.setIssuer('urn:gradido:issuer')
|
||||
.setAudience('urn:gradido:audience')
|
||||
.setExpirationTime(payload.expiration)
|
||||
.sign(secret)
|
||||
return token
|
||||
}
|
||||
|
||||
export const decodeJwtType = async (token: string, signkey: Buffer): Promise<string> => {
|
||||
const payload = await decode(token, signkey)
|
||||
return payload ? payload.tokentype : 'unknown token type'
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
// import { JWTPayload } from 'jose'
|
||||
import { JwtPayloadType } from './JwtPayloadType'
|
||||
|
||||
export class DisbursementJwtPayloadType extends JwtPayloadType {
|
||||
static REDEEM_ACTIVATION_TYPE = 'redeem-activation'
|
||||
|
||||
sendercommunityuuid: string
|
||||
sendergradidoid: string
|
||||
sendername: string // alias or firstname
|
||||
redeemcode: string
|
||||
amount: string
|
||||
memo: string
|
||||
|
||||
constructor(
|
||||
senderCom: string,
|
||||
senderUser: string,
|
||||
sendername: string,
|
||||
code: string,
|
||||
amount: string,
|
||||
memo: string,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
super()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
this.tokentype = DisbursementJwtPayloadType.REDEEM_ACTIVATION_TYPE
|
||||
this.sendercommunityuuid = senderCom
|
||||
this.sendergradidoid = senderUser
|
||||
this.sendername = sendername
|
||||
this.redeemcode = code
|
||||
this.amount = amount
|
||||
this.memo = memo
|
||||
}
|
||||
}
|
||||
19
backend/src/auth/jwt/payloadtypes/JwtPayloadType.ts
Normal file
19
backend/src/auth/jwt/payloadtypes/JwtPayloadType.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { JWTPayload } from 'jose'
|
||||
|
||||
export class JwtPayloadType implements JWTPayload {
|
||||
iat?: number | undefined
|
||||
exp?: number | undefined
|
||||
nbf?: number | undefined
|
||||
jti?: string | undefined
|
||||
aud?: string | string[] | undefined
|
||||
sub?: string | undefined
|
||||
iss?: string | undefined;
|
||||
[propName: string]: unknown
|
||||
|
||||
tokentype: string
|
||||
expiration: string // in minutes (format: 10m for ten minutes)
|
||||
constructor() {
|
||||
this.tokentype = 'unknown jwt type'
|
||||
this.expiration = '10m'
|
||||
}
|
||||
}
|
||||
@ -78,6 +78,7 @@ export class FederationClient {
|
||||
)
|
||||
return data.getPublicCommunityInfo
|
||||
} catch (err) {
|
||||
logger.warn(' err', err)
|
||||
const errorString = JSON.stringify(err)
|
||||
logger.warn('Federation: getPublicCommunityInfo failed for endpoint', {
|
||||
endpoint: this.endpoint,
|
||||
|
||||
@ -12,7 +12,7 @@ export class PublicCommunityInfoLoggingView extends AbstractLoggingView {
|
||||
return {
|
||||
name: this.self.name,
|
||||
description: this.self.description,
|
||||
creationDate: this.dateToString(this.self.creationDate),
|
||||
creationDate: this.self.creationDate,
|
||||
publicKey: this.self.publicKey,
|
||||
}
|
||||
}
|
||||
|
||||
37
backend/src/graphql/arg/RedeemJwtArgs.ts
Normal file
37
backend/src/graphql/arg/RedeemJwtArgs.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { MaxLength, MinLength } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Field, ArgsType, InputType } from 'type-graphql'
|
||||
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from '@/graphql/resolver/const/const'
|
||||
import { IsPositiveDecimal } from '@/graphql/validator/Decimal'
|
||||
|
||||
@InputType()
|
||||
@ArgsType()
|
||||
export class RedeemJwtArgs {
|
||||
@Field(() => String, { nullable: false })
|
||||
gradidoID: string
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
alias?: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
firstName?: string | null
|
||||
|
||||
@Field(() => String, { nullable: false })
|
||||
communityUuid: string
|
||||
|
||||
@Field(() => String, { nullable: false })
|
||||
communityName: string
|
||||
|
||||
@Field(() => String, { nullable: false })
|
||||
code: string
|
||||
|
||||
@Field(() => Decimal, { nullable: false })
|
||||
@IsPositiveDecimal()
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String, { nullable: false })
|
||||
@MaxLength(MEMO_MAX_CHARS)
|
||||
@MinLength(MEMO_MIN_CHARS)
|
||||
memo: string
|
||||
}
|
||||
@ -1,26 +1,42 @@
|
||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
import { Community } from './Community'
|
||||
import { User } from './User'
|
||||
|
||||
@ObjectType()
|
||||
export class TransactionLink {
|
||||
constructor(transactionLink: dbTransactionLink, user: User, redeemedBy: User | null = null) {
|
||||
this.id = transactionLink.id
|
||||
this.user = user
|
||||
this.amount = transactionLink.amount
|
||||
this.holdAvailableAmount = transactionLink.holdAvailableAmount
|
||||
this.memo = transactionLink.memo
|
||||
this.code = transactionLink.code
|
||||
this.createdAt = transactionLink.createdAt
|
||||
this.validUntil = transactionLink.validUntil
|
||||
this.deletedAt = transactionLink.deletedAt
|
||||
this.redeemedAt = transactionLink.redeemedAt
|
||||
this.redeemedBy = redeemedBy
|
||||
this.link = CONFIG.COMMUNITY_REDEEM_URL + this.code
|
||||
constructor(
|
||||
dbTransactionLink?: DbTransactionLink,
|
||||
user?: User,
|
||||
redeemedBy?: User,
|
||||
dbCommunities?: DbCommunity[],
|
||||
) {
|
||||
if (dbTransactionLink !== undefined) {
|
||||
this.id = dbTransactionLink.id
|
||||
this.amount = dbTransactionLink.amount
|
||||
this.holdAvailableAmount = dbTransactionLink.holdAvailableAmount
|
||||
this.memo = dbTransactionLink.memo
|
||||
this.code = dbTransactionLink.code
|
||||
this.link = CONFIG.COMMUNITY_REDEEM_URL + this.code
|
||||
this.createdAt = dbTransactionLink.createdAt
|
||||
this.validUntil = dbTransactionLink.validUntil
|
||||
this.deletedAt = dbTransactionLink.deletedAt
|
||||
this.redeemedAt = dbTransactionLink.redeemedAt
|
||||
}
|
||||
if (user !== undefined) {
|
||||
this.user = user
|
||||
}
|
||||
if (redeemedBy !== undefined) {
|
||||
this.redeemedBy = redeemedBy
|
||||
}
|
||||
if (dbCommunities !== undefined) {
|
||||
this.communities = dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom))
|
||||
}
|
||||
}
|
||||
|
||||
@Field(() => Int)
|
||||
@ -58,6 +74,12 @@ export class TransactionLink {
|
||||
|
||||
@Field(() => String)
|
||||
link: string
|
||||
|
||||
@Field(() => String)
|
||||
communityName: string
|
||||
|
||||
@Field(() => [Community])
|
||||
communities: Community[]
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Point } from '@dbTools/typeorm'
|
||||
import { User as dbUser } from '@entity/User'
|
||||
import { User as DbUser } from '@entity/User'
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
|
||||
import { GmsPublishLocationType } from '@enum/GmsPublishLocationType'
|
||||
@ -14,41 +14,43 @@ import { UserContact } from './UserContact'
|
||||
|
||||
@ObjectType()
|
||||
export class User {
|
||||
constructor(user: dbUser | null) {
|
||||
if (user) {
|
||||
this.id = user.id
|
||||
this.foreign = user.foreign
|
||||
this.communityUuid = user.communityUuid
|
||||
if (user.community) {
|
||||
this.communityName = user.community.name
|
||||
constructor(dbUser: DbUser | null) {
|
||||
if (dbUser) {
|
||||
this.id = dbUser.id
|
||||
this.foreign = dbUser.foreign
|
||||
this.communityUuid = dbUser.communityUuid
|
||||
if (dbUser.community) {
|
||||
this.communityName = dbUser.community.name
|
||||
}
|
||||
this.gradidoID = user.gradidoID
|
||||
this.alias = user.alias
|
||||
this.gradidoID = dbUser.gradidoID
|
||||
this.alias = dbUser.alias
|
||||
|
||||
const publishNameLogic = new PublishNameLogic(user)
|
||||
this.humhubUsername = publishNameLogic.getUsername(user.humhubPublishName as PublishNameType)
|
||||
const publishNameLogic = new PublishNameLogic(dbUser)
|
||||
this.humhubUsername = publishNameLogic.getUsername(
|
||||
dbUser.humhubPublishName as PublishNameType,
|
||||
)
|
||||
|
||||
if (user.emailContact) {
|
||||
this.emailChecked = user.emailContact.emailChecked
|
||||
this.emailContact = new UserContact(user.emailContact)
|
||||
if (dbUser.emailContact) {
|
||||
this.emailChecked = dbUser.emailContact.emailChecked
|
||||
this.emailContact = new UserContact(dbUser.emailContact)
|
||||
}
|
||||
this.firstName = user.firstName
|
||||
this.lastName = user.lastName
|
||||
this.deletedAt = user.deletedAt
|
||||
this.createdAt = user.createdAt
|
||||
this.language = user.language
|
||||
this.publisherId = user.publisherId
|
||||
this.roles = user.userRoles?.map((userRole) => userRole.role) ?? []
|
||||
this.firstName = dbUser.firstName
|
||||
this.lastName = dbUser.lastName
|
||||
this.deletedAt = dbUser.deletedAt
|
||||
this.createdAt = dbUser.createdAt
|
||||
this.language = dbUser.language
|
||||
this.publisherId = dbUser.publisherId
|
||||
this.roles = dbUser.userRoles?.map((userRole) => userRole.role) ?? []
|
||||
this.klickTipp = null
|
||||
this.hasElopage = null
|
||||
this.hideAmountGDD = user.hideAmountGDD
|
||||
this.hideAmountGDT = user.hideAmountGDT
|
||||
this.humhubAllowed = user.humhubAllowed
|
||||
this.gmsAllowed = user.gmsAllowed
|
||||
this.gmsPublishName = user.gmsPublishName
|
||||
this.humhubPublishName = user.humhubPublishName
|
||||
this.gmsPublishLocation = user.gmsPublishLocation
|
||||
this.userLocation = user.location ? Point2Location(user.location as Point) : null
|
||||
this.hideAmountGDD = dbUser.hideAmountGDD
|
||||
this.hideAmountGDT = dbUser.hideAmountGDT
|
||||
this.humhubAllowed = dbUser.humhubAllowed
|
||||
this.gmsAllowed = dbUser.gmsAllowed
|
||||
this.gmsPublishName = dbUser.gmsPublishName
|
||||
this.humhubPublishName = dbUser.humhubPublishName
|
||||
this.gmsPublishLocation = dbUser.gmsPublishLocation
|
||||
this.userLocation = dbUser.location ? Point2Location(dbUser.location as Point) : null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { randomBytes } from 'crypto'
|
||||
|
||||
import { getConnection } from '@dbTools/typeorm'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||
@ -10,6 +11,7 @@ import { Decimal } from 'decimal.js-light'
|
||||
import { Resolver, Args, Arg, Authorized, Ctx, Mutation, Query, Int } from 'type-graphql'
|
||||
|
||||
import { Paginated } from '@arg/Paginated'
|
||||
import { RedeemJwtArgs } from '@arg/RedeemJwtArgs'
|
||||
import { TransactionLinkArgs } from '@arg/TransactionLinkArgs'
|
||||
import { TransactionLinkFilters } from '@arg/TransactionLinkFilters'
|
||||
import { ContributionCycleType } from '@enum/ContributionCycleType'
|
||||
@ -22,6 +24,8 @@ import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink'
|
||||
import { User } from '@model/User'
|
||||
import { QueryLinkResult } from '@union/QueryLinkResult'
|
||||
|
||||
import { decode, encode } from '@/auth/jwt/JWT'
|
||||
import { DisbursementJwtPayloadType } from '@/auth/jwt/payloadtypes/DisbursementJwtPayloadType'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import {
|
||||
EVENT_CONTRIBUTION_LINK_REDEEM,
|
||||
@ -39,6 +43,7 @@ import { fullName } from '@/util/utilities'
|
||||
import { calculateBalance } from '@/util/validate'
|
||||
|
||||
import { executeTransaction } from './TransactionResolver'
|
||||
import { getAuthenticatedCommunities, getHomeCommunity } from './util/communities'
|
||||
import { getUserCreation, validateContribution } from './util/creations'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
|
||||
@ -136,6 +141,8 @@ export class TransactionLinkResolver {
|
||||
@Authorized([RIGHTS.QUERY_TRANSACTION_LINK])
|
||||
@Query(() => QueryLinkResult)
|
||||
async queryTransactionLink(@Arg('code') code: string): Promise<typeof QueryLinkResult> {
|
||||
logger.debug('TransactionLinkResolver.queryTransactionLink... code=', code)
|
||||
const transactionLink = new TransactionLink()
|
||||
if (code.match(/^CL-/)) {
|
||||
const contributionLink = await DbContributionLink.findOneOrFail({
|
||||
where: { code: code.replace('CL-', '') },
|
||||
@ -143,19 +150,48 @@ export class TransactionLinkResolver {
|
||||
})
|
||||
return new ContributionLink(contributionLink)
|
||||
} else {
|
||||
const transactionLink = await DbTransactionLink.findOneOrFail({
|
||||
where: { code },
|
||||
withDeleted: true,
|
||||
})
|
||||
const user = await DbUser.findOneOrFail({ where: { id: transactionLink.userId } })
|
||||
let redeemedBy: User | null = null
|
||||
if (transactionLink?.redeemedBy) {
|
||||
redeemedBy = new User(
|
||||
await DbUser.findOneOrFail({ where: { id: transactionLink.redeemedBy } }),
|
||||
)
|
||||
let txLinkFound = false
|
||||
let dbTransactionLink!: DbTransactionLink
|
||||
try {
|
||||
dbTransactionLink = await DbTransactionLink.findOneOrFail({
|
||||
where: { code },
|
||||
withDeleted: true,
|
||||
})
|
||||
txLinkFound = true
|
||||
} catch (err) {
|
||||
txLinkFound = false
|
||||
}
|
||||
// normal redeem code
|
||||
if (txLinkFound) {
|
||||
const user = await DbUser.findOneOrFail({ where: { id: dbTransactionLink.userId } })
|
||||
let redeemedBy
|
||||
if (dbTransactionLink.redeemedBy) {
|
||||
redeemedBy = new User(
|
||||
await DbUser.findOneOrFail({ where: { id: dbTransactionLink.redeemedBy } }),
|
||||
)
|
||||
}
|
||||
const communities = await getAuthenticatedCommunities()
|
||||
return new TransactionLink(dbTransactionLink, new User(user), redeemedBy, communities)
|
||||
} else {
|
||||
// disbursement jwt-token
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||
const homeCom = await getHomeCommunity()
|
||||
const jwtPayload = decode(code, homeCom.publicKey)
|
||||
if (jwtPayload !== null && jwtPayload instanceof DisbursementJwtPayloadType) {
|
||||
const disburseJwtPayload: DisbursementJwtPayloadType = jwtPayload
|
||||
transactionLink.communityName = homeCom.name !== null ? homeCom.name : 'unknown'
|
||||
// transactionLink.user = new User()
|
||||
transactionLink.user.alias = disburseJwtPayload.sendername
|
||||
transactionLink.amount = new Decimal(disburseJwtPayload.amount)
|
||||
transactionLink.memo = disburseJwtPayload.memo
|
||||
transactionLink.code = disburseJwtPayload.redeemcode
|
||||
return transactionLink
|
||||
} else {
|
||||
throw new LogError('Redeem with wrong type of JWT-Token! jwtType=', jwtPayload)
|
||||
}
|
||||
}
|
||||
return new TransactionLink(transactionLink, new User(user), redeemedBy)
|
||||
}
|
||||
return transactionLink
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.REDEEM_TRANSACTION_LINK])
|
||||
@ -364,6 +400,36 @@ export class TransactionLinkResolver {
|
||||
}
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.QUERY_REDEEM_JWT])
|
||||
@Mutation(() => String)
|
||||
async createRedeemJwt(@Args() redeemJwtArgs: RedeemJwtArgs): Promise<string> {
|
||||
logger.debug('TransactionLinkResolver.queryRedeemJwt... args=', {
|
||||
gradidoID: redeemJwtArgs.gradidoID,
|
||||
alias: redeemJwtArgs.alias,
|
||||
firstName: redeemJwtArgs.firstName,
|
||||
communityUuid: redeemJwtArgs.communityUuid,
|
||||
communityName: redeemJwtArgs.communityName,
|
||||
code: redeemJwtArgs.code,
|
||||
amount: redeemJwtArgs.amount,
|
||||
memo: redeemJwtArgs.memo,
|
||||
})
|
||||
|
||||
const disbursementJwtPayloadType = new DisbursementJwtPayloadType(
|
||||
redeemJwtArgs.communityUuid,
|
||||
redeemJwtArgs.gradidoID,
|
||||
redeemJwtArgs.alias ?? redeemJwtArgs.firstName ?? '',
|
||||
redeemJwtArgs.code,
|
||||
redeemJwtArgs.amount.toString(),
|
||||
redeemJwtArgs.memo,
|
||||
)
|
||||
const homeCom = await getHomeCommunity()
|
||||
if (!homeCom.privateKey) {
|
||||
throw new LogError('Home community private key is not set')
|
||||
}
|
||||
const redeemJwt = await encode(disbursementJwtPayloadType, homeCom.privateKey)
|
||||
return redeemJwt
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.LIST_TRANSACTION_LINKS])
|
||||
@Query(() => TransactionLinkResult)
|
||||
async listTransactionLinks(
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { FindOneOptions } from '@dbTools/typeorm'
|
||||
import { FindOneOptions, IsNull, Not } from '@dbTools/typeorm'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
|
||||
@ -87,6 +87,17 @@ export async function getCommunityByUuid(communityUuid: string): Promise<DbCommu
|
||||
})
|
||||
}
|
||||
|
||||
export async function getAuthenticatedCommunities(): Promise<DbCommunity[]> {
|
||||
const dbCommunities: DbCommunity[] = await DbCommunity.find({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
||||
where: { communityUuid: Not(IsNull()) }, //, authenticatedAt: Not(IsNull()) },
|
||||
order: {
|
||||
name: 'ASC',
|
||||
},
|
||||
})
|
||||
return dbCommunities
|
||||
}
|
||||
|
||||
export async function getCommunityByIdentifier(
|
||||
communityIdentifier: string,
|
||||
): Promise<DbCommunity | null> {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@ -46,6 +46,7 @@ const validCommunityIdentifier = ref(false)
|
||||
const { onResult } = useQuery(selectCommunities)
|
||||
|
||||
onResult(({ data }) => {
|
||||
console.log('CommunitySwitch.onResult...data=', data)
|
||||
if (data) {
|
||||
communities.value = data.communities
|
||||
setDefaultCommunity()
|
||||
@ -55,22 +56,29 @@ onResult(({ data }) => {
|
||||
const communityIdentifier = computed(() => route.params.communityIdentifier)
|
||||
|
||||
function updateCommunity(community) {
|
||||
console.log('CommunitySwitch.updateCommunity...community=', community)
|
||||
emit('update:model-value', community)
|
||||
}
|
||||
|
||||
function setDefaultCommunity() {
|
||||
console.log('CommunitySwitch.setDefaultCommunity... communityIdentifier= communities=', communityIdentifier, communities)
|
||||
if (communityIdentifier.value && communities.value.length >= 1) {
|
||||
console.log('CommunitySwitch.setDefaultCommunity... communities.value.length=', communities.value.length)
|
||||
const foundCommunity = communities.value.find((community) => {
|
||||
console.log('CommunitySwitch.setDefaultCommunity... community=', community)
|
||||
if (
|
||||
community.uuid === communityIdentifier.value ||
|
||||
community.name === communityIdentifier.value
|
||||
) {
|
||||
validCommunityIdentifier.value = true
|
||||
console.log('CommunitySwitch.setDefaultCommunity...true validCommunityIdentifier=', validCommunityIdentifier)
|
||||
return true
|
||||
}
|
||||
console.log('CommunitySwitch.setDefaultCommunity...false validCommunityIdentifier=', validCommunityIdentifier)
|
||||
return false
|
||||
})
|
||||
if (foundCommunity) {
|
||||
console.log('CommunitySwitch.setDefaultCommunity...foundCommunity=', foundCommunity)
|
||||
updateCommunity(foundCommunity)
|
||||
return
|
||||
}
|
||||
@ -79,10 +87,13 @@ function setDefaultCommunity() {
|
||||
|
||||
if (validCommunityIdentifier.value && !communityIdentifier.value) {
|
||||
validCommunityIdentifier.value = false
|
||||
console.log('CommunitySwitch.setDefaultCommunity...validCommunityIdentifier=', validCommunityIdentifier)
|
||||
}
|
||||
|
||||
if (props.modelValue?.uuid === '' && communities.value.length) {
|
||||
console.log('CommunitySwitch.setDefaultCommunity...props.modelValue= communities=', props.modelValue, communities.value.length)
|
||||
const foundCommunity = communities.value.find((community) => !community.foreign)
|
||||
console.log('CommunitySwitch.setDefaultCommunity...foundCommunity=', foundCommunity)
|
||||
if (foundCommunity) {
|
||||
updateCommunity(foundCommunity)
|
||||
}
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div
|
||||
:link-data="linkData"
|
||||
:redeem-code="redeemCode"
|
||||
:is-contribution-link="isContributionLink"
|
||||
class="redeem-community-selection"
|
||||
>
|
||||
<BCard bg-variant="muted" text-variant="dark" border-variant="info">
|
||||
<h1 v-if="linkData.amount === ''">{{ $t('gdd_per_link.redeemlink-error') }}</h1>
|
||||
<h1 v-if="!isContributionLink && linkData.amount !== ''">
|
||||
<BCol class="mb-4" cols="12">
|
||||
<BRow>
|
||||
<BCol>{{ $t('gdd_per_link.recipientCommunity') }}</BCol>
|
||||
</BRow>
|
||||
<BRow>
|
||||
<BCol class="fw-bold">
|
||||
<community-switch
|
||||
:disabled="isBalanceDisabled"
|
||||
:model-value="targetCommunity"
|
||||
@update:model-value="setTargetCommunity"
|
||||
/>
|
||||
</BCol>
|
||||
<BCol v-if="isForeignCommunitySelected" sm="12" md="6" class="mt-4 mt-lg-0">
|
||||
<p>{{ $t('gdd_per_link.switchCommunity') }}</p>
|
||||
<BButton variant="gradido" @click="onSwitch">
|
||||
{{ $t('gdd_per_link.to-switch') }}
|
||||
</BButton>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</BCol>
|
||||
<template v-if="linkData.user">
|
||||
{{ linkData.user.firstName }}
|
||||
{{ $t('transaction-link.send_you') }} {{ $filters.GDD(linkData.amount) }}
|
||||
</template>
|
||||
</h1>
|
||||
<b>{{ linkData.memo }}</b>
|
||||
</BCard>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import CONFIG from '@/config'
|
||||
import { computed } from 'vue'
|
||||
import { createRedeemJwtMutation } from '@/graphql/mutations'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
|
||||
const props = defineProps({
|
||||
linkData: { type: Object, required: true },
|
||||
redeemCode: { type: String, required: true },
|
||||
isContributionLink: { type: Boolean, default: false },
|
||||
targetCommunity: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({ uuid: '', name: CONFIG.COMMUNITY_NAME }),
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:targetCommunity'])
|
||||
|
||||
const isForeignCommunitySelected = computed(() => {
|
||||
if (props.targetCommunity.name !== CONFIG.COMMUNITY_NAME) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
function setTargetCommunity(community) {
|
||||
console.log('RedeemCommunitySelection.setTargetCommunity...community=', community)
|
||||
emit('update:targetCommunity', community)
|
||||
}
|
||||
|
||||
const { mutate: createRedeemJwt } = useMutation(createRedeemJwtMutation)
|
||||
|
||||
async function onSwitch(event) {
|
||||
event.preventDefault() // Prevent the default navigation
|
||||
console.log('RedeemCommunitySelection.onSwitch... props=', props)
|
||||
if (isForeignCommunitySelected.value) {
|
||||
const redeemJwtArgs = {
|
||||
gradidoID: props.linkData.user.gradidoID,
|
||||
firstName: props.linkData.user.firstName,
|
||||
alias: props.linkData.user.alias,
|
||||
communityUuid: props.targetCommunity.uuid,
|
||||
communityName: props.targetCommunity.name,
|
||||
code: props.redeemCode,
|
||||
amount: props.linkData.amount,
|
||||
memo: props.linkData.memo,
|
||||
}
|
||||
|
||||
console.log('RedeemCommunitySelection.onSwitch vor createRedeemJwt params:', redeemJwtArgs)
|
||||
try {
|
||||
const { data } = await createRedeemJwt({ variables: redeemJwtArgs })
|
||||
console.log('RedeemCommunitySelection.onSwitch... response=', data)
|
||||
if (!data?.createRedeemJwt) {
|
||||
throw new Error('Failed to get redeem token')
|
||||
}
|
||||
const targetUrl = props.targetCommunity.url.replace(/\/api\/?$/, '')
|
||||
window.location.href = targetUrl + '/redeem/' + data.createRedeemJwt
|
||||
} catch (error) {
|
||||
console.error('RedeemCommunitySelection.onSwitch error:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="redeem-select-community">
|
||||
<redeem-community-selection
|
||||
v-model:target-community="yourTargetCommunity"
|
||||
:link-data="props.linkData"
|
||||
:redeem-code="props.redeemCode"
|
||||
:is-contribution-link="props.isContributionLink"
|
||||
/>
|
||||
|
||||
<BCard>
|
||||
<div class="mb-2">
|
||||
<h2>{{ $t('gdd_per_link.redeem') }}</h2>
|
||||
</div>
|
||||
|
||||
<BRow>
|
||||
<BCol sm="12" md="6">
|
||||
<p>{{ $t('gdd_per_link.no-account') }}</p>
|
||||
<BButton variant="primary" :to="register()">
|
||||
{{ $t('gdd_per_link.to-register') }}
|
||||
</BButton>
|
||||
</BCol>
|
||||
<BCol sm="12" md="6" class="mt-4 mt-lg-0">
|
||||
<p>{{ $t('gdd_per_link.has-account') }}</p>
|
||||
<BButton variant="gradido" :to="login()">
|
||||
{{ $t('gdd_per_link.to-login') }}
|
||||
</BButton>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</BCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import CONFIG from '@/config'
|
||||
import { useAuthLinks } from '@/composables/useAuthLinks'
|
||||
|
||||
const { login, register } = useAuthLinks()
|
||||
const props = defineProps({
|
||||
linkData: { type: Object, required: true },
|
||||
redeemCode: { type: String, required: true },
|
||||
isContributionLink: { type: Boolean, default: false },
|
||||
})
|
||||
|
||||
const yourTargetCommunity = ref({
|
||||
uuid: '',
|
||||
name: CONFIG.COMMUNITY_NAME,
|
||||
})
|
||||
</script>
|
||||
@ -198,3 +198,27 @@ export const logout = gql`
|
||||
logout
|
||||
}
|
||||
`
|
||||
|
||||
export const createRedeemJwtMutation = gql`
|
||||
mutation (
|
||||
$gradidoID: String!
|
||||
$firstName: String!
|
||||
$alias: String!
|
||||
$communityUuid: String!
|
||||
$communityName: String!
|
||||
$code: String!
|
||||
$amount: Decimal!
|
||||
$memo: String!
|
||||
) {
|
||||
createRedeemJwt(
|
||||
gradidoID: $gradidoID
|
||||
firstName: $firstName
|
||||
alias: $alias
|
||||
communityUuid: $communityUuid
|
||||
communityName: $communityName
|
||||
code: $code
|
||||
amount: $amount
|
||||
memo: $memo
|
||||
)
|
||||
}
|
||||
`
|
||||
|
||||
@ -110,6 +110,7 @@ export const selectCommunities = gql`
|
||||
name
|
||||
description
|
||||
foreign
|
||||
url
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -142,6 +143,13 @@ export const queryTransactionLink = gql`
|
||||
firstName
|
||||
publisherId
|
||||
}
|
||||
communities {
|
||||
foreign
|
||||
name
|
||||
description
|
||||
url
|
||||
uuid
|
||||
}
|
||||
}
|
||||
... on ContributionLink {
|
||||
id
|
||||
|
||||
@ -251,12 +251,15 @@
|
||||
"no-account": "Du besitzt noch kein Gradido Konto?",
|
||||
"no-redeem": "Du darfst deinen eigenen Link nicht einlösen!",
|
||||
"not-copied": "Dein Gerät lässt das Kopieren leider nicht zu! Bitte kopiere den Link von Hand!",
|
||||
"recipientCommunity": "Wähle deine Gemeinschaft zum Einlösen des Link-Guthabens...",
|
||||
"redeem": "Einlösen",
|
||||
"redeemed": "Erfolgreich eingelöst! Deinem Konto wurden {n} GDD gutgeschrieben.",
|
||||
"redeemed-at": "Der Link wurde bereits am {date} eingelöst.",
|
||||
"redeemlink-error": "Dieser Einlöse-Link ist nicht vollständig.",
|
||||
"switchCommunity": "Du hast eine andere Gemeinschaft ausgewählt...",
|
||||
"to-login": "Log dich ein",
|
||||
"to-register": "Registriere ein neues Konto.",
|
||||
"to-switch": "Wechsle zur Gemeinschaft",
|
||||
"validUntil": "Gültig bis",
|
||||
"validUntilDate": "Der Link ist bis zum {date} gültig."
|
||||
},
|
||||
|
||||
@ -251,12 +251,15 @@
|
||||
"no-account": "You don't have a Gradido account yet?",
|
||||
"no-redeem": "You not allowed to redeem your own link!",
|
||||
"not-copied": "Unfortunately, your device does not allow copying! Please copy the link by hand!",
|
||||
"recipientCommunity": "Select your Community to redeem the link-deposit...",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Successfully redeemed! Your account has been credited with {n} GDD.",
|
||||
"redeemed-at": "The link was already redeemed on {date}.",
|
||||
"redeemlink-error": "This redemption link is not complete.",
|
||||
"switchCommunity": "You have selected a foreign Community...",
|
||||
"to-login": "Log in",
|
||||
"to-register": "Register a new account.",
|
||||
"to-switch": "Switch to Community",
|
||||
"validUntil": "Valid until",
|
||||
"validUntilDate": "The link is valid until {date}."
|
||||
},
|
||||
|
||||
@ -191,12 +191,15 @@
|
||||
"no-account": "Aún no tienes una cuenta de Gradido?",
|
||||
"no-redeem": "No puedes canjear tu propio enlace!",
|
||||
"not-copied": "¡Desafortunadamente, su dispositivo no permite copiar! Copie el enlace manualmente!",
|
||||
"recipientCommunity": "Select your Community to redeem the link-deposit...",
|
||||
"redeem": "Canjear",
|
||||
"redeemed": "¡Canjeado con éxito! Tu cuenta ha sido acreditada con {n} GDD.",
|
||||
"redeemed-at": "El enlace ya se canjeó el {date}.",
|
||||
"redeemed-title": "canjeado",
|
||||
"switchCommunity": "You have selected a foreign Community...",
|
||||
"to-login": "iniciar sesión",
|
||||
"to-register": "Registre una nueva cuenta.",
|
||||
"to-switch": "Switch to Community",
|
||||
"validUntil": "Válido hasta",
|
||||
"validUntilDate": "El enlace es válido hasta el {date} ."
|
||||
},
|
||||
|
||||
@ -197,12 +197,15 @@
|
||||
"no-account": "Vous n'avez pas encore de compte Gradido?",
|
||||
"no-redeem": "Vous n'êtes pas autorisé à percevoir votre propre lien!",
|
||||
"not-copied": "Malheureusement votre appareil ne permet pas de copier! Veuillez copier le lien manuellement svp!",
|
||||
"recipientCommunity": "Select your Community to redeem the link-deposit...",
|
||||
"redeem": "Encaisser",
|
||||
"redeemed": "Encaissé avec succès! Votre compte est crédité de {n} GDD.",
|
||||
"redeemed-at": "Le lien a déjà été perçu le {date}.",
|
||||
"redeemed-title": "encaisser",
|
||||
"switchCommunity": "You have selected a foreign Community...",
|
||||
"to-login": "Connexion",
|
||||
"to-register": "Enregistrer un nouveau compte.",
|
||||
"to-switch": "Switch to Community",
|
||||
"validUntil": "Valide jusqu'au",
|
||||
"validUntilDate": "Le lien est valide jusqu'au {date}."
|
||||
},
|
||||
|
||||
@ -191,12 +191,15 @@
|
||||
"no-account": "Je hebt nog geen Gradido rekening?",
|
||||
"no-redeem": "Je mag je eigen link niet inwisselen!",
|
||||
"not-copied": "Jouw apparaat laat het kopiëren helaas niet toe! Kopieer de link alsjeblieft met de hand!",
|
||||
"recipientCommunity": "Select your Community to redeem the link-deposit...",
|
||||
"redeem": "Inwisselen",
|
||||
"redeemed": "Succesvol ingewisseld! Op jouw rekening werden {n} GDD bijgeschreven.",
|
||||
"redeemed-at": "De link werd al op {date} ingewisseld.",
|
||||
"redeemed-title": "ingewisseld",
|
||||
"switchCommunity": "You have selected a foreign Community...",
|
||||
"to-login": "Inloggen",
|
||||
"to-register": "Registreer een nieuwe rekening.",
|
||||
"to-switch": "Switch to Community",
|
||||
"validUntil": "Geldig tot",
|
||||
"validUntilDate": "De link is geldig tot {date}."
|
||||
},
|
||||
|
||||
@ -176,13 +176,16 @@
|
||||
"no-account": "Henüz bir Gradido hesabınız yok mu?",
|
||||
"no-redeem": "Kendi linkini kullanarak GDD'lerini paraya dönüştüremezsin!",
|
||||
"not-copied": "Maalesef cihazın kopyalamaya izin vermiyor! Lütfen bağlantıyı elle kopyala!",
|
||||
"recipientCommunity": "Select your Community to redeem the link-deposit...",
|
||||
"redeem": "Paraya dönüştürme",
|
||||
"redeem-text": "Şu anda paraya dönüştürmek ister misin?",
|
||||
"redeemed": "Başarıyla dönüştürüldü! Hesabına {n} GDD yatırıldı.",
|
||||
"redeemed-at": "Link {date} tarihinde paraya dönüştürüldü.",
|
||||
"redeemed-title": "paraya dönüştürüldü",
|
||||
"switchCommunity": "You have selected a foreign Community...",
|
||||
"to-login": "Giriş yap",
|
||||
"to-register": "Yeni hesap aç.",
|
||||
"to-switch": "Switch to Community",
|
||||
"validUntil": "Geçerlilik süresi",
|
||||
"validUntilDate": "Link {date} tarihine kadar geçerli."
|
||||
},
|
||||
|
||||
@ -2,8 +2,12 @@
|
||||
<div class="show-transaction-link-informations">
|
||||
<div class="mt-4">
|
||||
<transaction-link-item :type="itemTypeExt">
|
||||
<template #LOGGED_OUT>
|
||||
<redeem-logged-out :link-data="linkData" :is-contribution-link="isContributionLink" />
|
||||
<template #REDEEM_SELECT_COMMUNITY>
|
||||
<redeem-select-community
|
||||
:link-data="linkData"
|
||||
:redeem-code="redeemCode"
|
||||
:is-contribution-link="isContributionLink"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #SELF_CREATOR>
|
||||
@ -33,7 +37,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { useQuery, useMutation } from '@vue/apollo-composable'
|
||||
import TransactionLinkItem from '@/components/TransactionLinkItem'
|
||||
import RedeemLoggedOut from '@/components/LinkInformations/RedeemLoggedOut'
|
||||
import RedeemSelectCommunity from '@/components/LinkInformations/RedeemSelectCommunity'
|
||||
import RedeemSelfCreator from '@/components/LinkInformations/RedeemSelfCreator'
|
||||
import RedeemValid from '@/components/LinkInformations/RedeemValid'
|
||||
import RedeemedTextBox from '@/components/LinkInformations/RedeemedTextBox'
|
||||
@ -52,11 +56,17 @@ const linkData = ref({
|
||||
__typename: 'TransactionLink',
|
||||
amount: '',
|
||||
memo: '',
|
||||
user: {
|
||||
firstName: '',
|
||||
},
|
||||
user: null,
|
||||
deletedAt: null,
|
||||
validLink: false,
|
||||
communities: [],
|
||||
// ContributionLink fields
|
||||
validTo: null,
|
||||
validFrom: null,
|
||||
name: '',
|
||||
cycle: null,
|
||||
link: '',
|
||||
maxAmountPerMonth: null,
|
||||
})
|
||||
|
||||
const redeemedBoxText = ref('')
|
||||
@ -75,6 +85,8 @@ const isContributionLink = computed(() => {
|
||||
return params.code?.search(/^CL-/) === 0
|
||||
})
|
||||
|
||||
const redeemCode = computed(() => params.code)
|
||||
|
||||
const tokenExpiresInSeconds = computed(() => {
|
||||
const remainingSecs = Math.floor(
|
||||
(new Date(store.state.tokenTime * 1000).getTime() - new Date().getTime()) / 1000,
|
||||
@ -87,18 +99,34 @@ const validLink = computed(() => {
|
||||
})
|
||||
|
||||
const itemType = computed(() => {
|
||||
if (linkData.value.deletedAt) return 'TEXT_DELETED'
|
||||
if (new Date(linkData.value.validUntil) < new Date()) return 'TEXT_EXPIRED'
|
||||
if (linkData.value.redeemedAt) return 'TEXT_REDEEMED'
|
||||
|
||||
if (store.state.token && store.state.tokenTime) {
|
||||
if (tokenExpiresInSeconds.value < 5) return 'LOGGED_OUT'
|
||||
if (linkData.value.user && store.state.gradidoID === linkData.value.user.gradidoID)
|
||||
return 'SELF_CREATOR'
|
||||
if (!linkData.value.redeemedAt && !linkData.value.deletedAt) return 'VALID'
|
||||
if (linkData.value.deletedAt) {
|
||||
console.log('TransactionLink.itemType... TEXT_DELETED')
|
||||
return 'TEXT_DELETED'
|
||||
}
|
||||
|
||||
return 'LOGGED_OUT'
|
||||
if (new Date(linkData.value.validUntil) < new Date()) {
|
||||
console.log('TransactionLink.itemType... TEXT_EXPIRED')
|
||||
return 'TEXT_EXPIRED'
|
||||
}
|
||||
if (linkData.value.redeemedAt) {
|
||||
console.log('TransactionLink.itemType... TEXT_REDEEMED')
|
||||
return 'TEXT_REDEEMED'
|
||||
}
|
||||
if (store.state.token && store.state.tokenTime) {
|
||||
if (tokenExpiresInSeconds.value < 5) {
|
||||
console.log('TransactionLink.itemType... REDEEM_SELECT_COMMUNITY')
|
||||
return 'REDEEM_SELECT_COMMUNITY'
|
||||
}
|
||||
if (linkData.value.user && store.state.gradidoID === linkData.value.user.gradidoID) {
|
||||
console.log('TransactionLink.itemType... SELF_CREATOR')
|
||||
return 'SELF_CREATOR'
|
||||
}
|
||||
if (!linkData.value.redeemedAt && !linkData.value.deletedAt) {
|
||||
console.log('TransactionLink.itemType... VALID')
|
||||
return 'VALID'
|
||||
}
|
||||
}
|
||||
console.log('TransactionLink.itemType...last return= REDEEM_SELECT_COMMUNITY')
|
||||
return 'REDEEM_SELECT_COMMUNITY'
|
||||
})
|
||||
|
||||
const itemTypeExt = computed(() => {
|
||||
@ -109,10 +137,12 @@ const itemTypeExt = computed(() => {
|
||||
})
|
||||
|
||||
watch(itemType, (newItemType) => {
|
||||
console.log('TransactionLink.watch... itemType=', itemType)
|
||||
updateRedeemedBoxText(newItemType)
|
||||
})
|
||||
|
||||
function updateRedeemedBoxText(type) {
|
||||
console.log('TransactionLink.updateRedeemedBoxText... type=', type)
|
||||
switch (type) {
|
||||
case 'TEXT_DELETED':
|
||||
redeemedBoxText.value = t('gdd_per_link.link-deleted', {
|
||||
@ -132,15 +162,18 @@ function updateRedeemedBoxText(type) {
|
||||
default:
|
||||
redeemedBoxText.value = ''
|
||||
}
|
||||
console.log('TransactionLink.updateRedeemedBoxText... redeemedBoxText=', redeemedBoxText)
|
||||
}
|
||||
|
||||
const emit = defineEmits(['set-mobile-start'])
|
||||
|
||||
onMounted(() => {
|
||||
console.log('TransactionLink.onMounted... params=', params)
|
||||
emit('set-mobile-start', false)
|
||||
})
|
||||
|
||||
onResult(() => {
|
||||
console.log('TransactionLink.onResult... result=', result)
|
||||
if (!result || !result.value) return
|
||||
setTransactionLinkInformation()
|
||||
})
|
||||
@ -150,19 +183,27 @@ onError(() => {
|
||||
})
|
||||
|
||||
function setTransactionLinkInformation() {
|
||||
console.log('TransactionLink.setTransactionLinkInformation... result=', result)
|
||||
const { queryTransactionLink } = result.value
|
||||
console.log(
|
||||
'TransactionLink.setTransactionLinkInformation... queryTransactionLink=',
|
||||
queryTransactionLink,
|
||||
)
|
||||
if (queryTransactionLink) {
|
||||
linkData.value = queryTransactionLink
|
||||
console.log('TransactionLink.setTransactionLinkInformation... linkData.value=', linkData.value)
|
||||
if (linkData.value.__typename === 'ContributionLink' && store.state.token) {
|
||||
console.log('TransactionLink.setTransactionLinkInformation... typename === ContributionLink')
|
||||
mutationLink(linkData.value.amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function mutationLink(amount) {
|
||||
console.log('TransactionLink.mutationLink... params=', params)
|
||||
try {
|
||||
await redeemMutate({
|
||||
code: params.code,
|
||||
code: redeemCode.value,
|
||||
})
|
||||
toastSuccess(t('gdd_per_link.redeemed', { n: amount }))
|
||||
await router.push('/overview')
|
||||
|
||||
174
frontend/src/pages/TransactionLinkDisburse.vue
Normal file
174
frontend/src/pages/TransactionLinkDisburse.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div class="show-transaction-link-informations">
|
||||
<div class="mt-4">
|
||||
<transaction-link-item :type="itemTypeExt">
|
||||
<template #LOGGED_OUT>
|
||||
<redeem-logged-out :link-data="linkData" :is-contribution-link="isContributionLink" />
|
||||
</template>
|
||||
|
||||
<template #SELF_CREATOR>
|
||||
<redeem-self-creator :link-data="linkData" />
|
||||
</template>
|
||||
|
||||
<template #VALID>
|
||||
<redeem-valid
|
||||
:link-data="linkData"
|
||||
:is-contribution-link="isContributionLink"
|
||||
:valid-link="validLink"
|
||||
@mutation-link="mutationLink"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #TEXT>
|
||||
<redeemed-text-box :text="redeemedBoxText" />
|
||||
</template>
|
||||
</transaction-link-item>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { useQuery, useMutation } from '@vue/apollo-composable'
|
||||
import TransactionLinkItem from '@/components/TransactionLinkItem'
|
||||
import RedeemLoggedOut from '@/components/LinkInformations/RedeemLoggedOut'
|
||||
import RedeemSelfCreator from '@/components/LinkInformations/RedeemSelfCreator'
|
||||
import RedeemValid from '@/components/LinkInformations/RedeemValid'
|
||||
import RedeemedTextBox from '@/components/LinkInformations/RedeemedTextBox'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
import { queryTransactionLink } from '@/graphql/queries'
|
||||
import { redeemTransactionLink } from '@/graphql/mutations'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
const router = useRouter()
|
||||
const { params } = useRoute()
|
||||
const store = useStore()
|
||||
const { d, t } = useI18n()
|
||||
|
||||
const linkData = ref({
|
||||
__typename: 'TransactionLink',
|
||||
amount: '',
|
||||
memo: '',
|
||||
user: {
|
||||
firstName: '',
|
||||
},
|
||||
deletedAt: null,
|
||||
validLink: false,
|
||||
})
|
||||
|
||||
const redeemedBoxText = ref('')
|
||||
|
||||
const { result, onResult, loading, error, onError } = useQuery(queryTransactionLink, {
|
||||
code: params.code,
|
||||
})
|
||||
|
||||
const {
|
||||
mutate: redeemMutate,
|
||||
loading: redeemLoading,
|
||||
error: redeemError,
|
||||
} = useMutation(redeemTransactionLink)
|
||||
|
||||
const isContributionLink = computed(() => {
|
||||
return params.code?.search(/^CL-/) === 0
|
||||
})
|
||||
|
||||
const tokenExpiresInSeconds = computed(() => {
|
||||
const remainingSecs = Math.floor(
|
||||
(new Date(store.state.tokenTime * 1000).getTime() - new Date().getTime()) / 1000,
|
||||
)
|
||||
return remainingSecs <= 0 ? 0 : remainingSecs
|
||||
})
|
||||
|
||||
const validLink = computed(() => {
|
||||
return new Date(linkData.value.validUntil) > new Date()
|
||||
})
|
||||
|
||||
const itemType = computed(() => {
|
||||
if (linkData.value.deletedAt) return 'TEXT_DELETED'
|
||||
if (new Date(linkData.value.validUntil) < new Date()) return 'TEXT_EXPIRED'
|
||||
if (linkData.value.redeemedAt) return 'TEXT_REDEEMED'
|
||||
|
||||
if (store.state.token && store.state.tokenTime) {
|
||||
if (tokenExpiresInSeconds.value < 5) return 'LOGGED_OUT'
|
||||
if (linkData.value.user && store.state.gradidoID === linkData.value.user.gradidoID)
|
||||
return 'SELF_CREATOR'
|
||||
if (!linkData.value.redeemedAt && !linkData.value.deletedAt) return 'VALID'
|
||||
}
|
||||
|
||||
return 'LOGGED_OUT'
|
||||
})
|
||||
|
||||
const itemTypeExt = computed(() => {
|
||||
if (itemType.value.startsWith('TEXT')) {
|
||||
return 'TEXT'
|
||||
}
|
||||
return itemType.value
|
||||
})
|
||||
|
||||
watch(itemType, (newItemType) => {
|
||||
updateRedeemedBoxText(newItemType)
|
||||
})
|
||||
|
||||
function updateRedeemedBoxText(type) {
|
||||
switch (type) {
|
||||
case 'TEXT_DELETED':
|
||||
redeemedBoxText.value = t('gdd_per_link.link-deleted', {
|
||||
date: d(new Date(linkData.value.deletedAt), 'long'),
|
||||
})
|
||||
break
|
||||
case 'TEXT_EXPIRED':
|
||||
redeemedBoxText.value = t('gdd_per_link.link-expired', {
|
||||
date: d(new Date(linkData.value.validUntil), 'long'),
|
||||
})
|
||||
break
|
||||
case 'TEXT_REDEEMED':
|
||||
redeemedBoxText.value = t('gdd_per_link.redeemed-at', {
|
||||
date: d(new Date(linkData.value.redeemedAt), 'long'),
|
||||
})
|
||||
break
|
||||
default:
|
||||
redeemedBoxText.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits(['set-mobile-start'])
|
||||
|
||||
onMounted(() => {
|
||||
emit('set-mobile-start', false)
|
||||
})
|
||||
|
||||
onResult(() => {
|
||||
if (!result || !result.value) return
|
||||
setTransactionLinkInformation()
|
||||
})
|
||||
|
||||
onError(() => {
|
||||
toastError(t('gdd_per_link.redeemlink-error'))
|
||||
})
|
||||
|
||||
function setTransactionLinkInformation() {
|
||||
const { queryTransactionLink } = result.value
|
||||
if (queryTransactionLink) {
|
||||
linkData.value = queryTransactionLink
|
||||
if (linkData.value.__typename === 'ContributionLink' && store.state.token) {
|
||||
mutationLink(linkData.value.amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function mutationLink(amount) {
|
||||
try {
|
||||
await redeemMutate({
|
||||
code: params.code,
|
||||
})
|
||||
toastSuccess(t('gdd_per_link.redeemed', { n: amount }))
|
||||
await router.push('/overview')
|
||||
} catch (err) {
|
||||
toastError(err.message)
|
||||
await router.push('/overview')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Loading…
x
Reference in New Issue
Block a user