Skip to content

Commit

Permalink
CXAN-464 Add mentions related models and APIs (#627)
Browse files Browse the repository at this point in the history
* CXAN-464 Add rich text models and related json adapters

* CXAN-464 Add create/edit note APIs with Note parameter

* CXAN-464 Add guestName property to Note

* Add new fields for mention feature (#629)

* CXAN-472 Add the field personalTeamFolderOwner to the FolderConnection

* CXAN-472 Add capability team_mention

---------

Co-authored-by: Alexander Baltser <[email protected]>

---------

Co-authored-by: Alexander Baltser <[email protected]>
Co-authored-by: Alexander Baltser <[email protected]>
  • Loading branch information
3 people authored Aug 21, 2023
1 parent 3c96373 commit e4f31d4
Show file tree
Hide file tree
Showing 21 changed files with 346 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
package com.vimeo.networking2.config

import com.squareup.moshi.Moshi
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
import com.vimeo.networking2.internal.ErrorHandlingCallAdapterFactory
import com.vimeo.networking2.internal.adapters.Iso8601NoMillisAdapter
import com.vimeo.networking2.internal.adapters.RichTextStringJsonAdapter
import com.vimeo.networking2.internal.adapters.TimeAdapter
import com.vimeo.networking2.internal.interceptor.AcceptHeaderInterceptor
import com.vimeo.networking2.internal.interceptor.CacheControlHeaderInterceptor
Expand All @@ -36,6 +38,12 @@ import com.vimeo.networking2.internal.params.SafeObjectJsonAdapterFactory
import com.vimeo.networking2.internal.params.StringValueJsonAdapterFactory
import com.vimeo.networking2.internal.params.VimeoParametersConverterFactory
import com.vimeo.networking2.logging.VimeoLogger
import com.vimeo.networking2.richtext.Mention
import com.vimeo.networking2.richtext.RichText
import com.vimeo.networking2.richtext.RichTextContainer
import com.vimeo.networking2.richtext.RichTextType
import com.vimeo.networking2.richtext.Text
import com.vimeo.networking2.richtext.UnknownRichTextNode
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.OkHttpClient
Expand All @@ -60,6 +68,15 @@ object RetrofitSetupModule {
.add(IntValueJsonAdapterFactory())
.add(TimeAdapter())
.add(Iso8601NoMillisAdapter())
.add(RichTextStringJsonAdapter())
.add(
PolymorphicJsonAdapterFactory.of(RichText::class.java, RichText.TYPE_FIELD)
.withSubtype(RichTextContainer::class.java, RichTextType.DOC.value)
.withSubtype(RichTextContainer::class.java, RichTextType.PARAGRAPH.value)
.withSubtype(Mention::class.java, RichTextType.MENTION.value)
.withSubtype(Text::class.java, RichTextType.TEXT.value)
.withDefaultValue(UnknownRichTextNode())
)
.build()

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.vimeo.networking2.internal.adapters

import com.squareup.moshi.FromJson
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.ToJson
import com.vimeo.networking2.annotations.RichTextString
import com.vimeo.networking2.richtext.RichText

/**
* Json adapter for [RichText] properties annotated with [RichTextString].
*/
class RichTextStringJsonAdapter {

/**
* Converts JSON string rich text representation to [RichText].
*/
@FromJson
@RichTextString
fun fromJson(
jsonReader: JsonReader,
adapter: JsonAdapter<RichText>,
): RichText? = if (jsonReader.peek() == JsonReader.Token.NULL) {
jsonReader.nextNull()
} else {
adapter.fromJson(jsonReader.nextString())
}

/**
* Converts [RichText] instance to JSON string.
*/
@ToJson
fun toJson(
jsonWriter: JsonWriter,
@RichTextString value: RichText?,
adapter: JsonAdapter<RichText>,
) {
jsonWriter.value(value?.let(adapter::toJson))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,15 @@ import com.vimeo.networking2.enums.StringValue
* A [JsonAdapter] that can convert [StringValue] implementation to its JSON value.
*/
class StringValueJsonAdapter<T : StringValue>(
private val creator: (String) -> T
private val creator: (String) -> T,
) : JsonAdapter<T>() {

constructor(values: Array<T>, fallback: T? = null) : this({ value ->
values.firstOrNull { it.value == value }
?: fallback
?: error("No value matching: \"$value\". Provide fallback.")
})

override fun fromJson(reader: JsonReader): T? = if (reader.peek() == JsonReader.Token.NULL) {
reader.nextNull()
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.vimeo.networking2.enums.ScheduleType
import com.vimeo.networking2.enums.StringValue
import com.vimeo.networking2.richtext.RichTextType
import java.lang.reflect.Type

/**
Expand All @@ -34,10 +35,12 @@ import java.lang.reflect.Type
class StringValueJsonAdapterFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? =
if (type is Class<*> && StringValue::class.java.isAssignableFrom(type)) {
if (ScheduleType::class.java.isAssignableFrom(type)) {
StringValueJsonAdapter { value -> ScheduleType.values().first { it.value == value } }
} else {
StringValueJsonAdapter.NON_READING
when {
ScheduleType::class.java.isAssignableFrom(type) ->
StringValueJsonAdapter(ScheduleType.values())
RichTextType::class.java.isAssignableFrom(type) ->
StringValueJsonAdapter(RichTextType.values(), RichTextType.UNKNOWN)
else -> StringValueJsonAdapter.NON_READING
}
} else {
null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.vimeo.networking2.annotations

import com.squareup.moshi.JsonQualifier

@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class RichTextString
6 changes: 6 additions & 0 deletions models/src/main/java/com/vimeo/networking2/Capabilities.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,10 @@ data class Capabilities(
*/
@Json(name = "contributor_plus_enabled")
val contributorPlusEnabled: Boolean? = null,

/**
* Whether the user's team members can mention each other in private comments.
*/
@Json(name = "team_mentions")
val teamMentions: Boolean? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.squareup.moshi.JsonClass
* @param parentFolder Information about the folder's parent folder if there is one.
* @param videos A basic connection object indicating how to return all the videos in the folder.
* @param teamPermissions A basic connection to team permissions associated with this folder resource
* @param personalTeamFolderOwner Information about the owner of the personal team folder.
*/
@JsonClass(generateAdapter = true)
data class FolderConnections(
Expand All @@ -39,5 +40,8 @@ data class FolderConnections(
val videos: BasicConnection? = null,

@Json(name = "team_permissions")
val teamPermissions: BasicConnection? = null
val teamPermissions: BasicConnection? = null,

@Json(name = "personal_team_folder_owner")
val personalTeamFolderOwner: BasicConnection? = null
)
11 changes: 11 additions & 0 deletions models/src/main/java/com/vimeo/networking2/Note.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.vimeo.networking2

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.vimeo.networking2.annotations.RichTextString
import com.vimeo.networking2.enums.asEnum
import com.vimeo.networking2.richtext.RichText
import java.util.Date

/**
Expand All @@ -17,6 +19,8 @@ import java.util.Date
* @param rawStatus The status of the note.
* @param timeCode Time code at which point in the video the note was left.
* @param coordinates Point on the video surface which note relates to.
* @param richText The content of the note in rich text format.
* @param guestName A guest name if the user is not logged in.
*/
@JsonClass(generateAdapter = true)
data class Note(
Expand Down Expand Up @@ -47,6 +51,13 @@ data class Note(

@Json(name = "coordinates")
val coordinates: Coordinates? = null,

@RichTextString
@Json(name = "richtext")
val richText: RichText? = null,

@Json(name = "name")
val guestName: String? = null,
) : AbstractComment

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.squareup.moshi.JsonClass
* @param folderInvite True if the user can invite others to the folder, false otherwise.
* @param folderView True if the user can view the folder, false otherwise.
* @param folderAddSubFolders True if the user can add a subfolder, false otherwise.
* @param clipView True if the user can view video, false otherwise.
*/
@JsonClass(generateAdapter = true)
data class PermissionActions(
Expand All @@ -28,6 +29,8 @@ data class PermissionActions(
val folderView: Boolean? = null,

@Json(name = "folder.add_subfolders")
val folderAddSubFolders: Boolean? = null
val folderAddSubFolders: Boolean? = null,

@Json(name = "clip.view")
val clipView: Boolean? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.vimeo.networking2.annotations.Internal
*
* @param comments Information about the comments on this video.
* @param notes Information about the notes on this video.
* @param privateComments Information about the private comments on this video.
* @param credit Information about the users credited in this video.
* @param likes Information about the users who have liked this video.
* @param liveStats Information about this video's live stream stats.
Expand Down Expand Up @@ -37,6 +38,9 @@ data class VideoConnections(
@Json(name = "notes")
val notes: BasicConnection? = null,

@Json(name = "private_comments")
val privateComments: BasicConnection? = null,

@Json(name = "credit")
val credit: BasicConnection? = null,

Expand Down
23 changes: 23 additions & 0 deletions models/src/main/java/com/vimeo/networking2/richtext/Mention.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.vimeo.networking2.richtext

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* [RichText] node model for mentions.
*
* @param attrs The attributes of the mention.
* @param text The text this node represents.
*/
@JsonClass(generateAdapter = true)
data class Mention(
@Json(name = "attrs")
val attrs: MentionAttrs? = null,

@Json(name = "text")
val text: String? = null
) : RichText {

@Json(name = "type")
override val type: RichTextType = RichTextType.MENTION
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.vimeo.networking2.richtext

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* Attributes of [Mention] rich text node.
*
* @param label The label of the mention.
* @param userId The user id of the user this mention represents.
* @param name The name of the user this mention represents.
* @param avatar The avatar URL of the user this mention represents.
* @param email The email of the user this mention represents.
*/
@JsonClass(generateAdapter = true)
data class MentionAttrs(
@Json(name = "label")
val label: String? = null,

@Json(name = "userId")
val userId: Long? = null,

@Json(name = "name")
val name: String? = null,

@Json(name = "avatar")
val avatar: String? = null,

@Json(name = "email")
val email: String? = null,
)
18 changes: 18 additions & 0 deletions models/src/main/java/com/vimeo/networking2/richtext/RichText.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.vimeo.networking2.richtext

/**
* Interface for all models representing rich text nodes.
*/
sealed interface RichText {
/**
* The [RichTextType] of this node.
*/
val type: RichTextType?

companion object {
/**
* The name of the JSON field representing type of the rich text node.
*/
const val TYPE_FIELD = "type"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.vimeo.networking2.richtext

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* [RichText] node model that that contains other nodes.
*
* @param type The [RichTextType] of this node.
* @param content The [RichText] nodes this node contains.
*/
@JsonClass(generateAdapter = true)
data class RichTextContainer(
@Json(name = "type")
override val type: RichTextType? = null,

@Json(name = "content")
val content: List<RichText>? = null,
) : RichText
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.vimeo.networking2.richtext

import com.vimeo.networking2.enums.StringValue

/**
* Enum with types of [RichText] nodes.
*/
enum class RichTextType(override val value: String?) : StringValue {

/**
* Type of node that represents document.
*/
DOC("doc"),

/**
* Type of node that represents paragraph.
*/
PARAGRAPH("paragraph"),

/**
* Type of node that represents mention.
*/
MENTION("mention"),

/**
* Type of node that represents simple text.
*/
TEXT("text"),

/**
* Unknown node type.
*/
UNKNOWN(null),
}
19 changes: 19 additions & 0 deletions models/src/main/java/com/vimeo/networking2/richtext/Text.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.vimeo.networking2.richtext

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* [RichText] node model for simple text.
*
* @param text The text this node represents.
*/
@JsonClass(generateAdapter = true)
data class Text(
@Json(name = "text")
val text: String? = null
) : RichText {

@Json(name = "type")
override val type: RichTextType = RichTextType.TEXT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.vimeo.networking2.richtext

/**
* The fallback [RichText] node.
*
* Appears on unknown type.
*/
class UnknownRichTextNode : RichText {
override val type: RichTextType get() = RichTextType.UNKNOWN
}
Loading

0 comments on commit e4f31d4

Please sign in to comment.