Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ subprojects {
"implementation"("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
"implementation"("dev.usbharu:kmp-logger:1.1.0")
"implementation"("com.michael-bull.kotlin-result:kotlin-result:1.1.16")
"implementation"("io.github.reactivecircus.cache4k:cache4k:0.9.0")

"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.9.0")
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.9.0")
Expand Down
23 changes: 23 additions & 0 deletions core/src/main/kotlin/dev/usbharu/multim/api/AccountApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,36 @@ interface AccountApi {
until: StatusId? = null
): Result<List<Status>,MultiMError>

val FOLLOW: String
get() = "account/follow"

suspend fun follow(account: Account): Result<Unit,MultiMError>

val UNFOLLOW: String
get() = "account/unfollow"
suspend fun unfollow(account: Account): Result<Unit,MultiMError>

val PROFILE: String
get() = "account/profile"
suspend fun profile(account: Account): Result<Profile,MultiMError>

val STATUSES:String
get() = "account/statuses"
suspend fun statuses(account: Account, includeRepost: Boolean = false): Result<List<Status>,MultiMError>

val RELEATIONSHIPS:String
get() = "account/relationships"
suspend fun relationships(myself: Account, other: Account): Result<Relation,MultiMError>

val REQUEST_CANCEL:String
get() = "account/requestCancel"
suspend fun requestCancel(account: Account): Result<Unit,MultiMError>

val REQUEST_ACCEPT:String
get() = "account/requestAccept"
suspend fun requestAccept(account: Account): Result<Unit,MultiMError>
val REQUEST_REJECT:String
get() = "account/requestReject"
suspend fun requestReject(account: Account): Result<Unit,MultiMError>
}

Expand Down
5 changes: 5 additions & 0 deletions core/src/main/kotlin/dev/usbharu/multim/api/EmojiApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ import dev.usbharu.multim.error.MultiMResult
import dev.usbharu.multim.model.Emoji

interface EmojiApi {
val GET:String
get() = "emoji/get"
suspend fun get(name:String):MultiMResult<Emoji>

val FIND_BY_NAME:String
get() = "emoji/findByName"

suspend fun findByName(name:String):MultiMResult<List<Emoji>>
}

Expand Down
28 changes: 27 additions & 1 deletion core/src/main/kotlin/dev/usbharu/multim/api/StatusApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,22 @@ import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

