{"id":17118133,"url":"https://github.com/zach-klippenstein/compose-undo","last_synced_at":"2025-04-13T02:32:06.079Z","repository":{"id":59645484,"uuid":"537715472","full_name":"zach-klippenstein/compose-undo","owner":"zach-klippenstein","description":"Undo snapshot state changes in Compose.","archived":false,"fork":false,"pushed_at":"2022-09-20T17:54:31.000Z","size":2021,"stargazers_count":147,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-26T20:21:18.488Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zach-klippenstein.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-17T06:05:40.000Z","updated_at":"2025-02-08T10:33:24.000Z","dependencies_parsed_at":"2022-09-19T19:05:49.437Z","dependency_job_id":null,"html_url":"https://github.com/zach-klippenstein/compose-undo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zach-klippenstein%2Fcompose-undo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zach-klippenstein%2Fcompose-undo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zach-klippenstein%2Fcompose-undo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zach-klippenstein%2Fcompose-undo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zach-klippenstein","download_url":"https://codeload.github.com/zach-klippenstein/compose-undo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248657793,"owners_count":21140842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T17:53:31.618Z","updated_at":"2025-04-13T02:32:04.400Z","avatar_url":"https://github.com/zach-klippenstein.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# compose-undo ![Maven Central](https://img.shields.io/maven-central/v/com.zachklipp.compose-undo/statehistory)\n\nTrack changes to any snapshot state object and restore state from any point in the past.\n\n## Usage\n\n```groovy\nimplementation 'com.zachklipp.compose-undo:statehistory:{version}'\n```\n\nThe simplest way to get started is to use the `WithStateHistory` composable:\n\n```kotlin\n@Composable\nfun App() {\n    WithStateHistory { history -\u003e\n        var text by remember { mutableStateOf(TextFieldValue(\"\")) }.trackStateChanges()\n        TextField(text, onValueChange = { text = it })\n\n        Button(onClick { history.undo() }) {\n            Text(\"Undo\")\n        }\n    }\n}\n```\n\nThe key is to call `trackStateChanges` on every snapshot state object you want to track. If you're\ncreating state objects outside a composition, call `StateHistory.startTrackingState` yourself.\n\n## Advanced usage\n\nThe main API is the `StateHistory` class. See\n[its kdoc](/statehistory/src/main/java/com/zachklipp/statehistory/StateHistory.kt) for more detailed\ninformation.\n\n## Demo\n\nThis repo includes a demo app you can run and tinker with if you fork the repo. Here's a little\npreview:\n\nhttps://user-images.githubusercontent.com/101754/190877868-160456a2-5bd1-498d-9c76-147fb04958e6.mp4\n\n## How it works\n\n`StateHistory` keeps a set of all the state objects that were registered on it. It registers an\napply listener to the snapshot system, and any time a snapshot is applied to the global snapshot it\nchecks if any of the objects changed by that snapshot are being tracked. For every tracked changed\nobject, it makes a copy of its latest state record. It collects all changes to tracked objects in a\nmap (called a \"frame\"), then when `saveFrame` is called, it pushes that map onto the list of frames\nthat represents the history.\n\nWhen asked to restore states to a particular frame, it goes through every tracked state object and\nsearches the frame list from the requested frame to find the latest frame that captured a change to\nthat object. It then asks the snapshot system for a writable record for that object and copies the\nsaved record back into the writable record, effectively setting the state object's value.\n\nThis is a very unconventional and probably unsupported use case of the\n[`StateObject`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/StateObject)\nand \n[`StateRecord`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/StateRecord)\nAPIs, but it allows the library to support any type of state object, even custom third-party ones.\nThe actual implementation for saving and restoring state values looks something like this\n(`stateObject` is a `StateObject`):\n\n```kotlin\n// Save a state object's current value\nval savedRecord = stateObject.firstStateRecord.create()\nstateObject.firstStateRecord.withCurrent { currentRecord -\u003e\n    savedRecord.assign(currentRecord)\n}\n\n// Restore the value\nstateObject.firstStateRecord.writable(stateObject) {\n    assign(savedRecord)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzach-klippenstein%2Fcompose-undo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzach-klippenstein%2Fcompose-undo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzach-klippenstein%2Fcompose-undo/lists"}