Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Mastodonの公開範囲に対応しました #1277

Merged
merged 8 commits into from
Jan 23, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ data class TootStatusDTO(
@SerialName("created_at") val createdAt: Instant,
val account: MastodonAccountDTO,
val content: String,
val visibility: String,
val visibility: StatusVisibilityType,
val sensitive: Boolean,
@SerialName("spoiler_text") val spoilerText: String,
@SerialName("media_attachments") val mediaAttachments: List<TootMediaAttachment>,
Expand All @@ -43,6 +43,8 @@ data class TootStatusDTO(
val filtered: Boolean? = null,
@SerialName("emoji_reactions") val emojiReactions: List<EmojiReactionCount>? = null,
@SerialName("quote") val quote: TootStatusDTO? = null,
@SerialName("circle_id") val circleId: String? = null,
@SerialName("visibility_ex") val visibilityEx: String? = null,
) {
@kotlinx.serialization.Serializable
data class Mention(
Expand Down Expand Up @@ -119,4 +121,12 @@ data class TootStatusDTO(
}
}
}
}

@kotlinx.serialization.Serializable
enum class StatusVisibilityType {
@SerialName("private") Private,
@SerialName("unlisted") Unlisted,
@SerialName("public") Public,
@SerialName("direct") Direct
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import kotlinx.serialization.SerialName
import net.pantasystem.milktea.api.misskey.auth.App
import net.pantasystem.milktea.api.misskey.drive.FilePropertyDTO
import net.pantasystem.milktea.api.misskey.users.UserDTO
import net.pantasystem.milktea.common.serializations.EnumIgnoreUnknownSerializer
import net.pantasystem.milktea.model.emoji.Emoji
import java.io.Serializable

Expand All @@ -24,7 +25,7 @@ data class NoteDTO(
val renoteId: String? = null,

val viaMobile: Boolean? = null,
val visibility: String? = null,
val visibility: NoteVisibilityType? = null,
val localOnly: Boolean? = null,

@SerialName("visibleUserIds")
Expand Down Expand Up @@ -66,4 +67,11 @@ data class NoteDTO(
) : Serializable


@kotlinx.serialization.Serializable(with = NoteVisibilityTypeSerializer::class)
enum class NoteVisibilityType {
@SerialName("public") Public, @SerialName("home") Home, @SerialName("followers") Followers, @SerialName("specified") Specified
}

object NoteVisibilityTypeSerializer : EnumIgnoreUnknownSerializer<NoteVisibilityType>(NoteVisibilityType.values(), NoteVisibilityType.Public)


Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.pantasystem.milktea.common.serializations

import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
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

abstract class EnumIgnoreUnknownSerializer<T : Enum<T>>(values: Array<out T>, private val defaultValue: T) :
KSerializer<T> {
// Alternative to taking values in param, take clazz: Class<T>
// - private val values = clazz.enumConstants
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(values.first()::class.qualifiedName!!, PrimitiveKind.STRING)
// Build maps for faster parsing, used @SerialName annotation if present, fall back to name
private val lookup = values.associateBy({ it }, { it.serialName })
private val revLookup = values.associateBy { it.serialName }

private val Enum<T>.serialName: String
get() = this::class.java.getField(this.name).getAnnotation(SerialName::class.java)?.value ?: name

override fun serialize(encoder: Encoder, value: T) {
encoder.encodeString(lookup.getValue(value))
}

override fun deserialize(decoder: Decoder): T {
// only run 'decoder.decodeString()' once
return revLookup[decoder.decodeString()] ?: defaultValue // map.getOrDefault is not available < API-24
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ object DateFormatHelper {
is Visibility.Home -> R.drawable.ic_home_black_24dp
is Visibility.Public -> null
is Visibility.Specified -> R.drawable.ic_email_black_24dp
is Visibility.Limited -> R.drawable.ic_groups
Visibility.Mutual -> R.drawable.ic_sync_alt_24px
Visibility.Personal -> R.drawable.ic_person_black_24dp
}
val text = GetElapsedTimeStringSource(
SimpleElapsedTime(
Expand Down
10 changes: 10 additions & 0 deletions modules/common_resource/src/main/res/drawable/ic_sync_alt_24px.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M7,21 L2,16 7,11 8.425,12.4 5.825,15H21V17H5.825L8.425,19.6ZM17,13 L15.575,11.6 18.175,9H3V7H18.175L15.575,4.4L17,3L22,8Z"/>
</vector>
3 changes: 3 additions & 0 deletions modules/common_resource/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -419,5 +419,8 @@
<string name="poll">投票</string>
<string name="add_to_bookmark">ブックマークに追加</string>
<string name="remove_bookmark">ブックマークを解除</string>
<string name="visibility_personal">自分限定</string>
<string name="visibility_limited">サークル</string>
<string name="visibility_mutual">相互フォロー</string>

</resources>
3 changes: 3 additions & 0 deletions modules/common_resource/src/main/res/values-zh/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -416,5 +416,8 @@
<string name="poll">投票</string>
<string name="remove_bookmark">删除书签</string>
<string name="add_to_bookmark">添加到书签</string>
<string name="visibility_personal">自我限制</string>
<string name="visibility_limited">圆圈</string>
<string name="visibility_mutual">相互关注</string>

</resources>
3 changes: 3 additions & 0 deletions modules/common_resource/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -409,5 +409,8 @@
<string name="poll">poll</string>
<string name="add_to_bookmark">Add to Bookmark</string>
<string name="remove_bookmark">Remove Bookmark</string>
<string name="visibility_personal">Personal</string>
<string name="visibility_limited">Circle</string>
<string name="visibility_mutual">Mutual Follow</string>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import net.pantasystem.milktea.api.misskey.groups.GroupDTO
import net.pantasystem.milktea.api.misskey.list.UserListDTO
import net.pantasystem.milktea.api.misskey.messaging.MessageDTO
import net.pantasystem.milktea.api.misskey.notes.NoteDTO
import net.pantasystem.milktea.api.misskey.notes.NoteVisibilityType
import net.pantasystem.milktea.api.misskey.notes.PollDTO
import net.pantasystem.milktea.api.misskey.notification.NotificationDTO
import net.pantasystem.milktea.api.misskey.users.UserDTO
Expand Down Expand Up @@ -158,7 +159,7 @@ fun PollDTO?.toPoll(): Poll? {


fun NoteDTO.toNote(account: Account, nodeInfo: NodeInfo?): Note {
val visibility = Visibility(this.visibility?: "public", isLocalOnly = localOnly?: false, visibleUserIds = visibleUserIds?.map { id ->
val visibility = Visibility(this.visibility?: NoteVisibilityType.Public, isLocalOnly = localOnly?: false, visibleUserIds = visibleUserIds?.map { id ->
User.Id(account.accountId, id)
}?: emptyList())
return Note(
Expand Down Expand Up @@ -195,6 +196,17 @@ fun NoteDTO.toNote(account: Account, nodeInfo: NodeInfo?): Note {
)
}

@Throws(IllegalArgumentException::class)
fun Visibility(type: NoteVisibilityType, isLocalOnly: Boolean, visibleUserIds: List<User.Id>? = null): Visibility {
return when(type){
NoteVisibilityType.Public -> Visibility.Public(isLocalOnly)
NoteVisibilityType.Followers -> Visibility.Followers(isLocalOnly)
NoteVisibilityType.Home -> Visibility.Home(isLocalOnly)
NoteVisibilityType.Specified -> Visibility.Specified(visibleUserIds ?: emptyList())
else -> Visibility.Public(isLocalOnly)
}
}


fun NoteDTO.toNoteAndUser(account: Account, nodeInfo: NodeInfo?): Pair<Note, User> {
val note = this.toNote(account, nodeInfo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package net.pantasystem.milktea.data.infrastructure
import net.pantasystem.milktea.api.mastodon.accounts.MastodonAccountRelationshipDTO
import net.pantasystem.milktea.api.mastodon.media.TootMediaAttachment
import net.pantasystem.milktea.api.mastodon.poll.TootPollDTO
import net.pantasystem.milktea.api.mastodon.status.StatusVisibilityType
import net.pantasystem.milktea.api.mastodon.status.TootStatusDTO
import net.pantasystem.milktea.model.account.Account
import net.pantasystem.milktea.model.drive.FileProperty
Expand Down Expand Up @@ -60,9 +61,7 @@ fun TootStatusDTO.toNote(account: Account, nodeInfo: NodeInfo?): Note {
renoteId = this.quote?.id?.let { Note.Id(account.accountId, it) }
?: this.reblog?.id?.let { Note.Id(account.accountId, this.reblog?.id!!) },
viaMobile = null,

// TODO: 正しいVisibilityを得るようにする
visibility = Visibility.Public(false),
visibility = Visibility(visibility, circleId, visibilityEx),
localOnly = null,
emojis = emojis.map {
it.toEmoji()
Expand Down Expand Up @@ -149,4 +148,21 @@ fun MastodonAccountRelationshipDTO.toUserRelated(): User.Related {
isMuting = muting,
hasPendingFollowRequestToYou = false,
)
}
}

fun Visibility(type: StatusVisibilityType, circleId: String? = null, visibilityEx: String? = null,): Visibility {
return when(type) {
StatusVisibilityType.Private -> {
when(visibilityEx) {
"limited" -> Visibility.Limited(circleId)
else -> Visibility.Followers(false)
}
}
StatusVisibilityType.Unlisted -> Visibility.Home(false)
StatusVisibilityType.Public -> Visibility.Public(false)
StatusVisibilityType.Direct -> when(visibilityEx) {
"personal" -> Visibility.Personal
else -> Visibility.Specified(emptyList())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class LocalConfigRepositoryImpl(
"home" -> Visibility.Home(localOnly)
"followers" -> Visibility.Followers(localOnly)
"specified" -> Visibility.Specified(emptyList())
"mutual" -> Visibility.Mutual
"personal" -> Visibility.Personal
else -> Visibility.Public(localOnly)
}
RememberVisibility.Remember(
Expand All @@ -82,6 +84,9 @@ class LocalConfigRepositoryImpl(
is Visibility.Home -> "home"
is Visibility.Followers -> "followers"
is Visibility.Specified -> "specified"
is Visibility.Limited -> "limited"
Visibility.Mutual -> "mutual"
Visibility.Personal -> "personal"
}
sharedPreference.edit {
putString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,19 @@ fun VisibilitySelectionTile(
is Visibility.Home -> stringResource(id = R.string.visibility_home)
is Visibility.Public -> stringResource(id = R.string.visibility_public)
is Visibility.Specified -> stringResource(id = R.string.visibility_specified)
is Visibility.Limited -> stringResource(id = R.string.visibility_limited)
Visibility.Mutual -> stringResource(id = R.string.visibility_mutual)
Visibility.Personal -> stringResource(id = R.string.visibility_personal)
}

val iconDrawable = when (item) {
is Visibility.Followers -> R.drawable.ic_lock_black_24dp
is Visibility.Home -> R.drawable.ic_home_black_24dp
is Visibility.Public -> R.drawable.ic_language_black_24dp
is Visibility.Specified -> R.drawable.ic_email_black_24dp
is Visibility.Limited -> net.pantasystem.milktea.common_android.R.drawable.ic_groups
Visibility.Mutual -> net.pantasystem.milktea.common_android.R.drawable.ic_sync_alt_24px
Visibility.Personal -> net.pantasystem.milktea.common_android.R.drawable.ic_person_black_24dp
}

Surface(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class ReactionHistoryPagerViewModel @Inject constructor(

private val author = note.filterNotNull().map {
userRepository.find(it.userId)
}.catch {
logger.error("投稿したユーザの取得に失敗", it)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
val uiState = combine(note, account, author, types) { note, ac, author, types ->
ReactionHistoryPagerUiState(note, ac, author,types)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ data class Note(
*/
fun canRenote(userId: User.Id): Boolean {
return when (type) {
is Type.Mastodon -> true
is Type.Mastodon -> visibility is Visibility.Public
|| visibility is Visibility.Home
Type.Misskey -> id.accountId == userId.accountId
&& (visibility is Visibility.Public
|| visibility is Visibility.Home
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package net.pantasystem.milktea.model.notes

import net.pantasystem.milktea.model.nodeinfo.NodeInfo
import net.pantasystem.milktea.model.user.User
import java.io.Serializable
import java.lang.IllegalArgumentException
import java.util.*
import kotlin.jvm.Throws

sealed class Visibility : Serializable{
data class Public(
Expand Down Expand Up @@ -40,6 +39,11 @@ sealed class Visibility : Serializable{
override val isLocalOnly: Boolean = true
}*/

object Personal : Visibility()

data class Limited(val circleId: String?) : Visibility()

object Mutual : Visibility()
}

interface CanLocalOnly {
Expand All @@ -63,6 +67,21 @@ fun Visibility.type(): String {
is Visibility.Specified -> {
"specified"
}
is Visibility.Limited -> "limited"
Visibility.Mutual -> "mutual"
Visibility.Personal -> "personal"
}
}

fun Visibility.type4Mastodon(): String {
return when(this) {
is Visibility.Followers -> "private"
is Visibility.Home -> "unlisted"
is Visibility.Public -> "public"
is Visibility.Specified -> "direct"
is Visibility.Limited -> "limited"
Visibility.Mutual -> "mutual"
Visibility.Personal -> "personal"
}
}

Expand All @@ -73,13 +92,28 @@ fun Visibility(type: String, isLocalOnly: Boolean, visibleUserIds: List<User.Id>
"home" -> Visibility.Home(isLocalOnly)
"followers" -> Visibility.Followers(isLocalOnly)
"specified" -> Visibility.Specified(visibleUserIds ?: emptyList())
else -> throw IllegalArgumentException("public, home, followers, specified以外許可されていません。与えられたデータ:$type")
else -> Visibility(type)
}
/*require((isLocalOnly && visibility is CanLocalOnly) || !isLocalOnly) {
"$type では localOnlyは指定できません。"
}*/

}

fun Visibility(type: String, circleId: String? = null, visibilityEx: String? = null,): Visibility {
return when(type.lowercase()) {
"private" -> {
when(visibilityEx) {
"limited" -> Visibility.Limited(circleId)
else -> Visibility.Followers(false)
}
}
"unlisted" -> Visibility.Home(false)
"public" -> Visibility.Public(false)
"direct" -> when(visibilityEx) {
"personal" -> Visibility.Personal
else -> Visibility.Specified(emptyList())
}
"followers" -> Visibility.Followers(false)
"home" -> Visibility.Home(false)
else -> throw IllegalArgumentException("limited, direct, unlisted, private public, home, followers, specified以外許可されていません。与えられたデータ:$type")
}
}

fun Visibility.isLocalOnly(): Boolean {
Expand All @@ -92,11 +126,23 @@ fun CreateNote.visibleUserIds(): List<String>? {
}
}

fun Visibility.getName() : String{
fun Visibility.getName(softwareType: NodeInfo.SoftwareType? = null) : String{
return when(this) {
is Visibility.Public -> "public"
is Visibility.Home -> "home"
is Visibility.Specified -> "specified"
is Visibility.Followers -> "followers"
is Visibility.Home -> when(softwareType) {
is NodeInfo.SoftwareType.Mastodon -> "unlisted"
else -> "home"
}
is Visibility.Specified -> when(softwareType) {
is NodeInfo.SoftwareType.Mastodon -> "direct"
else -> "specified"
}
is Visibility.Followers -> when(softwareType) {
is NodeInfo.SoftwareType.Mastodon -> "private"
else -> "followers"
}
is Visibility.Limited -> "limited"
is Visibility.Mutual -> "mutual"
is Visibility.Personal -> "personal"
}
}