diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json index ecd62d820e2..1cf8b9ecc75 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json @@ -163,11 +163,6 @@ } } }, - "2bf960db8adf": { - "name": "git.status#1", - "ordinal": 4, - "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\",\"includeLineStats\":false}}" - }, "2cb17031e16a": { "branchCompareState": { "kind": "error", @@ -381,6 +376,62 @@ "startedAt": 0 } }, + "6a5580a606dc": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "branch": "feature", + "entries": [ + { + "added": 4, + "area": "unstaged", + "path": "src/app.ts", + "removed": 1, + "status": "modified" + }, + { + "area": "staged", + "oldPath": "src/older.ts", + "path": "src/old.ts", + "status": "renamed" + } + ], + "head": "head-oid", + "upstreamStatus": { + "ahead": 1, + "behind": 0, + "hasUpstream": true, + "upstreamName": "origin/feature" + } + } + } + } + }, "6d220bb7b42a": { "name": "worktree.show#1", "ordinal": 5, @@ -407,6 +458,32 @@ "startedAt": 0 } }, + "72e251ae531b": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, "830aa65db9d0": { "branchCompareState": { "kind": "idle" @@ -645,6 +722,11 @@ } } }, + "973ac78137f2": { + "name": "git.status#1", + "ordinal": 4, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" + }, "9b643a151075": { "name": "git.branchCompare#1", "ordinal": 11, @@ -729,95 +811,11 @@ } } }, - "aa8c5813ba8a": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "branch": "feature", - "entries": [ - { - "added": 4, - "area": "unstaged", - "path": "src/app.ts", - "removed": 1, - "status": "modified" - }, - { - "area": "staged", - "oldPath": "src/older.ts", - "path": "src/old.ts", - "status": "renamed" - } - ], - "head": "head-oid", - "upstreamStatus": { - "ahead": 1, - "behind": 0, - "hasUpstream": true, - "upstreamName": "origin/feature" - } - } - } - } - }, "ad2a2a1df35b": { "name": "worktree.show#1", "ordinal": 9, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"worktree.show\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" }, - "ae602a492660": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "pending", - "startedAt": 0 - } - }, "c2212473f0c1": { "name": "repo.list#1", "ordinal": 10, @@ -987,8 +985,8 @@ { "id": "sc-changes-loaded.prelude:status-pending", "observation": { - "sender": ["e3973bb12da1", "ae602a492660"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "72e251ae531b"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -999,8 +997,8 @@ { "id": "sc-changes-loaded.prelude:compare-pending", "observation": { - "sender": ["e3973bb12da1", "aa8c5813ba8a", "6d220bb7b42a", "587de4c7f7fc"], - "payloads": ["f648b042d810", "2bf960db8adf", "ad2a2a1df35b", "c2212473f0c1"], + "sender": ["e3973bb12da1", "6a5580a606dc", "6d220bb7b42a", "587de4c7f7fc"], + "payloads": ["f648b042d810", "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1013,14 +1011,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1037,14 +1035,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "893813584d8b" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1061,14 +1059,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "55d62e9e07b4" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1085,14 +1083,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "2b6257fd1172" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1109,14 +1107,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "1650cbe07807" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1133,14 +1131,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "a76331b97c21" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1157,14 +1155,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "967f1b227050" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1181,14 +1179,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "00fe64c7bc78" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1205,14 +1203,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "c75999ea5a6c" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1229,14 +1227,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "47205e8686ad" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1253,14 +1251,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "9b643a151075" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json index aade53b10dc..21a989a692d 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json @@ -4,6 +4,75 @@ "family": "git.changes-load", "namedDeltas": [], "values": { + "04f6ea51fb83": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "error": "refused" + } + } + } + }, + "0a645f4241a6": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "method_not_found", + "message": "Unknown method" + }, + "id": "frame-2", + "ok": false + } + } + }, "19bb6c737352": { "branchCompareState": { "kind": "idle" @@ -65,6 +134,40 @@ } } }, + "200efb648720": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "$rpc": "null" + } + } + } + }, "21ab22282843": { "branchCompareState": { "kind": "idle" @@ -83,11 +186,6 @@ "message": "Update Orca desktop to use Source Control on mobile." } }, - "2bf960db8adf": { - "name": "git.status#1", - "ordinal": 4, - "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\",\"includeLineStats\":false}}" - }, "35cc9e325d08": { "name": "git.branchCompare#1", "ordinal": 11, @@ -148,6 +246,41 @@ "message": "" } }, + "4d11fcc4af02": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "" + }, + "id": "frame-2", + "ok": false + } + } + }, "587de4c7f7fc": { "name": "repo.list#1", "ordinal": 6, @@ -183,7 +316,7 @@ "message": "outer refused" } }, - "60f6e2d48ef7": { + "6a5580a606dc": { "name": "git.status#1", "ordinal": 2, "args": [ @@ -194,7 +327,6 @@ { "name": "params", "value": { - "includeLineStats": false, "worktree": "id:repo42::/p" } }, @@ -206,13 +338,37 @@ } ], "settlement": { - "status": "rejected", + "status": "fulfilled", "startedAt": 0, "settledAt": 0, - "error": { - "category": "Error", - "message": "transport failure", - "isRpcDeliveryUnknown": true + "value": { + "id": "frame-2", + "ok": true, + "result": { + "branch": "feature", + "entries": [ + { + "added": 4, + "area": "unstaged", + "path": "src/app.ts", + "removed": 1, + "status": "modified" + }, + { + "area": "staged", + "oldPath": "src/older.ts", + "path": "src/old.ts", + "status": "renamed" + } + ], + "head": "head-oid", + "upstreamStatus": { + "ahead": 1, + "behind": 0, + "hasUpstream": true, + "upstreamName": "origin/feature" + } + } } } }, @@ -242,16 +398,7 @@ "startedAt": 0 } }, - "74b7ff5e1818": { - "branchCompareState": { - "kind": "idle" - }, - "screenState": { - "kind": "error", - "message": "transport failure" - } - }, - "885a150a2ecd": { + "72e251ae531b": { "name": "git.status#1", "ordinal": 2, "args": [ @@ -262,7 +409,6 @@ { "name": "params", "value": { - "includeLineStats": false, "worktree": "id:repo42::/p" } }, @@ -274,20 +420,20 @@ } ], "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": { - "code": "method_not_found", - "message": "Unknown method" - }, - "id": "frame-2", - "ok": false - } + "status": "pending", + "startedAt": 0 } }, - "8af3432e9742": { + "74b7ff5e1818": { + "branchCompareState": { + "kind": "idle" + }, + "screenState": { + "kind": "error", + "message": "transport failure" + } + }, + "7bb6d5b22b62": { "name": "git.status#1", "ordinal": 2, "args": [ @@ -298,7 +444,6 @@ { "name": "params", "value": { - "includeLineStats": false, "worktree": "id:repo42::/p" } }, @@ -317,7 +462,9 @@ "id": "frame-2", "ok": true, "result": { - "error": "inner refused", + "error": { + "message": "inner refused" + }, "ok": false } } @@ -392,6 +539,37 @@ } } }, + "91448c30aa24": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true + } + } + }, "9536c968b88a": { "name": "repo.list#1", "ordinal": 6, @@ -426,7 +604,12 @@ } } }, - "9cedf6bac284": { + "973ac78137f2": { + "name": "git.status#1", + "ordinal": 4, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" + }, + "9809f1b5d628": { "name": "git.status#1", "ordinal": 2, "args": [ @@ -437,7 +620,6 @@ { "name": "params", "value": { - "includeLineStats": false, "worktree": "id:repo42::/p" } }, @@ -449,16 +631,13 @@ } ], "settlement": { - "status": "fulfilled", + "status": "rejected", "startedAt": 0, "settledAt": 0, - "value": { - "error": { - "code": "refused", - "message": "" - }, - "id": "frame-2", - "ok": false + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true } } }, @@ -470,47 +649,12 @@ "kind": "loading" } }, - "a0e97f94a377": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "$rpc": "null" - } - } - } - }, "a6482f604002": { "name": "source-control.status-load-success", "ordinal": 8, "value": {} }, - "aa8c5813ba8a": { + "aae82d534914": { "name": "git.status#1", "ordinal": 2, "args": [ @@ -521,7 +665,6 @@ { "name": "params", "value": { - "includeLineStats": false, "worktree": "id:repo42::/p" } }, @@ -540,29 +683,8 @@ "id": "frame-2", "ok": true, "result": { - "branch": "feature", - "entries": [ - { - "added": 4, - "area": "unstaged", - "path": "src/app.ts", - "removed": 1, - "status": "modified" - }, - { - "area": "staged", - "oldPath": "src/older.ts", - "path": "src/old.ts", - "status": "renamed" - } - ], - "head": "head-oid", - "upstreamStatus": { - "ahead": 1, - "behind": 0, - "hasUpstream": true, - "upstreamName": "origin/feature" - } + "error": "inner refused", + "ok": false } } } @@ -572,7 +694,7 @@ "ordinal": 9, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"worktree.show\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" }, - "ae602a492660": { + "c212210ceca6": { "name": "git.status#1", "ordinal": 2, "args": [ @@ -583,139 +705,6 @@ { "name": "params", "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "pending", - "startedAt": 0 - } - }, - "b0d4cb96a6f3": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true - } - } - }, - "b660ecdc7911": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "error": "refused" - } - } - } - }, - "bbdf2a03af16": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "error": { - "message": "inner refused" - }, - "ok": false - } - } - } - }, - "c1923dfd79bf": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, "worktree": "id:repo42::/p" } }, @@ -754,6 +743,38 @@ } } }, + "d858ce00f4f0": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + } + }, "e2d7409ece1c": { "name": "git.branchCompare#1", "ordinal": 12, @@ -793,39 +814,6 @@ "$rpc": "undefined" } }, - "f5e9af0a8858": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "Error", - "message": "", - "isRpcDeliveryUnknown": true - } - } - }, "f648b042d810": { "name": "settings.get#1", "ordinal": 3, @@ -838,8 +826,8 @@ { "id": "sc-changes-loaded.prelude:status-pending", "observation": { - "sender": ["e3973bb12da1", "ae602a492660"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "72e251ae531b"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -850,8 +838,8 @@ { "id": "sc-changes-loaded.normal:compare-pending", "observation": { - "sender": ["e3973bb12da1", "aa8c5813ba8a", "6d220bb7b42a", "587de4c7f7fc"], - "payloads": ["f648b042d810", "2bf960db8adf", "ad2a2a1df35b", "c2212473f0c1"], + "sender": ["e3973bb12da1", "6a5580a606dc", "6d220bb7b42a", "587de4c7f7fc"], + "payloads": ["f648b042d810", "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1"], "settlements": { "mount": "eb79a9b3682a" }, @@ -864,14 +852,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -886,8 +874,8 @@ { "id": "sc-changes-loaded.result-absent:compare-pending", "observation": { - "sender": ["e3973bb12da1", "b0d4cb96a6f3"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "91448c30aa24"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -898,8 +886,8 @@ { "id": "sc-changes-loaded.result-absent:settled", "observation": { - "sender": ["e3973bb12da1", "b0d4cb96a6f3"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "91448c30aa24"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -910,8 +898,8 @@ { "id": "sc-changes-loaded.result-null:compare-pending", "observation": { - "sender": ["e3973bb12da1", "a0e97f94a377"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "200efb648720"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -922,8 +910,8 @@ { "id": "sc-changes-loaded.result-null:settled", "observation": { - "sender": ["e3973bb12da1", "a0e97f94a377"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "200efb648720"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -934,8 +922,8 @@ { "id": "sc-changes-loaded.inner-ok-missing:compare-pending", "observation": { - "sender": ["e3973bb12da1", "b660ecdc7911"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "04f6ea51fb83"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -946,8 +934,8 @@ { "id": "sc-changes-loaded.inner-ok-missing:settled", "observation": { - "sender": ["e3973bb12da1", "b660ecdc7911"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "04f6ea51fb83"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -958,8 +946,8 @@ { "id": "sc-changes-loaded.inner-false-string-error:compare-pending", "observation": { - "sender": ["e3973bb12da1", "8af3432e9742"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "aae82d534914"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -970,8 +958,8 @@ { "id": "sc-changes-loaded.inner-false-string-error:settled", "observation": { - "sender": ["e3973bb12da1", "8af3432e9742"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "aae82d534914"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -982,8 +970,8 @@ { "id": "sc-changes-loaded.inner-false-object-error:compare-pending", "observation": { - "sender": ["e3973bb12da1", "bbdf2a03af16"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "7bb6d5b22b62"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -994,8 +982,8 @@ { "id": "sc-changes-loaded.inner-false-object-error:settled", "observation": { - "sender": ["e3973bb12da1", "bbdf2a03af16"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "7bb6d5b22b62"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1006,8 +994,8 @@ { "id": "sc-changes-loaded.outer-refused:compare-pending", "observation": { - "sender": ["e3973bb12da1", "c1923dfd79bf"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "c212210ceca6"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1018,8 +1006,8 @@ { "id": "sc-changes-loaded.outer-refused:settled", "observation": { - "sender": ["e3973bb12da1", "c1923dfd79bf"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "c212210ceca6"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1030,8 +1018,8 @@ { "id": "sc-changes-loaded.outer-refused-no-message:compare-pending", "observation": { - "sender": ["e3973bb12da1", "9cedf6bac284"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "4d11fcc4af02"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1042,8 +1030,8 @@ { "id": "sc-changes-loaded.outer-refused-no-message:settled", "observation": { - "sender": ["e3973bb12da1", "9cedf6bac284"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "4d11fcc4af02"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1054,8 +1042,8 @@ { "id": "sc-changes-loaded.method-not-found:compare-pending", "observation": { - "sender": ["e3973bb12da1", "885a150a2ecd"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "0a645f4241a6"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1066,8 +1054,8 @@ { "id": "sc-changes-loaded.method-not-found:settled", "observation": { - "sender": ["e3973bb12da1", "885a150a2ecd"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "0a645f4241a6"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1078,8 +1066,8 @@ { "id": "sc-changes-loaded.transport-rejection:compare-pending", "observation": { - "sender": ["e3973bb12da1", "60f6e2d48ef7"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "d858ce00f4f0"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1090,8 +1078,8 @@ { "id": "sc-changes-loaded.transport-rejection:settled", "observation": { - "sender": ["e3973bb12da1", "60f6e2d48ef7"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "d858ce00f4f0"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1102,8 +1090,8 @@ { "id": "sc-changes-loaded.transport-rejection-no-message:compare-pending", "observation": { - "sender": ["e3973bb12da1", "f5e9af0a8858"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "9809f1b5d628"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -1114,8 +1102,8 @@ { "id": "sc-changes-loaded.transport-rejection-no-message:settled", "observation": { - "sender": ["e3973bb12da1", "f5e9af0a8858"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "9809f1b5d628"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json index 82cc3f5246d..7bc156d150a 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json @@ -159,11 +159,6 @@ } } }, - "2bf960db8adf": { - "name": "git.status#1", - "ordinal": 4, - "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\",\"includeLineStats\":false}}" - }, "35cc9e325d08": { "name": "git.branchCompare#1", "ordinal": 11, @@ -273,6 +268,62 @@ "startedAt": 0 } }, + "6a5580a606dc": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "branch": "feature", + "entries": [ + { + "added": 4, + "area": "unstaged", + "path": "src/app.ts", + "removed": 1, + "status": "modified" + }, + { + "area": "staged", + "oldPath": "src/older.ts", + "path": "src/old.ts", + "status": "renamed" + } + ], + "head": "head-oid", + "upstreamStatus": { + "ahead": 1, + "behind": 0, + "hasUpstream": true, + "upstreamName": "origin/feature" + } + } + } + } + }, "6b974d7f9507": { "name": "repo.list#1", "ordinal": 6, @@ -371,6 +422,32 @@ "startedAt": 0 } }, + "72e251ae531b": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, "84280af3ca36": { "name": "repo.list#1", "ordinal": 6, @@ -541,6 +618,11 @@ } } }, + "973ac78137f2": { + "name": "git.status#1", + "ordinal": 4, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" + }, "a0e527bdf205": { "branchCompareState": { "kind": "idle" @@ -554,95 +636,11 @@ "ordinal": 8, "value": {} }, - "aa8c5813ba8a": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "branch": "feature", - "entries": [ - { - "added": 4, - "area": "unstaged", - "path": "src/app.ts", - "removed": 1, - "status": "modified" - }, - { - "area": "staged", - "oldPath": "src/older.ts", - "path": "src/old.ts", - "status": "renamed" - } - ], - "head": "head-oid", - "upstreamStatus": { - "ahead": 1, - "behind": 0, - "hasUpstream": true, - "upstreamName": "origin/feature" - } - } - } - } - }, "ad2a2a1df35b": { "name": "worktree.show#1", "ordinal": 9, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"worktree.show\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" }, - "ae602a492660": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "pending", - "startedAt": 0 - } - }, "bc1d0b381f25": { "name": "repo.list#1", "ordinal": 6, @@ -774,8 +772,8 @@ { "id": "sc-changes-loaded.prelude:status-pending", "observation": { - "sender": ["e3973bb12da1", "ae602a492660"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "72e251ae531b"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -786,8 +784,8 @@ { "id": "sc-changes-loaded.prelude:compare-pending", "observation": { - "sender": ["e3973bb12da1", "aa8c5813ba8a", "6d220bb7b42a", "587de4c7f7fc"], - "payloads": ["f648b042d810", "2bf960db8adf", "ad2a2a1df35b", "c2212473f0c1"], + "sender": ["e3973bb12da1", "6a5580a606dc", "6d220bb7b42a", "587de4c7f7fc"], + "payloads": ["f648b042d810", "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1"], "settlements": { "mount": "eb79a9b3682a" }, @@ -800,14 +798,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -824,14 +822,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "bc1d0b381f25", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -848,14 +846,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "204b32e0c54e", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -872,14 +870,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "042d01f3263f", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -896,14 +894,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "26929519c12a", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -920,14 +918,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "6b974d7f9507", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -944,14 +942,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "877dcd616e1f", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -968,14 +966,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "6b9dc51089ad", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -992,14 +990,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "c7a8ab38e974", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1016,14 +1014,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "84280af3ca36", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -1040,14 +1038,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "49197a7eac62", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json index 4d4f3b48e14..2157784b934 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json @@ -56,11 +56,6 @@ } } }, - "2bf960db8adf": { - "name": "git.status#1", - "ordinal": 4, - "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\",\"includeLineStats\":false}}" - }, "35cc9e325d08": { "name": "git.branchCompare#1", "ordinal": 11, @@ -209,6 +204,62 @@ "startedAt": 0 } }, + "6a5580a606dc": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "branch": "feature", + "entries": [ + { + "added": 4, + "area": "unstaged", + "path": "src/app.ts", + "removed": 1, + "status": "modified" + }, + { + "area": "staged", + "oldPath": "src/older.ts", + "path": "src/old.ts", + "status": "renamed" + } + ], + "head": "head-oid", + "upstreamStatus": { + "ahead": 1, + "behind": 0, + "hasUpstream": true, + "upstreamName": "origin/feature" + } + } + } + } + }, "6d220bb7b42a": { "name": "worktree.show#1", "ordinal": 5, @@ -235,6 +286,32 @@ "startedAt": 0 } }, + "72e251ae531b": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, "7440046c1348": { "name": "worktree.show#1", "ordinal": 5, @@ -441,6 +518,11 @@ } } }, + "973ac78137f2": { + "name": "git.status#1", + "ordinal": 4, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" + }, "a0e527bdf205": { "branchCompareState": { "kind": "idle" @@ -454,63 +536,6 @@ "ordinal": 8, "value": {} }, - "aa8c5813ba8a": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "branch": "feature", - "entries": [ - { - "added": 4, - "area": "unstaged", - "path": "src/app.ts", - "removed": 1, - "status": "modified" - }, - { - "area": "staged", - "oldPath": "src/older.ts", - "path": "src/old.ts", - "status": "renamed" - } - ], - "head": "head-oid", - "upstreamStatus": { - "ahead": 1, - "behind": 0, - "hasUpstream": true, - "upstreamName": "origin/feature" - } - } - } - } - }, "ad2a2a1df35b": { "name": "worktree.show#1", "ordinal": 9, @@ -551,33 +576,6 @@ } } }, - "ae602a492660": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "pending", - "startedAt": 0 - } - }, "b7a249bf3627": { "name": "worktree.show#1", "ordinal": 5, @@ -805,8 +803,8 @@ { "id": "sc-changes-loaded.prelude:status-pending", "observation": { - "sender": ["e3973bb12da1", "ae602a492660"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "72e251ae531b"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -817,8 +815,8 @@ { "id": "sc-changes-loaded.prelude:compare-pending", "observation": { - "sender": ["e3973bb12da1", "aa8c5813ba8a", "6d220bb7b42a", "587de4c7f7fc"], - "payloads": ["f648b042d810", "2bf960db8adf", "ad2a2a1df35b", "c2212473f0c1"], + "sender": ["e3973bb12da1", "6a5580a606dc", "6d220bb7b42a", "587de4c7f7fc"], + "payloads": ["f648b042d810", "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1"], "settlements": { "mount": "eb79a9b3682a" }, @@ -831,14 +829,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" @@ -855,14 +853,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "54cc20e9cfbc", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -879,14 +877,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8cf0ca1cc85d", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -903,14 +901,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "bb66129d9391", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -927,14 +925,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "ecfe4b0cfe19", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -951,14 +949,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "7440046c1348", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -975,14 +973,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "ae3822726c00", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -999,14 +997,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "4fadbf6b1661", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -1023,14 +1021,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "b7a249bf3627", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -1047,14 +1045,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "d65d39bc05d3", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" @@ -1071,14 +1069,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "7689d7b4e8e2", "9536c968b88a", "f1bc948c977d" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "3ca74b9dc048" diff --git a/mobile/rpc-foundation/goldens/sc-changes-loaded.json b/mobile/rpc-foundation/goldens/sc-changes-loaded.json index 0365526da70..0e95a43e419 100644 --- a/mobile/rpc-foundation/goldens/sc-changes-loaded.json +++ b/mobile/rpc-foundation/goldens/sc-changes-loaded.json @@ -56,11 +56,6 @@ } } }, - "2bf960db8adf": { - "name": "git.status#1", - "ordinal": 4, - "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\",\"includeLineStats\":false}}" - }, "35cc9e325d08": { "name": "git.branchCompare#1", "ordinal": 11, @@ -138,6 +133,62 @@ "startedAt": 0 } }, + "6a5580a606dc": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "branch": "feature", + "entries": [ + { + "added": 4, + "area": "unstaged", + "path": "src/app.ts", + "removed": 1, + "status": "modified" + }, + { + "area": "staged", + "oldPath": "src/older.ts", + "path": "src/old.ts", + "status": "renamed" + } + ], + "head": "head-oid", + "upstreamStatus": { + "ahead": 1, + "behind": 0, + "hasUpstream": true, + "upstreamName": "origin/feature" + } + } + } + } + }, "6d220bb7b42a": { "name": "worktree.show#1", "ordinal": 5, @@ -164,6 +215,32 @@ "startedAt": 0 } }, + "72e251ae531b": { + "name": "git.status#1", + "ordinal": 2, + "args": [ + { + "name": "method", + "value": "git.status" + }, + { + "name": "params", + "value": { + "worktree": "id:repo42::/p" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, "8c8e528ca222": { "name": "worktree.show#1", "ordinal": 5, @@ -267,6 +344,11 @@ } } }, + "973ac78137f2": { + "name": "git.status#1", + "ordinal": 4, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"git.status\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" + }, "a0e527bdf205": { "branchCompareState": { "kind": "idle" @@ -280,95 +362,11 @@ "ordinal": 8, "value": {} }, - "aa8c5813ba8a": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "branch": "feature", - "entries": [ - { - "added": 4, - "area": "unstaged", - "path": "src/app.ts", - "removed": 1, - "status": "modified" - }, - { - "area": "staged", - "oldPath": "src/older.ts", - "path": "src/old.ts", - "status": "renamed" - } - ], - "head": "head-oid", - "upstreamStatus": { - "ahead": 1, - "behind": 0, - "hasUpstream": true, - "upstreamName": "origin/feature" - } - } - } - } - }, "ad2a2a1df35b": { "name": "worktree.show#1", "ordinal": 9, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"worktree.show\",\"params\":{\"worktree\":\"id:repo42::/p\"}}" }, - "ae602a492660": { - "name": "git.status#1", - "ordinal": 2, - "args": [ - { - "name": "method", - "value": "git.status" - }, - { - "name": "params", - "value": { - "includeLineStats": false, - "worktree": "id:repo42::/p" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "pending", - "startedAt": 0 - } - }, "c2212473f0c1": { "name": "repo.list#1", "ordinal": 10, @@ -434,8 +432,8 @@ { "id": "status-pending", "observation": { - "sender": ["e3973bb12da1", "ae602a492660"], - "payloads": ["f648b042d810", "2bf960db8adf"], + "sender": ["e3973bb12da1", "72e251ae531b"], + "payloads": ["f648b042d810", "973ac78137f2"], "settlements": { "mount": "eb79a9b3682a" }, @@ -446,8 +444,8 @@ { "id": "compare-pending", "observation": { - "sender": ["e3973bb12da1", "aa8c5813ba8a", "6d220bb7b42a", "587de4c7f7fc"], - "payloads": ["f648b042d810", "2bf960db8adf", "ad2a2a1df35b", "c2212473f0c1"], + "sender": ["e3973bb12da1", "6a5580a606dc", "6d220bb7b42a", "587de4c7f7fc"], + "payloads": ["f648b042d810", "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1"], "settlements": { "mount": "eb79a9b3682a" }, @@ -460,14 +458,14 @@ "observation": { "sender": [ "e3973bb12da1", - "aa8c5813ba8a", + "6a5580a606dc", "8c8e528ca222", "9536c968b88a", "35cc9e325d08" ], "payloads": [ "f648b042d810", - "2bf960db8adf", + "973ac78137f2", "ad2a2a1df35b", "c2212473f0c1", "e2d7409ece1c" diff --git a/mobile/rpc-foundation/pilot-scenarios.json b/mobile/rpc-foundation/pilot-scenarios.json index 2376579cee6..b429d9d6347 100644 --- a/mobile/rpc-foundation/pilot-scenarios.json +++ b/mobile/rpc-foundation/pilot-scenarios.json @@ -20348,8 +20348,7 @@ { "complete": "git.status#1", "params": { - "worktree": "id:repo42::/p", - "includeLineStats": false + "worktree": "id:repo42::/p" }, "reply": { "ok": true, diff --git a/mobile/src/components/nav-bar/PullDownMenu.tsx b/mobile/src/components/nav-bar/PullDownMenu.tsx index 7e8f95d77d2..029dc1aadc6 100644 --- a/mobile/src/components/nav-bar/PullDownMenu.tsx +++ b/mobile/src/components/nav-bar/PullDownMenu.tsx @@ -5,6 +5,7 @@ import { Modal, Platform, Pressable, + ScrollView, StyleSheet, Text, View, @@ -13,7 +14,7 @@ import { import { Check } from 'lucide-react-native' import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' import { useBackClaim } from '../../navigation/use-back-claim' -import { colors, radii, spacing } from '../../theme/mobile-theme' +import { colors, radii, spacing, typography } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../../i18n/use-mobile-translation' @@ -153,6 +154,10 @@ export function PullDownMenu({ visible, anchorRef, groups, onClose }: PullDownMe style={[ styles.menu, anchor, + // Long menus scroll instead of running off a short (landscape) screen. + { + maxHeight: window.height - ('top' in anchor ? anchor.top : anchor.bottom) - EDGE_INSET + }, { opacity: appear.interpolate({ inputRange: [0, 1], @@ -173,44 +178,54 @@ export function PullDownMenu({ visible, anchorRef, groups, onClose }: PullDownMe ]} accessibilityRole="menu" > - {shown.map((group, groupIndex) => ( - 0 && styles.groupGap}> - {group.map((action, index) => { - const Icon = action.icon - const tint = action.destructive ? colors.statusRed : colors.textPrimary - return ( - [ - styles.item, - index > 0 && styles.itemDivider, - pressed && styles.itemPressed, - action.disabled && styles.itemDisabled - ]} - disabled={action.disabled || action.loading} - onPress={() => press(action)} - accessibilityRole="menuitem" - accessibilityLabel={action.label} - accessibilityHint={action.hint} - accessibilityState={ - action.checked === undefined ? undefined : { checked: action.checked } - } - > - - {action.label} - - {action.checked ? ( - - ) : action.renderIcon ? ( - action.renderIcon() - ) : ( - Icon && - )} - - ) - })} - - ))} + + {shown.map((group, groupIndex) => ( + 0 && styles.groupGap}> + {group.map((action, index) => { + const Icon = action.icon + const tint = action.destructive ? colors.statusRed : colors.textPrimary + return ( + [ + styles.item, + index > 0 && styles.itemDivider, + pressed && styles.itemPressed, + action.disabled && styles.itemDisabled + ]} + disabled={action.disabled || action.loading} + onPress={() => press(action)} + accessibilityRole="menuitem" + accessibilityLabel={action.label} + accessibilityHint={action.hint} + accessibilityState={ + action.checked === undefined ? undefined : { checked: action.checked } + } + > + + + {action.label} + + {/* No tooltips on a phone: a disabled item says why, a destructive one what it replaces. */} + {(action.disabled || action.destructive) && action.hint ? ( + + {action.hint} + + ) : null} + + {action.checked ? ( + + ) : action.renderIcon ? ( + action.renderIcon() + ) : ( + Icon && + )} + + ) + })} + + ))} + ) : null} @@ -254,8 +269,16 @@ const styles = themedStyleSheet(() => ({ itemDisabled: { opacity: 0.4 }, + labelBlock: { + flexShrink: 1, + gap: 2 + }, label: { flexShrink: 1, fontSize: 15.5 + }, + hint: { + color: colors.textSecondary, + fontSize: typography.metaSize } })) diff --git a/mobile/src/i18n/en-runtime-required.json b/mobile/src/i18n/en-runtime-required.json index 7957760ec78..82edfb8b3c1 100644 --- a/mobile/src/i18n/en-runtime-required.json +++ b/mobile/src/i18n/en-runtime-required.json @@ -307,11 +307,11 @@ "pushCommits_one": "Push {{count}} commit.", "pushCommits_other": "Push {{count}} commits." }, - "branchCompare": { + "changesHeader": { "commits_one": "{{count}} commit", "commits_other": "{{count}} commits", - "files_one": "{{count}} file", - "files_other": "{{count}} files" + "filesNeedDecision_one": "{{count}} file needs a decision before you can finish.", + "filesNeedDecision_other": "{{count}} files need a decision before you can finish." }, "commitDetail": { "files_one": "{{count}} file", @@ -355,10 +355,6 @@ "checksRunning_one": "{{count}} check still running", "checksRunning_other": "{{count}} checks still running" }, - "prChip": { - "openWithComments_one": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} unresolved comments. Open pull request.", - "openWithComments_other": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} unresolved comments. Open pull request." - }, "primaryAction": { "commitFiles_one": "Commit {{count}} file", "commitFiles_other": "Commit {{count}} files", diff --git a/mobile/src/i18n/locales/de.json b/mobile/src/i18n/locales/de.json index 5886d85bd67..c4438e3b46d 100644 --- a/mobile/src/i18n/locales/de.json +++ b/mobile/src/i18n/locales/de.json @@ -2670,7 +2670,6 @@ "checkingReviewStatus": "Review-Status wird geprüft.", "checkoutBeforePublish": "Vor dem Veröffentlichen von Commits einen Branch auschecken.", "commitStagedChanges": "Vorgemerkte Änderungen committen.", - "commits": "Commits", "committing": "Commit läuft.", "createPr": "PR erstellen", "enterCommitMessage": "Zum Committen eine Commit-Nachricht eingeben.", @@ -2698,23 +2697,11 @@ "resolveFailed": "Branch-Basis konnte nicht ermittelt werden." }, "branchCard": { - "branchCount": "{{count}} auf Branch", - "changedCount": "{{count}} geändert", - "cherryPickState": "Cherry-pick", - "mergeState": "Merge", - "rebaseState": "Rebase", - "stagedCount": "{{count}} vorgemerkt", "completingMerge": "Wird abgeschlossen…", "completeMerge": "Merge abschließen" }, "branchCompare": { - "commits_one": "{{count}} Commit", - "commits_other": "{{count}} Commits", - "files_one": "{{count}} Datei", - "files_other": "{{count}} Dateien", - "sectionTitle": "Auf Branch committet", - "summary": "{{files}} – gegenüber {{baseRef}}", - "summaryWithCommits": "{{files}} – {{commits}} – gegenüber {{baseRef}}" + "sectionTitle": "Auf Branch committet" }, "branchCompareOutcome": { "baseBranchUnavailable": "Basis-Branch für den Vergleich konnte nicht ermittelt werden.", @@ -2773,11 +2760,8 @@ "emptyMessage": "Der Worktree ist sauber.", "emptyTitle": "Keine lokalen Änderungen", "generateMessage": "Commit-Nachricht mit KI generieren", - "openActions": "Aktionen der Quellcodeverwaltung öffnen", "reconnecting": "Verbindung zum Desktop wird wiederhergestellt…", - "regenerateHint": "Nachricht leeren, um sie neu zu generieren.", - "stageAll": "Alle vormerken", - "unstageAll": "Alle Vormerkungen aufheben" + "regenerateHint": "Nachricht leeren, um sie neu zu generieren." }, "createPrAction": { "checkingStatus": "Review-Status wird geprüft…", @@ -2806,20 +2790,15 @@ "titleUnpublishedForcePushCount_one": "Push von {{count}} Branch-Commit mit Lease erzwingen und bei Bedarf einen Upstream festlegen.", "titleUnpublishedForcePushCount_other": "Push von {{count}} Branch-Commits mit Lease erzwingen und bei Bedarf einen Upstream festlegen." }, - "fileList": { - "committedSection": "Auf Branch committet" - }, "fileRows": { "addedByThem": "Von ihnen hinzugefügt", "addedByUs": "Von uns hinzugefügt", "bothAdded": "Beidseitig hinzugefügt", "bothDeleted": "Beidseitig gelöscht", "bothModified": "Beidseitig geändert", - "committedSection": "Auf Branch committet", "committedUnavailable": "Committete Änderungen nicht verfügbar.", "deletedByThem": "Von ihnen gelöscht", "deletedByUs": "Von uns gelöscht", - "discardFile": "Änderungen an {{path}} verwerfen", "loadingCommitted": "Committete Änderungen werden geladen…", "openChangedFile": "Geänderte Datei {{path}} öffnen", "openCommittedChange": "Committete Änderung an {{path}} öffnen", @@ -2852,9 +2831,7 @@ "waitingForDesktop": "Warten auf Desktop…" }, "gitStatus": { - "changes": "Änderungen", - "conflicts": "Konflikte", - "stagedChanges": "Vorgemerkte Änderungen" + "conflicts": "Konflikte" }, "header": { "backToSession": "Zurück zur Sitzung", @@ -2922,7 +2899,6 @@ "waitingForDesktop": "Warten auf den Desktop…" }, "modals": { - "actionsTitle": "Quellcodeverwaltung", "currentBranch": "aktuell", "discardButton": "Verwerfen", "discardConfirmMessage": "Änderungen an „{{path}}“ verwerfen? Dies kann nicht rückgängig gemacht werden.", @@ -2935,15 +2911,6 @@ "panel": { "unavailableTitle": "Quellcodeverwaltung nicht verfügbar" }, - "prChip": { - "createPr": "Pull Request erstellen", - "loading": "Pull Request wird geladen…", - "loadingLabel": "Pull Request wird geladen", - "openLabel": "Pull Request #{{number}}, {{state}}, {{checks}}. Pull Request öffnen.", - "openWithComments_one": "Pull Request #{{number}}, {{state}}, {{checks}}, {{count}} ungelöster Kommentar. Pull Request öffnen.", - "openWithComments_other": "Pull Request #{{number}}, {{state}}, {{checks}}, {{count}} ungelöste Kommentare. Pull Request öffnen.", - "unavailableLabel": "Pull Request nicht verfügbar: {{message}}" - }, "prCreate": { "authRequired": "Vor dem Erstellen eines {{reviewLabel}}s ist eine Authentifizierung erforderlich.", "baseNotOnRemote": "Den Basis-Branch pushen, bevor ein {{reviewLabel}} erstellt wird.", @@ -3031,7 +2998,6 @@ "noBranch": "Kein Branch" }, "state": { - "noUpstream": "Kein Upstream", "syncStatus": "{{ahead}} voraus, {{behind}} zurück" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/en.json b/mobile/src/i18n/locales/en.json index f6b6ac5c333..3ecd1df2797 100644 --- a/mobile/src/i18n/locales/en.json +++ b/mobile/src/i18n/locales/en.json @@ -3219,7 +3219,6 @@ "createPr": "Create PR", "pushAndCreatePr": "Push & Create PR", "switchBranch": "Switch branch", - "commits": "Commits", "committing": "Commit in progress.", "forcePushing": "Force push in progress.", "remoteOperationInProgress": "Remote operation in progress.", @@ -3241,7 +3240,8 @@ "pullCommits_other": "Pull {{count}} commits.", "pushCommits_one": "Push {{count}} commit.", "pushCommits_other": "Push {{count}} commits.", - "upToDate": "Nothing to commit. Branch is up to date." + "upToDate": "Nothing to commit. Branch is up to date.", + "openCommits": "Open commits" }, "dropdownCounts": { "pushCount_one": "Push {{count}} commit", @@ -3299,20 +3299,11 @@ "loading": "Loading commits…" }, "branchCard": { - "mergeState": "merge", - "rebaseState": "rebase", - "cherryPickState": "cherry-pick", - "changedCount": "{{count}} changed", - "stagedCount": "{{count}} staged", - "branchCount": "{{count}} on branch", "completingMerge": "Completing…", "completeMerge": "Complete merge" }, "content": { "reconnecting": "Reconnecting to desktop...", - "stageAll": "Stage All", - "unstageAll": "Unstage All", - "openActions": "Open source control actions", "emptyTitle": "No local changes", "emptyMessage": "Working tree is clean.", "commitMessageDisabled": "Commit message disabled. No staged files.", @@ -3321,9 +3312,6 @@ "cancelGeneration": "Cancel commit message generation", "generateMessage": "Generate commit message with AI" }, - "fileList": { - "committedSection": "Committed on Branch" - }, "fileRows": { "bothModified": "Both modified", "bothAdded": "Both added", @@ -3338,10 +3326,10 @@ "unstageFile": "Unstage {{path}}", "stageFile": "Stage {{path}}", "discardFile": "Discard {{path}}", - "committedSection": "Committed on Branch", "loadingCommitted": "Loading committed changes...", "committedUnavailable": "Committed changes unavailable.", - "openCommittedChange": "Open committed change {{path}}" + "openCommittedChange": "Open committed change {{path}}", + "discard": "Discard" }, "header": { "close": "Close source control", @@ -3353,7 +3341,6 @@ "moreActions": "More source control actions" }, "modals": { - "actionsTitle": "Source Control", "discardTitle": "Discard Change", "discardConfirmMessage": "Discard changes to \"{{path}}\"? This cannot be undone.", "discardButton": "Discard", @@ -3367,15 +3354,6 @@ "unavailableTitle": "Source Control Unavailable", "loading": "Loading changes…" }, - "prChip": { - "loading": "Loading pull request…", - "createPr": "Create pull request", - "loadingLabel": "Loading pull request", - "unavailableLabel": "Pull request unavailable: {{message}}", - "openWithComments_one": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} unresolved comments. Open pull request.", - "openWithComments_other": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} unresolved comments. Open pull request.", - "openLabel": "Pull request #{{number}}, {{state}}, {{checks}}. Open pull request." - }, "hostedReviewCopy": { "updatingBranch": "Updating branch...", "stagingChanges": "Staging changes...", @@ -3394,13 +3372,7 @@ "loadFailed": "Unable to load committed changes" }, "branchCompare": { - "sectionTitle": "Committed on Branch", - "summary": "{{files}} - vs {{baseRef}}", - "summaryWithCommits": "{{files}} - {{commits}} - vs {{baseRef}}", - "files_one": "{{count}} file", - "files_other": "{{count}} files", - "commits_one": "{{count}} commit", - "commits_other": "{{count}} commits" + "sectionTitle": "Committed on Branch" }, "commitMessageAi": { "generateFailed": "Failed to generate commit message" @@ -3422,8 +3394,8 @@ }, "gitStatus": { "conflicts": "Conflicts", - "changes": "Changes", - "stagedChanges": "Staged Changes" + "uncommitted": "Uncommitted", + "staged": "Staged" }, "hostedReviewCreateIntent": { "resolveConflicts": "Resolve conflicts before creating a pull request.", @@ -3498,8 +3470,7 @@ "actionFailed": "Source control action failed" }, "state": { - "syncStatus": "{{ahead}} ahead, {{behind}} behind", - "noUpstream": "No upstream" + "syncStatus": "{{ahead}} ahead, {{behind}} behind" }, "textGenerationFailure": { "openCodeInvalidEvents": "OpenCode returned invalid JSON events.", @@ -3641,6 +3612,33 @@ }, "prCreateDraft": { "generateFailed": "Failed to write a description" + }, + "changesHeader": { + "merging": "Merging", + "rebasing": "Rebasing", + "cherryPicking": "Cherry-picking", + "notPublished": "Not published", + "notPushed": "{{count}} not pushed", + "behind": "{{count}} behind", + "upToDate": "Up to date", + "switchBranch": "Switch branch from {{branch}}", + "mergeInProgress": "Merge in progress", + "rebaseInProgress": "Rebase in progress", + "cherryPickInProgress": "Cherry-pick in progress", + "filesNeedDecision_one": "{{count}} file needs a decision before you can finish.", + "filesNeedDecision_other": "{{count}} files need a decision before you can finish.", + "conflictsResolved": "Every conflict is resolved. Finish from the bar below.", + "continueOnComputer": "Every conflict is resolved. Continue on your computer to finish.", + "commits_one": "{{count}} commit", + "commits_other": "{{count}} commits", + "conflictsMayRemain": "Not every file is listed, so conflicts may remain. Check on your computer." + }, + "fileList": { + "stageAll": "Stage all", + "mergedCleanly": "Merged cleanly", + "unstageAll": "Unstage all", + "onThisBranch": "On this branch {{count}}", + "versusBase": "vs {{base}}" } }, "newWorkspace": { diff --git a/mobile/src/i18n/locales/es.json b/mobile/src/i18n/locales/es.json index a1741bf8b36..e06f495a16b 100644 --- a/mobile/src/i18n/locales/es.json +++ b/mobile/src/i18n/locales/es.json @@ -2742,7 +2742,6 @@ "checkingReviewStatus": "Comprobando estado de revisión.", "checkoutBeforePublish": "Haz checkout de una rama antes de publicar commits.", "commitStagedChanges": "Haz commit de los cambios preparados.", - "commits": "Commits", "committing": "Commit en curso.", "createPr": "Crear PR", "enterCommitMessage": "Introduce un mensaje de commit para hacer commit.", @@ -2772,25 +2771,11 @@ "resolveFailed": "No se pudo determinar la base de la rama" }, "branchCard": { - "branchCount": "{{count}} en la rama", - "changedCount": "{{count}} con cambios", - "cherryPickState": "cherry-pick", - "mergeState": "merge", - "rebaseState": "rebase", - "stagedCount": "{{count}} en stage", "completingMerge": "Finalizando…", "completeMerge": "Completar fusión" }, "branchCompare": { - "commits_many": "{{count}} commits", - "commits_one": "{{count}} commit", - "commits_other": "{{count}} commits", - "files_many": "{{count}} archivos", - "files_one": "{{count}} archivo", - "files_other": "{{count}} archivos", - "sectionTitle": "Commits en la rama", - "summary": "{{files}} - frente a {{baseRef}}", - "summaryWithCommits": "{{files}} - {{commits}} - frente a {{baseRef}}" + "sectionTitle": "Commits en la rama" }, "branchCompareOutcome": { "baseBranchUnavailable": "No se pudo determinar la rama base para la comparación.", @@ -2849,11 +2834,8 @@ "emptyMessage": "El árbol de trabajo está limpio.", "emptyTitle": "No hay cambios locales", "generateMessage": "Generar mensaje de commit con IA", - "openActions": "Abrir acciones de control de código fuente", "reconnecting": "Reconectando con el escritorio…", - "regenerateHint": "Borre el mensaje para regenerar.", - "stageAll": "Hacer stage de todo", - "unstageAll": "Quitar todo del stage" + "regenerateHint": "Borre el mensaje para regenerar." }, "createPrAction": { "checkingStatus": "Comprobando estado de revisión…", @@ -2889,20 +2871,15 @@ "titleUnpublishedForcePushCount_one": "Haz force push con lease de {{count}} commit de la rama y configura un upstream si es necesario.", "titleUnpublishedForcePushCount_other": "Haz force push con lease de {{count}} commits de la rama y configura un upstream si es necesario." }, - "fileList": { - "committedSection": "Commits en la rama" - }, "fileRows": { "addedByThem": "Agregado por ellos", "addedByUs": "Agregado por nosotros", "bothAdded": "Agregado por ambos", "bothDeleted": "Eliminado por ambos", "bothModified": "Modificado por ambos", - "committedSection": "Commits en la rama", "committedUnavailable": "Los cambios confirmados no están disponibles.", "deletedByThem": "Eliminado por ellos", "deletedByUs": "Eliminado por nosotros", - "discardFile": "Descartar {{path}}", "loadingCommitted": "Cargando cambios confirmados…", "openChangedFile": "Abrir archivo modificado {{path}}", "openCommittedChange": "Abrir cambio confirmado en {{path}}", @@ -2940,9 +2917,7 @@ "waitingForDesktop": "Esperando al escritorio…" }, "gitStatus": { - "changes": "Cambios", - "conflicts": "Conflictos", - "stagedChanges": "Cambios preparados" + "conflicts": "Conflictos" }, "header": { "backToSession": "Volver a la sesión", @@ -3010,7 +2985,6 @@ "waitingForDesktop": "Esperando al escritorio…" }, "modals": { - "actionsTitle": "Control de código fuente", "currentBranch": "actual", "discardButton": "Descartar", "discardConfirmMessage": "¿Descartar los cambios de \"{{path}}\"? Esta acción no se puede deshacer.", @@ -3023,16 +2997,6 @@ "panel": { "unavailableTitle": "Control de código fuente no disponible" }, - "prChip": { - "createPr": "Crear pull request", - "loading": "Cargando pull request…", - "loadingLabel": "Cargando pull request", - "openLabel": "Pull request #{{number}}, {{state}}, {{checks}}. Abrir pull request.", - "openWithComments_many": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} comentarios sin resolver. Abrir pull request.", - "openWithComments_one": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} comentario sin resolver. Abrir pull request.", - "openWithComments_other": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} comentarios sin resolver. Abrir pull request.", - "unavailableLabel": "Pull request no disponible: {{message}}" - }, "prCreate": { "authRequired": "Autentícate antes de crear una {{reviewLabel}}.", "baseNotOnRemote": "Haz push de la rama base antes de crear una {{reviewLabel}}.", @@ -3120,7 +3084,6 @@ "noBranch": "Sin rama" }, "state": { - "noUpstream": "Sin upstream", "syncStatus": "{{ahead}} por delante, {{behind}} por detrás" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/fr.json b/mobile/src/i18n/locales/fr.json index 8a2fea5270f..40cb6184fef 100644 --- a/mobile/src/i18n/locales/fr.json +++ b/mobile/src/i18n/locales/fr.json @@ -2742,7 +2742,6 @@ "checkingReviewStatus": "Vérification du statut de revue.", "checkoutBeforePublish": "Effectuez un checkout d'une branche avant de publier des commits.", "commitStagedChanges": "Committer les modifications indexées.", - "commits": "Commits", "committing": "Commit en cours.", "createPr": "Créer une PR", "enterCommitMessage": "Saisissez un message de commit pour committer.", @@ -2772,25 +2771,11 @@ "resolveFailed": "Impossible de déterminer la base de la branche" }, "branchCard": { - "branchCount": "{{count}} sur la branche", - "changedCount": "Modifiés : {{count}}", - "cherryPickState": "cherry-pick", - "mergeState": "merge", - "rebaseState": "rebase", - "stagedCount": "Indexés : {{count}}", "completingMerge": "Finalisation…", "completeMerge": "Terminer la fusion" }, "branchCompare": { - "commits_many": "{{count}} commits", - "commits_one": "{{count}} commit", - "commits_other": "{{count}} commits", - "files_many": "{{count}} fichiers", - "files_one": "{{count}} fichier", - "files_other": "{{count}} fichiers", - "sectionTitle": "Committé sur la branche", - "summary": "{{files}} - par rapport à {{baseRef}}", - "summaryWithCommits": "{{files}} - {{commits}} - par rapport à {{baseRef}}" + "sectionTitle": "Committé sur la branche" }, "branchCompareOutcome": { "baseBranchUnavailable": "Impossible de déterminer la branche de base pour la comparaison.", @@ -2849,11 +2834,8 @@ "emptyMessage": "Le worktree est propre.", "emptyTitle": "Aucune modification locale", "generateMessage": "Générer le message de commit avec l'IA", - "openActions": "Ouvrir les actions de contrôle de code source", "reconnecting": "Reconnexion à l'ordinateur…", - "regenerateHint": "Effacez le message pour le régénérer.", - "stageAll": "Tout indexer", - "unstageAll": "Tout désindexer" + "regenerateHint": "Effacez le message pour le régénérer." }, "createPrAction": { "checkingStatus": "Vérification du statut de revue…", @@ -2889,20 +2871,15 @@ "titleUnpublishedForcePushCount_one": "Faites un force push avec lease de {{count}} commit de la branche et définissez un upstream si nécessaire.", "titleUnpublishedForcePushCount_other": "Faites un force push avec lease de {{count}} commits de la branche et définissez un upstream si nécessaire." }, - "fileList": { - "committedSection": "Committé sur la branche" - }, "fileRows": { "addedByThem": "Ajouté chez eux", "addedByUs": "Ajouté chez nous", "bothAdded": "Ajouté des deux côtés", "bothDeleted": "Supprimé des deux côtés", "bothModified": "Modifié des deux côtés", - "committedSection": "Committé sur la branche", "committedUnavailable": "Modifications committées indisponibles.", "deletedByThem": "Supprimé chez eux", "deletedByUs": "Supprimé chez nous", - "discardFile": "Abandonner les modifications de {{path}}", "loadingCommitted": "Chargement des modifications committées…", "openChangedFile": "Ouvrir le fichier modifié {{path}}", "openCommittedChange": "Ouvrir la modification committée de {{path}}", @@ -2940,9 +2917,7 @@ "waitingForDesktop": "En attente de l'ordinateur…" }, "gitStatus": { - "changes": "Modifications", - "conflicts": "Conflits", - "stagedChanges": "Modifications stagées" + "conflicts": "Conflits" }, "header": { "backToSession": "Retour à la session", @@ -3010,7 +2985,6 @@ "waitingForDesktop": "En attente de l'ordinateur…" }, "modals": { - "actionsTitle": "Contrôle de code source", "currentBranch": "actuelle", "discardButton": "Abandonner", "discardConfirmMessage": "Abandonner les modifications de « {{path}} » ? Cette action est irréversible.", @@ -3023,16 +2997,6 @@ "panel": { "unavailableTitle": "Contrôle de code source indisponible" }, - "prChip": { - "createPr": "Créer la pull request", - "loading": "Chargement de la pull request…", - "loadingLabel": "Chargement de la pull request", - "openLabel": "Pull request #{{number}}, {{state}}, {{checks}}. Ouvrir la pull request.", - "openWithComments_many": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} commentaires non résolus. Ouvrir la pull request.", - "openWithComments_one": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} commentaire non résolu. Ouvrir la pull request.", - "openWithComments_other": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} commentaires non résolus. Ouvrir la pull request.", - "unavailableLabel": "Pull request indisponible : {{message}}" - }, "prCreate": { "authRequired": "Authentifiez-vous avant de créer une {{reviewLabel}}.", "baseNotOnRemote": "Poussez la branche de base avant de créer une {{reviewLabel}}.", @@ -3120,7 +3084,6 @@ "noBranch": "Aucune branche" }, "state": { - "noUpstream": "Aucune branche de suivi", "syncStatus": "{{ahead}} en avance, {{behind}} en retard" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/ja.json b/mobile/src/i18n/locales/ja.json index 65262ea77e0..32b9b5941a2 100644 --- a/mobile/src/i18n/locales/ja.json +++ b/mobile/src/i18n/locales/ja.json @@ -2666,7 +2666,6 @@ "checkingReviewStatus": "レビュー状態を確認中です。", "checkoutBeforePublish": "コミットを公開する前にブランチをチェックアウトしてください。", "commitStagedChanges": "ステージ済みの変更をコミットします。", - "commits": "コミット", "committing": "コミット中です。", "createPr": "PR を作成", "enterCommitMessage": "コミットするには、コミットメッセージを入力してください。", @@ -2694,23 +2693,11 @@ "resolveFailed": "ベースブランチを特定できませんでした。" }, "branchCard": { - "branchCount": "ブランチ内 {{count}} 件", - "changedCount": "変更 {{count}} 件", - "cherryPickState": "チェリーピック", - "mergeState": "マージ", - "rebaseState": "リベース", - "stagedCount": "ステージ済み {{count}} 件", "completingMerge": "完了しています…", "completeMerge": "マージを完了" }, "branchCompare": { - "commits_one": "{{count}} 件のコミット", - "commits_other": "{{count}} 件のコミット", - "files_one": "{{count}} 個のファイル", - "files_other": "{{count}} 個のファイル", - "sectionTitle": "ブランチのコミット済み変更", - "summary": "{{files}} - {{baseRef}} との比較", - "summaryWithCommits": "{{files}} - {{commits}} - {{baseRef}} との比較" + "sectionTitle": "ブランチのコミット済み変更" }, "branchCompareOutcome": { "baseBranchUnavailable": "比較用のベースブランチを特定できませんでした。", @@ -2769,11 +2756,8 @@ "emptyMessage": "ワークツリーに変更はありません。", "emptyTitle": "ローカルの変更なし", "generateMessage": "AIでコミットメッセージを生成", - "openActions": "ソース管理の操作を開く", "reconnecting": "デスクトップに再接続中…", - "regenerateHint": "再生成するには、メッセージをクリアしてください。", - "stageAll": "ステージオール", - "unstageAll": "すべてステージ解除" + "regenerateHint": "再生成するには、メッセージをクリアしてください。" }, "createPrAction": { "checkingStatus": "レビュー状態を確認中…", @@ -2802,20 +2786,15 @@ "titleUnpublishedForcePushCount_one": "{{count}} 件のブランチコミットを lease 付きで強制プッシュし、必要に応じてアップストリームを設定します。", "titleUnpublishedForcePushCount_other": "{{count}} 件のブランチコミットを lease 付きで強制プッシュし、必要に応じてアップストリームを設定します。" }, - "fileList": { - "committedSection": "ブランチのコミット済み変更" - }, "fileRows": { "addedByThem": "相手側で追加", "addedByUs": "こちら側で追加", "bothAdded": "双方で追加", "bothDeleted": "双方で削除", "bothModified": "双方で変更", - "committedSection": "ブランチのコミット済み変更", "committedUnavailable": "コミット済みの変更を表示できません。", "deletedByThem": "相手側で削除", "deletedByUs": "こちら側で削除", - "discardFile": "{{path}} の変更を破棄", "loadingCommitted": "コミット済みの変更を読み込み中…", "openChangedFile": "変更されたファイル {{path}} を開く", "openCommittedChange": "{{path}} のコミット済みの変更を開く", @@ -2848,9 +2827,7 @@ "waitingForDesktop": "デスクトップからの応答を待機中…" }, "gitStatus": { - "changes": "変更点", - "conflicts": "競合", - "stagedChanges": "ステージ済みの変更" + "conflicts": "競合" }, "header": { "backToSession": "セッションに戻る", @@ -2918,7 +2895,6 @@ "waitingForDesktop": "デスクトップへの接続を待機中…" }, "modals": { - "actionsTitle": "ソース管理", "currentBranch": "現在", "discardButton": "破棄", "discardConfirmMessage": "「{{path}}」への変更を破棄しますか?この操作は取り消せません。", @@ -2931,15 +2907,6 @@ "panel": { "unavailableTitle": "ソース管理を利用できません" }, - "prChip": { - "createPr": "PR を作成", - "loading": "PR を読み込み中…", - "loadingLabel": "PR を読み込み中", - "openLabel": "PR #{{number}}、{{state}}、{{checks}}。PR を開く。", - "openWithComments_one": "PR #{{number}}、{{state}}、{{checks}}、未解決のコメント {{count}} 件。PR を開く。", - "openWithComments_other": "PR #{{number}}、{{state}}、{{checks}}、未解決のコメント {{count}} 件。PR を開く。", - "unavailableLabel": "PR を表示できません:{{message}}" - }, "prCreate": { "authRequired": "{{reviewLabel}} を作成する前に認証してください。", "baseNotOnRemote": "{{reviewLabel}} を作成する前にベースブランチをプッシュしてください。", @@ -3027,7 +2994,6 @@ "noBranch": "ブランチなし" }, "state": { - "noUpstream": "アップストリームなし", "syncStatus": "{{ahead}} コミット先行、{{behind}} コミット遅れ" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/ko.json b/mobile/src/i18n/locales/ko.json index 428d61d66d3..bcca2fca555 100644 --- a/mobile/src/i18n/locales/ko.json +++ b/mobile/src/i18n/locales/ko.json @@ -2666,7 +2666,6 @@ "checkingReviewStatus": "리뷰 상태를 확인 중입니다.", "checkoutBeforePublish": "커밋을 게시하기 전에 브랜치를 체크아웃하세요.", "commitStagedChanges": "스테이징된 변경 사항을 커밋하세요.", - "commits": "커밋", "committing": "커밋이 진행 중입니다.", "createPr": "PR 작성", "enterCommitMessage": "커밋하려면 커밋 메시지를 입력하세요.", @@ -2694,23 +2693,11 @@ "resolveFailed": "브랜치의 기준점을 확인할 수 없습니다." }, "branchCard": { - "branchCount": "브랜치 변경 {{count}}개", - "changedCount": "변경 {{count}}개", - "cherryPickState": "체리픽", - "mergeState": "병합", - "rebaseState": "리베이스", - "stagedCount": "{{count}}개 스테이징됨", "completingMerge": "완료 중…", "completeMerge": "병합 완료" }, "branchCompare": { - "commits_one": "커밋 {{count}}개", - "commits_other": "커밋 {{count}}개", - "files_one": "파일 {{count}}개", - "files_other": "파일 {{count}}개", - "sectionTitle": "브랜치에 커밋됨", - "summary": "{{files}} - {{baseRef}} 대비", - "summaryWithCommits": "{{files}} - {{commits}} - {{baseRef}} 대비" + "sectionTitle": "브랜치에 커밋됨" }, "branchCompareOutcome": { "baseBranchUnavailable": "비교할 기본 브랜치를 확인할 수 없습니다.", @@ -2769,11 +2756,8 @@ "emptyMessage": "워크트리에 변경 사항이 없습니다.", "emptyTitle": "로컬 변경 사항 없음", "generateMessage": "AI로 commit 메시지 생성", - "openActions": "소스 제어 작업 열기", "reconnecting": "데스크톱에 다시 연결 중…", - "regenerateHint": "다시 생성하려면 메시지를 지웁니다.", - "stageAll": "모두 스테이지", - "unstageAll": "모두 언스테이징" + "regenerateHint": "다시 생성하려면 메시지를 지웁니다." }, "createPrAction": { "checkingStatus": "리뷰 상태 확인 중…", @@ -2802,20 +2786,15 @@ "titleUnpublishedForcePushCount_one": "브랜치 커밋 {{count}}개를 lease로 강제 푸시하고 필요하면 업스트림을 설정합니다.", "titleUnpublishedForcePushCount_other": "브랜치 커밋 {{count}}개를 lease로 강제 푸시하고 필요하면 업스트림을 설정합니다." }, - "fileList": { - "committedSection": "브랜치에 커밋됨" - }, "fileRows": { "addedByThem": "상대 쪽에서 추가됨", "addedByUs": "우리 쪽에서 추가됨", "bothAdded": "양쪽에서 추가됨", "bothDeleted": "양쪽에서 삭제됨", "bothModified": "양쪽에서 수정됨", - "committedSection": "브랜치에 커밋됨", "committedUnavailable": "커밋된 변경 사항을 확인할 수 없습니다.", "deletedByThem": "상대 쪽에서 삭제됨", "deletedByUs": "우리 쪽에서 삭제됨", - "discardFile": "{{path}} 변경사항 취소", "loadingCommitted": "커밋된 변경 사항 로드 중…", "openChangedFile": "변경된 파일 {{path}} 열기", "openCommittedChange": "{{path}}의 커밋된 변경 사항 열기", @@ -2848,9 +2827,7 @@ "waitingForDesktop": "데스크톱 응답 대기 중…" }, "gitStatus": { - "changes": "변경 사항", - "conflicts": "충돌", - "stagedChanges": "스테이징된 변경 사항" + "conflicts": "충돌" }, "header": { "backToSession": "세션으로 돌아가기", @@ -2918,7 +2895,6 @@ "waitingForDesktop": "데스크톱 연결 대기 중…" }, "modals": { - "actionsTitle": "소스 제어", "currentBranch": "현재", "discardButton": "버리기", "discardConfirmMessage": "\"{{path}}\"의 변경 사항을 버리시겠습니까? 이 작업은 되돌릴 수 없습니다.", @@ -2931,15 +2907,6 @@ "panel": { "unavailableTitle": "소스 제어 사용 불가" }, - "prChip": { - "createPr": "PR 생성", - "loading": "PR 로드 중…", - "loadingLabel": "PR 로드 중", - "openLabel": "PR #{{number}}, {{state}}, {{checks}}. PR 열기.", - "openWithComments_one": "PR #{{number}}, {{state}}, {{checks}}, 해결되지 않은 댓글 {{count}}개. PR 열기.", - "openWithComments_other": "PR #{{number}}, {{state}}, {{checks}}, 해결되지 않은 댓글 {{count}}개. PR 열기.", - "unavailableLabel": "PR 확인 불가: {{message}}" - }, "prCreate": { "authRequired": "{{reviewLabel}} 생성 전에 인증하세요.", "baseNotOnRemote": "{{reviewLabel}}을(를) 생성하기 전에 기본 브랜치를 푸시하세요.", @@ -3027,7 +2994,6 @@ "noBranch": "브랜치 없음" }, "state": { - "noUpstream": "업스트림 없음", "syncStatus": "{{ahead}}개 앞섬, {{behind}}개 뒤처짐" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/pt.json b/mobile/src/i18n/locales/pt.json index 5343c605d4c..93ed011a2b8 100644 --- a/mobile/src/i18n/locales/pt.json +++ b/mobile/src/i18n/locales/pt.json @@ -2742,7 +2742,6 @@ "checkingReviewStatus": "Verificando status da revisão.", "checkoutBeforePublish": "Faça checkout de uma branch antes de publicar commits.", "commitStagedChanges": "Faça commit das alterações staged.", - "commits": "Commits", "committing": "Commit em andamento.", "createPr": "Criar PR", "enterCommitMessage": "Digite uma mensagem de commit para fazer commit.", @@ -2772,25 +2771,11 @@ "resolveFailed": "Não foi possível determinar a branch base" }, "branchCard": { - "branchCount": "{{count}} na branch", - "changedCount": "Alterações: {{count}}", - "cherryPickState": "cherry-pick", - "mergeState": "merge", - "rebaseState": "rebase", - "stagedCount": "No stage: {{count}}", "completingMerge": "Concluindo…", "completeMerge": "Concluir mesclagem" }, "branchCompare": { - "commits_many": "{{count}} commits", - "commits_one": "{{count}} commit", - "commits_other": "{{count}} commits", - "files_many": "{{count}} arquivos", - "files_one": "{{count}} arquivo", - "files_other": "{{count}} arquivos", - "sectionTitle": "Commitado na Branch", - "summary": "{{files}} - em relação a {{baseRef}}", - "summaryWithCommits": "{{files}} - {{commits}} - em relação a {{baseRef}}" + "sectionTitle": "Commitado na Branch" }, "branchCompareOutcome": { "baseBranchUnavailable": "Não foi possível determinar a branch base para comparação.", @@ -2849,11 +2834,8 @@ "emptyMessage": "A árvore de trabalho está limpa.", "emptyTitle": "Nenhuma alteração local", "generateMessage": "Gerar mensagem de commit com IA", - "openActions": "Abrir ações de controle de versão", "reconnecting": "Reconectando ao desktop…", - "regenerateHint": "Limpe a mensagem para gerar novamente.", - "stageAll": "Colocar Tudo em Stage", - "unstageAll": "Remover Tudo do Stage" + "regenerateHint": "Limpe a mensagem para gerar novamente." }, "createPrAction": { "checkingStatus": "Verificando status de review…", @@ -2889,20 +2871,15 @@ "titleUnpublishedForcePushCount_one": "Faça force push com lease de {{count}} commit da branch e defina um upstream se necessário.", "titleUnpublishedForcePushCount_other": "Faça force push com lease de {{count}} commits da branch e defina um upstream se necessário." }, - "fileList": { - "committedSection": "Commitado na Branch" - }, "fileRows": { "addedByThem": "Adicionado por eles", "addedByUs": "Adicionado por nós", "bothAdded": "Adicionado por ambos", "bothDeleted": "Excluído por ambos", "bothModified": "Modificado por ambos", - "committedSection": "Commitado na Branch", "committedUnavailable": "Alterações commitadas indisponíveis.", "deletedByThem": "Excluído por eles", "deletedByUs": "Excluído por nós", - "discardFile": "Descartar {{path}}", "loadingCommitted": "Carregando alterações commitadas…", "openChangedFile": "Abrir arquivo alterado {{path}}", "openCommittedChange": "Abrir alteração commitada em {{path}}", @@ -2940,9 +2917,7 @@ "waitingForDesktop": "Aguardando o desktop…" }, "gitStatus": { - "changes": "Alterações", - "conflicts": "Conflitos", - "stagedChanges": "Alterações no Stage" + "conflicts": "Conflitos" }, "header": { "backToSession": "Voltar à sessão", @@ -3010,7 +2985,6 @@ "waitingForDesktop": "Aguardando o desktop…" }, "modals": { - "actionsTitle": "Controle de Código-Fonte", "currentBranch": "atual", "discardButton": "Descartar", "discardConfirmMessage": "Descartar alterações em \"{{path}}\"? Esta ação não pode ser desfeita.", @@ -3023,16 +2997,6 @@ "panel": { "unavailableTitle": "Controle de Código-Fonte Indisponível" }, - "prChip": { - "createPr": "Criar pull request", - "loading": "Carregando pull request…", - "loadingLabel": "Carregando pull request", - "openLabel": "Pull request #{{number}}, {{state}}, {{checks}}. Abrir pull request.", - "openWithComments_many": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} comentários não resolvidos. Abrir pull request.", - "openWithComments_one": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} comentário não resolvido. Abrir pull request.", - "openWithComments_other": "Pull request #{{number}}, {{state}}, {{checks}}, {{count}} comentários não resolvidos. Abrir pull request.", - "unavailableLabel": "Pull request indisponível: {{message}}" - }, "prCreate": { "authRequired": "Autentique-se antes de criar um {{reviewLabel}}.", "baseNotOnRemote": "Faça push da branch base antes de criar um {{reviewLabel}}.", @@ -3120,7 +3084,6 @@ "noBranch": "Sem branch" }, "state": { - "noUpstream": "Sem upstream", "syncStatus": "{{ahead}} à frente, {{behind}} atrás" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/ru.json b/mobile/src/i18n/locales/ru.json index eeda9c28825..0ca130d1b95 100644 --- a/mobile/src/i18n/locales/ru.json +++ b/mobile/src/i18n/locales/ru.json @@ -2814,7 +2814,6 @@ "checkingReviewStatus": "Получение статуса PR/MR.", "checkoutBeforePublish": "Переключитесь на ветку перед публикацией коммитов.", "commitStagedChanges": "Создать коммит с изменениями в индексе.", - "commits": "Коммиты", "committing": "Создаётся коммит.", "createPr": "Создать PR", "enterCommitMessage": "Введите сообщение, чтобы создать коммит.", @@ -2846,27 +2845,11 @@ "resolveFailed": "Не удалось определить базовую ветку" }, "branchCard": { - "branchCount": "В ветке: {{count}}", - "changedCount": "Изменено: {{count}}", - "cherryPickState": "cherry-pick", - "mergeState": "слияние", - "rebaseState": "перебазирование", - "stagedCount": "В индексе: {{count}}", "completingMerge": "Завершение…", "completeMerge": "Завершить слияние" }, "branchCompare": { - "commits_few": "{{count}} коммита", - "commits_many": "{{count}} коммитов", - "commits_one": "{{count}} коммит", - "commits_other": "{{count}} коммита", - "files_few": "{{count}} файла", - "files_many": "{{count}} файлов", - "files_one": "{{count}} файл", - "files_other": "{{count}} файла", - "sectionTitle": "Закоммичено в ветке", - "summary": "{{files}} — по сравнению с {{baseRef}}", - "summaryWithCommits": "{{files}} — {{commits}} — по сравнению с {{baseRef}}" + "sectionTitle": "Закоммичено в ветке" }, "branchCompareOutcome": { "baseBranchUnavailable": "Не удалось определить базовую ветку для сравнения.", @@ -2925,11 +2908,8 @@ "emptyMessage": "В рабочем дереве нет изменений.", "emptyTitle": "Нет локальных изменений", "generateMessage": "Создать сообщение коммита с помощью ИИ", - "openActions": "Открыть меню управления версиями", "reconnecting": "Переподключение к компьютеру…", - "regenerateHint": "Очистите сообщение для повторного создания.", - "stageAll": "Добавить всё в индекс", - "unstageAll": "Убрать всё из индекса" + "regenerateHint": "Очистите сообщение для повторного создания." }, "createPrAction": { "checkingStatus": "Получение статуса PR/MR…", @@ -2972,20 +2952,15 @@ "titleUnpublishedForcePushCount_one": "Принудительно отправьте {{count}} коммит этой ветки с проверкой состояния удалённой ветки и при необходимости настройте отслеживаемую удалённую ветку.", "titleUnpublishedForcePushCount_other": "Принудительно отправьте {{count}} коммита этой ветки с проверкой состояния удалённой ветки и при необходимости настройте отслеживаемую удалённую ветку." }, - "fileList": { - "committedSection": "Закоммичено в ветке" - }, "fileRows": { "addedByThem": "Добавлен ими", "addedByUs": "Добавлен нами", "bothAdded": "Добавлен с обеих сторон", "bothDeleted": "Удалён с обеих сторон", "bothModified": "Изменён с обеих сторон", - "committedSection": "Закоммичено в ветке", "committedUnavailable": "Закоммиченные изменения недоступны.", "deletedByThem": "Удалён ими", "deletedByUs": "Удалён нами", - "discardFile": "Отменить изменения в «{{path}}»", "loadingCommitted": "Загрузка закоммиченных изменений…", "openChangedFile": "Открыть изменённый файл {{path}}", "openCommittedChange": "Открыть закоммиченные изменения в «{{path}}»", @@ -3028,9 +3003,7 @@ "waitingForDesktop": "Ожидание ответа от компьютера…" }, "gitStatus": { - "changes": "Изменения", - "conflicts": "Конфликты", - "stagedChanges": "Изменения в индексе" + "conflicts": "Конфликты" }, "header": { "backToSession": "Вернуться к сессии", @@ -3098,7 +3071,6 @@ "waitingForDesktop": "Ожидание подключения к компьютеру…" }, "modals": { - "actionsTitle": "Управление версиями", "currentBranch": "текущая", "discardButton": "Отменить изменения", "discardConfirmMessage": "Отменить изменения в «{{path}}»? Это действие необратимо.", @@ -3111,17 +3083,6 @@ "panel": { "unavailableTitle": "Управление версиями недоступно" }, - "prChip": { - "createPr": "Создать пул-реквест", - "loading": "Загрузка пул-реквеста…", - "loadingLabel": "Загрузка пул-реквеста", - "openLabel": "Пул-реквест #{{number}}, {{state}}, {{checks}}. Открыть пул-реквест.", - "openWithComments_few": "Пул-реквест #{{number}}, {{state}}, {{checks}}, {{count}} неразрешённых комментария. Открыть пул-реквест.", - "openWithComments_many": "Пул-реквест #{{number}}, {{state}}, {{checks}}, {{count}} неразрешённых комментариев. Открыть пул-реквест.", - "openWithComments_one": "Пул-реквест #{{number}}, {{state}}, {{checks}}, {{count}} неразрешённый комментарий. Открыть пул-реквест.", - "openWithComments_other": "Пул-реквест #{{number}}, {{state}}, {{checks}}, {{count}} неразрешённого комментария. Открыть пул-реквест.", - "unavailableLabel": "Пул-реквест недоступен: {{message}}" - }, "prCreate": { "authRequired": "Прежде чем создать {{reviewLabel}}, пройдите аутентификацию.", "baseNotOnRemote": "Прежде чем создать {{reviewLabel}}, отправьте базовую ветку.", @@ -3209,7 +3170,6 @@ "noBranch": "Нет ветки" }, "state": { - "noUpstream": "Нет отслеживаемой ветки", "syncStatus": "Опережает на {{ahead}}, отстаёт на {{behind}}" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/tr.json b/mobile/src/i18n/locales/tr.json index b7acbef6c26..4706bed8d0f 100644 --- a/mobile/src/i18n/locales/tr.json +++ b/mobile/src/i18n/locales/tr.json @@ -2670,7 +2670,6 @@ "checkingReviewStatus": "İnceleme durumu kontrol ediliyor.", "checkoutBeforePublish": "Commit'leri yayınlamadan önce bir branch'i checkout edin.", "commitStagedChanges": "Stage edilmiş değişiklikleri commit'leyin.", - "commits": "Commit'ler", "committing": "Commit işlemi sürüyor.", "createPr": "PR Oluştur", "enterCommitMessage": "Commit'lemek için bir commit mesajı girin.", @@ -2698,23 +2697,11 @@ "resolveFailed": "Temel branch belirlenemedi" }, "branchCard": { - "branchCount": "branch'te {{count}}", - "changedCount": "{{count}} değişiklik", - "cherryPickState": "cherry-pick", - "mergeState": "merge", - "rebaseState": "rebase", - "stagedCount": "{{count}} stage edildi", "completingMerge": "Tamamlanıyor…", "completeMerge": "Birleştirmeyi tamamla" }, "branchCompare": { - "commits_one": "{{count}} commit", - "commits_other": "{{count}} commit", - "files_one": "{{count}} dosya", - "files_other": "{{count}} dosya", - "sectionTitle": "Branch'e Commit Edildi", - "summary": "{{files}} - {{baseRef}} ile karşılaştırma", - "summaryWithCommits": "{{files}} - {{commits}} - {{baseRef}} ile karşılaştırma" + "sectionTitle": "Branch'e Commit Edildi" }, "branchCompareOutcome": { "baseBranchUnavailable": "Karşılaştırma için temel branch belirlenemedi.", @@ -2773,11 +2760,8 @@ "emptyMessage": "Çalışma ağacı temiz.", "emptyTitle": "Yerel değişiklik yok", "generateMessage": "Commit mesajını AI ile oluştur", - "openActions": "Kaynak kontrolü eylemlerini aç", "reconnecting": "Masaüstüne yeniden bağlanılıyor…", - "regenerateHint": "Yeniden oluşturmak için mesajı temizleyin.", - "stageAll": "Tümünü Stage Et", - "unstageAll": "Tümünü Stage'den Çıkar" + "regenerateHint": "Yeniden oluşturmak için mesajı temizleyin." }, "createPrAction": { "checkingStatus": "İnceleme durumu kontrol ediliyor…", @@ -2806,20 +2790,15 @@ "titleUnpublishedForcePushCount_one": "Branch'teki {{count}} commit'i lease korumasıyla force push edin ve gerekirse bir upstream ayarlayın.", "titleUnpublishedForcePushCount_other": "Branch'teki {{count}} commit'i lease korumasıyla force push edin ve gerekirse bir upstream ayarlayın." }, - "fileList": { - "committedSection": "Branch'e Commit'lendi" - }, "fileRows": { "addedByThem": "Karşı tarafça eklendi", "addedByUs": "Tarafımızdan eklendi", "bothAdded": "İki tarafta da eklendi", "bothDeleted": "İki tarafta da silindi", "bothModified": "İki tarafta da değiştirildi", - "committedSection": "Branch'e Commit'lendi", "committedUnavailable": "Commit'lenen değişiklikler kullanılamıyor.", "deletedByThem": "Karşı tarafça silindi", "deletedByUs": "Tarafımızdan silindi", - "discardFile": "{{path}} değişikliklerini at", "loadingCommitted": "Commit'lenen değişiklikler yükleniyor…", "openChangedFile": "Değiştirilen {{path}} dosyasını aç", "openCommittedChange": "{{path}} için commit'lenen değişikliği aç", @@ -2852,9 +2831,7 @@ "waitingForDesktop": "Masaüstü bekleniyor…" }, "gitStatus": { - "changes": "Değişiklikler", - "conflicts": "Conflict'ler", - "stagedChanges": "Stage Edilen Değişiklikler" + "conflicts": "Conflict'ler" }, "header": { "backToSession": "Oturuma dön", @@ -2922,7 +2899,6 @@ "waitingForDesktop": "Masaüstü bekleniyor…" }, "modals": { - "actionsTitle": "Kaynak Kontrolü", "currentBranch": "geçerli", "discardButton": "At", "discardConfirmMessage": "“{{path}}” dosyasındaki değişiklikler atılsın mı? Bu işlem geri alınamaz.", @@ -2935,15 +2911,6 @@ "panel": { "unavailableTitle": "Kaynak Kontrolü Kullanılamıyor" }, - "prChip": { - "createPr": "Pull request oluştur", - "loading": "Pull request yükleniyor…", - "loadingLabel": "Pull request yükleniyor", - "openLabel": "#{{number}} numaralı pull request, {{state}}, {{checks}}. Pull request'i aç.", - "openWithComments_one": "#{{number}} numaralı pull request, {{state}}, {{checks}}, {{count}} çözülmemiş yorum. Pull request'i aç.", - "openWithComments_other": "#{{number}} numaralı pull request, {{state}}, {{checks}}, {{count}} çözülmemiş yorum. Pull request'i aç.", - "unavailableLabel": "Pull request kullanılamıyor: {{message}}" - }, "prCreate": { "authRequired": "Bir {{reviewLabel}} oluşturmadan önce kimliğinizi doğrulayın.", "baseNotOnRemote": "Bir {{reviewLabel}} oluşturmadan önce temel branch'i push edin.", @@ -3031,7 +2998,6 @@ "noBranch": "Branch yok" }, "state": { - "noUpstream": "Upstream yok", "syncStatus": "{{ahead}} ileride, {{behind}} geride" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/zh-Hant.json b/mobile/src/i18n/locales/zh-Hant.json index 4f420e895c8..2377832a35d 100644 --- a/mobile/src/i18n/locales/zh-Hant.json +++ b/mobile/src/i18n/locales/zh-Hant.json @@ -2666,7 +2666,6 @@ "checkingReviewStatus": "正在檢查審查狀態。", "checkoutBeforePublish": "請先檢出分支再發布提交。", "commitStagedChanges": "提交已暫存的變更。", - "commits": "提交", "committing": "正在提交。", "createPr": "建立 PR", "enterCommitMessage": "請輸入提交訊息以進行提交。", @@ -2694,23 +2693,11 @@ "resolveFailed": "無法判定分支基準" }, "branchCard": { - "branchCount": "已提交 {{count}} 項", - "changedCount": "已變更 {{count}} 項", - "cherryPickState": "挑選", - "mergeState": "合併", - "rebaseState": "變基", - "stagedCount": "已暫存 {{count}} 項", "completingMerge": "正在完成…", "completeMerge": "完成合併" }, "branchCompare": { - "commits_one": "{{count}} 筆提交", - "commits_other": "{{count}} 筆提交", - "files_one": "{{count}} 個檔案", - "files_other": "{{count}} 個檔案", - "sectionTitle": "已在分支上提交", - "summary": "{{files}} - 與 {{baseRef}} 比較", - "summaryWithCommits": "{{files}} - {{commits}} - 與 {{baseRef}} 比較" + "sectionTitle": "已在分支上提交" }, "branchCompareOutcome": { "baseBranchUnavailable": "無法判定用於比較的基礎分支。", @@ -2769,11 +2756,8 @@ "emptyMessage": "工作樹沒有變更。", "emptyTitle": "沒有本機變更", "generateMessage": "使用 AI 產生提交訊息", - "openActions": "開啟原始碼控制操作", "reconnecting": "正在重新連線至電腦…", - "regenerateHint": "清除訊息以重新產生。", - "stageAll": "暫存全部", - "unstageAll": "全部取消暫存" + "regenerateHint": "清除訊息以重新產生。" }, "createPrAction": { "checkingStatus": "正在檢查審查狀態…", @@ -2802,20 +2786,15 @@ "titleUnpublishedForcePushCount_one": "使用租約強制推送此分支的 {{count}} 筆提交,並視需要設定上游分支。", "titleUnpublishedForcePushCount_other": "使用租約強制推送此分支的 {{count}} 筆提交,並視需要設定上游分支。" }, - "fileList": { - "committedSection": "已在分支上提交" - }, "fileRows": { "addedByThem": "對方已新增", "addedByUs": "我方已新增", "bothAdded": "雙方皆已新增", "bothDeleted": "雙方皆已刪除", "bothModified": "雙方皆已修改", - "committedSection": "已在分支上提交", "committedUnavailable": "無法取得已提交的變更。", "deletedByThem": "對方已刪除", "deletedByUs": "我方已刪除", - "discardFile": "放棄 {{path}} 的變更", "loadingCommitted": "正在載入已提交的變更…", "openChangedFile": "開啟已變更的檔案 {{path}}", "openCommittedChange": "開啟 {{path}} 的已提交變更", @@ -2848,9 +2827,7 @@ "waitingForDesktop": "正在等待電腦…" }, "gitStatus": { - "changes": "變更", - "conflicts": "衝突", - "stagedChanges": "已暫存的變更" + "conflicts": "衝突" }, "header": { "backToSession": "返回工作階段", @@ -2918,7 +2895,6 @@ "waitingForDesktop": "等待電腦…" }, "modals": { - "actionsTitle": "原始碼控制", "currentBranch": "目前分支", "discardButton": "放棄", "discardConfirmMessage": "要放棄「{{path}}」的變更嗎?此操作無法復原。", @@ -2931,15 +2907,6 @@ "panel": { "unavailableTitle": "無法使用原始碼控制" }, - "prChip": { - "createPr": "建立 PR", - "loading": "正在載入 PR…", - "loadingLabel": "正在載入 PR", - "openLabel": "PR #{{number}},{{state}},{{checks}}。開啟 PR。", - "openWithComments_one": "PR #{{number}},{{state}},{{checks}},有 {{count}} 則未解決的留言。開啟 PR。", - "openWithComments_other": "PR #{{number}},{{state}},{{checks}},有 {{count}} 則未解決的留言。開啟 PR。", - "unavailableLabel": "無法取得 PR:{{message}}" - }, "prCreate": { "authRequired": "請在建立 {{reviewLabel}} 之前完成身分驗證。", "baseNotOnRemote": "建立 {{reviewLabel}} 前,請先推送基準分支。", @@ -3027,7 +2994,6 @@ "noBranch": "無分支" }, "state": { - "noUpstream": "無上游分支", "syncStatus": "領先 {{ahead}} 個提交,落後 {{behind}} 個提交" }, "textGenerationFailure": { diff --git a/mobile/src/i18n/locales/zh.json b/mobile/src/i18n/locales/zh.json index 335d1c310fb..4c83f3760a4 100644 --- a/mobile/src/i18n/locales/zh.json +++ b/mobile/src/i18n/locales/zh.json @@ -2666,7 +2666,6 @@ "checkingReviewStatus": "正在检查评审状态。", "checkoutBeforePublish": "请先检出分支再发布提交。", "commitStagedChanges": "提交已暂存的更改。", - "commits": "提交", "committing": "正在提交。", "createPr": "创建PR", "enterCommitMessage": "请输入提交信息后再提交。", @@ -2694,23 +2693,11 @@ "resolveFailed": "无法解析基础分支" }, "branchCard": { - "branchCount": "已提交 {{count}} 个文件", - "changedCount": "已更改 {{count}} 个文件", - "cherryPickState": "cherry-pick", - "mergeState": "合并", - "rebaseState": "变基", - "stagedCount": "已暂存 {{count}} 个文件", "completingMerge": "正在完成…", "completeMerge": "完成合并" }, "branchCompare": { - "commits_one": "{{count}} 次提交", - "commits_other": "{{count}} 次提交", - "files_one": "{{count}} 个文件", - "files_other": "{{count}} 个文件", - "sectionTitle": "已提交的更改", - "summary": "{{files}} - 与 {{baseRef}} 比较", - "summaryWithCommits": "{{files}} - {{commits}} - 与 {{baseRef}} 比较" + "sectionTitle": "已提交的更改" }, "branchCompareOutcome": { "baseBranchUnavailable": "无法解析用于比较的基础分支。", @@ -2769,11 +2756,8 @@ "emptyMessage": "工作树无更改。", "emptyTitle": "无本地更改", "generateMessage": "使用 AI 生成提交信息", - "openActions": "打开源代码控制操作菜单", "reconnecting": "正在重新连接桌面…", - "regenerateHint": "清除消息以重新生成。", - "stageAll": "暂存全部", - "unstageAll": "取消全部暂存" + "regenerateHint": "清除消息以重新生成。" }, "createPrAction": { "checkingStatus": "正在检查评审状态…", @@ -2802,20 +2786,15 @@ "titleUnpublishedForcePushCount_one": "使用 lease 强制推送 {{count}} 个分支提交,并在需要时设置上游。", "titleUnpublishedForcePushCount_other": "使用 lease 强制推送 {{count}} 个分支提交,并在需要时设置上游。" }, - "fileList": { - "committedSection": "已提交的更改" - }, "fileRows": { "addedByThem": "对方已添加", "addedByUs": "我方已添加", "bothAdded": "双方均已添加", "bothDeleted": "双方均已删除", "bothModified": "双方均已修改", - "committedSection": "已提交的更改", "committedUnavailable": "已提交的更改不可用。", "deletedByThem": "对方已删除", "deletedByUs": "我方已删除", - "discardFile": "放弃 {{path}} 的更改", "loadingCommitted": "正在加载已提交的更改…", "openChangedFile": "打开已更改的文件 {{path}}", "openCommittedChange": "打开 {{path}} 中已提交的更改", @@ -2848,9 +2827,7 @@ "waitingForDesktop": "正在等待桌面…" }, "gitStatus": { - "changes": "更改", - "conflicts": "冲突", - "stagedChanges": "已暂存的更改" + "conflicts": "冲突" }, "header": { "backToSession": "返回会话", @@ -2918,7 +2895,6 @@ "waitingForDesktop": "正在等待桌面…" }, "modals": { - "actionsTitle": "源代码控制", "currentBranch": "当前", "discardButton": "放弃", "discardConfirmMessage": "放弃对“{{path}}”的更改?此操作无法撤销。", @@ -2931,15 +2907,6 @@ "panel": { "unavailableTitle": "源代码控制不可用" }, - "prChip": { - "createPr": "创建 PR", - "loading": "正在加载 PR…", - "loadingLabel": "正在加载 PR", - "openLabel": "PR #{{number}},{{state}},{{checks}}。打开 PR。", - "openWithComments_one": "PR #{{number}},{{state}},{{checks}},{{count}} 条未解决的评论。打开 PR。", - "openWithComments_other": "PR #{{number}},{{state}},{{checks}},{{count}} 条未解决的评论。打开 PR。", - "unavailableLabel": "PR 不可用:{{message}}" - }, "prCreate": { "authRequired": "请在创建 {{reviewLabel}} 之前完成身份验证。", "baseNotOnRemote": "请先推送基础分支,再创建 {{reviewLabel}}。", @@ -3027,7 +2994,6 @@ "noBranch": "无分支" }, "state": { - "noUpstream": "无上游分支", "syncStatus": "领先 {{ahead}},落后 {{behind}}" }, "textGenerationFailure": { diff --git a/mobile/src/source-control/MobileSourceControlBranchCard.tsx b/mobile/src/source-control/MobileSourceControlBranchCard.tsx deleted file mode 100644 index 44862bcddb9..00000000000 --- a/mobile/src/source-control/MobileSourceControlBranchCard.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { Pressable, Text, View } from 'react-native' -import { GitBranch } from 'lucide-react-native' -import { colors } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { styles } from './mobile-source-control-styles' -import { MobileSourceControlPrChip } from './MobileSourceControlPrChip' -import type { MobilePrChipSummary } from './mobile-pr-chip-summary' -import { mobileConflictAbortLabel } from './mobile-source-control-conflict-abort' - -type Props = { - branchLabel: string - syncLabel: string | null - unstagedCount: number - stagedCount: number - branchCount: number - conflictOperation: string | null - conflictBusy: boolean - conflictAborting: boolean - onAbortConflict: (operation: string) => void - prChip: MobilePrChipSummary | null - onOpenPr: () => void -} - -export function MobileSourceControlBranchCard({ - branchLabel, - syncLabel, - unstagedCount, - stagedCount, - branchCount, - conflictOperation, - conflictBusy, - conflictAborting, - onAbortConflict, - prChip, - onOpenPr -}: Props) { - useMobileColorScheme() - const t = useMobileTranslation() - const showConflict = conflictOperation !== null && conflictOperation !== 'unknown' - const conflictLabel = - conflictOperation === 'merge' - ? t('sourceControl.branchCard.mergeState', 'merge') - : conflictOperation === 'rebase' - ? t('sourceControl.branchCard.rebaseState', 'rebase') - : conflictOperation === 'cherry-pick' - ? t('sourceControl.branchCard.cherryPickState', 'cherry-pick') - : conflictOperation - return ( - - - - - - {branchLabel} - - - {syncLabel ? {syncLabel} : null} - - - - {t('sourceControl.branchCard.changedCount', '{{count}} changed', { - count: unstagedCount - })} - - - {t('sourceControl.branchCard.stagedCount', '{{count}} staged', { count: stagedCount })} - - {branchCount > 0 ? ( - - {t('sourceControl.branchCard.branchCount', '{{count}} on branch', { - count: branchCount - })} - - ) : null} - - {/* Keep Abort on its own row so long counts cannot overflow the card. */} - {showConflict ? ( - - {conflictLabel} - {conflictOperation === 'merge' || conflictOperation === 'rebase' ? ( - [ - styles.abortButton, - conflictBusy && styles.abortButtonDisabled, - pressed && !conflictBusy && styles.abortPressed - ]} - disabled={conflictBusy} - onPress={() => onAbortConflict(conflictOperation)} - > - - {mobileConflictAbortLabel(conflictOperation, conflictAborting)} - - - ) : null} - - ) : null} - {prChip ? : null} - - ) -} diff --git a/mobile/src/source-control/MobileSourceControlChangesHeader.tsx b/mobile/src/source-control/MobileSourceControlChangesHeader.tsx new file mode 100644 index 00000000000..c7e8e4ccf8f --- /dev/null +++ b/mobile/src/source-control/MobileSourceControlChangesHeader.tsx @@ -0,0 +1,192 @@ +import { Pressable, Text, View } from 'react-native' +import { + ChevronDown, + ChevronRight, + GitBranch, + GitCommitHorizontal, + TriangleAlert +} from 'lucide-react-native' +import { colors } from '../theme/mobile-theme' +import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import type { Translate } from '../i18n/mobile-i18n' +import { useMobileTranslation } from '../i18n/use-mobile-translation' +import type { GitConflictOperation } from '../../../src/shared/git-status-types' +import { + isMobileConflictAborting, + mobileConflictAbortLabel +} from './mobile-source-control-conflict-abort' +import type { MobileSourceControlState } from './use-mobile-source-control-state' +import { styles } from './mobile-source-control-styles' + +type HeaderState = Pick< + MobileSourceControlState, + | 'status' + | 'branchLabel' + | 'branchCompareResult' + | 'busyAction' + | 'openBranchPicker' + | 'abortConflictOperation' +> + +function syncMeta( + status: MobileSourceControlState['status'], + operation: GitConflictOperation | null, + t: Translate +): string | null { + switch (operation) { + case 'merge': + return t('sourceControl.changesHeader.merging', 'Merging') + case 'rebase': + return t('sourceControl.changesHeader.rebasing', 'Rebasing') + case 'cherry-pick': + return t('sourceControl.changesHeader.cherryPicking', 'Cherry-picking') + case 'unknown': + case null: + break + } + const upstream = status?.upstreamStatus + if (!upstream) { + return null + } + if (!upstream.hasUpstream) { + return t('sourceControl.changesHeader.notPublished', 'Not published') + } + if (upstream.ahead > 0 && upstream.behind > 0) { + return t('sourceControl.state.syncStatus', '{{ahead}} ahead, {{behind}} behind', upstream) + } + if (upstream.ahead > 0) { + return t('sourceControl.changesHeader.notPushed', '{{count}} not pushed', { + count: upstream.ahead + }) + } + if (upstream.behind > 0) { + return t('sourceControl.changesHeader.behind', '{{count}} behind', { count: upstream.behind }) + } + return t('sourceControl.changesHeader.upToDate', 'Up to date') +} + +/** Above the Changes list: the branch and its sync state, the Commits row, and a paused merge. */ +export function MobileSourceControlChangesHeader({ + state, + onOpenCommits +}: { + state: HeaderState + onOpenCommits: () => void +}) { + useMobileColorScheme() + const t = useMobileTranslation() + const { status, busyAction } = state + const rawOperation = status?.conflictOperation ?? null + const operation = rawOperation === 'unknown' ? null : rawOperation + const meta = syncMeta(status, operation, t) + const commits = state.branchCompareResult?.summary.commitsAhead ?? 0 + const unresolved = + status?.entries.filter((entry) => entry.conflictStatus === 'unresolved').length ?? 0 + const aborting = isMobileConflictAborting(busyAction, operation) + return ( + + + [styles.branchButton, pressed && styles.sectionActionPressed]} + onPress={state.openBranchPicker} + disabled={busyAction !== null} + accessibilityRole="button" + accessibilityLabel={t( + 'sourceControl.changesHeader.switchBranch', + 'Switch branch from {{branch}}', + { + branch: state.branchLabel + } + )} + > + + + {state.branchLabel} + + + + {meta ? {meta} : null} + + {operation ? ( + + + + + {operation === 'merge' + ? t('sourceControl.changesHeader.mergeInProgress', 'Merge in progress') + : operation === 'rebase' + ? t('sourceControl.changesHeader.rebaseInProgress', 'Rebase in progress') + : t( + 'sourceControl.changesHeader.cherryPickInProgress', + 'Cherry-pick in progress' + )} + + {operation === 'merge' || operation === 'rebase' ? ( + [ + styles.abortButton, + busyAction !== null && styles.abortButtonDisabled, + pressed && busyAction === null && styles.abortPressed + ]} + disabled={busyAction !== null} + onPress={() => void state.abortConflictOperation(operation)} + accessibilityLabel={mobileConflictAbortLabel(operation, aborting)} + > + + {mobileConflictAbortLabel(operation, aborting)} + + + ) : null} + + + {unresolved > 0 + ? unresolved === 1 + ? t( + 'sourceControl.changesHeader.filesNeedDecision_one', + '{{count}} file needs a decision before you can finish.', + { count: unresolved } + ) + : t( + 'sourceControl.changesHeader.filesNeedDecision_other', + '{{count}} files need a decision before you can finish.', + { count: unresolved } + ) + : status?.didHitLimit === true + ? // A capped listing can hide the remaining conflicts; never claim they're done. + t( + 'sourceControl.changesHeader.conflictsMayRemain', + 'Not every file is listed, so conflicts may remain. Check on your computer.' + ) + : operation === 'merge' + ? t( + 'sourceControl.changesHeader.conflictsResolved', + 'Every conflict is resolved. Finish from the bar below.' + ) + : // Mobile has no continue for a rebase or cherry-pick. + t( + 'sourceControl.changesHeader.continueOnComputer', + 'Every conflict is resolved. Continue on your computer to finish.' + )} + + + ) : null} + {commits > 0 ? ( + [styles.commitsRow, pressed && styles.fileRowPressed]} + onPress={onOpenCommits} + accessibilityRole="button" + > + + + {commits === 1 + ? t('sourceControl.changesHeader.commits_one', '{{count}} commit', { count: commits }) + : t('sourceControl.changesHeader.commits_other', '{{count}} commits', { + count: commits + })} + + + + ) : null} + + ) +} diff --git a/mobile/src/source-control/MobileSourceControlContent.test.tsx b/mobile/src/source-control/MobileSourceControlContent.test.tsx index 9716e64b8b3..c40b3d2f57e 100644 --- a/mobile/src/source-control/MobileSourceControlContent.test.tsx +++ b/mobile/src/source-control/MobileSourceControlContent.test.tsx @@ -9,7 +9,6 @@ import type { MobileSourceControlState } from './use-mobile-source-control-state type MobileSourceControlContentState = Omit & { primaryAction: MobileSourceControlPrimaryAction } -import * as branchFormat from './mobile-branch-entry-format' import type { SectionListProps } from 'react-native' import type { MobileBranchEntryView, @@ -23,7 +22,8 @@ vi.mock('react-native', () => ({ SectionList: (props: SectionListProps) => createElement( 'SectionList', - props, + { ...props, ListHeaderComponent: undefined }, + typeof props.ListHeaderComponent === 'function' ? null : props.ListHeaderComponent, typeof props.ListFooterComponent === 'function' ? createElement(props.ListFooterComponent) : props.ListFooterComponent, @@ -53,16 +53,42 @@ vi.mock('react-native', () => ({ View: 'View' })) vi.mock('expo-clipboard', () => ({ setStringAsync: async () => {} })) +// Counts row renders: every file row draws its line stats once. +const rowRenders = vi.hoisted(() => ({ count: 0 })) +vi.mock('../components/MobileDiffReviewFileSummary', () => ({ + MobileDiffLineStats: () => { + rowRenders.count += 1 + return null + } +})) +vi.mock('react-native-gesture-handler', async () => { + const chain = new Proxy({}, { get: () => () => chain }) + return { + Gesture: { Pan: () => chain }, + GestureDetector: ({ children }: { children: unknown }) => children + } +}) +vi.mock('react-native-reanimated', () => ({ + default: { View: ({ children }: { children: unknown }) => children }, + Easing: { bezier: () => () => 0 }, + ReduceMotion: { Never: 'never' }, + useAnimatedStyle: () => ({}), + useReducedMotion: () => false, + useSharedValue: (value: number) => ({ value }), + withTiming: (value: number) => value +})) vi.mock('../components/ConfirmModal', () => ({ ConfirmModal: () => null })) vi.mock('lucide-react-native', () => ({ ArrowDown: 'ArrowDown', ArrowDownUp: 'ArrowDownUp', ArrowUp: 'ArrowUp', Check: 'Check', + ChevronDown: 'ChevronDown', ChevronRight: 'ChevronRight', CloudUpload: 'CloudUpload', FileText: 'FileText', GitBranch: 'GitBranch', + GitCommitHorizontal: 'GitCommitHorizontal', GitMerge: 'GitMerge', GitPullRequest: 'GitPullRequest', GitPullRequestArrow: 'GitPullRequestArrow', @@ -72,7 +98,9 @@ vi.mock('lucide-react-native', () => ({ Plus: 'Plus', RefreshCw: 'RefreshCw', Sparkles: 'Sparkles', - Trash2: 'Trash2' + Trash2: 'Trash2', + TriangleAlert: 'TriangleAlert', + Undo2: 'Undo2' })) const noop = () => {} @@ -154,7 +182,6 @@ function initialState(): MobileSourceControlContentState { status: null, sections: [], branchCompareResult, - branchCompareSummaryText: '20 files vs main', branchEntries, shouldShowBranchCompareSection: true, hasVisibleChanges: true, @@ -164,7 +191,6 @@ function initialState(): MobileSourceControlContentState { unstagedCount: 0, branchLabel: 'fixture', upstreamKnown: false, - syncLabel: null, primaryAction: { kind: 'commit', label: 'Commit', @@ -227,7 +253,11 @@ function Fixture({ initial }: { initial: MobileSourceControlContentState }) { return createElement( 'View', null, - createElement(MobileSourceControlContent, { state: liveState, bottomInset: 0 }), + createElement(MobileSourceControlContent, { + state: liveState, + bottomInset: 0, + onOpenCommits: noop + }), createElement(MobileSourceControlNextStepBar, { view: { kind: 'composer' }, notice: null, @@ -263,7 +293,10 @@ function rows() { ) } function text() { - return JSON.stringify(renderer?.toJSON()) + // Section headers carry React elements in props; serialize only what renders. + return JSON.stringify(renderer?.toJSON(), (key, value) => + key === 'sections' ? undefined : value + ) } afterEach(() => { @@ -279,10 +312,10 @@ describe('committed branch files during commit-message typing', () => { if (layout === 'dirty') { initial.sections = [{ id: 'staged', area: 'staged', title: 'Staged Changes', data: [] }] } - const format = vi.spyOn(branchFormat, 'formatMobileBranchEntryMeta') + rowRenders.count = 0 render(initial) - expect(format).toHaveBeenCalledTimes(20) - format.mockClear() + expect(rowRenders.count).toBe(20) + rowRenders.count = 0 for (let index = 0; index < 50; index++) { act(() => { const input = renderer?.root.find((node) => String(node.type) === 'TextInput') @@ -296,7 +329,7 @@ describe('committed branch files during commit-message typing', () => { 'message 49' ) expect(rows()).toHaveLength(20) - expect(format).not.toHaveBeenCalled() + expect(rowRenders.count).toBe(0) }) it.each([false, true])( @@ -324,10 +357,7 @@ describe('committed branch files during commit-message typing', () => { it('updates rows and section messages when their inputs change', () => { const initial = initialState() - const format = vi.spyOn(branchFormat, 'formatMobileBranchEntryMeta') render(initial) - patch({ branchCompareSummaryText: 'changed summary' }) - expect(text()).toContain('changed summary') const first = initial.branchEntries[0] if (!first) { throw new Error('missing fixture entry') @@ -370,14 +400,12 @@ describe('committed branch files during commit-message typing', () => { patch({ branchCompareResult: initial.branchCompareResult }) expect(rows()).toHaveLength(1) patch({ shouldShowBranchCompareSection: false }) - expect(text()).not.toContain('Committed on Branch') + expect(text()).not.toContain('On this branch') patch({ shouldShowBranchCompareSection: true }) expect(rows()).toHaveLength(1) - format.mockClear() - patch({ connState: 'disconnected', actionError: 'offline', keyboardLift: 100 }) + patch({ connState: 'disconnected', actionError: 'offline' }) expect(text()).toContain('Reconnecting to desktop') expect(text()).toContain('offline') - expect(format).not.toHaveBeenCalled() }) }) diff --git a/mobile/src/source-control/MobileSourceControlContent.tsx b/mobile/src/source-control/MobileSourceControlContent.tsx index 8742c66e25d..aace32ca2d9 100644 --- a/mobile/src/source-control/MobileSourceControlContent.tsx +++ b/mobile/src/source-control/MobileSourceControlContent.tsx @@ -1,11 +1,11 @@ import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react' -import { ActivityIndicator, Animated, Easing, Pressable, Text, View } from 'react-native' -import { Minus, MoreHorizontal, Plus } from 'lucide-react-native' +import { ActivityIndicator, Animated, Easing, Text, View } from 'react-native' import { colors, spacing } from '../theme/mobile-theme' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../i18n/use-mobile-translation' import { useReducedMotionEnabled } from '../platform/use-reduced-motion' import { SHEET_CURVE } from '../components/sheet-motion-values' +import { MobileSourceControlChangesHeader } from './MobileSourceControlChangesHeader' import { MobileCommitFailurePanel } from './MobileCommitFailurePanel' import { MobileSourceControlFileList } from './MobileSourceControlFileList' import type { MobileSourceControlState } from './use-mobile-source-control-state' @@ -44,31 +44,13 @@ type Props = { state: Omit /** Clears the next-step bar the panel floats over the list. */ bottomInset: number + onOpenCommits: () => void } -export function MobileSourceControlContent({ state, bottomInset }: Props) { +export function MobileSourceControlContent({ state, bottomInset, onOpenCommits }: Props) { useMobileColorScheme() const t = useMobileTranslation() - const { - connState, - busyAction, - setShowActionSheet, - setDiscardTarget, - actionError, - commitFailureRecovery, - commitFailureRecoveryAction, - sections, - hasVisibleChanges, - status, - stageablePaths, - unstageablePaths, - stageAll, - unstageAll, - openDiffForFile, - resolveConflict, - runGitAction - } = state - const ioBusy = busyAction !== null + const { busyAction, hasVisibleChanges } = state // Commit runners keep busyAction set ('commit…') until the refreshed status has landed. const [listShown, setListShown] = useState(hasVisibleChanges) const [cleanedByCommit, setCleanedByCommit] = useState(false) @@ -76,56 +58,111 @@ export function MobileSourceControlContent({ state, bottomInset }: Props) { setListShown(hasVisibleChanges) setCleanedByCommit(!hasVisibleChanges && busyAction?.startsWith('commit') === true) } - // A capped all-staged prefix can hide unstaged rows. - const canStageAll = stageablePaths.length > 0 || status?.didHitLimit === true - const listBottomInset = bottomInset + spacing.lg - const fileList = useMemo( + // Pick only what the list reads, so commit-message keystrokes never re-render its rows. + const { + connState, + status, + branchLabel, + branchCompareResult, + actionError, + commitFailureRecovery, + commitFailureRecoveryAction, + openBranchPicker, + abortConflictOperation, + sections, + shouldShowBranchCompareSection, + branchEntries, + branchCompareState, + stageablePaths, + unstageablePaths, + stageAll, + unstageAll, + openDiffForFile, + resolveConflict, + runGitAction, + setDiscardTarget + } = state + const header = useMemo( () => ( - ), [ - listBottomInset, - sections, + abortConflictOperation, + branchCompareResult, + branchLabel, + busyAction, + onOpenCommits, + openBranchPicker, + status + ] + ) + const list = useMemo( + () => ( + + ), + [ + bottomInset, + branchCompareResult, + branchCompareState, + branchEntries, + busyAction, + header, openDiffForFile, resolveConflict, runGitAction, + sections, setDiscardTarget, - state.shouldShowBranchCompareSection, - state.branchCompareSummaryText, - state.branchEntries, - state.branchCompareState, - state.branchCompareResult, - busyAction + shouldShowBranchCompareSection, + stageAll, + stageablePaths, + status, + unstageAll, + unstageablePaths ] ) - - return ( - <> - {connState !== 'connected' ? ( - // Retained rows look actionable offline; show reconnect state beside them. - - - - {t('sourceControl.content.reconnecting', 'Reconnecting to desktop...')} - - - ) : null} + // Outside the list, so a failure stays in view however far the rows are scrolled. + const feedback = + connState !== 'connected' || commitFailureRecovery || actionError ? ( + {connState !== 'connected' ? ( + // Retained rows look actionable offline; show reconnect state beside them. + + + + {t('sourceControl.content.reconnecting', 'Reconnecting to desktop...')} + + + ) : null} {commitFailureRecovery ? ( ) : null} - - [ - styles.bulkButton, - (!canStageAll || ioBusy) && styles.bulkButtonDisabled, - pressed && styles.bulkButtonPressed - ]} - onPress={() => void stageAll()} - disabled={ioBusy || !canStageAll} - > - {busyAction === 'stage-all' ? ( - - ) : ( - - )} - - {t('sourceControl.content.stageAll', 'Stage All')} - - - [ - styles.bulkButton, - (unstageablePaths.length === 0 || ioBusy) && styles.bulkButtonDisabled, - pressed && styles.bulkButtonPressed - ]} - onPress={() => void unstageAll()} - disabled={ioBusy || unstageablePaths.length === 0} - > - {busyAction === 'unstage-all' ? ( - - ) : ( - - )} - - {t('sourceControl.content.unstageAll', 'Unstage All')} - - - [ - styles.bulkMenuButton, - pressed && styles.bulkButtonPressed, - ioBusy && styles.bulkButtonDisabled - ]} - onPress={() => setShowActionSheet(true)} - disabled={ioBusy} - hitSlop={8} - accessibilityLabel={t( - 'sourceControl.content.openActions', - 'Open source control actions' - )} - > - - - - - {!hasVisibleChanges ? ( + ) : null + if (!hasVisibleChanges) { + return ( + + {feedback} + {header} {t('sourceControl.content.emptyTitle', 'No local changes')} @@ -203,9 +190,13 @@ export function MobileSourceControlContent({ state, bottomInset }: Props) { {t('sourceControl.content.emptyMessage', 'Working tree is clean.')} - ) : ( - fileList - )} - + + ) + } + return ( + + {feedback} + {list} + ) } diff --git a/mobile/src/source-control/MobileSourceControlFileList.tsx b/mobile/src/source-control/MobileSourceControlFileList.tsx index ed9807375f1..c416c93094d 100644 --- a/mobile/src/source-control/MobileSourceControlFileList.tsx +++ b/mobile/src/source-control/MobileSourceControlFileList.tsx @@ -1,10 +1,8 @@ -import { SectionList, View } from 'react-native' +import type { ReactNode } from 'react' +import { ActivityIndicator, Pressable, SectionList, Text, View } from 'react-native' import { SectionHeader } from '../components/list/ListGroup' -import { - BranchCompareHeader, - BranchFileRow, - makeRenderFileRow -} from './MobileSourceControlFileRows' +import { colors } from '../theme/mobile-theme' +import { BranchFileRow, ChangeFileRow, type FileRowState } from './MobileSourceControlFileRows' import type { MobileBranchEntryView, MobileGitStatusEntryView @@ -20,57 +18,124 @@ function isWorkingTreeEntry(entry: FileEntry): entry is MobileGitStatusEntryView return typeof entry.stageActionId === 'string' } -type FileSection = { key: string; title: string; data: readonly FileEntry[] } +type FileSection = { + key: string + title: string + trailing?: ReactNode + data: readonly FileEntry[] +} -export type FileListState = Pick< - MobileSourceControlState, - | 'sections' - | 'shouldShowBranchCompareSection' - | 'branchCompareSummaryText' - | 'branchEntries' - | 'branchCompareState' - | 'branchCompareResult' - | 'busyAction' - | 'openDiffForFile' - | 'resolveConflict' - | 'runGitAction' - | 'setDiscardTarget' -> +export type FileListState = FileRowState & + Pick< + MobileSourceControlState, + | 'sections' + | 'status' + | 'shouldShowBranchCompareSection' + | 'branchEntries' + | 'branchCompareState' + | 'branchCompareResult' + | 'stageablePaths' + | 'unstageablePaths' + | 'stageAll' + | 'unstageAll' + > export function MobileSourceControlFileList({ state, + header, bottomInset }: { state: FileListState - /** Clears the floating commit bar, which grows with its message. */ + /** The status line, Commits row and conflict banner above the sections. */ + header: ReactNode + /** Clears the next-step bar the panel floats over the list. */ bottomInset: number }) { const colorScheme = useMobileColorScheme() const t = useMobileTranslation() - const sections: FileSection[] = state.sections.map((section) => ({ - ...section, - key: section.id - })) + const busy = state.busyAction !== null + const merging = state.status?.conflictOperation === 'merge' + // A capped all-staged prefix can hide unstaged rows. + const canStageAll = state.stageablePaths.length > 0 || state.status?.didHitLimit === true + const sectionAction = (label: string, onPress: () => void, enabled: boolean) => ( + [ + (busy || !enabled) && styles.iconButtonDisabled, + pressed && styles.sectionActionPressed + ]} + > + {label} + + ) + // A capped listing can hide every unstaged file behind a staged prefix; keep Stage all reachable. + const cappedWithoutUnstaged = + state.status?.didHitLimit === true && + !state.sections.some((section) => section.id === 'unstaged') + const stageAllOnStaged = + cappedWithoutUnstaged && state.sections.some((section) => section.id === 'staged') + const stageAllOnConflicts = cappedWithoutUnstaged && !stageAllOnStaged + const stageAllAction = sectionAction( + t('sourceControl.fileList.stageAll', 'Stage all'), + () => void state.stageAll(), + canStageAll + ) + const sections: FileSection[] = state.sections.map((section) => { + const title = + merging && section.id === 'staged' + ? t('sourceControl.fileList.mergedCleanly', 'Merged cleanly') + : section.title + return { + key: section.id, + title: `${title} ${section.data.length}`, + trailing: + section.id === 'unstaged' || (section.id === 'conflicts' && stageAllOnConflicts) ? ( + stageAllAction + ) : section.id === 'staged' ? ( + + {stageAllOnStaged ? stageAllAction : null} + {merging + ? null + : sectionAction( + t('sourceControl.fileList.unstageAll', 'Unstage all'), + () => void state.unstageAll(), + state.unstageablePaths.length > 0 + )} + + ) : undefined, + data: section.data + } + }) + const compare = state.branchCompareResult if (state.shouldShowBranchCompareSection) { + const ready = state.branchCompareState.kind === 'ready' && compare?.summary.status === 'ready' sections.push({ key: 'committed', - title: t('sourceControl.fileList.committedSection', 'Committed on Branch'), - data: - state.branchCompareState.kind === 'ready' && - state.branchCompareResult?.summary.status === 'ready' - ? state.branchEntries - : [] + title: t('sourceControl.fileList.onThisBranch', 'On this branch {{count}}', { + count: state.branchEntries.length + }), + trailing: compare?.summary.baseRef ? ( + + {t('sourceControl.fileList.versusBase', 'vs {{base}}', { + base: compare.summary.baseRef.replace(/^origin\//, '') + })} + + ) : undefined, + data: ready ? state.branchEntries : [] }) } - const renderFileRow = makeRenderFileRow(state, t) return ( style={hubStyles.tabBody} sections={sections} extraData={colorScheme} + ListHeaderComponent={<>{header}} renderItem={({ item }) => isWorkingTreeEntry(item) ? ( - renderFileRow({ item }) + ) : ( ) @@ -78,17 +143,43 @@ export function MobileSourceControlFileList({ keyExtractor={(item) => `${isWorkingTreeEntry(item) ? item.area : 'committed'}:${item.path}:${item.oldPath ?? ''}` } - renderSectionHeader={({ section }) => - section.key === 'committed' ? ( - - ) : ( + renderSectionHeader={({ section }) => ( + - + - ) - } + {section.key === 'committed' ? : null} + + )} stickySectionHeadersEnabled={false} contentContainerStyle={[styles.listContent, { paddingBottom: bottomInset }]} /> ) } + +// Loading or failure under "On this branch", where its rows would be. +function BranchCompareState({ state }: { state: FileListState }) { + useMobileColorScheme() + const t = useMobileTranslation() + const { branchCompareState, branchCompareResult } = state + const message = + branchCompareState.kind === 'error' + ? branchCompareState.message + : branchCompareResult && branchCompareResult.summary.status !== 'ready' + ? (branchCompareResult.summary.errorMessage ?? + t('sourceControl.fileRows.committedUnavailable', 'Committed changes unavailable.')) + : null + if (branchCompareState.kind !== 'loading' && message === null) { + return null + } + return ( + + {branchCompareState.kind === 'loading' ? ( + + ) : null} + + {message ?? t('sourceControl.fileRows.loadingCommitted', 'Loading committed changes...')} + + + ) +} diff --git a/mobile/src/source-control/MobileSourceControlFileList.web.test.tsx b/mobile/src/source-control/MobileSourceControlFileList.web.test.tsx index 04a409dc2c7..b84e3071a72 100644 --- a/mobile/src/source-control/MobileSourceControlFileList.web.test.tsx +++ b/mobile/src/source-control/MobileSourceControlFileList.web.test.tsx @@ -5,7 +5,24 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { MobileSourceControlFileList, type FileListState } from './MobileSourceControlFileList' vi.mock('react-native', () => vi.importActual('react-native-web')) +vi.mock('react-native-gesture-handler', async () => { + const chain = new Proxy({}, { get: () => () => chain }) + return { + Gesture: { Pan: () => chain }, + GestureDetector: ({ children }: { children: unknown }) => children + } +}) +vi.mock('react-native-reanimated', () => ({ + default: { View: ({ children }: { children: unknown }) => children }, + Easing: { bezier: () => () => 0 }, + ReduceMotion: { Never: 'never' }, + useAnimatedStyle: () => ({}), + useReducedMotion: () => false, + useSharedValue: (value: number) => ({ value }), + withTiming: (value: number) => value +})) vi.mock('lucide-react-native', () => ({ + Undo2: () => null, ArrowDown: () => null, ArrowDownUp: () => null, ArrowUp: () => null, @@ -66,8 +83,12 @@ function fileListState(count: number, dirty: boolean): FileListState { } ] : [], + status: null, + stageablePaths: dirty ? ['src/uncommitted.ts'] : [], + unstageablePaths: [], + stageAll: vi.fn(async () => {}), + unstageAll: vi.fn(async () => {}), shouldShowBranchCompareSection: true, - branchCompareSummaryText: `${count} files vs main`, branchEntries, branchCompareState: { kind: 'ready', result: branchCompareResult }, branchCompareResult, @@ -102,14 +123,19 @@ describe('source-control file virtualization in the mobile web page', () => { const state = fileListState(count, dirty) await act(async () => { root.render( - createElement(MobileSourceControlFileList, { key: count, state, bottomInset: 0 }) + createElement(MobileSourceControlFileList, { + key: count, + state, + header: null, + bottomInset: 0 + }) ) }) const rows = container.querySelectorAll('[aria-label^="Open committed change "]') mountedCounts.push(rows.length) expect(rows.length).toBeGreaterThan(0) expect(rows.length).toBeLessThan(50) - expect(container.textContent).toContain(`${count} files vs main`) + expect(container.textContent).toContain(`On this branch ${count}`) expect( container.querySelector('[aria-label="Open committed change src/committed-999.ts"]') ).toBeNull() diff --git a/mobile/src/source-control/MobileSourceControlFileRows.tsx b/mobile/src/source-control/MobileSourceControlFileRows.tsx index 01b4bb26f1c..85cb7dbad3c 100644 --- a/mobile/src/source-control/MobileSourceControlFileRows.tsx +++ b/mobile/src/source-control/MobileSourceControlFileRows.tsx @@ -1,13 +1,23 @@ +import type { ReactNode } from 'react' import { ActivityIndicator, Pressable, Text, View } from 'react-native' -import { ChevronRight, FileText, Minus, Plus, Trash2 } from 'lucide-react-native' +import { Gesture, GestureDetector } from 'react-native-gesture-handler' +import Animated, { + Easing, + ReduceMotion, + useAnimatedStyle, + useSharedValue, + withTiming +} from 'react-native-reanimated' +import { SHEET_CURVE } from '../components/sheet-motion-values' +import { useDrawerReducedMotion } from '../components/bottom-drawer-constants' +import { Minus, Plus, Undo2 } from 'lucide-react-native' import { colors } from '../theme/mobile-theme' -import { SectionHeader } from '../components/list/ListGroup' import { STATUS_LABELS } from '../../../src/shared/git-status-labels' -import type { GitConflictKind } from '../../../src/shared/git-status-types' +import type { GitConflictKind, GitFileStatus } from '../../../src/shared/git-status-types' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' import type { Translate } from '../i18n/mobile-i18n' import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { formatMobileBranchEntryMeta } from './mobile-branch-entry-format' +import { MobileDiffLineStats } from '../components/MobileDiffReviewFileSummary' import type { MobileBranchEntryView, MobileGitStatusEntryView @@ -17,7 +27,7 @@ import { styles } from './mobile-source-control-styles' import { mobileConflictResolutionLabel } from './mobile-source-control-conflict-actions' import { getUnstageablePaths, statusColor } from './mobile-git-status' -type RowState = Pick< +export type FileRowState = Pick< MobileSourceControlState, 'busyAction' | 'openDiffForFile' | 'resolveConflict' | 'runGitAction' | 'setDiscardTarget' > @@ -43,227 +53,288 @@ function conflictKindLabel(kind: GitConflictKind | undefined, t: Translate): str } } -export function makeRenderFileRow(state: RowState, t: Translate) { - const { busyAction, openDiffForFile, resolveConflict, runGitAction, setDiscardTarget } = state - return function renderFileRow({ item }: { item: MobileGitStatusEntryView }) { - const rowBusy = - busyAction === item.stageActionId || - busyAction === item.unstageActionId || - busyAction === item.discardActionId || - busyAction === item.resolveActionId - const rowDisabled = !item.canOpen || busyAction !== null - const ioBusy = busyAction !== null - const hasConflictActions = item.conflictResolutions.length > 0 - return ( +// Git paths always use '/'; the name leads so it never truncates behind its folder. +function splitGitPath(path: string): { name: string; folder: string } { + const slash = path.lastIndexOf('/') + return slash === -1 + ? { name: path, folder: '' } + : { name: path.slice(slash + 1), folder: path.slice(0, slash) } +} + +/** Status letter, file name over its folder, then the row's stats and action. */ +function FileRowBody({ + status, + path, + detail, + added, + removed, + trailing, + actions +}: { + status: GitFileStatus + path: string + /** Leads the folder line, e.g. a conflict kind or a rename source. */ + detail?: string + added?: number + removed?: number + trailing?: ReactNode + /** Wrapping buttons under the name, e.g. a conflict's resolutions, so the name keeps its width. */ + actions?: ReactNode +}) { + useMobileColorScheme() + const { name, folder } = splitGitPath(path) + const subtitle = detail ? (folder ? `${detail} · ${folder}` : detail) : folder + return ( + <> + + + {STATUS_LABELS[status]} + + + + + {name} + + {subtitle ? ( + + {subtitle} + + ) : null} + {actions ? {actions} : null} + + + {trailing} + + ) +} + +// The width the Discard action uncovers; a swipe past half of it settles open. +const DISCARD_WIDTH = 84 +const SWIPE_SETTLE_MS = 250 +const DRAG_CLICK_WINDOW_MS = 400 + +/** Discard sits behind a left swipe and still asks first; it is also on the diff screen. */ +function SwipeToDiscard({ + available, + enabled, + label, + accessibilityLabel, + onDiscard, + children +}: { + /** Only discardable rows get the swipe; toggling it would remount the row. */ + available: boolean + enabled: boolean + label: string + accessibilityLabel: string + onDiscard: () => void + /** Gets a wrapper for the row's press: a tap on a swiped row closes it instead. */ + children: (guardPress: (onPress: () => void) => () => void) => ReactNode +}) { + useMobileColorScheme() + const reducedMotion = useDrawerReducedMotion() + const offset = useSharedValue(0) + const start = useSharedValue(0) + // When the last drag ended: on the web, releasing a mouse drag also clicks the row. + const dragEndedAt = useSharedValue(0) + // Built per render: a module-level easing would run before a test's Reanimated mock can serve it. + const settle = (to: number) => { + 'worklet' + return withTiming( + to, + reducedMotion + ? { duration: 0, reduceMotion: ReduceMotion.Never } + : { duration: SWIPE_SETTLE_MS, easing: Easing.bezier(...SHEET_CURVE) } + ) + } + // Horizontal only, so the list keeps scrolling under a vertical drag. + const pan = Gesture.Pan() + .enabled(enabled) + .activeOffsetX([-12, 12]) + .failOffsetY([-10, 10]) + .onBegin(() => { + start.value = offset.value + }) + .onUpdate((event) => { + offset.value = Math.min(0, Math.max(-DISCARD_WIDTH, start.value + event.translationX)) + }) + .onEnd(() => { + // oxlint-disable-next-line react/purity -- runs when the drag ends, never during render. + dragEndedAt.value = Date.now() + offset.value = settle(offset.value < -DISCARD_WIDTH / 2 ? -DISCARD_WIDTH : 0) + }) + // Web has no worklet plugin; mapper hooks need this explicit dependency list. + const rowStyle = useAnimatedStyle(() => ({ transform: [{ translateX: offset.value }] }), [offset]) + // A tap on an open row closes it rather than opening the diff. + const guardPress = (onPress: () => void) => () => { + // The click a drag release produces belongs to the drag, not to the row. + if (Date.now() - dragEndedAt.value < DRAG_CLICK_WINDOW_MS) { + return + } + if (offset.value !== 0) { + offset.value = settle(0) + return + } + onPress() + } + if (!available) { + return children((onPress) => onPress) + } + return ( + [ - styles.fileRow, - pressed && item.canOpen && styles.fileRowPressed, - // Conflict rows cannot open but carry live actions; their buttons dim themselves while busy. - rowDisabled && !hasConflictActions && styles.fileRowDisabled, - !item.canOpen && !hasConflictActions && styles.fileRowUnavailable - ]} + style={({ pressed }) => [styles.discardAction, pressed && styles.iconButtonPressed]} + disabled={!enabled} onPress={() => { - if (item.area !== undefined) { - openDiffForFile({ kind: 'changes', filePath: item.path, area: item.area }) - } + offset.value = settle(0) + onDiscard() }} - disabled={rowDisabled} - // An accessible row hides nested buttons from iOS VoiceOver; conflict rows cannot open anyway. - accessible={!hasConflictActions} - accessibilityLabel={t( - 'sourceControl.fileRows.openChangedFile', - 'Open changed file {{path}}', - { - path: item.path - } - )} + accessibilityRole="button" + accessibilityLabel={accessibilityLabel} > - - - {STATUS_LABELS[item.status]} - - - - - - {item.path} - - {item.oldPath ? ( - - {t('sourceControl.fileRows.renamedFrom', 'from {{path}}', { path: item.oldPath })} - - ) : item.conflictStatus === 'unresolved' ? ( - - {conflictKindLabel(item.conflictKind, t)} - - ) : null} - {!rowBusy && hasConflictActions ? ( - - {item.conflictResolutions.map((resolution) => ( - [ - styles.conflictActionButton, - ioBusy && styles.iconButtonDisabled, - pressed && styles.iconButtonPressed - ]} - disabled={ioBusy} - onPress={() => void resolveConflict(item, resolution)} - hitSlop={4} - accessibilityLabel={t( - 'sourceControl.conflictActions.resolveFile', - '{{action}}: {{path}}', - { - action: mobileConflictResolutionLabel(resolution, t), - path: item.path - } - )} - > - - {mobileConflictResolutionLabel(resolution, t)} - - - ))} - - ) : null} - - {rowBusy ? ( - - ) : item.canUnstage ? ( + + {label} + + {/* pan-y keeps the page scrolling vertically over the row on the web. */} + + {children(guardPress)} + + + ) +} + +export function ChangeFileRow({ + item, + state +}: { + item: MobileGitStatusEntryView + state: FileRowState +}) { + useMobileColorScheme() + const t = useMobileTranslation() + const { busyAction, openDiffForFile, resolveConflict, runGitAction, setDiscardTarget } = state + const rowBusy = + busyAction === item.stageActionId || + busyAction === item.unstageActionId || + busyAction === item.discardActionId || + busyAction === item.resolveActionId + const ioBusy = busyAction !== null + const hasConflictActions = item.conflictResolutions.length > 0 + const detail = item.oldPath + ? t('sourceControl.fileRows.renamedFrom', 'from {{path}}', { path: item.oldPath }) + : item.conflictStatus === 'unresolved' + ? conflictKindLabel(item.conflictKind, t) + : undefined + const stage = () => void runGitAction(item.stageActionId, 'git.stage', { filePath: item.path }) + const unstage = () => { + const filePaths = getUnstageablePaths([item]) + void (filePaths.length > 1 + ? runGitAction(item.unstageActionId, 'git.bulkUnstage', { filePaths }) + : runGitAction(item.unstageActionId, 'git.unstage', { filePath: item.path })) + } + // Every press in the row goes through the swipe's guard, so a drag never also presses a button. + const conflictActions = (guard: (onPress: () => void) => () => void) => + !rowBusy && hasConflictActions + ? item.conflictResolutions.map((resolution) => ( [ - styles.iconButton, + styles.conflictActionButton, ioBusy && styles.iconButtonDisabled, pressed && styles.iconButtonPressed ]} disabled={ioBusy} - onPress={() => { - const filePaths = getUnstageablePaths([item]) - void (filePaths.length > 1 - ? runGitAction(item.unstageActionId, 'git.bulkUnstage', { filePaths }) - : runGitAction(item.unstageActionId, 'git.unstage', { filePath: item.path })) - }} - hitSlop={8} - accessibilityLabel={t('sourceControl.fileRows.unstageFile', 'Unstage {{path}}', { - path: item.path - })} + onPress={guard(() => void resolveConflict(item, resolution))} + hitSlop={4} + accessibilityLabel={t( + 'sourceControl.conflictActions.resolveFile', + '{{action}}: {{path}}', + { + action: mobileConflictResolutionLabel(resolution, t), + path: item.path + } + )} > - + + {mobileConflictResolutionLabel(resolution, t)} + - ) : item.canStage || item.canDiscard ? ( - - {item.canStage ? ( - [ - styles.iconButton, - ioBusy && styles.iconButtonDisabled, - pressed && styles.iconButtonPressed - ]} - disabled={ioBusy} - onPress={() => - void runGitAction(item.stageActionId, 'git.stage', { filePath: item.path }) - } - hitSlop={8} - accessibilityLabel={t('sourceControl.fileRows.stageFile', 'Stage {{path}}', { - path: item.path - })} - > - - - ) : null} - {item.canDiscard ? ( - [ - styles.iconButton, - ioBusy && styles.iconButtonDisabled, - pressed && styles.iconButtonPressed - ]} - disabled={ioBusy} - onPress={() => setDiscardTarget(item)} - hitSlop={8} - accessibilityLabel={t('sourceControl.fileRows.discardFile', 'Discard {{path}}', { - path: item.path - })} - > - - - ) : null} - - ) : null} - {!rowBusy && item.canOpen ? ( - - ) : null} + )) + : null + const trailing = (guard: (onPress: () => void) => () => void) => + rowBusy ? ( + + ) : !hasConflictActions && (item.canUnstage || item.canStage) ? ( + [ + styles.iconButton, + ioBusy && styles.iconButtonDisabled, + pressed && styles.iconButtonPressed + ]} + disabled={ioBusy} + onPress={guard(item.canUnstage ? unstage : stage)} + hitSlop={8} + accessibilityLabel={ + item.canUnstage + ? t('sourceControl.fileRows.unstageFile', 'Unstage {{path}}', { path: item.path }) + : t('sourceControl.fileRows.stageFile', 'Stage {{path}}', { path: item.path }) + } + > + {item.canUnstage ? ( + + ) : ( + + )} - ) - } -} - -type BranchState = Pick< - MobileSourceControlState, - | 'shouldShowBranchCompareSection' - | 'branchCompareSummaryText' - | 'branchEntries' - | 'branchCompareState' - | 'branchCompareResult' - | 'busyAction' - | 'openDiffForFile' -> - -export function BranchCompareHeader({ state }: { state: BranchState }) { - useMobileColorScheme() - const t = useMobileTranslation() - const { - shouldShowBranchCompareSection, - branchCompareSummaryText, - branchEntries, - branchCompareState, - branchCompareResult - } = state - if (!shouldShowBranchCompareSection) { - return null - } - + ) : null return ( - - - - - {branchCompareSummaryText ? ( - - {branchCompareSummaryText} - - ) : null} - {branchCompareState.kind === 'loading' ? ( - - - - {t('sourceControl.fileRows.loadingCommitted', 'Loading committed changes...')} - - - ) : branchCompareState.kind === 'error' ? ( - - {branchCompareState.message} - - ) : branchCompareResult && branchCompareResult.summary.status !== 'ready' ? ( - - - {branchCompareResult.summary.errorMessage ?? - t('sourceControl.fileRows.committedUnavailable', 'Committed changes unavailable.')} - - - ) : null} - + setDiscardTarget(item)} + > + {(guardPress) => ( + [ + styles.fileRow, + pressed && item.canOpen && styles.fileRowPressed, + !item.canOpen && !hasConflictActions && styles.fileRowUnavailable + ]} + onPress={guardPress(() => { + if (item.area !== undefined) { + openDiffForFile({ kind: 'changes', filePath: item.path, area: item.area }) + } + })} + disabled={!item.canOpen || ioBusy} + // An accessible row hides nested buttons from iOS VoiceOver; conflict rows cannot open anyway. + accessible={!hasConflictActions} + accessibilityLabel={t( + 'sourceControl.fileRows.openChangedFile', + 'Open changed file {{path}}', + { + path: item.path + } + )} + > + + + )} + ) } @@ -272,55 +343,38 @@ export function BranchFileRow({ state }: { entry: MobileBranchEntryView - state: BranchState + state: Pick }) { useMobileColorScheme() const t = useMobileTranslation() - const { busyAction, openDiffForFile } = state - const rowDisabled = !entry.canOpen || busyAction !== null - const meta = formatMobileBranchEntryMeta(entry) return ( [ styles.fileRow, pressed && entry.canOpen && styles.fileRowPressed, - rowDisabled && styles.fileRowDisabled, !entry.canOpen && styles.fileRowUnavailable ]} - onPress={() => openDiffForFile({ kind: 'changes', filePath: entry.path, area: 'branch' })} - disabled={rowDisabled} + onPress={() => + state.openDiffForFile({ kind: 'changes', filePath: entry.path, area: 'branch' }) + } + disabled={!entry.canOpen || state.busyAction !== null} accessibilityLabel={t( 'sourceControl.fileRows.openCommittedChange', 'Open committed change {{path}}', - { - path: entry.path - } + { path: entry.path } )} > - - - {STATUS_LABELS[entry.status]} - - - - - - {entry.path} - - {meta ? ( - - {meta} - - ) : null} - - {entry.canOpen ? : null} ) } diff --git a/mobile/src/source-control/MobileSourceControlModals.tsx b/mobile/src/source-control/MobileSourceControlModals.tsx index 1d1c4686561..85dd3de09de 100644 --- a/mobile/src/source-control/MobileSourceControlModals.tsx +++ b/mobile/src/source-control/MobileSourceControlModals.tsx @@ -1,4 +1,3 @@ -import { ActionSheetModal, type ActionSheetAction } from '../components/ActionSheetModal' import { ConfirmModal } from '../components/ConfirmModal' import { PickerModal } from '../components/PickerModal' import { openMobilePrUrl } from '../components/mobile-pr-url' @@ -7,14 +6,11 @@ import type { MobileSourceControlState } from './use-mobile-source-control-state type Props = { state: MobileSourceControlState - actionSheetActions: ActionSheetAction[] } -export function MobileSourceControlModals({ state, actionSheetActions }: Props) { +export function MobileSourceControlModals({ state }: Props) { const t = useMobileTranslation() const { - showActionSheet, - setShowActionSheet, discardTarget, setDiscardTarget, showBranchPicker, @@ -24,21 +20,12 @@ export function MobileSourceControlModals({ state, actionSheetActions }: Props) setCreatedPrUrl, createdPrWarning, setCreatedPrWarning, - branchLabel, checkoutBranch, runGitAction } = state return ( <> - setShowActionSheet(false)} - /> - openHubScreen('commits'), [openHubScreen]) - const openPrTab = useCallback(() => selectTab('pr'), [selectTab]) // The PR tab owns the Create PR and Merge sheets; the bar and Changes reach them through this. const prOpeners = useRef({ createPr: () => {}, merge: () => {} }) const openCreatePr = useCallback(() => prOpeners.current.createPr(), []) @@ -99,13 +96,7 @@ export function MobileSourceControlPanel({ busyAction, loadStatus, status, - branchCompareResult, - branchLabel, - syncLabel, - unstagedCount, - stagedCount, - branchEntries, - abortConflictOperation + branchCompareResult } = state const ioBusy = busyAction !== null const ready = screenState.kind === 'ready' @@ -144,6 +135,18 @@ export function MobileSourceControlPanel({ ) const primaryActionArgs = state.primaryActionArgs const actionSheetActions = useMobileSourceControlActionSheet(state, review) + // The menu closes on a pick; the bar and the rows show the action's progress. + const gitMenu = useMemo( + () => + groupMobileSourceControlMenu( + actionSheetActions.map((action) => ({ + ...action, + id: action.id ?? '', + skipAutoClose: false + })) + ), + [actionSheetActions] + ) const primaryAction = useMemo( () => buildMobileSourceControlPrimaryAction({ ...primaryActionArgs, review }), [primaryActionArgs, review, t] @@ -197,18 +200,6 @@ export function MobileSourceControlPanel({ } }, [activeTab, isHostedRepo, prBranch, prSidebarKind, prDetailsMissingFor]) - const prChip = useMemo(() => { - // Detached HEAD never loads PR state; hide the chip instead of leaving it spinning. - if (!isHostedRepo || !prBranch) { - return null - } - const commentCount = - prController.prSidebarState.kind === 'ready' - ? countUnresolvedReviewThreads(prController.prSidebarState.data.details?.comments) - : null - return buildMobilePrChipSummary(prController.prSidebarState, commentCount) - }, [isHostedRepo, prBranch, prController.prSidebarState, t]) - const nextStep = useMobileSourceControlNextStep({ worktreeId, state, @@ -238,6 +229,23 @@ export function MobileSourceControlPanel({ } void refetchPr({ includeDetails: false }) }, [activeTab, isHostedRepo, loadStatus, refetchPr]) + // Changes: the rarer git actions, then Refresh, then Force push last and red. + const changesMenuGroups = useMemo( + () => [ + ...gitMenu.groups, + [ + { + id: 'refresh', + label: t('sourceControl.header.refresh', 'Refresh source control'), + icon: RefreshCw, + disabled: ioBusy, + onPress: onRefresh + } + ], + ...(gitMenu.forcePush ? [[{ ...gitMenu.forcePush, destructive: true }]] : []) + ], + [gitMenu, ioBusy, onRefresh, t] + ) const { prTab, prMenuGroups, branchCompare } = useMobileSourceControlPrTab({ state, activeTab, @@ -273,7 +281,13 @@ export function MobileSourceControlPanel({ onBack={onBack} onClose={onClose} onRefresh={onRefresh} - menuGroups={activeTab === 'pr' ? prMenuGroups : null} + menuGroups={ + activeTab === 'pr' + ? prMenuGroups + : activeTab === 'changes' && ready + ? changesMenuGroups + : null + } onOpenPrWeb={prWebUrl ? () => openMobilePrUrl(prWebUrl) : undefined} prNumber={prWebNumber} accessory={ @@ -317,9 +331,6 @@ export function MobileSourceControlPanel({ const showChanges = ready && (activeTab === 'changes' || visitedTabs.has('changes')) // Unmount inactive PR content to release Mermaid WebViews and avoid comment rerenders per keystroke. const showPrBody = ready && activeTab === 'pr' - const conflictOperation = status?.conflictOperation ?? null - const hasActiveConflict = conflictOperation != null && conflictOperation !== 'unknown' - const conflictAborting = isMobileConflictAborting(busyAction, conflictOperation) const hub = ( @@ -331,26 +342,13 @@ export function MobileSourceControlPanel({ )} - {/* Ready PRs duplicate the branch card; retain it only when needed to abort a conflict. */} - {ready && (activeTab !== 'pr' || hasActiveConflict) ? ( - void abortConflictOperation(operation)} - prChip={prChip} - onOpenPr={openPrTab} - /> - ) : null} - {showChanges ? ( - + ) : activeTab === 'changes' ? ( statusGate @@ -418,7 +416,7 @@ export function MobileSourceControlPanel({ ) : null } /> - + ) diff --git a/mobile/src/source-control/MobileSourceControlPrChip.tsx b/mobile/src/source-control/MobileSourceControlPrChip.tsx deleted file mode 100644 index 32318b52eb3..00000000000 --- a/mobile/src/source-control/MobileSourceControlPrChip.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { ActivityIndicator, Pressable, Text, View } from 'react-native' -import { - AlertTriangle, - Check, - ChevronRight, - CircleDot, - GitPullRequest, - MessageSquare, - X -} from 'lucide-react-native' -import { colors } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import type { Translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { statusColor } from '../components/pr-sidebar/pr-sidebar-status-color' -import { hubStyles } from './mobile-source-control-hub-styles' -import type { MobilePrChipRollup, MobilePrChipSummary } from './mobile-pr-chip-summary' - -type Props = { - summary: MobilePrChipSummary - onPress: () => void -} - -export function MobileSourceControlPrChip({ summary, onPress }: Props) { - useMobileColorScheme() - const t = useMobileTranslation() - return ( - [hubStyles.chip, pressed && hubStyles.chipPressed]} - onPress={onPress} - accessibilityRole="button" - accessibilityLabel={chipAccessibilityLabel(summary, t)} - > - - - - {summary.kind === 'loading' ? ( - <> - - - {t('sourceControl.prChip.loading', 'Loading pull request…')} - - - ) : summary.kind === 'none' ? ( - <> - - {t('sourceControl.prChip.createPr', 'Create pull request')} - - - - - ) : summary.kind === 'unavailable' ? ( - <> - - {summary.message} - - - - ) : ( - <> - #{summary.number} - - - {summary.stateLabel} - - - - {summary.commentCount != null && summary.commentCount > 0 ? ( - - - {summary.commentCount} - - ) : null} - - - - )} - - ) -} - -function ChipRollup({ rollup }: { rollup: MobilePrChipRollup }) { - useMobileColorScheme() - const color = statusColor(rollup.token) - return ( - - - {rollup.text} - - ) -} - -function RollupIcon({ kind, color }: { kind: MobilePrChipRollup['kind']; color: string }) { - const size = 13 - const strokeWidth = 2.3 - switch (kind) { - case 'conflict': - return - case 'failing': - return - case 'running': - return - case 'passed': - return - case 'none': - return null - } -} - -function chipAccessibilityLabel(summary: MobilePrChipSummary, t: Translate): string { - switch (summary.kind) { - case 'loading': - return t('sourceControl.prChip.loadingLabel', 'Loading pull request') - case 'none': - return t('sourceControl.prChip.createPr', 'Create pull request') - case 'unavailable': - return t('sourceControl.prChip.unavailableLabel', 'Pull request unavailable: {{message}}', { - message: summary.message - }) - case 'ready': { - const count = summary.commentCount - const details = { - number: summary.number, - state: summary.stateLabel, - checks: summary.rollup.text - } - if (count != null && count > 0) { - return count === 1 - ? t( - 'sourceControl.prChip.openWithComments_one', - 'Pull request #{{number}}, {{state}}, {{checks}}, {{count}} unresolved comments. Open pull request.', - { ...details, count } - ) - : t( - 'sourceControl.prChip.openWithComments_other', - 'Pull request #{{number}}, {{state}}, {{checks}}, {{count}} unresolved comments. Open pull request.', - { ...details, count } - ) - } - return t( - 'sourceControl.prChip.openLabel', - 'Pull request #{{number}}, {{state}}, {{checks}}. Open pull request.', - details - ) - } - } -} diff --git a/mobile/src/source-control/mobile-branch-compare.test.ts b/mobile/src/source-control/mobile-branch-compare.test.ts index 9e85f3d3938..e1a9095560b 100644 --- a/mobile/src/source-control/mobile-branch-compare.test.ts +++ b/mobile/src/source-control/mobile-branch-compare.test.ts @@ -1,8 +1,7 @@ import { describe, expect, it } from 'vitest' import { buildMobileBranchCompareSection, - canOpenMobileBranchCompareDiff, - formatMobileBranchCompareSummary + canOpenMobileBranchCompareDiff } from './mobile-branch-compare' describe('mobile branch compare helpers', () => { @@ -16,22 +15,6 @@ describe('mobile branch compare helpers', () => { expect(section?.data.map((entry) => entry.path)).toEqual(['alpha.ts', 'zeta.ts']) }) - it('summarizes ready branch compares', () => { - expect( - formatMobileBranchCompareSummary({ - baseRef: 'origin/main', - baseOid: 'a'.repeat(40), - compareRef: 'HEAD', - headOid: 'b'.repeat(40), - mergeBase: 'c'.repeat(40), - changedFiles: 2, - commitsAhead: 1, - errorMessage: undefined, - status: 'ready' - }) - ).toBe('2 files - 1 commit - vs origin/main') - }) - it('only opens branch diffs when compare object ids are available', () => { expect( canOpenMobileBranchCompareDiff({ diff --git a/mobile/src/source-control/mobile-branch-compare.ts b/mobile/src/source-control/mobile-branch-compare.ts index 298a41f3e81..428ff6720d1 100644 --- a/mobile/src/source-control/mobile-branch-compare.ts +++ b/mobile/src/source-control/mobile-branch-compare.ts @@ -30,38 +30,6 @@ export function buildMobileBranchCompareSection { it('builds sections in the mobile source control order', () => { const sections = buildMobileSourceControlSections(entries, 'changes-first') - expect(sections.map((section) => section.title)).toEqual(['Changes', 'Staged Changes']) + expect(sections.map((section) => section.title)).toEqual(['Uncommitted', 'Staged']) }) it('computes actionable path sets', () => { @@ -106,7 +106,7 @@ describe('mobile source control status helpers', () => { sections.map((section) => [section.title, section.data.map((entry) => entry.path)]) ).toEqual([ ['Conflicts', ['alpha.ts', 'beta.ts']], - ['Changes', ['aardvark.ts', 'zeta.ts']] + ['Uncommitted', ['aardvark.ts', 'zeta.ts']] ]) }) diff --git a/mobile/src/source-control/mobile-git-status.ts b/mobile/src/source-control/mobile-git-status.ts index 45239451ac4..7f3fea4c54d 100644 --- a/mobile/src/source-control/mobile-git-status.ts +++ b/mobile/src/source-control/mobile-git-status.ts @@ -50,8 +50,8 @@ export type MobileSourceControlSection { return { conflicts: translate('sourceControl.gitStatus.conflicts', 'Conflicts'), - unstaged: translate('sourceControl.gitStatus.changes', 'Changes'), - staged: translate('sourceControl.gitStatus.stagedChanges', 'Staged Changes') + unstaged: translate('sourceControl.gitStatus.uncommitted', 'Uncommitted'), + staged: translate('sourceControl.gitStatus.staged', 'Staged') } } diff --git a/mobile/src/source-control/mobile-pr-chip-summary.test.ts b/mobile/src/source-control/mobile-pr-chip-summary.test.ts deleted file mode 100644 index d8b97d85e91..00000000000 --- a/mobile/src/source-control/mobile-pr-chip-summary.test.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { describe, it, expect } from 'vitest' -import type { PRCheckDetail } from '../../../src/shared/github/check-types' -import type { PRComment } from '../../../src/shared/github/comment-types' -import type { PRInfo } from '../../../src/shared/github/pull-request-types' -import type { PrSidebarState } from '../session/mobile-pr-sidebar-state' -import { buildMobilePrChipSummary, countUnresolvedReviewThreads } from './mobile-pr-chip-summary' - -function pr(overrides: Partial = {}): PRInfo { - return { - number: 7701, - title: 'Make center split more visible', - state: 'open', - url: 'https://example.test/pr/7701', - checksStatus: 'success', - updatedAt: '2026-07-07T00:00:00Z', - mergeable: 'MERGEABLE', - ...overrides - } -} - -function check( - conclusion: PRCheckDetail['conclusion'], - status: PRCheckDetail['status'] = 'completed' -): PRCheckDetail { - return { name: `check-${conclusion}-${status}`, status, conclusion, url: null } -} - -function ready(prInfo: PRInfo, checks: PRCheckDetail[]): PrSidebarState { - return { - kind: 'ready', - data: { pr: prInfo, checks, details: null, checksError: null, pushTarget: null } - } -} - -describe('buildMobilePrChipSummary', () => { - it('maps non-ready states', () => { - expect(buildMobilePrChipSummary({ kind: 'hidden' })).toEqual({ kind: 'loading' }) - expect(buildMobilePrChipSummary({ kind: 'loading' })).toEqual({ kind: 'loading' }) - expect(buildMobilePrChipSummary({ kind: 'none' })).toEqual({ kind: 'none' }) - expect(buildMobilePrChipSummary({ kind: 'error', message: 'net' })).toEqual({ - kind: 'unavailable', - message: 'net' - }) - expect(buildMobilePrChipSummary({ kind: 'blocked', message: 'auth' })).toEqual({ - kind: 'unavailable', - message: 'auth' - }) - }) - - it('surfaces the PR number and state badge', () => { - const summary = buildMobilePrChipSummary(ready(pr({ state: 'draft' }), [check('success')])) - expect(summary.kind).toBe('ready') - if (summary.kind !== 'ready') { - return - } - expect(summary.number).toBe(7701) - expect(summary.stateLabel).toBe('Draft') - }) - - // Skipped checks count as satisfied, matching desktop and the tasks grid. - it('rolls up passed checks as passed/total, counting skipped as passed', () => { - const summary = buildMobilePrChipSummary( - ready(pr(), [check('success'), check('success'), check('skipped')]) - ) - if (summary.kind !== 'ready') { - throw new Error('expected ready') - } - expect(summary.rollup).toEqual({ kind: 'passed', text: '3/3', token: 'statusGreen' }) - }) - - it('treats a merge-blocking action_required gate as failing, not passing', () => { - const summary = buildMobilePrChipSummary( - ready(pr(), [check('success'), check('action_required')]) - ) - if (summary.kind !== 'ready') { - throw new Error('expected ready') - } - expect(summary.rollup).toEqual({ kind: 'failing', text: '1 failing', token: 'statusRed' }) - }) - - it('distinguishes checks that resolved to nothing actionable from having no checks', () => { - const summary = buildMobilePrChipSummary(ready(pr(), [check('neutral')])) - if (summary.kind !== 'ready') { - throw new Error('expected ready') - } - expect(summary.rollup).toEqual({ - kind: 'none', - text: 'Unresolved checks', - token: 'textSecondary' - }) - }) - - it('prefers failing over running and passing', () => { - const summary = buildMobilePrChipSummary( - ready(pr(), [check('success'), check('failure'), check(null, 'in_progress')]) - ) - if (summary.kind !== 'ready') { - throw new Error('expected ready') - } - expect(summary.rollup).toEqual({ kind: 'failing', text: '1 failing', token: 'statusRed' }) - }) - - it('shows running when nothing has failed yet', () => { - const summary = buildMobilePrChipSummary(ready(pr(), [check('success'), check(null, 'queued')])) - if (summary.kind !== 'ready') { - throw new Error('expected ready') - } - expect(summary.rollup).toEqual({ kind: 'running', text: '1 running', token: 'statusAmber' }) - }) - - it('lets a merge conflict win over green checks', () => { - const summary = buildMobilePrChipSummary( - ready(pr({ mergeable: 'CONFLICTING' }), [check('success')]) - ) - if (summary.kind !== 'ready') { - throw new Error('expected ready') - } - expect(summary.rollup.kind).toBe('conflict') - }) - - it('reports no checks when the list is empty', () => { - const summary = buildMobilePrChipSummary(ready(pr(), [])) - if (summary.kind !== 'ready') { - throw new Error('expected ready') - } - expect(summary.rollup).toEqual({ kind: 'none', text: 'No checks', token: 'textSecondary' }) - }) - - it('passes through the unresolved comment count', () => { - const summary = buildMobilePrChipSummary(ready(pr(), [check('success')]), 3) - if (summary.kind !== 'ready') { - throw new Error('expected ready') - } - expect(summary.commentCount).toBe(3) - }) -}) - -describe('countUnresolvedReviewThreads', () => { - function comment(overrides: Partial): PRComment { - return { - id: 1, - author: 'octocat', - authorAvatarUrl: '', - body: 'x', - createdAt: '2026-07-07T00:00:00Z', - url: '', - ...overrides - } - } - - it('returns null when details have not loaded', () => { - expect(countUnresolvedReviewThreads(null)).toBeNull() - expect(countUnresolvedReviewThreads(undefined)).toBeNull() - }) - - it('counts each unresolved thread once and ignores resolved threads', () => { - const comments = [ - comment({ id: 1, threadId: 't1', isResolved: false }), - comment({ id: 2, threadId: 't1', isResolved: false }), - comment({ id: 3, threadId: 't2', isResolved: true }), - comment({ id: 4, threadId: 't3' }), - comment({ id: 5 }) - ] - expect(countUnresolvedReviewThreads(comments)).toBe(2) - }) -}) diff --git a/mobile/src/source-control/mobile-pr-chip-summary.ts b/mobile/src/source-control/mobile-pr-chip-summary.ts deleted file mode 100644 index 9d62b270f22..00000000000 --- a/mobile/src/source-control/mobile-pr-chip-summary.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { PRComment } from '../../../src/shared/github/comment-types' -import type { PrSidebarState } from '../session/mobile-pr-sidebar-state' -import { summarizeProviderChecks } from '../../../src/shared/provider-check-summary' -import { - prStateBadge, - type MobileStatusToken -} from '../components/pr-sidebar/pr-checks-presentation' - -// Use the sidebar’s same state so the chip and linked checks list cannot disagree. - -export type MobilePrChipRollup = - | { kind: 'conflict'; text: string; token: MobileStatusToken } - | { kind: 'failing'; text: string; token: MobileStatusToken } - | { kind: 'running'; text: string; token: MobileStatusToken } - | { kind: 'passed'; text: string; token: MobileStatusToken } - | { kind: 'none'; text: string; token: MobileStatusToken } - -export type MobilePrChipSummary = - | { kind: 'loading' } - | { kind: 'none' } - | { kind: 'unavailable'; message: string } - | { - kind: 'ready' - number: number - stateLabel: string - stateToken: MobileStatusToken - rollup: MobilePrChipRollup - commentCount: number | null - } - -// Count unresolved inline threads once per threadId; conversation comments are not review threads. -export function countUnresolvedReviewThreads( - comments: PRComment[] | null | undefined -): number | null { - if (!comments) { - return null - } - const unresolved = new Set() - for (const comment of comments) { - if (comment.threadId && comment.isResolved !== true) { - unresolved.add(comment.threadId) - } - } - return unresolved.size -} - -export function buildMobilePrChipSummary( - state: PrSidebarState, - commentCount: number | null = null -): MobilePrChipSummary { - switch (state.kind) { - case 'hidden': - case 'loading': - return { kind: 'loading' } - case 'none': - return { kind: 'none' } - case 'error': - case 'blocked': - return { kind: 'unavailable', message: state.message } - case 'ready': { - const badge = prStateBadge(state.data.pr.state) - return { - kind: 'ready', - number: state.data.pr.number, - stateLabel: badge.label, - stateToken: badge.token, - rollup: buildChipRollup(state), - commentCount - } - } - } -} - -function buildChipRollup(state: Extract): MobilePrChipRollup { - if (state.data.pr.mergeable === 'CONFLICTING') { - return { kind: 'conflict', text: 'Conflicts', token: 'statusAmber' } - } - // Share classification with the checks list and tasks grid. - const checks = summarizeProviderChecks(state.data.checks) - if (checks.failed > 0) { - return { kind: 'failing', text: `${checks.failed} failing`, token: 'statusRed' } - } - if (checks.pending > 0) { - return { kind: 'running', text: `${checks.pending} running`, token: 'statusAmber' } - } - if (checks.passed > 0) { - return { kind: 'passed', text: `${checks.passed}/${checks.total}`, token: 'statusGreen' } - } - // Existing checks with no actionable outcome are still distinct from no checks. - return { - kind: 'none', - text: checks.total === 0 ? 'No checks' : 'Unresolved checks', - token: 'textSecondary' - } -} diff --git a/mobile/src/source-control/mobile-source-control-actions.ts b/mobile/src/source-control/mobile-source-control-actions.ts index 8540fe62d3b..936eff41189 100644 --- a/mobile/src/source-control/mobile-source-control-actions.ts +++ b/mobile/src/source-control/mobile-source-control-actions.ts @@ -145,12 +145,12 @@ export function buildMobileSourceControlActions( label: t('sourceControl.actions.switchBranch', 'Switch branch'), iconKey: 'branch', disabled: busy, - skipAutoClose: true, + closeBeforePress: true, onPress: handlers.checkout }, { id: 'history', - label: t('sourceControl.actions.commits', 'Commits'), + label: t('sourceControl.actions.openCommits', 'Open commits'), iconKey: 'history', disabled: busy, onPress: handlers.history @@ -158,6 +158,27 @@ export function buildMobileSourceControlActions( ] } +// The ⋯ menu's git actions, grouped; Create PR lives in the next-step bar and the PR tab. +const MENU_GROUPS: readonly (readonly string[])[] = [ + ['commit-push', 'commit-sync'], + // Push and Publish stay here too: the bar shows the commit box while anything is uncommitted. + ['push', 'publish', 'fetch', 'pull', 'fast-forward', 'sync', 'rebase'], + ['checkout', 'history'] +] + +export function groupMobileSourceControlMenu( + actions: readonly T[] +): { groups: T[][]; forcePush: T | null } { + const byId = new Map(actions.map((action) => [action.id, action])) + const groups = MENU_GROUPS.map((ids) => + ids.flatMap((id) => { + const action = byId.get(id) + return action ? [action] : [] + }) + ).filter((group) => group.length > 0) + return { groups, forcePush: byId.get('force-push') ?? null } +} + export function getMobilePrimaryActionHint( decision: SourceControlCommitAreaPrimaryActionDecision ): string { diff --git a/mobile/src/source-control/mobile-source-control-list-styles.ts b/mobile/src/source-control/mobile-source-control-list-styles.ts index 04fed32b715..6b43b3a73e7 100644 --- a/mobile/src/source-control/mobile-source-control-list-styles.ts +++ b/mobile/src/source-control/mobile-source-control-list-styles.ts @@ -11,13 +11,21 @@ export const listStyles = themedStyleSheet(() => ({ sectionHeaderBleed: { marginHorizontal: -spacing.lg }, - branchCompareBlock: { - paddingBottom: spacing.sm + sectionAction: { + color: colors.textPrimary, + fontSize: 13, + fontWeight: '600' }, - branchSectionSubtitle: { - color: colors.textMuted, - fontSize: typography.metaSize, - paddingBottom: spacing.xs + sectionActions: { + flexDirection: 'row', + gap: spacing.lg + }, + sectionActionPressed: { + opacity: 0.6 + }, + sectionMeta: { + color: colors.textSecondary, + fontSize: 13 }, branchStateRow: { minHeight: 44, @@ -34,21 +42,20 @@ export const listStyles = themedStyleSheet(() => ({ fontSize: typography.metaSize, lineHeight: 18 }, + // Opaque so a left swipe uncovers Discard only as the row slides. fileRow: { minHeight: 50, flexDirection: 'row', alignItems: 'center', gap: spacing.sm, paddingVertical: spacing.sm, + backgroundColor: colors.bgBase, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: colors.borderSubtle }, fileRowPressed: { backgroundColor: colors.bgPanel }, - fileRowDisabled: { - opacity: 0.78 - }, fileRowUnavailable: { opacity: 0.72 }, @@ -66,23 +73,19 @@ export const listStyles = themedStyleSheet(() => ({ flex: 1, minWidth: 0 }, - filePath: { + fileName: { color: colors.textPrimary, fontSize: typography.bodySize }, - filePathDisabled: { - color: colors.textSecondary + fileNameGone: { + color: colors.textSecondary, + textDecorationLine: 'line-through' }, fileMeta: { color: colors.textMuted, fontSize: typography.metaSize, marginTop: 2 }, - rowActions: { - flexDirection: 'row', - alignItems: 'center', - gap: spacing.xs - }, iconButton: { width: 32, height: 32, @@ -90,6 +93,23 @@ export const listStyles = themedStyleSheet(() => ({ alignItems: 'center', justifyContent: 'center' }, + // Behind the row's right end; the row slides left to uncover it. + discardAction: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + width: 84, + alignItems: 'center', + justifyContent: 'center', + gap: 2, + backgroundColor: colors.statusRed + }, + discardActionText: { + color: colors.onStatusRed, + fontSize: typography.metaSize, + fontWeight: '600' + }, conflictActions: { flexDirection: 'row', flexWrap: 'wrap', diff --git a/mobile/src/source-control/mobile-source-control-styles.ts b/mobile/src/source-control/mobile-source-control-styles.ts index 4068ae43a78..5c84eaf35f2 100644 --- a/mobile/src/source-control/mobile-source-control-styles.ts +++ b/mobile/src/source-control/mobile-source-control-styles.ts @@ -13,62 +13,67 @@ const baseStyles = themedStyleSheet(() => ({ header: { backgroundColor: colors.bgBase }, - summaryCard: { - margin: spacing.lg, - marginBottom: spacing.sm, + statusLine: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm, + paddingTop: spacing.md, + paddingBottom: spacing.xs + }, + branchButton: { + flexShrink: 1, + minHeight: 32, + flexDirection: 'row', + alignItems: 'center', + gap: 6 + }, + branchText: { + flexShrink: 1, + color: colors.textPrimary, + fontSize: typography.bodySize, + fontWeight: '600' + }, + conflictBanner: { + marginTop: spacing.sm, padding: spacing.md, + gap: spacing.sm, borderRadius: radii.surface, backgroundColor: colors.bgPanel, borderWidth: StyleSheet.hairlineWidth, - borderColor: colors.borderSubtle + borderColor: colors.statusAmber }, - summaryHeader: { + conflictBannerTitleRow: { flexDirection: 'row', alignItems: 'center', - justifyContent: 'space-between', - gap: spacing.md + gap: spacing.sm }, - branchLine: { - flex: 1, - minWidth: 0, - flexDirection: 'row', - alignItems: 'center', - gap: spacing.xs - }, - branchText: { + conflictBannerTitle: { flex: 1, color: colors.textPrimary, fontSize: typography.bodySize, fontWeight: '600' }, - syncText: { + conflictBannerText: { color: colors.textSecondary, fontSize: typography.metaSize }, - countRow: { + commitsRow: { + minHeight: 48, flexDirection: 'row', - flexWrap: 'wrap', - gap: spacing.md, - marginTop: spacing.sm - }, - countText: { - color: colors.textSecondary, - fontSize: typography.metaSize - }, - // Keep Abort below counts so narrow cards cannot overflow. - conflictRow: { - flexDirection: 'row', - flexWrap: 'wrap', alignItems: 'center', gap: spacing.sm, - marginTop: spacing.sm, - alignSelf: 'flex-start', - maxWidth: '100%' + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: colors.borderSubtle }, - conflictText: { - color: colors.statusAmber, - fontSize: typography.metaSize, - textTransform: 'capitalize' + commitsRowText: { + flex: 1, + color: colors.textPrimary, + fontSize: typography.bodySize + }, + syncText: { + marginLeft: 'auto', + color: colors.textSecondary, + fontSize: typography.metaSize }, abortButton: { minHeight: 32, @@ -125,40 +130,6 @@ const baseStyles = themedStyleSheet(() => ({ color: colors.textPrimary, fontSize: typography.metaSize, lineHeight: 16 - }, - bulkRow: { - flexDirection: 'row', - gap: spacing.sm, - marginTop: spacing.md - }, - bulkButton: { - flex: 1, - minHeight: 36, - borderRadius: radii.control, - backgroundColor: colors.bgRaised, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - gap: spacing.xs - }, - bulkMenuButton: { - width: 42, - minHeight: 36, - borderRadius: radii.control, - backgroundColor: colors.bgRaised, - alignItems: 'center', - justifyContent: 'center' - }, - bulkButtonDisabled: { - opacity: 0.45 - }, - bulkButtonPressed: { - opacity: 0.75 - }, - bulkButtonText: { - color: colors.textPrimary, - fontSize: typography.bodySize, - fontWeight: '600' } })) diff --git a/mobile/src/source-control/source-control-status-retry.test.tsx b/mobile/src/source-control/source-control-status-retry.test.tsx index 0ae212e0ef9..a212a66c54b 100644 --- a/mobile/src/source-control/source-control-status-retry.test.tsx +++ b/mobile/src/source-control/source-control-status-retry.test.tsx @@ -20,6 +20,7 @@ vi.mock('react-native-safe-area-context', () => ({ SafeAreaView: 'SafeAreaView' vi.mock('lucide-react-native', () => ({ CircleAlert: 'Icon', GitBranch: 'Icon', + RefreshCw: 'Icon', WifiOff: 'Icon' })) vi.mock('./MobileSourceControlHeader', () => ({ MobileSourceControlHeader: () => null })) @@ -55,7 +56,7 @@ vi.mock('../components/pr-sidebar/use-mobile-pr-tab', () => ({ vi.mock('../components/pr-sidebar/use-pr-tab-menu', () => ({ usePrTabMenuGroups: () => null })) vi.mock('../navigation/use-back-claim', () => ({ useBackClaim: () => {} })) vi.mock('./use-mobile-source-control-action-sheet', () => ({ - useMobileSourceControlActionSheet: () => ({}) + useMobileSourceControlActionSheet: () => [] })) vi.mock('../session/use-mobile-pr-sidebar-controller', () => ({ useMobilePrSidebarController: () => ({ @@ -82,7 +83,6 @@ vi.mock('./use-mobile-source-control-state', () => ({ status: null, branchCompareResult: null, branchLabel: '', - syncLabel: '', unstagedCount: 0, stagedCount: 0, branchEntries: [], diff --git a/mobile/src/source-control/use-mobile-source-control-loaders.ts b/mobile/src/source-control/use-mobile-source-control-loaders.ts index 4a2b821dce9..97913c55776 100644 --- a/mobile/src/source-control/use-mobile-source-control-loaders.ts +++ b/mobile/src/source-control/use-mobile-source-control-loaders.ts @@ -185,10 +185,9 @@ export function useMobileSourceControlLoaders(params: Params): MobileSourceContr setScreenState((prev) => (prev.kind === 'ready' ? prev : { kind: 'loading' })) try { for (let attempt = 0; attempt <= SELECTOR_RETRY_COUNT; attempt += 1) { + // Changes rows show each file's +/- counts, so ask the host for line stats. const reply = await gitStatusHostPayloadRead.request(client, { - worktree: `id:${worktreeId}`, - // Changes rows never show line counts; older hosts strip the unknown key. - includeLineStats: false + worktree: `id:${worktreeId}` }) if (!isCurrentLoad()) { return false diff --git a/mobile/src/source-control/use-mobile-source-control-state.ts b/mobile/src/source-control/use-mobile-source-control-state.ts index 0caa6fdf156..5206db5c678 100644 --- a/mobile/src/source-control/use-mobile-source-control-state.ts +++ b/mobile/src/source-control/use-mobile-source-control-state.ts @@ -15,8 +15,7 @@ import { useMobileSourceControlKeyboardLift } from './use-mobile-source-control- import type { RuntimeGitLocalBranches } from '../../../src/shared/runtime-types' import { buildMobileBranchCompareSection, - canOpenMobileBranchCompareDiff, - formatMobileBranchCompareSummary + canOpenMobileBranchCompareDiff } from './mobile-branch-compare' import { @@ -89,9 +88,6 @@ export function useMobileSourceControlState(params: MobileSourceControlStatePara () => buildMobileBranchCompareSection(branchCompareResult?.entries ?? []), [branchCompareResult, t] ) - const branchCompareSummaryText = branchCompareResult - ? formatMobileBranchCompareSummary(branchCompareResult.summary) - : null const branchCompareCanOpen = branchCompareResult ? canOpenMobileBranchCompareDiff(branchCompareResult.summary) : false @@ -124,12 +120,6 @@ export function useMobileSourceControlState(params: MobileSourceControlStatePara const branchLabel = formatBranchLabel(status?.branch, status?.head) const upstream = status?.upstreamStatus const upstreamKnown = upstream !== undefined - const syncLabel = - upstream && upstream.hasUpstream - ? t('sourceControl.state.syncStatus', '{{ahead}} ahead, {{behind}} behind', upstream) - : upstream && !upstream.hasUpstream - ? t('sourceControl.state.noUpstream', 'No upstream') - : null const { sendGitRequest, sendCommitRequest, runGitSyncSteps } = useMobileGitRequests({ client, @@ -239,7 +229,6 @@ export function useMobileSourceControlState(params: MobileSourceControlStatePara status, sections, branchCompareResult, - branchCompareSummaryText, branchEntries, shouldShowBranchCompareSection, hasVisibleChanges, @@ -249,7 +238,6 @@ export function useMobileSourceControlState(params: MobileSourceControlStatePara unstagedCount, branchLabel, upstreamKnown, - syncLabel, primaryActionArgs, createPrAction, loadStatus,