universo-platform-3d

Форк
0
430 строк · 11.1 Кб
1
import { PREMIUM_ACCESS } from './../option-sets/premium-tiers'
2
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
3
import * as mongoose from 'mongoose'
4
import { Document, SchemaTypes } from 'mongoose'
5
import { UserGroupInvite } from '../user-groups/user-group-invite.schema'
6
import { ApiProperty } from '@nestjs/swagger'
7
import { USER_AVATAR_TYPE } from '../option-sets/user-avatar-types'
8
import { CustomData } from '../custom-data/models/custom-data.schema'
9
import { UserCartItem, UserCartItemSchema } from './models/user-cart.schema'
10
import { UserRecents, UserRecentsSchema } from './models/user-recents.schema'
11
import {
12
  UserMarketing,
13
  UserMarketingSchema
14
} from './models/user-marketing.schema'
15

16
export type UserDocument = User & Document
17

18
export const USER_VIRTUAL_PROPERTY_PUBLIC_ASSETS = 'publicAssets'
19
export const USER_VIRTUAL_PROPERTY_PUBLIC_GROUPS = 'publicGroups'
20

21
/**
22
 * Tutorial nested object. The generic approach is for all of these to be undefined. In the consuming app, check for truthiness of user.tutorial[propertyName], e.g. user.tutorial.shownFirstSpacePopupV1
23
 */
24
@Schema()
25
export class UserTutorial {
26
  // be sure to keep UpdateUserTutorialDto up to date with these properties
27
  @Prop({ type: Boolean })
28
  shownFirstInSpacePopupV1?: boolean
29
  @Prop({ type: Boolean })
30
  shownFirstHomeScreenPopupV1?: boolean
31
  @Prop({ type: Boolean })
32
  shownWebAppPopupV1?: boolean
33
}
34

35
// Properties must not be undefined so that getPublicPropertiesForMongooseQuery can work
36
export class UserPublicData {
37
  @ApiProperty({ type: 'string' }) // @ApiProperty must be included to be exposed by the API and flow to FE codegen
38
  _id = '' // Must not be undefined
39
  @ApiProperty({ type: Date })
40
  createdAt = new Date()
41
  @ApiProperty({ type: Date })
42
  updatedAt = new Date()
43
  @ApiProperty({ type: 'string' })
44
  discordUserId? = ''
45
  @ApiProperty({ type: 'string' })
46
  isInternalAdmin = ''
47
  @ApiProperty({ type: 'string' })
48
  displayName = ''
49
  @ApiProperty({ type: 'string' })
50
  email = ''
51
  @ApiProperty({ type: 'string' })
52
  publicBio = ''
53
  // We only need a string to decipher the current avatar.
54
  @ApiProperty({ type: 'string' })
55
  avatarUrl = ''
56

57
  // TODO: We can probably get rid of avatar type and any variables specific to ready player me.
58
  @ApiProperty({ enum: USER_AVATAR_TYPE })
59
  avatarType = ''
60
  @ApiProperty({ type: 'string' })
61
  readyPlayerMeUrlGlb? = ''
62
  @ApiProperty({ type: [String] })
63
  readyPlayerMeAvatarUrls? = []
64
  @ApiProperty({ type: 'string' })
65
  polygonPublicKey? = ''
66
  @ApiProperty({ type: 'string' })
67
  ethereumPublicKey? = ''
68
  @ApiProperty({ type: 'string' })
69
  twitterUsername? = ''
70
  @ApiProperty({ type: 'string' })
71
  githubUsername? = ''
72
  @ApiProperty({ type: 'string' })
73
  instagramUsername? = ''
74
  @ApiProperty({ type: 'string' })
75
  youtubeChannel? = ''
76
  @ApiProperty({ type: 'string' })
77
  artStationUsername? = ''
78
  @ApiProperty({ type: 'string' })
79
  sketchfabUsername? = ''
80
  @ApiProperty({ type: 'string' })
81
  profileImage? = ''
82
  @ApiProperty({ type: 'string' })
83
  coverImage? = ''
84
  @ApiProperty({ type: [String] })
85
  sidebarTags? = []
86

87
  /**
88
   * Closed Beta
89
   */
90
  @ApiProperty({ type: 'boolean' })
91
  closedBetaHasClickedInterestedInBeta? = false
92
  @ApiProperty({ type: 'boolean' })
93
  closedBetaIsInClosedBeta? = false
94

95
  /**
96
   * Terms
97
   */
98
  @ApiProperty({ type: 'boolean' })
99
  termsAgreedtoClosedAlpha? = false
100

101
  @ApiProperty({ type: 'boolean' })
102
  termsAgreedtoGeneralTOSandPP? = false
103

104
  @ApiProperty({ enum: PREMIUM_ACCESS, isArray: true })
105
  premiumAccess: PREMIUM_ACCESS[] = []
106
}
107

