@@ -10,6 +10,7 @@ import androidx.activity.viewModels
1010import androidx.appcompat.app.ActionBarDrawerToggle
1111import androidx.appcompat.app.AppCompatActivity
1212import androidx.appcompat.widget.SearchView
13+ import androidx.lifecycle.asLiveData
1314import androidx.lifecycle.lifecycleScope
1415import androidx.recyclerview.widget.ItemTouchHelper
1516import androidx.recyclerview.widget.RecyclerView
@@ -135,12 +136,20 @@ class MainActivity : AppCompatActivity() {
135136 showingArchived -> {
136137 vm.toggleArchive(nwt.note)
137138 Snackbar .make(binding.root, " Restored to notes" , Snackbar .LENGTH_LONG )
138- .setAction(" Undo" ) { vm.toggleArchive(nwt.note) }.show()
139+ .setAction(" Undo" ) {
140+ lifecycleScope.launch {
141+ vm.repo.archive(nwt.note)
142+ }
143+ }.show()
139144 }
140145 else -> {
141146 vm.trash(nwt.note)
142147 Snackbar .make(binding.root, " Moved to trash" , Snackbar .LENGTH_LONG )
143- .setAction(" Undo" ) { lifecycleScope.launch { vm.trash(nwt.note) } }.show()
148+ .setAction(" Undo" ) {
149+ lifecycleScope.launch {
150+ vm.repo.restoreFromTrash(nwt.note)
151+ }
152+ }.show()
144153 }
145154 }
146155 }
@@ -170,16 +179,14 @@ class MainActivity : AppCompatActivity() {
170179 }
171180
172181 private fun loadTrashedNotes () {
173- lifecycleScope.launch {
174- vm.repo.trashedNotes.collect { list ->
175- adapter.submitList(list)
176- binding.layoutEmpty.visibility = if (list.isEmpty()) View .VISIBLE else View .GONE
177- binding.recyclerView.visibility = if (list.isEmpty()) View .GONE else View .VISIBLE
178- supportActionBar?.subtitle = " ${list.size} trashed note${if (list.size != 1 ) " s" else " " } "
179- binding.emptyIcon.text = " "
180- binding.emptyTitle.text = " Trash is empty"
181- binding.emptySubtitle.text = " Deleted notes appear here for 30 days"
182- }
182+ vm.repo.trashedNotes.asLiveData().observe(this ) { list ->
183+ adapter.submitList(list)
184+ binding.layoutEmpty.visibility = if (list.isEmpty()) View .VISIBLE else View .GONE
185+ binding.recyclerView.visibility = if (list.isEmpty()) View .GONE else View .VISIBLE
186+ supportActionBar?.subtitle = " ${list.size} trashed note${if (list.size != 1 ) " s" else " " } "
187+ binding.emptyIcon.text = " "
188+ binding.emptyTitle.text = " Trash is empty"
189+ binding.emptySubtitle.text = " Deleted notes appear here for 30 days"
183190 }
184191 }
185192
@@ -264,20 +271,23 @@ class MainActivity : AppCompatActivity() {
264271 if (showingTrashed) opts.add(" Restore" )
265272 opts.add(" Delete" )
266273 opts.add(" Share" )
274+ val restoreIdx = opts.indexOf(" Restore" )
275+ val deleteIdx = opts.indexOf(" Delete" )
276+ val shareIdx = opts.indexOf(" Share" )
267277 MaterialAlertDialogBuilder (this , R .style.KitabuDialog )
268278 .setItems(opts.toTypedArray()) { _, i ->
269279 when {
270- showingTrashed && i == opts.size - 3 - > { /* restore */
271- lifecycleScope.launch { vm.trash (note) }
280+ restoreIdx != - 1 && i == restoreIdx - > {
281+ lifecycleScope.launch { vm.repo.restoreFromTrash (note) }
272282 }
273283 i == 0 -> vm.togglePin(note)
274284 i == 1 -> vm.toggleFavorite(note)
275285 i == 2 -> vm.toggleLock(note)
276286 i == 3 && ! showingTrashed -> {
277- if (showingArchived) vm.toggleArchive(note) else vm.toggleArchive(note)
287+ vm.toggleArchive(note)
278288 }
279- i == opts.size - 2 -> confirmDelete(note)
280- i == opts.size - 1 -> shareNote(nwt)
289+ i == deleteIdx -> confirmDelete(note)
290+ i == shareIdx -> shareNote(nwt)
281291 }
282292 }.show()
283293 }
@@ -333,9 +343,59 @@ class MainActivity : AppCompatActivity() {
333343 val content = contentResolver.openInputStream(uri)?.use { stream ->
334344 BufferedReader (InputStreamReader (stream)).readText()
335345 } ? : return @launch
336- Snackbar .make(binding.root, " Import started..." , Snackbar .LENGTH_SHORT ).show()
337- // Basic import - show the JSON for now
338- Snackbar .make(binding.root, " Import: ${content.take(100 )} " , Snackbar .LENGTH_LONG ).show()
346+ val json = org.json.JSONObject (content)
347+ val notesArr = json.optJSONArray(" notes" ) ? : org.json.JSONArray ()
348+ val tagsArr = json.optJSONArray(" tags" ) ? : org.json.JSONArray ()
349+ val noteTagsArr = json.optJSONArray(" noteTags" ) ? : org.json.JSONArray ()
350+
351+ // Import tags first (build oldId -> newId map)
352+ val tagIdMap = mutableMapOf<Int , Int >()
353+ for (i in 0 until tagsArr.length()) {
354+ val t = tagsArr.getJSONObject(i)
355+ val oldId = t.getInt(" id" )
356+ val name = t.getString(" name" )
357+ val newTag = vm.getOrCreateTag(name)
358+ tagIdMap[oldId] = newTag.id
359+ }
360+
361+ // Import notes
362+ val noteIdMap = mutableMapOf<Int , Int >()
363+ var imported = 0
364+ for (i in 0 until notesArr.length()) {
365+ val n = notesArr.getJSONObject(i)
366+ val oldId = n.getInt(" id" )
367+ val note = Note (
368+ title = n.optString(" title" , " " ),
369+ content = n.optString(" content" , " " ),
370+ color = n.optInt(" color" , 0 ),
371+ isPinned = n.optBoolean(" isPinned" , false ),
372+ isLocked = n.optBoolean(" isLocked" , false ),
373+ isArchived = n.optBoolean(" isArchived" , false ),
374+ isFavorite = n.optBoolean(" isFavorite" , false ),
375+ isDaily = n.optBoolean(" isDaily" , false ),
376+ dailyDate = n.optString(" dailyDate" , null ),
377+ reminderTime = if (n.isNull(" reminderTime" )) null else n.optLong(" reminderTime" ),
378+ createdAt = n.optLong(" createdAt" , System .currentTimeMillis()),
379+ updatedAt = n.optLong(" updatedAt" , System .currentTimeMillis())
380+ )
381+ val newId = vm.repo.insert(note)
382+ noteIdMap[oldId] = newId.toInt()
383+ imported++
384+ }
385+
386+ // Import note-tag associations
387+ for (i in 0 until noteTagsArr.length()) {
388+ val nt = noteTagsArr.getJSONObject(i)
389+ val oldNoteId = nt.getInt(" noteId" )
390+ val oldTagId = nt.getInt(" tagId" )
391+ val newNoteId = noteIdMap[oldNoteId]
392+ val newTagId = tagIdMap[oldTagId]
393+ if (newNoteId != null && newTagId != null ) {
394+ vm.setTagsForNote(newNoteId, listOf (newTagId))
395+ }
396+ }
397+
398+ Snackbar .make(binding.root, " Imported $imported notes" , Snackbar .LENGTH_LONG ).show()
339399 } catch (e: Exception ) {
340400 Snackbar .make(binding.root, " Import failed: ${e.message} " , Snackbar .LENGTH_SHORT ).show()
341401 }
0 commit comments