Added basic follows
This commit is contained in:
parent
e14367edaf
commit
0ca65307b5
1
mari/.github/FUNDING.yml
vendored
Normal file
1
mari/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
patreon: nise_moe
|
||||||
@ -1,7 +1,32 @@
|
|||||||
package org.nisemoe.mari.judgements
|
package org.nisemoe.mari.judgements
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backwards compatibility with time values that were persisted as doubles.
|
||||||
|
*/
|
||||||
|
object TimeSerializer : KSerializer<Int> {
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Time", PrimitiveKind.INT)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: Int) {
|
||||||
|
encoder.encodeInt(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): Int {
|
||||||
|
val doubleValue = decoder.decodeDouble()
|
||||||
|
return doubleValue.roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a judgement on a hit object.
|
* Represents a judgement on a hit object.
|
||||||
@ -9,7 +34,9 @@ import kotlinx.serialization.Serializable
|
|||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Judgement(
|
data class Judgement(
|
||||||
|
@Serializable(with = TimeSerializer::class)
|
||||||
val time: Int,
|
val time: Int,
|
||||||
|
|
||||||
val x: Double,
|
val x: Double,
|
||||||
val y: Double,
|
val y: Double,
|
||||||
val type: Type,
|
val type: Type,
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import com.nisemoe.generated.tables.Scores
|
|||||||
import com.nisemoe.generated.tables.ScoresJudgements
|
import com.nisemoe.generated.tables.ScoresJudgements
|
||||||
import com.nisemoe.generated.tables.ScoresSimilarity
|
import com.nisemoe.generated.tables.ScoresSimilarity
|
||||||
import com.nisemoe.generated.tables.UpdateUserQueue
|
import com.nisemoe.generated.tables.UpdateUserQueue
|
||||||
|
import com.nisemoe.generated.tables.UserFollows
|
||||||
import com.nisemoe.generated.tables.UserScores
|
import com.nisemoe.generated.tables.UserScores
|
||||||
import com.nisemoe.generated.tables.UserScoresSimilarity
|
import com.nisemoe.generated.tables.UserScoresSimilarity
|
||||||
import com.nisemoe.generated.tables.Users
|
import com.nisemoe.generated.tables.Users
|
||||||
@ -87,6 +88,11 @@ open class Public : SchemaImpl("public", DefaultCatalog.DEFAULT_CATALOG) {
|
|||||||
*/
|
*/
|
||||||
val UPDATE_USER_QUEUE: UpdateUserQueue get() = UpdateUserQueue.UPDATE_USER_QUEUE
|
val UPDATE_USER_QUEUE: UpdateUserQueue get() = UpdateUserQueue.UPDATE_USER_QUEUE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table <code>public.user_follows</code>.
|
||||||
|
*/
|
||||||
|
val USER_FOLLOWS: UserFollows get() = UserFollows.USER_FOLLOWS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The table <code>public.user_scores</code>.
|
* The table <code>public.user_scores</code>.
|
||||||
*/
|
*/
|
||||||
@ -126,6 +132,7 @@ open class Public : SchemaImpl("public", DefaultCatalog.DEFAULT_CATALOG) {
|
|||||||
ScoresJudgements.SCORES_JUDGEMENTS,
|
ScoresJudgements.SCORES_JUDGEMENTS,
|
||||||
ScoresSimilarity.SCORES_SIMILARITY,
|
ScoresSimilarity.SCORES_SIMILARITY,
|
||||||
UpdateUserQueue.UPDATE_USER_QUEUE,
|
UpdateUserQueue.UPDATE_USER_QUEUE,
|
||||||
|
UserFollows.USER_FOLLOWS,
|
||||||
UserScores.USER_SCORES,
|
UserScores.USER_SCORES,
|
||||||
UserScoresSimilarity.USER_SCORES_SIMILARITY,
|
UserScoresSimilarity.USER_SCORES_SIMILARITY,
|
||||||
Users.USERS
|
Users.USERS
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import com.nisemoe.generated.tables.Scores
|
|||||||
import com.nisemoe.generated.tables.ScoresJudgements
|
import com.nisemoe.generated.tables.ScoresJudgements
|
||||||
import com.nisemoe.generated.tables.ScoresSimilarity
|
import com.nisemoe.generated.tables.ScoresSimilarity
|
||||||
import com.nisemoe.generated.tables.UpdateUserQueue
|
import com.nisemoe.generated.tables.UpdateUserQueue
|
||||||
|
import com.nisemoe.generated.tables.UserFollows
|
||||||
import com.nisemoe.generated.tables.UserScoresSimilarity
|
import com.nisemoe.generated.tables.UserScoresSimilarity
|
||||||
import com.nisemoe.generated.tables.Users
|
import com.nisemoe.generated.tables.Users
|
||||||
import com.nisemoe.generated.tables.records.BeatmapsRecord
|
import com.nisemoe.generated.tables.records.BeatmapsRecord
|
||||||
@ -22,6 +23,7 @@ import com.nisemoe.generated.tables.records.ScoresJudgementsRecord
|
|||||||
import com.nisemoe.generated.tables.records.ScoresRecord
|
import com.nisemoe.generated.tables.records.ScoresRecord
|
||||||
import com.nisemoe.generated.tables.records.ScoresSimilarityRecord
|
import com.nisemoe.generated.tables.records.ScoresSimilarityRecord
|
||||||
import com.nisemoe.generated.tables.records.UpdateUserQueueRecord
|
import com.nisemoe.generated.tables.records.UpdateUserQueueRecord
|
||||||
|
import com.nisemoe.generated.tables.records.UserFollowsRecord
|
||||||
import com.nisemoe.generated.tables.records.UserScoresSimilarityRecord
|
import com.nisemoe.generated.tables.records.UserScoresSimilarityRecord
|
||||||
import com.nisemoe.generated.tables.records.UsersRecord
|
import com.nisemoe.generated.tables.records.UsersRecord
|
||||||
|
|
||||||
@ -46,6 +48,7 @@ val SCORES_JUDGEMENTS_PKEY: UniqueKey<ScoresJudgementsRecord> = Internal.createU
|
|||||||
val SCORES_SIMILARITY_PKEY: UniqueKey<ScoresSimilarityRecord> = Internal.createUniqueKey(ScoresSimilarity.SCORES_SIMILARITY, DSL.name("scores_similarity_pkey"), arrayOf(ScoresSimilarity.SCORES_SIMILARITY.ID), true)
|
val SCORES_SIMILARITY_PKEY: UniqueKey<ScoresSimilarityRecord> = Internal.createUniqueKey(ScoresSimilarity.SCORES_SIMILARITY, DSL.name("scores_similarity_pkey"), arrayOf(ScoresSimilarity.SCORES_SIMILARITY.ID), true)
|
||||||
val UNIQUE_BEATMAP_REPLAY_IDS: UniqueKey<ScoresSimilarityRecord> = Internal.createUniqueKey(ScoresSimilarity.SCORES_SIMILARITY, DSL.name("unique_beatmap_replay_ids"), arrayOf(ScoresSimilarity.SCORES_SIMILARITY.BEATMAP_ID, ScoresSimilarity.SCORES_SIMILARITY.REPLAY_ID_1, ScoresSimilarity.SCORES_SIMILARITY.REPLAY_ID_2), true)
|
val UNIQUE_BEATMAP_REPLAY_IDS: UniqueKey<ScoresSimilarityRecord> = Internal.createUniqueKey(ScoresSimilarity.SCORES_SIMILARITY, DSL.name("unique_beatmap_replay_ids"), arrayOf(ScoresSimilarity.SCORES_SIMILARITY.BEATMAP_ID, ScoresSimilarity.SCORES_SIMILARITY.REPLAY_ID_1, ScoresSimilarity.SCORES_SIMILARITY.REPLAY_ID_2), true)
|
||||||
val UPDATE_USER_QUEUE_PKEY: UniqueKey<UpdateUserQueueRecord> = Internal.createUniqueKey(UpdateUserQueue.UPDATE_USER_QUEUE, DSL.name("update_user_queue_pkey"), arrayOf(UpdateUserQueue.UPDATE_USER_QUEUE.ID), true)
|
val UPDATE_USER_QUEUE_PKEY: UniqueKey<UpdateUserQueueRecord> = Internal.createUniqueKey(UpdateUserQueue.UPDATE_USER_QUEUE, DSL.name("update_user_queue_pkey"), arrayOf(UpdateUserQueue.UPDATE_USER_QUEUE.ID), true)
|
||||||
|
val USER_FOLLOWS_PKEY: UniqueKey<UserFollowsRecord> = Internal.createUniqueKey(UserFollows.USER_FOLLOWS, DSL.name("user_follows_pkey"), arrayOf(UserFollows.USER_FOLLOWS.USER_ID, UserFollows.USER_FOLLOWS.FOLLOWS_USER_ID), true)
|
||||||
val USER_SCORES_SIMILARITY_PKEY: UniqueKey<UserScoresSimilarityRecord> = Internal.createUniqueKey(UserScoresSimilarity.USER_SCORES_SIMILARITY, DSL.name("user_scores_similarity_pkey"), arrayOf(UserScoresSimilarity.USER_SCORES_SIMILARITY.ID), true)
|
val USER_SCORES_SIMILARITY_PKEY: UniqueKey<UserScoresSimilarityRecord> = Internal.createUniqueKey(UserScoresSimilarity.USER_SCORES_SIMILARITY, DSL.name("user_scores_similarity_pkey"), arrayOf(UserScoresSimilarity.USER_SCORES_SIMILARITY.ID), true)
|
||||||
val USER_SCORES_UNIQUE_BEATMAP_REPLAY_IDS: UniqueKey<UserScoresSimilarityRecord> = Internal.createUniqueKey(UserScoresSimilarity.USER_SCORES_SIMILARITY, DSL.name("user_scores_unique_beatmap_replay_ids"), arrayOf(UserScoresSimilarity.USER_SCORES_SIMILARITY.BEATMAP_ID, UserScoresSimilarity.USER_SCORES_SIMILARITY.REPLAY_ID_USER, UserScoresSimilarity.USER_SCORES_SIMILARITY.REPLAY_ID_OSU), true)
|
val USER_SCORES_UNIQUE_BEATMAP_REPLAY_IDS: UniqueKey<UserScoresSimilarityRecord> = Internal.createUniqueKey(UserScoresSimilarity.USER_SCORES_SIMILARITY, DSL.name("user_scores_unique_beatmap_replay_ids"), arrayOf(UserScoresSimilarity.USER_SCORES_SIMILARITY.BEATMAP_ID, UserScoresSimilarity.USER_SCORES_SIMILARITY.REPLAY_ID_USER, UserScoresSimilarity.USER_SCORES_SIMILARITY.REPLAY_ID_OSU), true)
|
||||||
val USERS_PKEY: UniqueKey<UsersRecord> = Internal.createUniqueKey(Users.USERS, DSL.name("users_pkey"), arrayOf(Users.USERS.USER_ID), true)
|
val USERS_PKEY: UniqueKey<UsersRecord> = Internal.createUniqueKey(Users.USERS, DSL.name("users_pkey"), arrayOf(Users.USERS.USER_ID), true)
|
||||||
@ -55,3 +58,5 @@ val USERS_PKEY: UniqueKey<UsersRecord> = Internal.createUniqueKey(Users.USERS, D
|
|||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
val SCORES_JUDGEMENTS__SCORES_JUDGEMENTS_SCORE_ID_FKEY: ForeignKey<ScoresJudgementsRecord, ScoresRecord> = Internal.createForeignKey(ScoresJudgements.SCORES_JUDGEMENTS, DSL.name("scores_judgements_score_id_fkey"), arrayOf(ScoresJudgements.SCORES_JUDGEMENTS.SCORE_ID), com.nisemoe.generated.keys.SCORES_PKEY, arrayOf(Scores.SCORES.ID), true)
|
val SCORES_JUDGEMENTS__SCORES_JUDGEMENTS_SCORE_ID_FKEY: ForeignKey<ScoresJudgementsRecord, ScoresRecord> = Internal.createForeignKey(ScoresJudgements.SCORES_JUDGEMENTS, DSL.name("scores_judgements_score_id_fkey"), arrayOf(ScoresJudgements.SCORES_JUDGEMENTS.SCORE_ID), com.nisemoe.generated.keys.SCORES_PKEY, arrayOf(Scores.SCORES.ID), true)
|
||||||
|
val USER_FOLLOWS__USER_FOLLOWS_FOLLOWS_USER_ID_FKEY: ForeignKey<UserFollowsRecord, UsersRecord> = Internal.createForeignKey(UserFollows.USER_FOLLOWS, DSL.name("user_follows_follows_user_id_fkey"), arrayOf(UserFollows.USER_FOLLOWS.FOLLOWS_USER_ID), com.nisemoe.generated.keys.USERS_PKEY, arrayOf(Users.USERS.USER_ID), true)
|
||||||
|
val USER_FOLLOWS__USER_FOLLOWS_USER_ID_FKEY: ForeignKey<UserFollowsRecord, UsersRecord> = Internal.createForeignKey(UserFollows.USER_FOLLOWS, DSL.name("user_follows_user_id_fkey"), arrayOf(UserFollows.USER_FOLLOWS.USER_ID), com.nisemoe.generated.keys.USERS_PKEY, arrayOf(Users.USERS.USER_ID), true)
|
||||||
|
|||||||
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* This file is generated by jOOQ.
|
||||||
|
*/
|
||||||
|
package com.nisemoe.generated.tables
|
||||||
|
|
||||||
|
|
||||||
|
import com.nisemoe.generated.Public
|
||||||
|
import com.nisemoe.generated.keys.USER_FOLLOWS_PKEY
|
||||||
|
import com.nisemoe.generated.keys.USER_FOLLOWS__USER_FOLLOWS_FOLLOWS_USER_ID_FKEY
|
||||||
|
import com.nisemoe.generated.keys.USER_FOLLOWS__USER_FOLLOWS_USER_ID_FKEY
|
||||||
|
import com.nisemoe.generated.tables.records.UserFollowsRecord
|
||||||
|
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
import kotlin.collections.List
|
||||||
|
|
||||||
|
import org.jooq.Field
|
||||||
|
import org.jooq.ForeignKey
|
||||||
|
import org.jooq.Identity
|
||||||
|
import org.jooq.Name
|
||||||
|
import org.jooq.Record
|
||||||
|
import org.jooq.Records
|
||||||
|
import org.jooq.Row2
|
||||||
|
import org.jooq.Schema
|
||||||
|
import org.jooq.SelectField
|
||||||
|
import org.jooq.Table
|
||||||
|
import org.jooq.TableField
|
||||||
|
import org.jooq.TableOptions
|
||||||
|
import org.jooq.UniqueKey
|
||||||
|
import org.jooq.impl.DSL
|
||||||
|
import org.jooq.impl.Internal
|
||||||
|
import org.jooq.impl.SQLDataType
|
||||||
|
import org.jooq.impl.TableImpl
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is generated by jOOQ.
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
open class UserFollows(
|
||||||
|
alias: Name,
|
||||||
|
child: Table<out Record>?,
|
||||||
|
path: ForeignKey<out Record, UserFollowsRecord>?,
|
||||||
|
aliased: Table<UserFollowsRecord>?,
|
||||||
|
parameters: Array<Field<*>?>?
|
||||||
|
): TableImpl<UserFollowsRecord>(
|
||||||
|
alias,
|
||||||
|
Public.PUBLIC,
|
||||||
|
child,
|
||||||
|
path,
|
||||||
|
aliased,
|
||||||
|
parameters,
|
||||||
|
DSL.comment(""),
|
||||||
|
TableOptions.table()
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The reference instance of <code>public.user_follows</code>
|
||||||
|
*/
|
||||||
|
val USER_FOLLOWS: UserFollows = UserFollows()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class holding records for this type
|
||||||
|
*/
|
||||||
|
override fun getRecordType(): Class<UserFollowsRecord> = UserFollowsRecord::class.java
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The column <code>public.user_follows.user_id</code>.
|
||||||
|
*/
|
||||||
|
val USER_ID: TableField<UserFollowsRecord, Long?> = createField(DSL.name("user_id"), SQLDataType.BIGINT.nullable(false).identity(true), this, "")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The column <code>public.user_follows.follows_user_id</code>.
|
||||||
|
*/
|
||||||
|
val FOLLOWS_USER_ID: TableField<UserFollowsRecord, Long?> = createField(DSL.name("follows_user_id"), SQLDataType.BIGINT.nullable(false).identity(true), this, "")
|
||||||
|
|
||||||
|
private constructor(alias: Name, aliased: Table<UserFollowsRecord>?): this(alias, null, null, aliased, null)
|
||||||
|
private constructor(alias: Name, aliased: Table<UserFollowsRecord>?, parameters: Array<Field<*>?>?): this(alias, null, null, aliased, parameters)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an aliased <code>public.user_follows</code> table reference
|
||||||
|
*/
|
||||||
|
constructor(alias: String): this(DSL.name(alias))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an aliased <code>public.user_follows</code> table reference
|
||||||
|
*/
|
||||||
|
constructor(alias: Name): this(alias, null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a <code>public.user_follows</code> table reference
|
||||||
|
*/
|
||||||
|
constructor(): this(DSL.name("user_follows"), null)
|
||||||
|
|
||||||
|
constructor(child: Table<out Record>, key: ForeignKey<out Record, UserFollowsRecord>): this(Internal.createPathAlias(child, key), child, key, USER_FOLLOWS, null)
|
||||||
|
override fun getSchema(): Schema? = if (aliased()) null else Public.PUBLIC
|
||||||
|
override fun getIdentity(): Identity<UserFollowsRecord, Long?> = super.getIdentity() as Identity<UserFollowsRecord, Long?>
|
||||||
|
override fun getPrimaryKey(): UniqueKey<UserFollowsRecord> = USER_FOLLOWS_PKEY
|
||||||
|
override fun getReferences(): List<ForeignKey<UserFollowsRecord, *>> = listOf(USER_FOLLOWS__USER_FOLLOWS_USER_ID_FKEY, USER_FOLLOWS__USER_FOLLOWS_FOLLOWS_USER_ID_FKEY)
|
||||||
|
|
||||||
|
private lateinit var _userFollowsUserIdFkey: Users
|
||||||
|
private lateinit var _userFollowsFollowsUserIdFkey: Users
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the implicit join path to the <code>public.users</code> table, via
|
||||||
|
* the <code>user_follows_user_id_fkey</code> key.
|
||||||
|
*/
|
||||||
|
fun userFollowsUserIdFkey(): Users {
|
||||||
|
if (!this::_userFollowsUserIdFkey.isInitialized)
|
||||||
|
_userFollowsUserIdFkey = Users(this, USER_FOLLOWS__USER_FOLLOWS_USER_ID_FKEY)
|
||||||
|
|
||||||
|
return _userFollowsUserIdFkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
val userFollowsUserIdFkey: Users
|
||||||
|
get(): Users = userFollowsUserIdFkey()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the implicit join path to the <code>public.users</code> table, via
|
||||||
|
* the <code>user_follows_follows_user_id_fkey</code> key.
|
||||||
|
*/
|
||||||
|
fun userFollowsFollowsUserIdFkey(): Users {
|
||||||
|
if (!this::_userFollowsFollowsUserIdFkey.isInitialized)
|
||||||
|
_userFollowsFollowsUserIdFkey = Users(this, USER_FOLLOWS__USER_FOLLOWS_FOLLOWS_USER_ID_FKEY)
|
||||||
|
|
||||||
|
return _userFollowsFollowsUserIdFkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
val userFollowsFollowsUserIdFkey: Users
|
||||||
|
get(): Users = userFollowsFollowsUserIdFkey()
|
||||||
|
override fun `as`(alias: String): UserFollows = UserFollows(DSL.name(alias), this)
|
||||||
|
override fun `as`(alias: Name): UserFollows = UserFollows(alias, this)
|
||||||
|
override fun `as`(alias: Table<*>): UserFollows = UserFollows(alias.getQualifiedName(), this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename this table
|
||||||
|
*/
|
||||||
|
override fun rename(name: String): UserFollows = UserFollows(DSL.name(name), null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename this table
|
||||||
|
*/
|
||||||
|
override fun rename(name: Name): UserFollows = UserFollows(name, null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename this table
|
||||||
|
*/
|
||||||
|
override fun rename(name: Table<*>): UserFollows = UserFollows(name.getQualifiedName(), null)
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Row2 type methods
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
override fun fieldsRow(): Row2<Long?, Long?> = super.fieldsRow() as Row2<Long?, Long?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience mapping calling {@link SelectField#convertFrom(Function)}.
|
||||||
|
*/
|
||||||
|
fun <U> mapping(from: (Long?, Long?) -> U): SelectField<U> = convertFrom(Records.mapping(from))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience mapping calling {@link SelectField#convertFrom(Class,
|
||||||
|
* Function)}.
|
||||||
|
*/
|
||||||
|
fun <U> mapping(toType: Class<U>, from: (Long?, Long?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from))
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@ import org.jooq.ForeignKey
|
|||||||
import org.jooq.Name
|
import org.jooq.Name
|
||||||
import org.jooq.Record
|
import org.jooq.Record
|
||||||
import org.jooq.Records
|
import org.jooq.Records
|
||||||
import org.jooq.Row18
|
import org.jooq.Row20
|
||||||
import org.jooq.Schema
|
import org.jooq.Schema
|
||||||
import org.jooq.SelectField
|
import org.jooq.SelectField
|
||||||
import org.jooq.Table
|
import org.jooq.Table
|
||||||
@ -153,6 +153,16 @@ open class Users(
|
|||||||
*/
|
*/
|
||||||
val COUNT_MISS: TableField<UsersRecord, Long?> = createField(DSL.name("count_miss"), SQLDataType.BIGINT, this, "")
|
val COUNT_MISS: TableField<UsersRecord, Long?> = createField(DSL.name("count_miss"), SQLDataType.BIGINT, this, "")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The column <code>public.users.is_banned</code>.
|
||||||
|
*/
|
||||||
|
val IS_BANNED: TableField<UsersRecord, Boolean?> = createField(DSL.name("is_banned"), SQLDataType.BOOLEAN.defaultValue(DSL.field(DSL.raw("false"), SQLDataType.BOOLEAN)), this, "")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The column <code>public.users.approx_ban_date</code>.
|
||||||
|
*/
|
||||||
|
val APPROX_BAN_DATE: TableField<UsersRecord, OffsetDateTime?> = createField(DSL.name("approx_ban_date"), SQLDataType.TIMESTAMPWITHTIMEZONE(6), this, "")
|
||||||
|
|
||||||
private constructor(alias: Name, aliased: Table<UsersRecord>?): this(alias, null, null, aliased, null)
|
private constructor(alias: Name, aliased: Table<UsersRecord>?): this(alias, null, null, aliased, null)
|
||||||
private constructor(alias: Name, aliased: Table<UsersRecord>?, parameters: Array<Field<*>?>?): this(alias, null, null, aliased, parameters)
|
private constructor(alias: Name, aliased: Table<UsersRecord>?, parameters: Array<Field<*>?>?): this(alias, null, null, aliased, parameters)
|
||||||
|
|
||||||
@ -194,18 +204,18 @@ open class Users(
|
|||||||
override fun rename(name: Table<*>): Users = Users(name.getQualifiedName(), null)
|
override fun rename(name: Table<*>): Users = Users(name.getQualifiedName(), null)
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Row18 type methods
|
// Row20 type methods
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
override fun fieldsRow(): Row18<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?> = super.fieldsRow() as Row18<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?>
|
override fun fieldsRow(): Row20<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?> = super.fieldsRow() as Row20<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience mapping calling {@link SelectField#convertFrom(Function)}.
|
* Convenience mapping calling {@link SelectField#convertFrom(Function)}.
|
||||||
*/
|
*/
|
||||||
fun <U> mapping(from: (Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?) -> U): SelectField<U> = convertFrom(Records.mapping(from))
|
fun <U> mapping(from: (Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?) -> U): SelectField<U> = convertFrom(Records.mapping(from))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience mapping calling {@link SelectField#convertFrom(Class,
|
* Convenience mapping calling {@link SelectField#convertFrom(Class,
|
||||||
* Function)}.
|
* Function)}.
|
||||||
*/
|
*/
|
||||||
fun <U> mapping(toType: Class<U>, from: (Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from))
|
fun <U> mapping(toType: Class<U>, from: (Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* This file is generated by jOOQ.
|
||||||
|
*/
|
||||||
|
package com.nisemoe.generated.tables.records
|
||||||
|
|
||||||
|
|
||||||
|
import com.nisemoe.generated.tables.UserFollows
|
||||||
|
|
||||||
|
import org.jooq.Field
|
||||||
|
import org.jooq.Record2
|
||||||
|
import org.jooq.Row2
|
||||||
|
import org.jooq.impl.UpdatableRecordImpl
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is generated by jOOQ.
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
open class UserFollowsRecord private constructor() : UpdatableRecordImpl<UserFollowsRecord>(UserFollows.USER_FOLLOWS), Record2<Long?, Long?> {
|
||||||
|
|
||||||
|
open var userId: Long?
|
||||||
|
set(value): Unit = set(0, value)
|
||||||
|
get(): Long? = get(0) as Long?
|
||||||
|
|
||||||
|
open var followsUserId: Long?
|
||||||
|
set(value): Unit = set(1, value)
|
||||||
|
get(): Long? = get(1) as Long?
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Primary key information
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
override fun key(): Record2<Long?, Long?> = super.key() as Record2<Long?, Long?>
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Record2 type implementation
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
override fun fieldsRow(): Row2<Long?, Long?> = super.fieldsRow() as Row2<Long?, Long?>
|
||||||
|
override fun valuesRow(): Row2<Long?, Long?> = super.valuesRow() as Row2<Long?, Long?>
|
||||||
|
override fun field1(): Field<Long?> = UserFollows.USER_FOLLOWS.USER_ID
|
||||||
|
override fun field2(): Field<Long?> = UserFollows.USER_FOLLOWS.FOLLOWS_USER_ID
|
||||||
|
override fun component1(): Long? = userId
|
||||||
|
override fun component2(): Long? = followsUserId
|
||||||
|
override fun value1(): Long? = userId
|
||||||
|
override fun value2(): Long? = followsUserId
|
||||||
|
|
||||||
|
override fun value1(value: Long?): UserFollowsRecord {
|
||||||
|
set(0, value)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun value2(value: Long?): UserFollowsRecord {
|
||||||
|
set(1, value)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun values(value1: Long?, value2: Long?): UserFollowsRecord {
|
||||||
|
this.value1(value1)
|
||||||
|
this.value2(value2)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a detached, initialised UserFollowsRecord
|
||||||
|
*/
|
||||||
|
constructor(userId: Long? = null, followsUserId: Long? = null): this() {
|
||||||
|
this.userId = userId
|
||||||
|
this.followsUserId = followsUserId
|
||||||
|
resetChangedOnNotNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,8 +11,8 @@ import java.time.OffsetDateTime
|
|||||||
|
|
||||||
import org.jooq.Field
|
import org.jooq.Field
|
||||||
import org.jooq.Record1
|
import org.jooq.Record1
|
||||||
import org.jooq.Record18
|
import org.jooq.Record20
|
||||||
import org.jooq.Row18
|
import org.jooq.Row20
|
||||||
import org.jooq.impl.UpdatableRecordImpl
|
import org.jooq.impl.UpdatableRecordImpl
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ import org.jooq.impl.UpdatableRecordImpl
|
|||||||
* This class is generated by jOOQ.
|
* This class is generated by jOOQ.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(Users.USERS), Record18<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?> {
|
open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(Users.USERS), Record20<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?> {
|
||||||
|
|
||||||
open var userId: Long?
|
open var userId: Long?
|
||||||
set(value): Unit = set(0, value)
|
set(value): Unit = set(0, value)
|
||||||
@ -96,6 +96,16 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
|
|||||||
set(value): Unit = set(17, value)
|
set(value): Unit = set(17, value)
|
||||||
get(): Long? = get(17) as Long?
|
get(): Long? = get(17) as Long?
|
||||||
|
|
||||||
|
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||||
|
@set:JvmName("setIsBanned")
|
||||||
|
open var isBanned: Boolean?
|
||||||
|
set(value): Unit = set(18, value)
|
||||||
|
get(): Boolean? = get(18) as Boolean?
|
||||||
|
|
||||||
|
open var approxBanDate: OffsetDateTime?
|
||||||
|
set(value): Unit = set(19, value)
|
||||||
|
get(): OffsetDateTime? = get(19) as OffsetDateTime?
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Primary key information
|
// Primary key information
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@ -103,11 +113,11 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
|
|||||||
override fun key(): Record1<Long?> = super.key() as Record1<Long?>
|
override fun key(): Record1<Long?> = super.key() as Record1<Long?>
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Record18 type implementation
|
// Record20 type implementation
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
override fun fieldsRow(): Row18<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?> = super.fieldsRow() as Row18<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?>
|
override fun fieldsRow(): Row20<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?> = super.fieldsRow() as Row20<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?>
|
||||||
override fun valuesRow(): Row18<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?> = super.valuesRow() as Row18<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?>
|
override fun valuesRow(): Row20<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?> = super.valuesRow() as Row20<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?, Long?, Boolean?, OffsetDateTime?>
|
||||||
override fun field1(): Field<Long?> = Users.USERS.USER_ID
|
override fun field1(): Field<Long?> = Users.USERS.USER_ID
|
||||||
override fun field2(): Field<String?> = Users.USERS.USERNAME
|
override fun field2(): Field<String?> = Users.USERS.USERNAME
|
||||||
override fun field3(): Field<LocalDateTime?> = Users.USERS.JOIN_DATE
|
override fun field3(): Field<LocalDateTime?> = Users.USERS.JOIN_DATE
|
||||||
@ -126,6 +136,8 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
|
|||||||
override fun field16(): Field<OffsetDateTime?> = Users.USERS.SYS_LAST_UPDATE
|
override fun field16(): Field<OffsetDateTime?> = Users.USERS.SYS_LAST_UPDATE
|
||||||
override fun field17(): Field<Boolean?> = Users.USERS.IS_ADMIN
|
override fun field17(): Field<Boolean?> = Users.USERS.IS_ADMIN
|
||||||
override fun field18(): Field<Long?> = Users.USERS.COUNT_MISS
|
override fun field18(): Field<Long?> = Users.USERS.COUNT_MISS
|
||||||
|
override fun field19(): Field<Boolean?> = Users.USERS.IS_BANNED
|
||||||
|
override fun field20(): Field<OffsetDateTime?> = Users.USERS.APPROX_BAN_DATE
|
||||||
override fun component1(): Long? = userId
|
override fun component1(): Long? = userId
|
||||||
override fun component2(): String? = username
|
override fun component2(): String? = username
|
||||||
override fun component3(): LocalDateTime? = joinDate
|
override fun component3(): LocalDateTime? = joinDate
|
||||||
@ -144,6 +156,8 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
|
|||||||
override fun component16(): OffsetDateTime? = sysLastUpdate
|
override fun component16(): OffsetDateTime? = sysLastUpdate
|
||||||
override fun component17(): Boolean? = isAdmin
|
override fun component17(): Boolean? = isAdmin
|
||||||
override fun component18(): Long? = countMiss
|
override fun component18(): Long? = countMiss
|
||||||
|
override fun component19(): Boolean? = isBanned
|
||||||
|
override fun component20(): OffsetDateTime? = approxBanDate
|
||||||
override fun value1(): Long? = userId
|
override fun value1(): Long? = userId
|
||||||
override fun value2(): String? = username
|
override fun value2(): String? = username
|
||||||
override fun value3(): LocalDateTime? = joinDate
|
override fun value3(): LocalDateTime? = joinDate
|
||||||
@ -162,6 +176,8 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
|
|||||||
override fun value16(): OffsetDateTime? = sysLastUpdate
|
override fun value16(): OffsetDateTime? = sysLastUpdate
|
||||||
override fun value17(): Boolean? = isAdmin
|
override fun value17(): Boolean? = isAdmin
|
||||||
override fun value18(): Long? = countMiss
|
override fun value18(): Long? = countMiss
|
||||||
|
override fun value19(): Boolean? = isBanned
|
||||||
|
override fun value20(): OffsetDateTime? = approxBanDate
|
||||||
|
|
||||||
override fun value1(value: Long?): UsersRecord {
|
override fun value1(value: Long?): UsersRecord {
|
||||||
set(0, value)
|
set(0, value)
|
||||||
@ -253,7 +269,17 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun values(value1: Long?, value2: String?, value3: LocalDateTime?, value4: String?, value5: Long?, value6: Long?, value7: Double?, value8: Double?, value9: Long?, value10: Long?, value11: Long?, value12: Long?, value13: Long?, value14: Long?, value15: Long?, value16: OffsetDateTime?, value17: Boolean?, value18: Long?): UsersRecord {
|
override fun value19(value: Boolean?): UsersRecord {
|
||||||
|
set(18, value)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun value20(value: OffsetDateTime?): UsersRecord {
|
||||||
|
set(19, value)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun values(value1: Long?, value2: String?, value3: LocalDateTime?, value4: String?, value5: Long?, value6: Long?, value7: Double?, value8: Double?, value9: Long?, value10: Long?, value11: Long?, value12: Long?, value13: Long?, value14: Long?, value15: Long?, value16: OffsetDateTime?, value17: Boolean?, value18: Long?, value19: Boolean?, value20: OffsetDateTime?): UsersRecord {
|
||||||
this.value1(value1)
|
this.value1(value1)
|
||||||
this.value2(value2)
|
this.value2(value2)
|
||||||
this.value3(value3)
|
this.value3(value3)
|
||||||
@ -272,13 +298,15 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
|
|||||||
this.value16(value16)
|
this.value16(value16)
|
||||||
this.value17(value17)
|
this.value17(value17)
|
||||||
this.value18(value18)
|
this.value18(value18)
|
||||||
|
this.value19(value19)
|
||||||
|
this.value20(value20)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a detached, initialised UsersRecord
|
* Create a detached, initialised UsersRecord
|
||||||
*/
|
*/
|
||||||
constructor(userId: Long? = null, username: String? = null, joinDate: LocalDateTime? = null, country: String? = null, countryRank: Long? = null, rank: Long? = null, ppRaw: Double? = null, accuracy: Double? = null, playcount: Long? = null, totalScore: Long? = null, rankedScore: Long? = null, secondsPlayed: Long? = null, count_100: Long? = null, count_300: Long? = null, count_50: Long? = null, sysLastUpdate: OffsetDateTime? = null, isAdmin: Boolean? = null, countMiss: Long? = null): this() {
|
constructor(userId: Long? = null, username: String? = null, joinDate: LocalDateTime? = null, country: String? = null, countryRank: Long? = null, rank: Long? = null, ppRaw: Double? = null, accuracy: Double? = null, playcount: Long? = null, totalScore: Long? = null, rankedScore: Long? = null, secondsPlayed: Long? = null, count_100: Long? = null, count_300: Long? = null, count_50: Long? = null, sysLastUpdate: OffsetDateTime? = null, isAdmin: Boolean? = null, countMiss: Long? = null, isBanned: Boolean? = null, approxBanDate: OffsetDateTime? = null): this() {
|
||||||
this.userId = userId
|
this.userId = userId
|
||||||
this.username = username
|
this.username = username
|
||||||
this.joinDate = joinDate
|
this.joinDate = joinDate
|
||||||
@ -297,6 +325,8 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
|
|||||||
this.sysLastUpdate = sysLastUpdate
|
this.sysLastUpdate = sysLastUpdate
|
||||||
this.isAdmin = isAdmin
|
this.isAdmin = isAdmin
|
||||||
this.countMiss = countMiss
|
this.countMiss = countMiss
|
||||||
|
this.isBanned = isBanned
|
||||||
|
this.approxBanDate = approxBanDate
|
||||||
resetChangedOnNotNull()
|
resetChangedOnNotNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import com.nisemoe.generated.tables.Scores
|
|||||||
import com.nisemoe.generated.tables.ScoresJudgements
|
import com.nisemoe.generated.tables.ScoresJudgements
|
||||||
import com.nisemoe.generated.tables.ScoresSimilarity
|
import com.nisemoe.generated.tables.ScoresSimilarity
|
||||||
import com.nisemoe.generated.tables.UpdateUserQueue
|
import com.nisemoe.generated.tables.UpdateUserQueue
|
||||||
|
import com.nisemoe.generated.tables.UserFollows
|
||||||
import com.nisemoe.generated.tables.UserScores
|
import com.nisemoe.generated.tables.UserScores
|
||||||
import com.nisemoe.generated.tables.UserScoresSimilarity
|
import com.nisemoe.generated.tables.UserScoresSimilarity
|
||||||
import com.nisemoe.generated.tables.Users
|
import com.nisemoe.generated.tables.Users
|
||||||
@ -58,6 +59,11 @@ val SCORES_SIMILARITY: ScoresSimilarity = ScoresSimilarity.SCORES_SIMILARITY
|
|||||||
*/
|
*/
|
||||||
val UPDATE_USER_QUEUE: UpdateUserQueue = UpdateUserQueue.UPDATE_USER_QUEUE
|
val UPDATE_USER_QUEUE: UpdateUserQueue = UpdateUserQueue.UPDATE_USER_QUEUE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table <code>public.user_follows</code>.
|
||||||
|
*/
|
||||||
|
val USER_FOLLOWS: UserFollows = UserFollows.USER_FOLLOWS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The table <code>public.user_scores</code>.
|
* The table <code>public.user_scores</code>.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
package com.nisemoe.nise
|
package com.nisemoe.nise
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
import org.springframework.cache.annotation.EnableCaching
|
import org.springframework.cache.annotation.EnableCaching
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling
|
import org.springframework.scheduling.annotation.EnableScheduling
|
||||||
|
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisIndexedHttpSession
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
|
@EnableRedisIndexedHttpSession(maxInactiveIntervalInSeconds = 2592000)
|
||||||
class NiseApplication
|
class NiseApplication
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.nisemoe.nise.controller
|
||||||
|
|
||||||
|
import com.nisemoe.generated.tables.references.USERS
|
||||||
|
import com.nisemoe.generated.tables.references.USER_FOLLOWS
|
||||||
|
import com.nisemoe.nise.service.AuthService
|
||||||
|
import jakarta.validation.Valid
|
||||||
|
import jakarta.validation.constraints.Size
|
||||||
|
import org.jooq.DSLContext
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
class BanlistController(
|
||||||
|
private val dslContext: DSLContext
|
||||||
|
) {
|
||||||
|
|
||||||
|
data class BanStatisticsResponse(
|
||||||
|
val totalUsersBanned: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
@GetMapping("banlist/statistics")
|
||||||
|
fun getBanStatistics(): BanStatisticsResponse {
|
||||||
|
val totalUsersBanned = dslContext.fetchCount(USERS, USERS.IS_BANNED.eq(true))
|
||||||
|
|
||||||
|
return BanStatisticsResponse(totalUsersBanned)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
package com.nisemoe.nise.controller
|
||||||
|
|
||||||
|
import com.nisemoe.generated.tables.references.USERS
|
||||||
|
import com.nisemoe.generated.tables.references.USER_FOLLOWS
|
||||||
|
import com.nisemoe.nise.service.AuthService
|
||||||
|
import jakarta.validation.Valid
|
||||||
|
import jakarta.validation.constraints.Size
|
||||||
|
import org.jooq.DSLContext
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
class FollowsController(
|
||||||
|
private val dslContext: DSLContext,
|
||||||
|
private val authService: AuthService
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val MAX_FOLLOWS_PER_USER = 1000
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FollowsBanStatusResponse(
|
||||||
|
val follows: List<FollowsBanStatusEntry>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class FollowsBanStatusEntry(
|
||||||
|
val userId: Long,
|
||||||
|
val username: String,
|
||||||
|
val isBanned: Boolean,
|
||||||
|
val lastUpdate: OffsetDateTime
|
||||||
|
)
|
||||||
|
|
||||||
|
@GetMapping("follows")
|
||||||
|
fun getFollowsBanStatus(): ResponseEntity<FollowsBanStatusResponse> {
|
||||||
|
if(!authService.isLoggedIn()) {
|
||||||
|
return ResponseEntity.status(401).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
val follows = dslContext.select(
|
||||||
|
USERS.USER_ID,
|
||||||
|
USERS.USERNAME,
|
||||||
|
USERS.IS_BANNED,
|
||||||
|
USERS.SYS_LAST_UPDATE
|
||||||
|
)
|
||||||
|
.from(USER_FOLLOWS)
|
||||||
|
.join(USERS).on(USER_FOLLOWS.FOLLOWS_USER_ID.eq(USERS.USER_ID))
|
||||||
|
.where(USER_FOLLOWS.USER_ID.eq(authService.getCurrentUser().userId))
|
||||||
|
.fetch()
|
||||||
|
.map {
|
||||||
|
FollowsBanStatusEntry(
|
||||||
|
it[USERS.USER_ID]!!,
|
||||||
|
it[USERS.USERNAME]!!,
|
||||||
|
it[USERS.IS_BANNED]!!,
|
||||||
|
it[USERS.SYS_LAST_UPDATE]!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(FollowsBanStatusResponse(follows))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: CSRF
|
||||||
|
|
||||||
|
@GetMapping("follows/{followsUserId}")
|
||||||
|
fun getFollowsBanStatusByUserId(@PathVariable followsUserId: Long): ResponseEntity<FollowsBanStatusEntry> {
|
||||||
|
if(!authService.isLoggedIn()) {
|
||||||
|
return ResponseEntity.status(401).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
val userId = authService.getCurrentUser().userId
|
||||||
|
|
||||||
|
val follows = dslContext.select(
|
||||||
|
USERS.USER_ID,
|
||||||
|
USERS.USERNAME,
|
||||||
|
USERS.IS_BANNED,
|
||||||
|
USERS.SYS_LAST_UPDATE
|
||||||
|
)
|
||||||
|
.from(USER_FOLLOWS)
|
||||||
|
.join(USERS).on(USER_FOLLOWS.FOLLOWS_USER_ID.eq(USERS.USER_ID))
|
||||||
|
.where(USER_FOLLOWS.USER_ID.eq(userId).and(USER_FOLLOWS.FOLLOWS_USER_ID.eq(followsUserId)))
|
||||||
|
.fetch()
|
||||||
|
.map {
|
||||||
|
FollowsBanStatusEntry(
|
||||||
|
it[USERS.USER_ID]!!,
|
||||||
|
it[USERS.USERNAME]!!,
|
||||||
|
it[USERS.IS_BANNED]!!,
|
||||||
|
it[USERS.SYS_LAST_UPDATE]!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return if(follows.isEmpty()) {
|
||||||
|
ResponseEntity.status(404).build()
|
||||||
|
} else {
|
||||||
|
ResponseEntity.ok(follows.first())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UpdateFollowsBanStatusRequest(
|
||||||
|
@Valid @field:Size(max = 200)
|
||||||
|
val userIds: List<Long>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@PutMapping("follows")
|
||||||
|
fun updateFollowsBanStatus(@RequestBody @Valid request: UpdateFollowsBanStatusRequest): ResponseEntity<Void> {
|
||||||
|
if(!authService.isLoggedIn()) {
|
||||||
|
return ResponseEntity.status(401).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user already has MAX_FOLLOWS_PER_USER or more
|
||||||
|
if(dslContext.fetchCount(USER_FOLLOWS, USER_FOLLOWS.USER_ID.eq(authService.getCurrentUser().userId)) >= MAX_FOLLOWS_PER_USER) {
|
||||||
|
return ResponseEntity.status(400).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
val userId = authService.getCurrentUser().userId
|
||||||
|
|
||||||
|
for(userIdToBan in request.userIds) {
|
||||||
|
dslContext.insertInto(USER_FOLLOWS)
|
||||||
|
.columns(USER_FOLLOWS.USER_ID, USER_FOLLOWS.FOLLOWS_USER_ID)
|
||||||
|
.values(userId, userIdToBan)
|
||||||
|
.onDuplicateKeyIgnore()
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok().build()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DeleteFollowsBanStatusRequest(
|
||||||
|
@Valid @field:Size(max = 200)
|
||||||
|
val userIds: List<Long>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@DeleteMapping("follows")
|
||||||
|
fun deleteFollowsBanStatus(@RequestBody @Valid request: DeleteFollowsBanStatusRequest): ResponseEntity<Void> {
|
||||||
|
if(!authService.isLoggedIn()) {
|
||||||
|
return ResponseEntity.status(401).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
val userId = authService.getCurrentUser().userId
|
||||||
|
|
||||||
|
for(userIdToUnban in request.userIds) {
|
||||||
|
dslContext.deleteFrom(USER_FOLLOWS)
|
||||||
|
.where(USER_FOLLOWS.USER_ID.eq(userId))
|
||||||
|
.and(USER_FOLLOWS.FOLLOWS_USER_ID.eq(userIdToUnban))
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok().build()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -532,7 +532,7 @@ class ScoreService(
|
|||||||
error = it.error!!,
|
error = it.error!!,
|
||||||
distanceToCenter = it.distanceCenter!!,
|
distanceToCenter = it.distanceCenter!!,
|
||||||
distanceToEdge = it.distanceEdge!!,
|
distanceToEdge = it.distanceEdge!!,
|
||||||
time = it.time!!,
|
time = it.time!!.toInt(),
|
||||||
type = mapLegacyJudgement(it.type!!)
|
type = mapLegacyJudgement(it.type!!)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -348,6 +348,11 @@ class ImportScores(
|
|||||||
.set(SCORES.IS_BANNED, true)
|
.set(SCORES.IS_BANNED, true)
|
||||||
.where(SCORES.USER_ID.eq(userId))
|
.where(SCORES.USER_ID.eq(userId))
|
||||||
.execute()
|
.execute()
|
||||||
|
dslContext.update(USERS)
|
||||||
|
.set(USERS.IS_BANNED, true)
|
||||||
|
.set(USERS.APPROX_BAN_DATE, OffsetDateTime.now())
|
||||||
|
.where(USERS.USER_ID.eq(userId))
|
||||||
|
.execute()
|
||||||
this.logger.info("User $userId is banned.")
|
this.logger.info("User $userId is banned.")
|
||||||
}
|
}
|
||||||
Thread.sleep(SLEEP_AFTER_API_CALL)
|
Thread.sleep(SLEEP_AFTER_API_CALL)
|
||||||
@ -724,7 +729,7 @@ class ImportScores(
|
|||||||
replayData = scoreReplay.content, beatmapData = beatmapFile, mods = Mod.combineModStrings(score.mods)
|
replayData = scoreReplay.content, beatmapData = beatmapFile, mods = Mod.combineModStrings(score.mods)
|
||||||
).get()
|
).get()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
this.logger.error("Circleguard failed to process replay with score_id: ${score.id}")
|
this.logger.error("Circleguard failed to process replay with score_id: ${score.id}", e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -107,6 +107,11 @@ class ImportUsers(
|
|||||||
.set(SCORES.IS_BANNED, true)
|
.set(SCORES.IS_BANNED, true)
|
||||||
.where(SCORES.USER_ID.eq(missingId))
|
.where(SCORES.USER_ID.eq(missingId))
|
||||||
.execute()
|
.execute()
|
||||||
|
dslContext.update(USERS)
|
||||||
|
.set(USERS.IS_BANNED, true)
|
||||||
|
.set(USERS.APPROX_BAN_DATE, OffsetDateTime.now())
|
||||||
|
.where(USERS.USER_ID.eq(missingId))
|
||||||
|
.execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,10 @@ spring.data.redis.port=${REDIS_PORT:6379}
|
|||||||
spring.data.redis.repositories.enabled=false
|
spring.data.redis.repositories.enabled=false
|
||||||
spring.data.redis.database=${REDIS_DB:2}
|
spring.data.redis.database=${REDIS_DB:2}
|
||||||
|
|
||||||
|
# session
|
||||||
|
server.servlet.session.timeout=2592000
|
||||||
|
spring.session.store-type=redis
|
||||||
|
|
||||||
# osu!auth
|
# osu!auth
|
||||||
|
|
||||||
spring.security.oauth2.client.registration.osu.clientId=${OSU_CLIENT_ID}
|
spring.security.oauth2.client.registration.osu.clientId=${OSU_CLIENT_ID}
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
alter table public.users
|
||||||
|
add column is_banned boolean default false,
|
||||||
|
add column approx_ban_date timestamp with time zone;
|
||||||
|
|
||||||
|
update public.users
|
||||||
|
set is_banned = true,
|
||||||
|
approx_ban_date = now()
|
||||||
|
from (select distinct user_id
|
||||||
|
from scores
|
||||||
|
where is_banned = true) banned_users
|
||||||
|
where public.users.user_id = banned_users.user_id;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
create table public.user_follows (
|
||||||
|
user_id bigserial not null,
|
||||||
|
follows_user_id bigserial not null,
|
||||||
|
|
||||||
|
primary key (user_id, follows_user_id),
|
||||||
|
foreign key (user_id) references public.users (user_id),
|
||||||
|
foreign key (follows_user_id) references public.users (user_id)
|
||||||
|
);
|
||||||
@ -8,6 +8,7 @@ import {ViewUserComponent} from "./view-user/view-user.component";
|
|||||||
import {ViewReplayPairComponent} from "./view-replay-pair/view-replay-pair.component";
|
import {ViewReplayPairComponent} from "./view-replay-pair/view-replay-pair.component";
|
||||||
import {SearchComponent} from "./search/search.component";
|
import {SearchComponent} from "./search/search.component";
|
||||||
import {ContributeComponent} from "./contribute/contribute.component";
|
import {ContributeComponent} from "./contribute/contribute.component";
|
||||||
|
import {BanlistComponent} from "./banlist/banlist.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: 'sus/:f', component: ViewSuspiciousScoresComponent, title: '/sus/'},
|
{path: 'sus/:f', component: ViewSuspiciousScoresComponent, title: '/sus/'},
|
||||||
@ -23,6 +24,7 @@ const routes: Routes = [
|
|||||||
|
|
||||||
{path: 'p/:replay1Id/:replay2Id', component: ViewReplayPairComponent},
|
{path: 'p/:replay1Id/:replay2Id', component: ViewReplayPairComponent},
|
||||||
|
|
||||||
|
{path: 'banlist', component: BanlistComponent, title: '/ban/'},
|
||||||
{path: 'contribute', component: ContributeComponent, title: '/contribute/ <3'},
|
{path: 'contribute', component: ContributeComponent, title: '/contribute/ <3'},
|
||||||
{path: '**', component: HomeComponent, title: '/nise.moe/'},
|
{path: '**', component: HomeComponent, title: '/nise.moe/'},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -7,3 +7,11 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link-pink {
|
||||||
|
color: #dd8fdcf7
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-pink:hover {
|
||||||
|
color: rgba(234, 78, 179, 0.97)
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<li><a [routerLink]="['/sus']">./suspicious-scores</a></li>
|
<li><a [routerLink]="['/sus']">./suspicious-scores</a></li>
|
||||||
<li><a [routerLink]="['/stolen']">./stolen-replays</a></li>
|
<li><a [routerLink]="['/stolen']">./stolen-replays</a></li>
|
||||||
<li><a [routerLink]="['/search']">./advanced-search</a></li>
|
<li><a [routerLink]="['/search']">./advanced-search</a></li>
|
||||||
<li><a style="color: #dd8fdcf7" [routerLink]="['/contribute']">./contribute <3</a></li>
|
<li><a class="link-pink" [routerLink]="['/contribute']">./contribute <3</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<form (ngSubmit)="onSubmit()">
|
<form (ngSubmit)="onSubmit()">
|
||||||
<input style="width: 100%" type="text" [(ngModel)]="term" [ngModelOptions]="{standalone: true}" id="nise-osu-username" required minlength="2" maxlength="50" placeholder="Search for users...">
|
<input style="width: 100%" type="text" [(ngModel)]="term" [ngModelOptions]="{standalone: true}" id="nise-osu-username" required minlength="2" maxlength="50" placeholder="Search for users...">
|
||||||
|
|||||||
3
nise-frontend/src/app/banlist/banlist.component.css
Normal file
3
nise-frontend/src/app/banlist/banlist.component.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
table td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
31
nise-frontend/src/app/banlist/banlist.component.html
Normal file
31
nise-frontend/src/app/banlist/banlist.component.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<div class="main term mb-2">
|
||||||
|
<div class="fade-stuff">
|
||||||
|
<h1 class="mb-4"># follow-list</h1>
|
||||||
|
<table *ngIf="this.follows">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Username</th>
|
||||||
|
<th>Is banned?</th>
|
||||||
|
<th>Last check</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let user of this.follows.follows">
|
||||||
|
<td>
|
||||||
|
<img [src]="'https://a.ppy.sh/' + user.userId" class="avatar" style="width: 16px; min-height: 16px; height: 16px;">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a [routerLink]="['/u', user.username]">
|
||||||
|
{{ user.username }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ user.isBanned }}</td>
|
||||||
|
<td>{{ calculateTimeAgo(user.lastUpdate) }}</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
61
nise-frontend/src/app/banlist/banlist.component.ts
Normal file
61
nise-frontend/src/app/banlist/banlist.component.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {environment} from "../../environments/environment";
|
||||||
|
import {JsonPipe, NgForOf, NgIf} from "@angular/common";
|
||||||
|
import {calculateTimeAgo} from "../format";
|
||||||
|
import {RouterLink} from "@angular/router";
|
||||||
|
|
||||||
|
interface BanStatisticsResponse {
|
||||||
|
totalUsersBanned: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FollowsBanStatusResponse {
|
||||||
|
follows: FollowsBanStatusEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FollowsBanStatusEntry {
|
||||||
|
userId: number;
|
||||||
|
username: string;
|
||||||
|
isBanned: boolean;
|
||||||
|
lastUpdate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-banlist',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
JsonPipe,
|
||||||
|
NgForOf,
|
||||||
|
NgIf,
|
||||||
|
RouterLink
|
||||||
|
],
|
||||||
|
templateUrl: './banlist.component.html',
|
||||||
|
styleUrl: './banlist.component.css'
|
||||||
|
})
|
||||||
|
export class BanlistComponent implements OnInit {
|
||||||
|
|
||||||
|
banStatistics: BanStatisticsResponse | null = null
|
||||||
|
follows: FollowsBanStatusResponse | null = null;
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.getBanStatistics();
|
||||||
|
this.getFollows();
|
||||||
|
}
|
||||||
|
|
||||||
|
getBanStatistics() {
|
||||||
|
this.httpClient.get<BanStatisticsResponse>(`${environment.apiUrl}/banlist/statistics`).subscribe(response => {
|
||||||
|
this.banStatistics = response;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getFollows(): void {
|
||||||
|
this.httpClient.get<FollowsBanStatusResponse>(`${environment.apiUrl}/follows`).subscribe(response => {
|
||||||
|
this.follows = response;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly calculateTimeAgo = calculateTimeAgo;
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import {ReplayData} from "./replays";
|
import {ReplayData} from "./replays";
|
||||||
|
import {differenceInDays, differenceInHours} from "date-fns/fp";
|
||||||
|
|
||||||
export function formatDuration(seconds: number): string | null {
|
export function formatDuration(seconds: number): string | null {
|
||||||
if(!seconds) {
|
if(!seconds) {
|
||||||
@ -50,3 +51,24 @@ export function calculateAccuracy(replayData: ReplayData): number {
|
|||||||
const accuracy = (300 * hit300 + 100 * hit100 + 50 * hit50) / (300 * totalHits);
|
const accuracy = (300 * hit300 + 100 * hit100 + 50 * hit50) / (300 * totalHits);
|
||||||
return accuracy * 100;
|
return accuracy * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function calculateTimeAgo(dateStr: string): string {
|
||||||
|
const inputDate = new Date(dateStr);
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
if (isNaN(inputDate.getTime())) {
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
const difference = Math.abs(differenceInHours(now, inputDate));
|
||||||
|
|
||||||
|
if (difference < 1) {
|
||||||
|
return "recently";
|
||||||
|
} else if (difference < 24) {
|
||||||
|
return `${difference}h ago`;
|
||||||
|
} else {
|
||||||
|
const days = Math.abs(differenceInDays(now, inputDate));
|
||||||
|
const hours = difference % 24;
|
||||||
|
return `${days}d ${hours}h ago`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -12,6 +12,11 @@
|
|||||||
<h1>
|
<h1>
|
||||||
<img [src]="'https://a.ppy.sh/' + this.userInfo.user_details.user_id" class="avatar">
|
<img [src]="'https://a.ppy.sh/' + this.userInfo.user_details.user_id" class="avatar">
|
||||||
{{ this.userInfo.user_details.username }}
|
{{ this.userInfo.user_details.username }}
|
||||||
|
|
||||||
|
<ng-container *ngIf="this.userService.isUserLoggedIn() && this.isFollowing != null">
|
||||||
|
<button *ngIf="!this.followService.userFollowStatus.get(this.userInfo.user_details.user_id)" (click)="this.followService.addNewUserFollow(this.userInfo.user_details.user_id)" class="btn btn-outline-secondary btn-sm">(+) Add Follow</button>
|
||||||
|
<button *ngIf="this.followService.userFollowStatus.get(this.userInfo.user_details.user_id)" (click)="this.followService.removeUserFollow(this.userInfo.user_details.user_id)" class="btn btn-outline-secondary btn-sm">(-) Remove follow</button>
|
||||||
|
</ng-container>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="mb-2 mt-2 btn-group">
|
<div class="mb-2 mt-2 btn-group">
|
||||||
@ -51,7 +56,7 @@
|
|||||||
<span class="btn-warning">wait a bit to force update</span>
|
<span class="btn-warning">wait a bit to force update</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<span style="margin-left: 4px">|</span>
|
<span style="margin-left: 4px">|</span>
|
||||||
last update: {{ this.userInfo.queue_details.lastCompletedUpdate ? this.calculateTimeAgo(this.userInfo.queue_details.lastCompletedUpdate) : 'never'}}
|
last update: {{ this.userInfo.queue_details.lastCompletedUpdate ? calculateTimeAgo(this.userInfo.queue_details.lastCompletedUpdate) : 'never'}}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #updateProgress>
|
<ng-template #updateProgress>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
|
|||||||
@ -6,14 +6,14 @@ import {environment} from "../../environments/environment";
|
|||||||
import {DecimalPipe, JsonPipe, NgForOf, NgIf, NgOptimizedImage} from "@angular/common";
|
import {DecimalPipe, JsonPipe, NgForOf, NgIf, NgOptimizedImage} from "@angular/common";
|
||||||
import {ActivatedRoute, RouterLink} from "@angular/router";
|
import {ActivatedRoute, RouterLink} from "@angular/router";
|
||||||
import {UserDetails, UserQueueDetails} from "../userDetails";
|
import {UserDetails, UserQueueDetails} from "../userDetails";
|
||||||
import {countryCodeToFlag, formatDuration} from "../format";
|
import {calculateTimeAgo, countryCodeToFlag, formatDuration} from "../format";
|
||||||
import {Title} from "@angular/platform-browser";
|
import {Title} from "@angular/platform-browser";
|
||||||
import {RxStompService} from "../../corelib/stomp/stomp.service";
|
import {RxStompService} from "../../corelib/stomp/stomp.service";
|
||||||
import {Message} from "@stomp/stompjs/esm6";
|
import {Message} from "@stomp/stompjs/esm6";
|
||||||
import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component";
|
import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component";
|
||||||
import {differenceInDays, differenceInHours} from "date-fns/fp";
|
|
||||||
import {FilterManagerService} from "../filter-manager.service";
|
import {FilterManagerService} from "../filter-manager.service";
|
||||||
import {UserService} from "../../corelib/service/user.service";
|
import {UserService} from "../../corelib/service/user.service";
|
||||||
|
import {FollowService} from "../../corelib/service/follow.service";
|
||||||
|
|
||||||
interface UserInfo {
|
interface UserInfo {
|
||||||
user_details: UserDetails;
|
user_details: UserDetails;
|
||||||
@ -49,6 +49,7 @@ interface UserScoresFilter {
|
|||||||
})
|
})
|
||||||
export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
|
|
||||||
|
isFollowing: boolean | null = null;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
notFound = false;
|
notFound = false;
|
||||||
userId: string | null = null;
|
userId: string | null = null;
|
||||||
@ -64,7 +65,8 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private title: Title,
|
private title: Title,
|
||||||
private rxStompService: RxStompService,
|
private rxStompService: RxStompService,
|
||||||
public userService: UserService
|
public userService: UserService,
|
||||||
|
public followService: FollowService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
getUserInfo(): Observable<UserInfo> {
|
getUserInfo(): Observable<UserInfo> {
|
||||||
@ -93,6 +95,14 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkIfUserIsFollowed(): void {
|
||||||
|
if(this.userService.isUserLoggedIn()) {
|
||||||
|
this.followService.checkIfUserIsFollowed(this.userInfo!.user_details.user_id).then(isFollowing => {
|
||||||
|
this.isFollowing = isFollowing;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private loadUser(isScoreUpdate = false) {
|
private loadUser(isScoreUpdate = false) {
|
||||||
this.getUserInfo().pipe(
|
this.getUserInfo().pipe(
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
@ -121,6 +131,7 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.title.setTitle(`${this.userInfo.user_details.username}`);
|
this.title.setTitle(`${this.userInfo.user_details.username}`);
|
||||||
this.subscribeToUser();
|
this.subscribeToUser();
|
||||||
}
|
}
|
||||||
|
this.checkIfUserIsFollowed();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -129,27 +140,6 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.liveUserSub?.unsubscribe();
|
this.liveUserSub?.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateTimeAgo(dateStr: string): string {
|
|
||||||
const inputDate = new Date(dateStr);
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
if (isNaN(inputDate.getTime())) {
|
|
||||||
return "???";
|
|
||||||
}
|
|
||||||
|
|
||||||
const difference = Math.abs(differenceInHours(now, inputDate));
|
|
||||||
|
|
||||||
if (difference < 1) {
|
|
||||||
return "recently";
|
|
||||||
} else if (difference < 24) {
|
|
||||||
return `${difference}h ago`;
|
|
||||||
} else {
|
|
||||||
const days = Math.abs(differenceInDays(now, inputDate));
|
|
||||||
const hours = difference % 24;
|
|
||||||
return `${days}d ${hours}h ago`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addUserToQueue(): void {
|
addUserToQueue(): void {
|
||||||
const body = {
|
const body = {
|
||||||
userId: this.userInfo?.user_details.user_id
|
userId: this.userInfo?.user_details.user_id
|
||||||
@ -202,6 +192,6 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
protected readonly formatDuration = formatDuration;
|
protected readonly formatDuration = formatDuration;
|
||||||
protected readonly countryCodeToFlag = countryCodeToFlag;
|
protected readonly countryCodeToFlag = countryCodeToFlag;
|
||||||
|
protected readonly calculateTimeAgo = calculateTimeAgo;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
86
nise-frontend/src/corelib/service/follow.service.ts
Normal file
86
nise-frontend/src/corelib/service/follow.service.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {environment} from "../../environments/environment";
|
||||||
|
import {UserService} from "./user.service";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class FollowService {
|
||||||
|
|
||||||
|
userFollowStatus: Map<number, boolean> = new Map<number, boolean>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private userService: UserService,
|
||||||
|
private httpClient: HttpClient
|
||||||
|
) { }
|
||||||
|
|
||||||
|
removeUserFollow(userId: number): Promise<boolean> {
|
||||||
|
if(!this.userService.isUserLoggedIn()) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
userIds: [userId]
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.httpClient.delete(`${environment.apiUrl}/follows`, { body: body })
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.userFollowStatus.set(userId, false);
|
||||||
|
resolve(true);
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addNewUserFollow(userId: number): Promise<boolean> {
|
||||||
|
if(!this.userService.isUserLoggedIn()) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
userIds: [userId]
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.httpClient.put(`${environment.apiUrl}/follows`, body)
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.userFollowStatus.set(userId, true);
|
||||||
|
resolve(true);
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkIfUserIsFollowed(userId: number): Promise<boolean> {
|
||||||
|
if(!this.userService.isUserLoggedIn()) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
this.httpClient.get<boolean>(`${environment.apiUrl}/follows/${userId}`)
|
||||||
|
.subscribe({
|
||||||
|
next: (isFollowing) => {
|
||||||
|
this.userFollowStatus.set(userId, isFollowing);
|
||||||
|
resolve(isFollowing);
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
if (err.status === 404) {
|
||||||
|
this.userFollowStatus.set(userId, false);
|
||||||
|
resolve(false);
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user