108
@Schema({
109
  timestamps: true,
110
  toJSON: {
111
    virtuals: true
112
  }
113
})
114
export class User {
115
  @ApiProperty()
116
  _id: string
117
  @ApiProperty()
118
  createdAt: Date // this is managed by mongoose timestamps: true, but defining it here so types will align
119
  @ApiProperty()
120
  updatedAt: Date // this is managed by mongoose timestamps: true, but defining it here so types will align
121

122
  @Prop()
123
  @ApiProperty()
124
  firebaseUID: string
125

126
  /**
127
   * Whether the user is a Mirror admin, exposing admin functionality. This should ONLY be used if the person works for The Mirror.
128
   */
129
  @Prop()
130
  @ApiProperty()
131
  isInternalAdmin: string
132

133
  @Prop({
134
    required: true,
135
    minLength: 3,
136
    maxLength: 40
137
  })
138
  @ApiProperty()
139
  displayName: string
140

141
  @Prop({ type: [mongoose.Schema.Types.ObjectId], ref: 'UserGroupInvite' })
142
  @ApiProperty()
143
  groupInvitations: UserGroupInvite[]
144

145
  /**
146
   * @description Note: Change of pattern: NOT using friends: User[] here since we don't want to always populate the friends. We can always add a friends getter if we want to populate them. I think this will be easier on type safety
147
   * @date 2023-06-22 23:42
148
   */
149
  @Prop({
150
    type: [mongoose.Schema.Types.ObjectId],
151
    ref: 'User',
152
    select: false,
153
    default: []
154
  })
155
  @ApiProperty({
156
    description: 'A list of User IDs of friends.'
157
  })
158
  friends?: mongoose.Schema.Types.ObjectId[]
159

160
  /**
161
   * @description A list of User IDs of friends.
162
   * Note: Change of pattern: NOT using friends: User[] here since we don't want to always populate the friends. We can always add a friends getter if we want to populate them. I think this will be easier on type safety
163
   * @date 2023-06-22 23:42
164
   */
165
  @Prop({
166
    type: [mongoose.Schema.Types.ObjectId],
167
    ref: 'User',
168
    select: false,
169
    default: []
170
  })
171
  @ApiProperty({
172
    description: 'A list of User IDs that this User has sent friend requests to'
173
  })
174
  sentFriendRequestsToUsers?: mongoose.Schema.Types.ObjectId[]
175

176
  @Prop({
177
    required: false,
178
    type: UserRecentsSchema
179
  })
180
  @ApiProperty()
181
  recents?: UserRecents
182

183
  @Prop({
184
    required: false,
185
    type: UserMarketingSchema,
186
    select: false
187
  })
188
  @ApiProperty()
189
  marketing?: UserMarketing
190

191
  @Prop({
192
    required: false
193
  })
194
  @ApiProperty()
195
  email: string
196

197
  @Prop(SchemaTypes.Boolean)
198
  @ApiProperty()
199
  emailVerified: boolean
200

201
  @Prop()
202
  @ApiProperty()
203
  publicBio: string
204

205
  // We only need a string to decipher the current avatar.
206
  @Prop()
207
  @ApiProperty()
208
  avatarUrl: string
209

210
  // TODO: We can probably get rid of avatar type and any variables specific to ready player me.
211
  /**
212
   * The high-level currently selected character (e.g. between a Mirror avatar, Ready Player Me avatar, or something else).
213
   * ex: If USER_AVATAR_TYPE.READY_PLAYER_ME is selected, then the readyPlayerMeUrlGlb should be used
214
   */
215
  @Prop({
216
    required: true,
217
    enum: USER_AVATAR_TYPE,
218
    default: USER_AVATAR_TYPE.MIRROR_AVATAR_V1,
219
    type: String
220
  })
221
  @ApiProperty({ enum: USER_AVATAR_TYPE })
222
  avatarType: string
223

224
  /**
225
   * @description I was previously adding this as an array, but it became super complex with needing to filter through to just find small pieces of data. We can always add support for arrays of CustomData in the future, but it's much simpler for now to 1 CustomData object per entity. Plus, it can have JSON, so array data can still be stored - it just won't be an array of CustomData types (rather, it will be an array of what the user specifies: string, numbers, etc. Note that we also want to support references to other entities: this will likely be via CustomDataEntityReference or something similar).
226
   * @date 2023-03-03 20:11
227
   */
228
  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'CustomData' })
229
  @ApiProperty()
230
  customData: CustomData
231

232
  @Prop({
233
    required: true,
234
    default: {}
235
  })
236
  tutorial: UserTutorial
237

238
  /**
239
   * The current string url ending in .glb
240
   */
241
  @Prop()
242
  @ApiProperty()
243
  readyPlayerMeUrlGlb?: string
244

245
  /**
246
   * Array of avatar IDs
247
   */
248
  @Prop([String])
249
  @ApiProperty()
250
  readyPlayerMeAvatarUrls: string[]
251

252
  /**
253
   * Closed Beta
254
   * @deprecated use premiumAccess
255
   */
256
  @Prop({
257
    default: false
258
  })
259
  @ApiProperty()