interface StatusApi {
val POST:String
get() = "status/post"
suspend fun post(status: StatusForPost): Result<Status,MultiMError>

val DELETE:String
get() = "status/delete"
suspend fun delete(id: StatusId): Result<Unit,MultiMError>
val FIND_BY_ID:String
get() = "status/findById"
suspend fun findById(id: StatusId): Result<Status,MultiMError>
val ADD_REACTION:String
get() = "status/addReaction"
suspend fun addReaction(id: StatusId, reaction: Reaction): Result<Unit,MultiMError>


val REMOVE_REACTION:String
get() = "status/remvoeReaction"
/**
* Remove reaction
*
Expand All @@ -26,13 +36,29 @@ interface StatusApi {
* @return
*/
suspend fun removeReaction(id: StatusId, reaction: Reaction?): Result<Unit,MultiMError>
val REACTIONS:String
get() = "status/reactions"
suspend fun reactions(id: StatusId): Result<Map<Reaction, Int>,MultiMError>
val REPLIES:String
get() = "status/replies"
suspend fun replies(id: StatusId): Result<List<Status>,MultiMError>
val REPOST:String
get() = "status/repost"
suspend fun repost(id: StatusId): Result<Status,MultiMError>
val UN_REPOST:String
get() = "status/unRepost"
suspend fun unRepost(id: StatusId): Result<Unit,MultiMError>
val REPLY_TO:String
get() = "status/replyTo"
suspend fun replyTo(id: StatusId, status: StatusForPost): Result<Status,MultiMError>
val ADD_TO_BOOKMARKS:String
get() = "status/addBookMarks"
suspend fun addToBookmarks(id: StatusId): Result<Unit,MultiMError>
val REMOVE_FROM_BOOKMARKS:String
get() = "status/removeFromBookmarks"
suspend fun removeFromBookmarks(id: StatusId): Result<Unit,MultiMError>
val GET_PREVIOUS_AND_NEXT:String
get() = "status/getPreviousAndNext"
suspend fun getPreviousAndNext(id: StatusId): Result<PreviousAndNextPosts,MultiMError>

fun getUniqueId(status: Status):Int{
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/kotlin/dev/usbharu/multim/api/TimelineApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@ import dev.usbharu.multim.model.Status
import dev.usbharu.multim.model.Timeline

interface TimelineApi {
val AVAILABLE_TIMELINES:String
get() = "timeline/availableTimelines"
suspend fun availableTimelines(): Result<List<Timeline>,MultiMError>

val LISTEN:String
get() = "timeline/listen"
suspend fun listen(timeline: Timeline,callback: (List<Status>) -> Unit):Result<Unit,MultiMError>

val CONNECT:String
get() = "timeline/connect"
// todo 返り値を詳細にする
suspend fun connect(timeline: Timeline): Result<Unit,MultiMError>
val DISCONNECT:String
get() = "timeline/disconnect"

/**
* Disconnect
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/kotlin/dev/usbharu/multim/cache/Cacheable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dev.usbharu.multim.cache

interface Cacheable {
val cacheKey:String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package dev.usbharu.multim.cache

import com.github.michaelbull.result.Result
import dev.usbharu.multim.api.AccountApi
import dev.usbharu.multim.error.MultiMError
import dev.usbharu.multim.model.*

class CacheableAccountApi(
private val accountApi: AccountApi,
private val cacheableApi: CacheableApi
) :
CacheableApi by cacheableApi, AccountApi by accountApi {
override suspend fun userTimeline(
account: Account,
since: StatusId?,
until: StatusId?
): Result<List<Status>, MultiMError> {
return cacheableApi.cacheOrGet(
CacheableApi.generateKey(
account,
since,
until
)
) { accountApi.userTimeline(account, since, until) }
}

override suspend fun profile(account: Account): Result<Profile, MultiMError> {
return cacheableApi.cacheOrGet(PROFILE, account) { accountApi.profile(account) }
}

override suspend fun statuses(
account: Account,
includeRepost: Boolean
): Result<List<Status>, MultiMError> {
return cacheableApi.cacheOrGet(
STATUSES,
CacheableApi.generateKey(account) + includeRepost
) {
accountApi.statuses(
account,
includeRepost
)
}
}

override suspend fun relationships(
myself: Account,
other: Account
): Result<Relation, MultiMError> {
return cacheableApi.cacheOrGet(RELEATIONSHIPS, myself, other) {
accountApi.relationships(
myself,
other
)
}
}
}
52 changes: 52 additions & 0 deletions core/src/main/kotlin/dev/usbharu/multim/cache/CacheableApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dev.usbharu.multim.cache

import kotlinx.datetime.Clock

interface CacheableApi {

companion object {
fun generateKey(vararg objects: Cacheable?): String {
var key: String = Clock.System.now().toString()
for (cacheable in objects) {
key += cacheable?.cacheKey
}
return key
}
}

fun purge()
fun disable()
fun enable()
fun withNoCache(block: () -> Unit) {
disable()
try {
block()
} finally {
enable()
}
}

fun <T :Any> cache(method: String, key: String, value: T): T

fun <T> get(method: String, key: String): T?
suspend fun <T:Any> cacheOrGet(method: String, key: String, block: suspend () -> T): T {
val get = get<T>(method, key)
if (get != null) {
return get
}
return cache(method, key, block())
}

suspend fun <T:Any> cacheOrGet(
method: String,
vararg objects: Cacheable?,
block: suspend () -> T
): T {
val key = generateKey(*objects)
val get = get<T>(method, key)
if (get != null) {
return get
}
return cache(method, key, block())
}
}
16 changes: 16 additions & 0 deletions core/src/main/kotlin/dev/usbharu/multim/cache/CacheableEmojiApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dev.usbharu.multim.cache

import dev.usbharu.multim.api.EmojiApi
import dev.usbharu.multim.error.MultiMResult
import dev.usbharu.multim.model.Emoji

class CacheableEmojiApi(private val cacheableApi: CacheableApi,private val emojiApi: EmojiApi) :
CacheableApi by cacheableApi, EmojiApi by emojiApi {
override suspend fun get(name: String): MultiMResult<Emoji> {
return cacheableApi.cacheOrGet(GET, name) { emojiApi.get(name) }
}

override suspend fun findByName(name: String): MultiMResult<List<Emoji>> {
return cacheableApi.cacheOrGet(FIND_BY_NAME, name) { emojiApi.findByName(name) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dev.usbharu.multim.cache

import com.github.michaelbull.result.Result
import dev.usbharu.multim.api.StatusApi
import dev.usbharu.multim.error.MultiMError
import dev.usbharu.multim.model.PreviousAndNextPosts
import dev.usbharu.multim.model.Reaction
import dev.usbharu.multim.model.Status
import dev.usbharu.multim.model.StatusId

class CacheableStatusApi(private val cacheableApi: CacheableApi,private val statusApi: StatusApi) :
CacheableApi by cacheableApi, StatusApi by statusApi {
override suspend fun findById(id: StatusId): Result<Status, MultiMError> {
return cacheableApi.cacheOrGet(FIND_BY_ID, id) { statusApi.findById(id) }
}

override suspend fun reactions(id: StatusId): Result<Map<Reaction, Int>, MultiMError> {
return cacheableApi.cacheOrGet(REACTIONS, id) { statusApi.reactions(id) }
}

override suspend fun replies(id: StatusId): Result<List<Status>, MultiMError> {
return cacheableApi.cacheOrGet(REPLIES, id) { statusApi.replies(id) }
}

override suspend fun getPreviousAndNext(id: StatusId): Result<PreviousAndNextPosts, MultiMError> {
return cacheableApi.cacheOrGet(
GET_PREVIOUS_AND_NEXT,
id
) { statusApi.getPreviousAndNext(id) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.usbharu.multim.cache

import com.github.michaelbull.result.Result
import dev.usbharu.multim.api.TimelineApi
import dev.usbharu.multim.error.MultiMError
import dev.usbharu.multim.model.Timeline

class CacheableTimelineApi(private val cacheableApi: CacheableApi,private val timelineApi: TimelineApi) :
CacheableApi by cacheableApi, TimelineApi by timelineApi {
override suspend fun availableTimelines(): Result<List<Timeline>, MultiMError> {
return cacheableApi.cacheOrGet(AVAILABLE_TIMELINES) { timelineApi.availableTimelines() }
}
}
38 changes: 38 additions & 0 deletions core/src/main/kotlin/dev/usbharu/multim/cache/InMemoryLRUCache.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dev.usbharu.multim.cache

import io.github.reactivecircus.cache4k.Cache

class InMemoryLRUCache(maxSize: Long) : CacheableApi {
private val cache = Cache.Builder().maximumCacheSize(maxSize).build<String, Any>()
private var enable: Boolean = true
override fun purge() {
cache.invalidateAll()
}

override fun disable() {
enable = false
}

override fun enable() {
enable = true
}

override fun <T : Any> cache(method: String, key: String, value: T): T {
if (enable) {
cache.put(method + key, value)
}
return value
}

override fun <T> get(method: String, key: String): T? {
if (enable) {
val any = cache.get(method + key)
return try {
any as T?
} catch (e: ClassCastException) {
null
}
}
return null
}
}
4 changes: 3 additions & 1 deletion core/src/main/kotlin/dev/usbharu/multim/model/Account.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dev.usbharu.multim.model

import dev.usbharu.multim.cache.Cacheable

abstract class Account(
val screenName: String,
val accountName: String,
val isBot: Boolean? = false,
val avatar: Avatar
)
) : Cacheable
4 changes: 3 additions & 1 deletion core/src/main/kotlin/dev/usbharu/multim/model/StatusId.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dev.usbharu.multim.model

abstract class StatusId {
import dev.usbharu.multim.cache.Cacheable

abstract class StatusId : Cacheable {
abstract override fun equals(other: Any?): Boolean
abstract override fun hashCode(): Int
abstract fun getUrl(): String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ class MultiAccountAccount(
innerData.accountName,
innerData.isBot,
innerData.avatar
)
) {
override val cacheKey: String
get() = innerData.cacheKey
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ class MultiAccountStatusId(override val innerData: StatusId, override val hashCo
override fun getUrl(): String {
return innerData.getUrl()
}

override val cacheKey: String
get() = innerData.cacheKey
}
Loading