diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd59fbacc..4439a39af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,8 @@ * [Kotlin](#kotlin) * [Code quality](#code-quality) * [Adding translations](#adding-translations) + * [Updating the instance list](#updating-the-instance-list) + * [Generate compose compiler metrics](#generate-compose-compiler-metrics) @@ -73,3 +75,25 @@ prettier --write "*.md" "*.yml"` You can find the translations in the `app/src/main/res/values-{locale}/strings.xml` file. You can open it in android studio, right click and click open translations editor or you can directly edit the files. + +## Updating the instance list + +There is a custom gradle task that generates all the lemmy instances that this app directly supports. +It updates the lemmy instances list in DefaultInstances.kt and the AndroidManifest. +It uses the fediverse api and filters on the monthly users. +You can run it by doing + +```shell + ./gradlew app:updateInstances --no-configuration-cache +``` + +## Generate compose compiler metrics + +You can generate the compose compiler metrics by executing the following gradle task. + +```shell +./gradlew assembleRelease --rerun-tasks -P com.jerboa.enableComposeCompilerReports=true +``` + +Then you will find the metrics in `app/build/compose_metrics` directory. +See [this link for more information on these metrics](https://github.com/androidx/androidx/blob/androidx-main/compose/compiler/design/compiler-metrics.md) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 35625b6a1..f3c715c9c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -7,6 +7,8 @@ plugins { id("androidx.baselineprofile") } +apply(from = "update_instances.gradle.kts") + android { compileSdk = 33 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0b6927abb..d30c7f06d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -59,26 +59,68 @@ - + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/jerboa/DefaultInstances.kt b/app/src/main/java/com/jerboa/DefaultInstances.kt new file mode 100644 index 000000000..f20ce474c --- /dev/null +++ b/app/src/main/java/com/jerboa/DefaultInstances.kt @@ -0,0 +1,59 @@ +package com.jerboa + +val DEFAULT_LEMMY_INSTANCES = arrayOf( + "lemmy.world", // 13911 monthly users + "lemmy.ml", // 4217 monthly users + "beehaw.org", // 3739 monthly users + "feddit.de", // 2300 monthly users + "sh.itjust.works", // 2221 monthly users + "www.hexbear.net", // 1602 monthly users + "lemmy.ca", // 1095 monthly users + "lemm.ee", // 994 monthly users + "lemmy.one", // 987 monthly users + "lemmy.fmhy.ml", // 945 monthly users + "lemmy.dbzer0.com", // 784 monthly users + "lemmy.blahaj.zone", // 769 monthly users + "lemmygrad.ml", // 630 monthly users + "programming.dev", // 624 monthly users + "discuss.tchncs.de", // 564 monthly users + "sopuli.xyz", // 563 monthly users + "lemmy.sdf.org", // 476 monthly users + "vlemmy.net", // 399 monthly users + "midwest.social", // 396 monthly users + "aussie.zone", // 373 monthly users + "startrek.website", // 317 monthly users + "infosec.pub", // 288 monthly users + "feddit.uk", // 286 monthly users + "dormi.zone", // 269 monthly users + "feddit.it", // 260 monthly users + "pawb.social", // 243 monthly users + "feddit.nl", // 197 monthly users + "burggit.moe", // 187 monthly users + "slrpnk.net", // 179 monthly users + "lemmy.nz", // 167 monthly users + "feddit.dk", // 159 monthly users + "delraymisfitsboard.com", // 158 monthly users + "reddthat.com", // 154 monthly users + "mander.xyz", // 152 monthly users + "feddit.cl", // 123 monthly users + "lemmy.zip", // 118 monthly users + "lemmy.pt", // 100 monthly users + "dataterm.digital", // 86 monthly users + "lemmy.eco.br", // 86 monthly users + "szmer.info", // 85 monthly users + "latte.isnot.coffee", // 85 monthly users + "monyet.cc", // 85 monthly users + "exploding-heads.com", // 84 monthly users + "waveform.social", // 78 monthly users + "lemmy.tedomum.net", // 76 monthly users + "enterprise.lemmy.ml", // 72 monthly users + "pathofexile-discuss.com", // 71 monthly users + "iusearchlinux.fyi", // 65 monthly users + "yiffit.net", // 62 monthly users + "ttrpg.network", // 61 monthly users + "lemmyrs.org", // 59 monthly users + "sub.wetshaving.social", // 57 monthly users + "monero.town", // 54 monthly users + "bakchodi.org", // 53 monthly users + "geddit.social", // 50 monthly users +) diff --git a/app/src/main/java/com/jerboa/Utils.kt b/app/src/main/java/com/jerboa/Utils.kt index 1a8f83706..70bafc7f1 100644 --- a/app/src/main/java/com/jerboa/Utils.kt +++ b/app/src/main/java/com/jerboa/Utils.kt @@ -83,23 +83,6 @@ val gson = Gson() const val DEBOUNCE_DELAY = 1000L const val MAX_POST_TITLE_LENGTH = 200 -val DEFAULT_LEMMY_INSTANCES = listOf( - "beehaw.org", - "feddit.de", - "feddit.it", - "lemmy.ca", - "lemmy.ml", - "lemmy.one", - "lemmy.world", - "lemmygrad.ml", - "midwest.social", - "mujico.org", - "sh.itjust.works", - "slrpnk.net", - "sopuli.xyz", - "szmer.info", -) - // convert a data class to a map fun T.serializeToMap(): Map { return convert() @@ -1320,9 +1303,9 @@ fun getLangPreferenceDropdownEntries(ctx: Context): Map { val localeList = getLocaleListFromXml(ctx) val map = mutableMapOf() - for (a in 0 until localeList.size()) { - localeList[a].let { - it?.let { it1 -> map.put(it, it.getDisplayName(it)) } + for (i in 0 until localeList.size()) { + localeList[i]?.let { + map.put(it, it.getDisplayName(it)) } } return map diff --git a/app/src/main/java/com/jerboa/ui/components/inbox/InboxActivity.kt b/app/src/main/java/com/jerboa/ui/components/inbox/InboxActivity.kt index b8aecbc86..e941f3fd8 100644 --- a/app/src/main/java/com/jerboa/ui/components/inbox/InboxActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/inbox/InboxActivity.kt @@ -293,9 +293,9 @@ fun InboxTabs( items( replies, key = { reply -> reply.comment_reply.id }, - ) { crv -> + ) { commentReplyView -> CommentReplyNode( - commentReplyView = crv, + commentReplyView = commentReplyView, onUpvoteClick = { cr -> account?.also { acct -> inboxViewModel.likeReply( diff --git a/app/update_instances.gradle.kts b/app/update_instances.gradle.kts new file mode 100644 index 000000000..61930aff6 --- /dev/null +++ b/app/update_instances.gradle.kts @@ -0,0 +1,142 @@ +import java.io.OutputStreamWriter +import java.net.URL +import java.net.HttpURLConnection + +// We can't import libraries here for some reason, so we must use what is provided +// by gradle, which isn't much. The groovy JSON library is meant for use by groovy code, +// so we need some creativity to use it in Kotlin. +import org.apache.groovy.json.internal.LazyMap +import groovy.json.JsonOutput +import groovy.json.JsonSlurper + +// All lemmy instances with at least this amount of monthly active users will be included. +val minimumMAU = 50 + + +val endpointUrl = "https://api.fediverse.observer/" +val instancesFilePath = "src/main/java/com/jerboa/DefaultInstances.kt" +val manifestPath = "src/main/AndroidManifest.xml" +val START_TAG = "" +val END_TAG = "" +val IDENT = 14 +val nsfwList = listOf("lemmynsfw.com") + +// Some extension methods to make the JsonSlurper output easier to process +fun LazyMap.getMap(key: String): LazyMap { + return this[key] as LazyMap +} + +fun LazyMap.getArray(key: String): ArrayList<*> { + return this[key] as ArrayList<*> +} + +@Suppress("UNCHECKED_CAST") +fun LazyMap.getAs(key: String): T { + return this[key] as T +} + +// Run this as `./gradlew app:updateInstances --no-configuration-cache` +tasks.register("updateInstances") { + description = "Fetches a list of popular Lemmy instances and writes it to the DefaultInstances.kt file" + + doFirst { + // Get sorted list of nodes + val nodes = getData() + .getMap("data") + .getArray("nodes") + .map { + val name = (it as LazyMap)["domain"] as String + val users = it["active_users_monthly"] as Int? + + Pair(name, users ?: 0) + } + .filter { + it.second >= minimumMAU && !nsfwList.contains(it.first) + } + .sortedBy { + it.second + } + .reversed() + + updateInstanceList(nodes) + updateManifest(nodes.map { it.first }) + } +} + + +fun getData(): LazyMap { + val url = URL(endpointUrl) + val query = """ +{ + nodes(softwarename: "lemmy") { + domain + active_users_monthly + } +}""" + + // Format JSON request body + val body = JsonOutput.toJson(mapOf("query" to query)) + + // Create POST request + val req = url.openConnection() as HttpURLConnection + req.requestMethod = "POST" + req.doOutput = true + req.setRequestProperty("Content-Type", "application/json") + + // Write body to request + OutputStreamWriter(req.outputStream, "UTF-8").use { + it.write(body) + } + + // Get response and JSON parse it + return JsonSlurper().parse(req.inputStream.reader()) as LazyMap +} + +fun updateInstanceList(nodes: List>) { + // Create output file and write header + val outFile = file(instancesFilePath) + outFile.writeText( + """package com.jerboa + +val DEFAULT_LEMMY_INSTANCES = arrayOf( +""" + ) + + // Write each node's name, one per line + for (n in nodes) { + outFile.appendText(" \"${n.first}\", // ${n.second} monthly users\n") + } + + outFile.appendText(")\n") +} + + +fun updateManifest(list: List) { + val manifest = file(manifestPath) + val lines = manifest.readLines() + manifest.writeText("") + + var skip = false + + for (line in lines) { + if (line.trim() == START_TAG) { + skip = true + manifest.appendText(" ".repeat(IDENT) + START_TAG) + manifest.appendText(genManifestHosts(list)) + manifest.appendText(" ".repeat(IDENT) + END_TAG + System.lineSeparator()) + } else if (line.trim() == END_TAG) { + skip = false + } else if (!skip) { + manifest.appendText(line + System.lineSeparator()) + } + } +} + +fun genManifestHosts(list: List): String { + return list.joinToString( + separator = System.lineSeparator(), + prefix = System.lineSeparator(), + postfix = System.lineSeparator(), + ) { " ".repeat(IDENT) + "" } +} + diff --git a/build.gradle.kts b/build.gradle.kts index 32b673cfd..3a6d6ca35 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,7 +45,3 @@ subprojects { } } } - -tasks.register("clean", Delete::class) { - delete(rootProject.buildDir) -}