-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
335 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package element_comment | ||
|
||
data class ElementComment( | ||
val id: Long, | ||
val elementId: Long, | ||
val comment: String, | ||
val createdAt: String, | ||
val updatedAt: String, | ||
) |
36 changes: 36 additions & 0 deletions
36
app/src/androidMain/kotlin/element_comment/ElementCommentJson.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package element_comment | ||
|
||
import json.toJsonArray | ||
import java.io.InputStream | ||
|
||
data class ElementCommentJson( | ||
val id: Long, | ||
val elementId: Long?, | ||
val comment: String?, | ||
val createdAt: String?, | ||
val updatedAt: String, | ||
val deletedAt: String?, | ||
) | ||
|
||
fun ElementCommentJson.toElementComment(): ElementComment { | ||
return ElementComment( | ||
id = id, | ||
elementId = elementId!!, | ||
comment = comment!!, | ||
createdAt = createdAt!!, | ||
updatedAt = updatedAt, | ||
) | ||
} | ||
|
||
fun InputStream.toElementCommentsJson(): List<ElementCommentJson> { | ||
return toJsonArray().map { | ||
ElementCommentJson( | ||
id = it.getLong("id"), | ||
elementId = it.optLong("element_id"), | ||
comment = it.optString("comment").ifBlank { null }, | ||
createdAt = it.optString("created_at").ifBlank { null }, | ||
updatedAt = it.getString("created_at"), | ||
deletedAt = it.optString("deleted_at").ifBlank { null }, | ||
) | ||
} | ||
} |
132 changes: 132 additions & 0 deletions
132
app/src/androidMain/kotlin/element_comment/ElementCommentQueries.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package element_comment | ||
|
||
import androidx.sqlite.SQLiteConnection | ||
import androidx.sqlite.use | ||
import db.getZonedDateTime | ||
import db.transaction | ||
import java.time.ZonedDateTime | ||
|
||
class ElementCommentQueries(private val conn: SQLiteConnection) { | ||
|
||
companion object { | ||
const val CREATE_TABLE = """ | ||
CREATE TABLE element_comment ( | ||
id INTEGER NOT NULL PRIMARY KEY, | ||
element_id INTEGER NOT NULL, | ||
comment TEXT NOT NULL, | ||
created_at TEXT NOT NULL, | ||
updated_at TEXT NOT NULL | ||
); | ||
""" | ||
} | ||
|
||
fun insertOrReplace(comments: List<ElementComment>) { | ||
conn.transaction { conn -> | ||
comments.forEach { comment -> | ||
conn.prepare( | ||
""" | ||
INSERT OR REPLACE | ||
INTO element_comment ( | ||
id, | ||
element_id, | ||
comment, | ||
created_at, | ||
updated_at | ||
) VALUES (?1, ?2, ?3, ?4, ?5) | ||
""" | ||
).use { | ||
it.bindLong(1, comment.id) | ||
it.bindLong(2, comment.elementId) | ||
it.bindText(3, comment.comment) | ||
it.bindText(4, comment.createdAt) | ||
it.bindText(5, comment.updatedAt) | ||
it.step() | ||
} | ||
} | ||
} | ||
} | ||
|
||
fun selectById(id: Long): ElementComment? { | ||
return conn.prepare( | ||
""" | ||
SELECT | ||
id, | ||
element_id, | ||
comment, | ||
created_at, | ||
updated_at | ||
FROM element_comment | ||
WHERE id = ?1 | ||
""" | ||
).use { | ||
it.bindLong(1, id) | ||
|
||
if (it.step()) { | ||
ElementComment( | ||
id = it.getLong(0), | ||
elementId = it.getLong(1), | ||
comment = it.getText(2), | ||
createdAt = it.getText(3), | ||
updatedAt = it.getText(4), | ||
) | ||
} else { | ||
null | ||
} | ||
} | ||
} | ||
|
||
fun selectByElementId(elementId: Long): List<ElementComment> { | ||
return conn.prepare( | ||
""" | ||
SELECT | ||
id, | ||
element_id, | ||
comment, | ||
created_at, | ||
updated_at | ||
FROM element_comment | ||
WHERE element_id = ?1 | ||
""" | ||
).use { | ||
it.bindLong(1, elementId) | ||
|
||
buildList { | ||
while (it.step()) { | ||
add( | ||
ElementComment( | ||
id = it.getLong(0), | ||
elementId = it.getLong(1), | ||
comment = it.getText(2), | ||
createdAt = it.getText(3), | ||
updatedAt = it.getText(4), | ||
) | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
fun selectMaxUpdatedAt(): ZonedDateTime? { | ||
return conn.prepare("SELECT max(updated_at) FROM element_comment").use { | ||
if (it.step()) { | ||
it.getZonedDateTime(0) | ||
} else { | ||
null | ||
} | ||
} | ||
} | ||
|
||
fun selectCount(): Long { | ||
return conn.prepare("SELECT count(*) FROM element_comment").use { | ||
it.step() | ||
it.getLong(0) | ||
} | ||
} | ||
|
||
fun deleteById(id: Long) { | ||
conn.prepare("DELETE FROM element_comment WHERE id = ?1").use { | ||
it.bindLong(1, id) | ||
it.step() | ||
} | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
app/src/androidMain/kotlin/element_comment/ElementCommentRepo.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package element_comment | ||
|
||
import android.app.Application | ||
import api.Api | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.withContext | ||
import java.time.Duration | ||
import java.time.ZoneOffset | ||
import java.time.ZonedDateTime | ||
|
||
class ElementCommentRepo( | ||
private val api: Api, | ||
private val app: Application, | ||
private val queries: ElementCommentQueries, | ||
) { | ||
|
||
suspend fun selectByElementId(elementId: Long): List<ElementComment> { | ||
return withContext(Dispatchers.IO) { | ||
queries.selectByElementId(elementId) | ||
} | ||
} | ||
|
||
suspend fun selectCount(): Long { | ||
return withContext(Dispatchers.IO) { | ||
queries.selectCount() | ||
} | ||
} | ||
|
||
suspend fun hasBundledElements(): Boolean { | ||
return withContext(Dispatchers.IO) { | ||
app.resources.assets.list("")!!.contains("element-comments.json") | ||
} | ||
} | ||
|
||
suspend fun fetchBundledElements() { | ||
withContext(Dispatchers.IO) { | ||
app.assets.open("element-comments.json").use { bundledElements -> | ||
queries.insertOrReplace(bundledElements | ||
.toElementCommentsJson() | ||
.filter { it.deletedAt == null } | ||
.map { it.toElementComment() }) | ||
} | ||
} | ||
} | ||
|
||
suspend fun sync(): SyncReport { | ||
return withContext(Dispatchers.IO) { | ||
val startedAt = ZonedDateTime.now(ZoneOffset.UTC) | ||
var newItems = 0L | ||
var updatedItems = 0L | ||
var deletedItems = 0L | ||
var maxKnownUpdatedAt = queries.selectMaxUpdatedAt() | ||
|
||
while (true) { | ||
val delta = api.getElementComments(maxKnownUpdatedAt, BATCH_SIZE) | ||
|
||
if (delta.isEmpty()) { | ||
break | ||
} else { | ||
maxKnownUpdatedAt = ZonedDateTime.parse(delta.maxBy { it.updatedAt }.updatedAt) | ||
} | ||
|
||
delta.forEach { | ||
val cached = queries.selectById(it.id) | ||
|
||
if (it.deletedAt == null) { | ||
if (cached == null) { | ||
newItems++ | ||
} else { | ||
updatedItems++ | ||
} | ||
|
||
queries.insertOrReplace(listOf(it.toElementComment())) | ||
} else { | ||
if (cached == null) { | ||
// Already evicted from cache, nothing to do here | ||
} else { | ||
queries.deleteById(it.id) | ||
deletedItems++ | ||
} | ||
} | ||
} | ||
|
||
if (delta.size < BATCH_SIZE) { | ||
break | ||
} | ||
} | ||
|
||
SyncReport( | ||
duration = Duration.between(startedAt, ZonedDateTime.now(ZoneOffset.UTC)), | ||
newElementComments = newItems, | ||
updatedElementComments = updatedItems, | ||
deletedElementComments = deletedItems, | ||
) | ||
} | ||
} | ||
|
||
data class SyncReport( | ||
val duration: Duration, | ||
val newElementComments: Long, | ||
val updatedElementComments: Long, | ||
val deletedElementComments: Long, | ||
) | ||
|
||
companion object { | ||
private const val BATCH_SIZE = 5000L | ||
} | ||
} |
Oops, something went wrong.