rename: Android is Inkwell — package, applicationId, uniffi class, assets

Step 4 of milestone 481 (Scribe note 5071: a full rename).

- namespace and applicationId com.fabledsword.inkwell; the Kotlin package moves
  with them, and ktlint re-sorted the imports the rename reordered (checked
  locally with CI's ktlint 1.4.0 and detekt 1.23.7, both clean)
- uniffi: class Inkwell in com.fabledsword.inkwell.core, InkwellApplication,
  InkwellTheme, Theme.Inkwell, log tags, prefs and work names, client agent
  inkwell-android
- the lane publishes inkwell.apk / inkwell-android.json; fetch-clients,
  guard-forward, publish-release and write-manifest read the same names

A new applicationId is a new app. The old ThoughtSync app keeps its own store
and stays installed beside it. Notes cross over by syncing, and the old app is
then removed by hand.

Kept: the signing keyAlias is still "thoughtsync". It names the key inside the
existing keystore, and the key, and so the certificate, are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 14:34:02 -04:00
co-authored by Claude Opus 5.5
parent 256fba3610
commit 81cd719327
55 changed files with 222 additions and 219 deletions
+13 -13
View File
@@ -134,7 +134,7 @@ jobs:
echo "code=$code" >> $GITHUB_OUTPUT
if [ -n "${ANDROID_KEYSTORE_BASE64:-}" ]; then
printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > /tmp/thoughtsync-release.jks
printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > /tmp/inkwell-release.jks
echo "variant=Release" >> $GITHUB_OUTPUT
echo "label=release" >> $GITHUB_OUTPUT
# DEBUG profile, in a release APK, deliberately — see the note above
@@ -143,7 +143,7 @@ jobs:
# outright (run 4077). Unpicking that is worth doing and is not worth
# blocking signed builds on.
echo "profile=debug" >> $GITHUB_OUTPUT
echo "keystore=/tmp/thoughtsync-release.jks" >> $GITHUB_OUTPUT
echo "keystore=/tmp/inkwell-release.jks" >> $GITHUB_OUTPUT
echo "apk=android/app/build/outputs/apk/release/app-release.apk" >> $GITHUB_OUTPUT
echo "Signed release build — $version (versionCode $code)"
else
@@ -167,7 +167,7 @@ jobs:
# from the built .so. Run as its own step so a Rust failure is legible as a
# Rust failure instead of arriving inside a Gradle stack trace.
- name: Build the native library and bindings
run: ./gradlew generateUniffiBindings -PTHOUGHTSYNC_CARGO_PROFILE=${{ steps.build.outputs.profile }}
run: ./gradlew generateUniffiBindings -PINKWELL_CARGO_PROFILE=${{ steps.build.outputs.profile }}
# The image's PINNED CLIs, not Gradle plugins. ci-rust-android carries both
# (M12 step 3) precisely so this lane needs no second image, and going
@@ -193,7 +193,7 @@ jobs:
# not exist (run 4082). It costs one extra Kotlin compile and buys the
# type-check on the debug variant, which is the one an emulator build
# would use.
run: ./gradlew testDebugUnitTest -PTHOUGHTSYNC_CARGO_PROFILE=${{ steps.build.outputs.profile }}
run: ./gradlew testDebugUnitTest -PINKWELL_CARGO_PROFILE=${{ steps.build.outputs.profile }}
- name: Assemble the APK
env:
@@ -203,9 +203,9 @@ jobs:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
run: |
./gradlew assemble${{ steps.build.outputs.variant }} \
-PTHOUGHTSYNC_CARGO_PROFILE=${{ steps.build.outputs.profile }} \
-PTHOUGHTSYNC_VERSION_NAME=${{ steps.build.outputs.name }} \
-PTHOUGHTSYNC_VERSION_CODE=${{ steps.build.outputs.code }}
-PINKWELL_CARGO_PROFILE=${{ steps.build.outputs.profile }} \
-PINKWELL_VERSION_NAME=${{ steps.build.outputs.name }} \
-PINKWELL_VERSION_CODE=${{ steps.build.outputs.code }}
# Prints the certificate the APK was actually signed with, so the operator
# can compare it against the fingerprint recorded when the key was
@@ -226,10 +226,10 @@ jobs:
if: steps.build.outputs.keystore != ''
run: |
mkdir -p dist
cp "app/build/outputs/apk/release/app-release.apk" dist/thoughtsync.apk
size="$(wc -c < dist/thoughtsync.apk | tr -d ' ')"
sha="$(sha256sum dist/thoughtsync.apk | cut -d' ' -f1)"
cat > dist/thoughtsync-android.json <<JSON
cp "app/build/outputs/apk/release/app-release.apk" dist/inkwell.apk
size="$(wc -c < dist/inkwell.apk | tr -d ' ')"
sha="$(sha256sum dist/inkwell.apk | cut -d' ' -f1)"
cat > dist/inkwell-android.json <<JSON
{
"version_name": "${{ steps.build.outputs.name }}",
"version_code": ${{ steps.build.outputs.code }},
@@ -237,7 +237,7 @@ jobs:
"sha256": "$sha"
}
JSON
cat dist/thoughtsync-android.json
cat dist/inkwell-android.json
# The rolling channel for this branch, the same fixed-tag releases the desktop
# bundles use. CI artifacts are per-run and auth-gated, so they are no use as a
@@ -276,7 +276,7 @@ jobs:
with:
# The APK's variant, NOT the Cargo profile — those are the same word
# for different things and the profile is pinned to debug (#2810).
name: thoughtsync-android-${{ steps.build.outputs.label }}-${{ github.sha }}
name: inkwell-android-${{ steps.build.outputs.label }}-${{ github.sha }}
path: ${{ steps.build.outputs.apk }}
if-no-files-found: error
+9 -6
View File
@@ -141,7 +141,7 @@ val rustInputs =
* build, and neither is worth holding signed APKs up for. Scribe #2810.
*/
val rustProfile =
(project.findProperty("THOUGHTSYNC_CARGO_PROFILE") as String?)?.takeIf { it.isNotBlank() }
(project.findProperty("INKWELL_CARGO_PROFILE") as String?)?.takeIf { it.isNotBlank() }
?: "debug"
val jniLibsOut = layout.buildDirectory.dir("rustJniLibs")
@@ -169,11 +169,11 @@ val generateBindings =
}
android {
namespace = "com.fabledsword.thoughtsync"
namespace = "com.fabledsword.inkwell"
compileSdk = 36
defaultConfig {
applicationId = "com.fabledsword.thoughtsync"
applicationId = "com.fabledsword.inkwell"
// 26 (Android 8, 2017) matches Minstrel and clears the NDK's floor with
// room to spare.
minSdk = 26
@@ -181,9 +181,9 @@ android {
// Injected by CI from the git tag + commit count for a release; "dev"
// locally so the About screen reads honestly rather than claiming 1.0.
val nameOverride =
(project.findProperty("THOUGHTSYNC_VERSION_NAME") as String?)?.takeIf { it.isNotBlank() }
(project.findProperty("INKWELL_VERSION_NAME") as String?)?.takeIf { it.isNotBlank() }
val codeOverride =
(project.findProperty("THOUGHTSYNC_VERSION_CODE") as String?)?.toIntOrNull()
(project.findProperty("INKWELL_VERSION_CODE") as String?)?.toIntOrNull()
versionCode = codeOverride ?: 1
versionName = nameOverride ?: "dev"
@@ -215,7 +215,10 @@ android {
// Hardcoded, and NOT a secret: the alias is fixed for the life of
// this app and is written into the certificate every install
// already carries. Hiding it would buy nothing and stop this file
// describing its own signing setup.
// describing its own signing setup. It still says `thoughtsync`
// after the rename to Inkwell, because it names the key inside the
// existing keystore, and renaming it would only stop that key being
// found. Same key, so the signing certificate did not change either.
keyAlias = "thoughtsync"
// PKCS12 cannot hold a key password distinct from the store
// password — keytool refuses to set one — so this is the same
+1 -1
View File
@@ -4,4 +4,4 @@
# is the worst possible time to learn it.
-keep class com.sun.jna.** { *; }
-keepclassmembers class * extends com.sun.jna.** { public *; }
-keep class com.fabledsword.thoughtsync.core.** { *; }
-keep class com.fabledsword.inkwell.core.** { *; }
+3 -3
View File
@@ -91,13 +91,13 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".ThoughtSyncApplication"
android:name=".InkwellApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ThoughtSync"
android:theme="@style/Theme.Inkwell"
android:usesCleartextTraffic="true">
<!--
launchMode="singleTop" exists for the SHARE filters below.
@@ -115,7 +115,7 @@
android:exported="true"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.ThoughtSync">
android:theme="@style/Theme.Inkwell">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.content.Context
import android.content.Intent
@@ -42,7 +42,7 @@ import java.io.File
* [UpdateReceiver], which is why a failure can be shown rather than guessed at.
*/
object AppUpdate {
private const val TAG = "ThoughtSyncUpdate"
private const val TAG = "InkwellUpdate"
/** This build's versionCode — what the server's is compared against. */
fun installedVersionCode(context: Context): Long =
@@ -173,5 +173,5 @@ object AppUpdate {
.intentSender
}
private const val WRITE_NAME = "thoughtsync-update"
private const val WRITE_NAME = "inkwell-update"
}
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.content.Context
@@ -1,9 +1,9 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.app.Application
import android.util.Log
import com.fabledsword.thoughtsync.core.ThoughtSync
import com.fabledsword.thoughtsync.core.setClientAgent
import com.fabledsword.inkwell.core.Inkwell
import com.fabledsword.inkwell.core.setClientAgent
/**
* Opens the shared Rust core once, for the process lifetime.
@@ -16,14 +16,14 @@ import com.fabledsword.thoughtsync.core.setClientAgent
* removed on uninstall, and never on external media. The core does not guess at
* platform paths; Android is the only thing that knows where this is.
*/
class ThoughtSyncApplication : Application() {
class InkwellApplication : Application() {
/**
* Null only if the store could not be opened — a corrupt or unwritable
* database. The UI reports that honestly rather than crashing on first
* touch, because a user whose notes won't open needs a message, not a
* stack trace.
*/
var core: ThoughtSync? = null
var core: Inkwell? = null
private set
var openFailure: String? = null
@@ -34,13 +34,13 @@ class ThoughtSyncApplication : Application() {
// Introduce this app to any server it links to, before anything can sync.
// The core cannot name us — the same crate is compiled into the desktop app,
// and it used to announce every phone as `thoughtsync-desktop` carrying the
// and it used to announce every phone as `inkwell-desktop` carrying the
// core crate's own version. "unknown" rather than a guess when the package
// manager will not say (note 3127 §5).
setClientAgent("thoughtsync-android", installedVersionName() ?: "unknown")
setClientAgent("inkwell-android", installedVersionName() ?: "unknown")
try {
val handle = ThoughtSync(filesDir.absolutePath)
val handle = Inkwell(filesDir.absolutePath)
core = handle
Log.i(TAG, "local store ready — ${handle.summary()}")
} catch (e: Exception) {
@@ -53,6 +53,6 @@ class ThoughtSyncApplication : Application() {
}
private companion object {
const val TAG = "ThoughtSync"
const val TAG = "Inkwell"
}
}
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.Manifest
import android.content.Intent
@@ -22,22 +22,22 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import com.fabledsword.thoughtsync.core.ThoughtSync
import com.fabledsword.thoughtsync.ui.BoardScreen
import com.fabledsword.thoughtsync.ui.BoardSync
import com.fabledsword.thoughtsync.ui.BoardUpdate
import com.fabledsword.thoughtsync.ui.BoardViewModel
import com.fabledsword.thoughtsync.ui.ForegroundTransitions
import com.fabledsword.thoughtsync.ui.NoteEditorScreen
import com.fabledsword.thoughtsync.ui.StoreUnavailableScreen
import com.fabledsword.thoughtsync.ui.SyncScreen
import com.fabledsword.thoughtsync.ui.SyncState
import com.fabledsword.thoughtsync.ui.SyncViewModel
import com.fabledsword.thoughtsync.ui.TagsScreen
import com.fabledsword.thoughtsync.ui.TagsViewModel
import com.fabledsword.thoughtsync.ui.ThoughtSyncTheme
import com.fabledsword.thoughtsync.ui.UpdateViewModel
import com.fabledsword.thoughtsync.ui.olderThan
import com.fabledsword.inkwell.core.Inkwell
import com.fabledsword.inkwell.ui.BoardScreen
import com.fabledsword.inkwell.ui.BoardSync
import com.fabledsword.inkwell.ui.BoardUpdate
import com.fabledsword.inkwell.ui.BoardViewModel
import com.fabledsword.inkwell.ui.ForegroundTransitions
import com.fabledsword.inkwell.ui.InkwellTheme
import com.fabledsword.inkwell.ui.NoteEditorScreen
import com.fabledsword.inkwell.ui.StoreUnavailableScreen
import com.fabledsword.inkwell.ui.SyncScreen
import com.fabledsword.inkwell.ui.SyncState
import com.fabledsword.inkwell.ui.SyncViewModel
import com.fabledsword.inkwell.ui.TagsScreen
import com.fabledsword.inkwell.ui.TagsViewModel
import com.fabledsword.inkwell.ui.UpdateViewModel
import com.fabledsword.inkwell.ui.olderThan
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -68,12 +68,12 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val app = application as ThoughtSyncApplication
val app = application as InkwellApplication
requestedNote.value = takeRequestedNote(intent)
sharedText.value = takeSharedText(intent)
setContent {
ThoughtSyncTheme {
InkwellTheme {
val core = app.core
if (core == null) {
// The store never opened. There is no board to show and no
@@ -174,7 +174,7 @@ private enum class Screen { BOARD, EDITOR, SYNC, TAGS }
*/
@Composable
private fun App(
core: ThoughtSync,
core: Inkwell,
requestedNote: MutableState<String?>,
sharedText: MutableState<String?>,
) {
@@ -360,7 +360,7 @@ private fun App(
* notifications this app would send — is spending it on nothing.
*/
@Composable
private fun ReminderAlarms(core: ThoughtSync) {
private fun ReminderAlarms(core: Inkwell) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
// Off the main thread: this reads every note that has a reminder, and a phone
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.app.NotificationChannel
import android.app.NotificationManager
@@ -8,7 +8,7 @@ import android.content.Intent
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.inkwell.core.Note
/**
* What a due reminder looks like in the shade.
@@ -117,5 +117,5 @@ internal object ReminderNotification {
)
private const val CHANNEL = "reminders"
private const val TAG = "ThoughtSyncReminders"
private const val TAG = "InkwellReminders"
}
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.content.BroadcastReceiver
import android.content.Context
@@ -36,7 +36,7 @@ class ReminderReceiver : BroadcastReceiver() {
context: Context,
intent: Intent,
) {
val core = (context.applicationContext as? ThoughtSyncApplication)?.core ?: return
val core = (context.applicationContext as? InkwellApplication)?.core ?: return
val action = intent.action
val noteId = intent.getStringExtra(Reminders.EXTRA_NOTE_ID)
val app = context.applicationContext
@@ -64,9 +64,9 @@ class ReminderReceiver : BroadcastReceiver() {
}
companion object {
const val ACTION_DUE = "com.fabledsword.thoughtsync.REMINDER_DUE"
const val ACTION_DONE = "com.fabledsword.thoughtsync.REMINDER_DONE"
const val ACTION_SNOOZE = "com.fabledsword.thoughtsync.REMINDER_SNOOZE"
private const val TAG = "ThoughtSyncReminders"
const val ACTION_DUE = "com.fabledsword.inkwell.REMINDER_DUE"
const val ACTION_DONE = "com.fabledsword.inkwell.REMINDER_DONE"
const val ACTION_SNOOZE = "com.fabledsword.inkwell.REMINDER_SNOOZE"
private const val TAG = "InkwellReminders"
}
}
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.app.AlarmManager
import android.app.PendingIntent
@@ -7,8 +7,8 @@ import android.content.Intent
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationManagerCompat
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.thoughtsync.core.ThoughtSync
import com.fabledsword.inkwell.core.Inkwell
import com.fabledsword.inkwell.core.Note
import java.time.OffsetDateTime
/**
@@ -52,7 +52,7 @@ object Reminders {
private const val MISSED_WINDOW_MS = 24L * 60 * 60 * 1000
private const val SNOOZE_MINUTES = 60L
private const val TAG = "ThoughtSyncReminders"
private const val TAG = "InkwellReminders"
/**
* Announce what is due, then schedule the next one.
@@ -63,7 +63,7 @@ object Reminders {
*/
fun refresh(
context: Context,
core: ThoughtSync,
core: Inkwell,
) {
ReminderNotification.ensureChannel(context)
val notes =
@@ -111,7 +111,7 @@ object Reminders {
*/
fun promptToNotifyDue(
context: Context,
core: ThoughtSync,
core: Inkwell,
): Boolean =
!Announced(context).askedToNotify &&
!NotificationManagerCompat.from(context).areNotificationsEnabled() &&
@@ -123,7 +123,7 @@ object Reminders {
/** Clear the reminder, as the notification's Done action. */
fun complete(
context: Context,
core: ThoughtSync,
core: Inkwell,
noteId: String,
) {
runCatching { core.completeReminder(noteId) }
@@ -134,7 +134,7 @@ object Reminders {
/** Push the reminder an hour out, as the notification's Snooze action. */
fun snooze(
context: Context,
core: ThoughtSync,
core: Inkwell,
noteId: String,
) {
runCatching { core.snoozeReminder(noteId, SNOOZE_MINUTES) }
@@ -237,7 +237,7 @@ private class Announced(
fun markAsked() = prefs.edit().putBoolean(KEY_ASKED, true).apply()
private companion object {
const val FILE = "thoughtsync-reminders"
const val FILE = "inkwell-reminders"
const val KEY_SEEN = "announced"
const val KEY_PRIMED = "primed"
const val KEY_ASKED = "asked_to_notify"
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.content.Context
import androidx.work.BackoffPolicy
@@ -85,8 +85,8 @@ object SyncSchedule {
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
private const val PERIODIC = "thoughtsync-periodic-sync"
private const val PUSH = "thoughtsync-push-pending"
private const val PERIODIC = "inkwell-periodic-sync"
private const val PUSH = "inkwell-push-pending"
/** WorkManager's own minimum for periodic work. Asking for less gets this. */
private const val PERIOD_MINUTES = 15L
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.content.Context
@@ -38,7 +38,7 @@ class SyncSettings(
}
private companion object {
const val FILE = "thoughtsync-sync"
const val FILE = "inkwell-sync"
const val KEY_AUTOMATIC = "automatic"
}
}
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.content.Context
import android.util.Log
@@ -8,7 +8,7 @@ import androidx.work.WorkerParameters
/**
* One sync cycle, run by the system rather than by a person.
*
* WorkManager may start the process to run this, which means [ThoughtSyncApplication.onCreate]
* WorkManager may start the process to run this, which means [InkwellApplication.onCreate]
* has already opened the store by the time [doWork] is called — the same handle
* the UI uses, so there is never a second SQLite connection racing the first.
*
@@ -30,7 +30,7 @@ class SyncWorker(
params: WorkerParameters,
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val core = (applicationContext as? ThoughtSyncApplication)?.core
val core = (applicationContext as? InkwellApplication)?.core
// Both of these are "nothing to do", not "something went wrong", so both
// report success and let the run retire quietly:
@@ -67,6 +67,6 @@ class SyncWorker(
}
private companion object {
const val TAG = "ThoughtSyncWorker"
const val TAG = "InkwellWorker"
}
}
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync
package com.fabledsword.inkwell
import android.content.BroadcastReceiver
import android.content.Context
@@ -71,7 +71,7 @@ class UpdateReceiver : BroadcastReceiver() {
}
companion object {
const val ACTION_INSTALLED = "com.fabledsword.thoughtsync.UPDATE_INSTALLED"
private const val TAG = "ThoughtSyncUpdate"
const val ACTION_INSTALLED = "com.fabledsword.inkwell.UPDATE_INSTALLED"
private const val TAG = "InkwellUpdate"
}
}
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
@@ -33,7 +33,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.inkwell.R
/**
* The note's body, as fields and checkboxes rather than as markup.
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -63,9 +63,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.core.Label
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.core.Label
import com.fabledsword.inkwell.core.Note
import kotlinx.coroutines.launch
@Composable
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -6,12 +6,12 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.fabledsword.thoughtsync.core.Label
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.thoughtsync.core.NoteDraft
import com.fabledsword.thoughtsync.core.NoteEdit
import com.fabledsword.thoughtsync.core.NoteQuery
import com.fabledsword.thoughtsync.core.ThoughtSync
import com.fabledsword.inkwell.core.Inkwell
import com.fabledsword.inkwell.core.Label
import com.fabledsword.inkwell.core.Note
import com.fabledsword.inkwell.core.NoteDraft
import com.fabledsword.inkwell.core.NoteEdit
import com.fabledsword.inkwell.core.NoteQuery
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@@ -99,7 +99,7 @@ data class BoardState(
*/
@Suppress("TooManyFunctions")
class BoardViewModel(
private val core: ThoughtSync,
private val core: Inkwell,
/**
* Called after any write that could have moved a reminder.
*
@@ -482,7 +482,7 @@ class BoardViewModel(
*/
private fun mutate(
closeEditor: Boolean = false,
block: (ThoughtSync) -> Note?,
block: (Inkwell) -> Note?,
) {
viewModelScope.launch {
state = state.copy(saving = true)
@@ -547,7 +547,7 @@ class BoardViewModel(
private const val VIEW_TRASH = "trash"
fun factory(
core: ThoughtSync,
core: Inkwell,
onRemindersChanged: () -> Unit,
): ViewModelProvider.Factory =
object : ViewModelProvider.Factory {
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
// The colour a LABEL wears when nobody picked one for it.
//
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
/**
* Everything the editor can ask for, as one type.
@@ -1,10 +1,10 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.runtime.saveable.Saver
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import com.fabledsword.thoughtsync.core.checklistItems
import com.fabledsword.thoughtsync.core.checklistRender
import com.fabledsword.inkwell.core.checklistItems
import com.fabledsword.inkwell.core.checklistRender
/**
* One piece of a note body, as the editor DRAWS it.
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import android.text.format.DateUtils
import androidx.compose.foundation.background
@@ -41,8 +41,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.core.Note
/**
* The editor's action bar, along the top of the surface.
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -36,9 +36,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.core.Label
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.core.Label
import com.fabledsword.inkwell.core.Note
import java.time.DayOfWeek
import java.time.Instant
import java.time.LocalDate
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.inkwell.R
// Shared by the board and the editor.
//
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Column
@@ -12,8 +12,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.core.LinkPreview
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.inkwell.core.LinkPreview
import com.fabledsword.inkwell.core.Note
private val PREVIEW_RADIUS = 8.dp
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -38,12 +38,12 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.core.BodyItem
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.thoughtsync.core.NoteLabel
import com.fabledsword.thoughtsync.core.bodyTags
import com.fabledsword.thoughtsync.core.checklistItems
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.core.BodyItem
import com.fabledsword.inkwell.core.Note
import com.fabledsword.inkwell.core.NoteLabel
import com.fabledsword.inkwell.core.bodyTags
import com.fabledsword.inkwell.core.checklistItems
@Composable
fun NoteCard(
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.isSystemInDarkTheme
@@ -25,8 +25,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.core.Label
import com.fabledsword.thoughtsync.core.Note
import com.fabledsword.inkwell.core.Label
import com.fabledsword.inkwell.core.Note
import kotlinx.coroutines.delay
/**
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.annotation.StringRes
import androidx.compose.foundation.background
@@ -20,7 +20,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.inkwell.R
/**
* A bordered block, tinted from the same table the notes use.
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxWidth
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import android.app.AlarmManager
import android.content.Context
@@ -17,8 +17,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.app.NotificationManagerCompat
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.Reminders
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.Reminders
/**
* Says so when a reminder would not actually reach anyone.
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import android.os.Build
import androidx.compose.foundation.layout.Arrangement
@@ -27,9 +27,9 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.core.Compatibility
import com.fabledsword.thoughtsync.core.RevokeOutcome
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.core.Compatibility
import com.fabledsword.inkwell.core.RevokeOutcome
// Becoming linked: the probe-then-sign-in flow, and the notices around it.
//
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -36,15 +36,15 @@ import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.UpdateOutcome
import com.fabledsword.thoughtsync.installedVersionName
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.UpdateOutcome
import com.fabledsword.inkwell.installedVersionName
/**
* Opt-in server pairing.
*
* The whole screen is written around one idea: **being unlinked is not a
* problem.** ThoughtSync is local-first and completely usable having never opened
* problem.** Inkwell is local-first and completely usable having never opened
* this screen, so the unlinked state leads with "Working offline on this device"
* and explains what connecting would ADD, rather than presenting an empty form as
* unfinished setup.
@@ -1,11 +1,11 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.core.Compatibility
import com.fabledsword.thoughtsync.core.SyncOutcome
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.core.Compatibility
import com.fabledsword.inkwell.core.SyncOutcome
// Turning sync results into sentences.
//
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -6,12 +6,12 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.fabledsword.thoughtsync.core.Compatibility
import com.fabledsword.thoughtsync.core.ProbeResult
import com.fabledsword.thoughtsync.core.RevokeOutcome
import com.fabledsword.thoughtsync.core.SyncOutcome
import com.fabledsword.thoughtsync.core.SyncStatus
import com.fabledsword.thoughtsync.core.ThoughtSync
import com.fabledsword.inkwell.core.Compatibility
import com.fabledsword.inkwell.core.Inkwell
import com.fabledsword.inkwell.core.ProbeResult
import com.fabledsword.inkwell.core.RevokeOutcome
import com.fabledsword.inkwell.core.SyncOutcome
import com.fabledsword.inkwell.core.SyncStatus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -113,7 +113,7 @@ data class SyncState(
* stored cursor next time (Scribe #2736).
*/
class SyncViewModel(
private val core: ThoughtSync,
private val core: Inkwell,
/**
* Called after a sync that changed the store.
*
@@ -316,7 +316,7 @@ class SyncViewModel(
companion object {
fun factory(
core: ThoughtSync,
core: Inkwell,
onStoreChanged: () -> Unit,
): ViewModelProvider.Factory =
object : ViewModelProvider.Factory {
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -43,8 +43,8 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.core.Label
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.core.Label
/** The swatch shown beside a tag, and tapped to change its colour. */
private val SWATCH = 22.dp
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -6,8 +6,8 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.fabledsword.thoughtsync.core.Label
import com.fabledsword.thoughtsync.core.ThoughtSync
import com.fabledsword.inkwell.core.Inkwell
import com.fabledsword.inkwell.core.Label
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -57,7 +57,7 @@ data class TagsState(
* the core marks the rows dirty and the next sync carries them.
*/
class TagsViewModel(
private val core: ThoughtSync,
private val core: Inkwell,
/**
* Called after any write that landed.
*
@@ -140,7 +140,7 @@ class TagsViewModel(
* `busy` is cleared in the same assignment that stores the result, so no path
* out of here can leave the screen stuck with its controls disabled.
*/
private fun write(block: (ThoughtSync) -> Unit) {
private fun write(block: (Inkwell) -> Unit) {
if (state.busy) return
state = state.copy(busy = true, error = null)
viewModelScope.launch {
@@ -164,7 +164,7 @@ class TagsViewModel(
companion object {
fun factory(
core: ThoughtSync,
core: Inkwell,
onStoreChanged: () -> Unit,
): ViewModelProvider.Factory =
object : ViewModelProvider.Factory {
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
@@ -59,7 +59,7 @@ private val DarkColors =
)
/**
* Material 3 in ThoughtSync's own colours, following the system light/dark setting.
* Material 3 in Inkwell's own colours, following the system light/dark setting.
*
* DELIBERATELY NOT Material You dynamic colour, which this used until the operator
* saw the first build. Dynamic colour is the more Android-native choice and it
@@ -72,7 +72,7 @@ private val DarkColors =
* If dynamic colour is ever wanted it belongs behind a setting, not as the default.
*/
@Composable
fun ThoughtSyncTheme(
fun InkwellTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import java.time.LocalDateTime
import java.time.OffsetDateTime
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -25,9 +25,9 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.fabledsword.thoughtsync.AppUpdate
import com.fabledsword.thoughtsync.R
import com.fabledsword.thoughtsync.UpdateOutcome
import com.fabledsword.inkwell.AppUpdate
import com.fabledsword.inkwell.R
import com.fabledsword.inkwell.UpdateOutcome
/**
* Updating the app from the server it is linked to.
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import android.content.Context
import androidx.compose.runtime.getValue
@@ -7,10 +7,10 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.fabledsword.thoughtsync.AppUpdate
import com.fabledsword.thoughtsync.UpdateOutcome
import com.fabledsword.thoughtsync.core.ClientUpdate
import com.fabledsword.thoughtsync.core.ThoughtSync
import com.fabledsword.inkwell.AppUpdate
import com.fabledsword.inkwell.UpdateOutcome
import com.fabledsword.inkwell.core.ClientUpdate
import com.fabledsword.inkwell.core.Inkwell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -61,7 +61,7 @@ data class UpdateState(
* secret this app holds across two languages for no gain.
*/
class UpdateViewModel(
private val core: ThoughtSync,
private val core: Inkwell,
/**
* MUST be the application context — it outlives this view model, and holding an
* Activity here is the textbook way to leak a window.
@@ -207,7 +207,7 @@ class UpdateViewModel(
companion object {
fun factory(
core: ThoughtSync,
core: Inkwell,
context: Context,
): ViewModelProvider.Factory =
object : ViewModelProvider.Factory {
+1 -1
View File
@@ -2,6 +2,6 @@
<resources>
<!-- The product's brand colour, same value the web app's manifest and
<meta name="theme-color"> already use. One source of truth for "what
colour is ThoughtSync" across the three surfaces. -->
colour is Inkwell" across the three surfaces. -->
<color name="ic_launcher_background">#F5C518</color>
</resources>
+1 -1
View File
@@ -6,5 +6,5 @@
truth in XML — the same reason the desktop reads its live theme rather
than hardcoding a window colour.
-->
<style name="Theme.ThoughtSync" parent="android:Theme.Material.NoActionBar" />
<style name="Theme.Inkwell" parent="android:Theme.Material.NoActionBar" />
</resources>
@@ -1,4 +1,4 @@
package com.fabledsword.thoughtsync.ui
package com.fabledsword.inkwell.ui
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
+1 -1
View File
@@ -74,7 +74,7 @@ exceptions:
# right and still applies.
excludes:
- "**/ui/**"
- "**/ThoughtSyncApplication.kt"
- "**/InkwellApplication.kt"
- "**/SyncWorker.kt"
- "**/ReminderReceiver.kt"
- "**/AppUpdate.kt"
+19 -19
View File
@@ -7,7 +7,7 @@
//!
//! ## Shape
//!
//! One `ThoughtSync` object holds the store and the blob directory, mirroring how
//! One `Inkwell` object holds the store and the blob directory, mirroring how
//! Tauri manages them as app state. Kotlin constructs it once, keeps it for the
//! process lifetime, and calls methods on it.
//!
@@ -108,13 +108,13 @@ impl CoreError {
/// behind its mutex, the blob store being a path — which is what lets uniffi share
/// one instance across coroutines.
#[derive(uniffi::Object)]
pub struct ThoughtSync {
pub struct Inkwell {
db: Db,
blobs: BlobStore,
}
#[uniffi::export]
impl ThoughtSync {
impl Inkwell {
/// Open (creating on first run) the store under `data_dir`, and the attachment
/// directory beside it.
///
@@ -131,7 +131,7 @@ impl ThoughtSync {
log::info!("local store ready — {}", local::summary(&db));
let blobs = BlobStore::new(dir.join("blobs")).map_err(CoreError::store)?;
Ok(Arc::new(ThoughtSync { db, blobs }))
Ok(Arc::new(Inkwell { db, blobs }))
}
/// A one-line count summary, for the boot log.
@@ -413,7 +413,7 @@ impl ThoughtSync {
/// functions. Split into its own impl block so the runtime attribute — and the fact
/// that everything in here touches the network — is visible at a glance.
#[uniffi::export(async_runtime = "tokio")]
impl ThoughtSync {
impl Inkwell {
/// Ask a server who it is, without committing to anything. Called as the user
/// finishes typing an address, so they see what answered before handing over
/// credentials.
@@ -525,7 +525,7 @@ impl ThoughtSync {
///
/// Takes the destination rather than choosing one: only Android knows a
/// directory its own package installer can read from, and the core has no
/// business guessing at platform paths — the same reason `ThoughtSync::new`
/// business guessing at platform paths — the same reason `Inkwell::new`
/// takes a data dir.
pub async fn download_client_update(&self, dest_path: String) -> Result<(), CoreError> {
let (base_url, token) = self.credentials()?;
@@ -587,7 +587,7 @@ pub fn checklist_render(text: String, checked: bool) -> String {
/// stamps and nobody has seen. The honest value is the installed package's own
/// `versionName`, which is what Kotlin passes here.
///
/// Called once from `ThoughtSyncApplication.onCreate`, before anything can sync.
/// Called once from `InkwellApplication.onCreate`, before anything can sync.
#[uniffi::export]
pub fn set_client_agent(name: String, version: String) {
compat::set_client_agent(&name, &version);
@@ -620,7 +620,7 @@ pub fn body_tags(body: String) -> Vec<BodyTag> {
/// Helpers, deliberately NOT exported — uniffi only binds what an `#[uniffi::export]`
/// block names, so these stay Rust-side.
impl ThoughtSync {
impl Inkwell {
/// Apply a `{text}` or `{checked}` patch to one checklist item.
///
/// The two public setters differ only in the key they write, and the lock +
@@ -701,7 +701,7 @@ mod tests {
#[test]
fn creates_a_store_and_round_trips_a_note() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let created = app
.create_note(draft("Groceries\nmilk"))
@@ -723,7 +723,7 @@ mod tests {
#[test]
fn a_note_is_named_by_its_first_line() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let created = app
.create_note(draft("just a thought"))
@@ -738,7 +738,7 @@ mod tests {
#[test]
fn a_note_with_only_items_is_named_by_its_first_item() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let created = app
.create_note(NoteDraft {
@@ -757,7 +757,7 @@ mod tests {
#[test]
fn syncing_unlinked_reports_not_linked() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let status = app.sync_status().expect("status should read");
assert!(!status.linked);
@@ -774,7 +774,7 @@ mod tests {
#[test]
fn checklist_items_can_be_added_ticked_retitled_and_removed() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let note = app
.create_note(NoteDraft {
body: "Packing".to_string(),
@@ -828,7 +828,7 @@ mod tests {
#[test]
fn setting_labels_leaves_tag_derived_ones_alone() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let note = app
.create_note(draft("Trip\nbook the ferry #travel"))
@@ -869,7 +869,7 @@ mod tests {
#[test]
fn deleting_forever_removes_the_note() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let note = app.create_note(draft("Ephemeral\nbody")).expect("create");
app.delete_note_forever(note.id.clone())
@@ -886,7 +886,7 @@ mod tests {
#[test]
fn reminders_can_be_snoozed_and_completed() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let note = app.create_note(draft("Call back")).expect("create");
assert_eq!(note.remind_at, None);
@@ -912,7 +912,7 @@ mod tests {
#[test]
fn completing_a_recurring_reminder_moves_it_rather_than_ending_it() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let note = app.create_note(draft("Water the plants")).expect("create");
let armed = app
@@ -974,7 +974,7 @@ mod tests {
#[test]
fn renaming_onto_an_existing_tag_merges_into_the_older_one() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let older = app.create_label("grocery".to_string()).expect("older");
// `created_at` is RFC3339 to the MILLISECOND. Without a gap the two rows can
@@ -1019,7 +1019,7 @@ mod tests {
#[test]
fn the_rename_merge_survivor_does_not_depend_on_the_direction() {
let dir = scratch_dir();
let app = ThoughtSync::new(dir.clone()).expect("a fresh data dir should open");
let app = Inkwell::new(dir.clone()).expect("a fresh data dir should open");
let older = app.create_label("grocery".to_string()).expect("older");
std::thread::sleep(std::time::Duration::from_millis(5));
+2 -2
View File
@@ -1,5 +1,5 @@
# Where the generated Kotlin lands. Matches the app's package so the bindings are
# `com.fabledsword.thoughtsync.core.*` rather than something the app has to alias.
# `com.fabledsword.inkwell.core.*` rather than something the app has to alias.
[bindings.kotlin]
package_name = "com.fabledsword.thoughtsync.core"
package_name = "com.fabledsword.inkwell.core"
cdylib_name = "inkwell_ffi"
+1 -1
View File
@@ -18,7 +18,7 @@ dependencyResolutionManagement {
mavenCentral()
}
}
rootProject.name = "ThoughtSync"
rootProject.name = "Inkwell"
// `ffi/` sits beside `app/` but is deliberately NOT a Gradle module: it is a Rust
// crate belonging to the Cargo workspace at the repo root. Gradle reaches it by
+2 -2
View File
@@ -72,8 +72,8 @@ ASSETS=(
"$BUNDLE_ROOT"/arch/*.pkg.tar.*
"$WIN_BUNDLE_ROOT"/nsis/*.exe
"$WIN_BUNDLE_ROOT"/nsis/*.exe.sig
"$REPO_ROOT"/android/dist/thoughtsync.apk
"$REPO_ROOT"/android/dist/thoughtsync-android.json
"$REPO_ROOT"/android/dist/inkwell.apk
"$REPO_ROOT"/android/dist/inkwell-android.json
)
# nullglob drops PATTERNS that match nothing — it does nothing for a path with no
# wildcard in it, which stays in the array as a literal and reaches curl as a file
+1 -1
View File
@@ -229,7 +229,7 @@ if [ "${PRUNE_OLD_ASSETS:-false}" = "true" ]; then
# every desktop push regardless. That is exactly what happened on run
# 4098, which swept the APK run 4092 had just published.
case "$asset_name" in
latest.json|inkwell-desktop.json|thoughtsync.apk|thoughtsync-android.json) continue ;;
latest.json|inkwell-desktop.json|inkwell.apk|inkwell-android.json) continue ;;
*"$APP_VERSION"*) continue ;;
esac
echo " removing $asset_name"
+2 -2
View File
@@ -88,8 +88,8 @@ echo "==> Collecting the $channel clients"
# Its sidecar is published whole by the Android lane — an APK keeps its version in
# a binary AXML manifest, so the values are recorded where they were already known.
# Copied verbatim rather than rebuilt here.
if fetch "$BASE/thoughtsync.apk" "$dest/inkwell.apk" &&
fetch "$BASE/thoughtsync-android.json" "$dest/inkwell-android.json"; then
if fetch "$BASE/inkwell.apk" "$dest/inkwell.apk" &&
fetch "$BASE/inkwell-android.json" "$dest/inkwell-android.json"; then
echo " android $(field "$dest/inkwell-android.json" version_name)"
else
# Both or neither. Half a pair is worse than none: the server would read a
+2 -2
View File
@@ -110,7 +110,7 @@ published_for() {
| sed -E 's/.*"([^"]+)"$/\1/'; } || true
;;
android)
{ fetch "$SERVER/$REPO/releases/download/$_tag/thoughtsync-android.json" \
{ fetch "$SERVER/$REPO/releases/download/$_tag/inkwell-android.json" \
| grep -oE '"version_code"[[:space:]]*:[[:space:]]*[0-9]+' | head -1 \
| grep -oE '[0-9]+$'; } || true
;;
@@ -126,7 +126,7 @@ published_name() {
case "$1" in
desktop) published_for desktop "$2" ;;
android)
{ fetch "$SERVER/$REPO/releases/download/$_tag/thoughtsync-android.json" \
{ fetch "$SERVER/$REPO/releases/download/$_tag/inkwell-android.json" \
| grep -oE '"version_name"[[:space:]]*:[[:space:]]*"[^"]+"' | head -1 \
| sed -E 's/.*"([^"]+)"$/\1/'; } || true
;;