260
  closedBetaHasClickedInterestedInBeta?: boolean
261

262
  /**
263
   * @deprecated use premiumAccess
264
   */
265
  @Prop({
266
    default: false
267
  })
268
  @ApiProperty()
269
  closedBetaIsInClosedBeta?: boolean
270

271
  /**
272
   * Terms: Closed Alpha
273
   * Future properties can start with `terms` as well, such as termsAgreedToOpenAlpha, termsAgreedToGeneralTOS, etc.
274
   */
275
  @Prop({
276
    default: false
277
  })
278
  @ApiProperty({
279
    description: 'Whether the user has agreed to the closed alpha agreement'
280
  })
281
  termsAgreedtoClosedAlpha?: boolean
282

283
  @Prop({
284
    default: false
285
  })
286
  @ApiProperty({
287
    description:
288
      'Whether the user has agreed to the general Terms of Service and Privacy Policy'
289
  })
290
  termsAgreedtoGeneralTOSandPP?: boolean
291

292
  /**
293
   * Social
294
   */
295
  @Prop()
296
  @ApiProperty()
297
  discordUserId?: string
298

299
  @Prop()
300
  @ApiProperty()
301
  polygonPublicKey?: string
302

303
  @Prop()
304
  @ApiProperty()
305
  ethereumPublicKey?: string
306

307
  @Prop()
308
  @ApiProperty()
309
  twitterUsername?: string
310

311
  @Prop()
312
  @ApiProperty()
313
  githubUsername?: string
314

315
  @Prop()
316
  @ApiProperty()
317
  instagramUsername?: string
318

319
  @Prop()
320
  @ApiProperty()
321
  youtubeChannel?: string
322

323
  @Prop()
324
  @ApiProperty()
325
  artStationUsername?: string
326

327
  @Prop()
328
  @ApiProperty()
329
  sketchfabUsername?: string
330

331
  /**
332
   * Premium access
333
   * @description Premium access. Note that users can have MULTIPLE. This is important because we want to continually store whether they were in closed alpha. Plus, even with premium tiers, we'll have some "super premium" users too, enterprise users, etc.
334
   */
335
  @Prop({
336
    type: [String]
337
    // enum: Object.keys(PREMIUM_ACCESS) // I tried using this here at one point but ran into issues. However, we do want to restrict the strings to enum values, so keeping this comment here.
338
  })
339
  @ApiProperty({ enum: PREMIUM_ACCESS })
340
  premiumAccess: PREMIUM_ACCESS[]
341

342
  /**
343
   * Stripe
344
   */
345
  @Prop()
346
  @ApiProperty()
347
  stripeCustomerId?: string
348

349
  @Prop()
350
  @ApiProperty()
351
  stripeAccountId?: string
352

353
  /**
354
   * Deep Linking a key-value pair, e.g. spaceId and 1234-5678-abcd-efgh
355
   */
356
  @Prop({
357
    required: false
358
  })
359
  @ApiProperty()
360
  deepLinkKey?: string
361

362
  @Prop({
363
    required: false
364
  })
365
  @ApiProperty()
366
  deepLinkValue?: string
367

368
  @Prop({
369
    required: false
370
  })
371
  @ApiProperty()
372
  deepLinkLastUpdatedAt?: Date
373

374
  /**
375
   * Profile Images
376
   */
377
  @Prop()
378
  @ApiProperty()
379
  profileImage?: string
380

381
  @Prop()
382
  @ApiProperty()
383
  coverImage?: string
384

385
  /**
386
   * Cart
387
   */
388
  @Prop({
389
    type: [UserCartItemSchema],
390
    required: false,
391
    select: false // note that select is false here so we don't return too many things for user by default
392
  })
393
  @ApiProperty({ type: () => UserCartItem })
394
  cartItems?: UserCartItem[]
395

396
  /**
397
   * Premium Access ID used for subscription handling.
398
   */
399

400
  @Prop()
401
  @ApiProperty()
402
  stripeSubscriptionId?: string
403

404
  @Prop({ required: false, type: [String] })
405
  @ApiProperty({ required: false })
406
  sidebarTags?: string[]
407

408
  @Prop({ required: false, type: Date })
409
  @ApiProperty({ required: false })
410
  lastActiveTimestamp?: Date
411

412
  /**
413
   * @description When a user requests to delete an account, it is marked with the deleted: true property. A user with the deleted: true property will be filtered out of all search results
414
   * @date 2023-12-22 17:31
415
   */
416

417
  @Prop({ required: false, type: Boolean })
418
  @ApiProperty({ required: false })
419
  deleted?: boolean
420
}
421

422
export const UserSchema = SchemaFactory.createForClass(User)
423

424
// Specifying a virtual with a `ref` property is how you enable virtual
425
// population
426
UserSchema.virtual(USER_VIRTUAL_PROPERTY_PUBLIC_ASSETS, {
427
  ref: 'Asset',
428
  localField: '_id',
429
  foreignField: 'owner'
430
})
431

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.