diff --git a/packages/core-mobile/app/assets/lotties/connect-waves.json b/packages/core-mobile/app/assets/lotties/connect-waves.json new file mode 100644 index 0000000000..4f768f2a8d --- /dev/null +++ b/packages/core-mobile/app/assets/lotties/connect-waves.json @@ -0,0 +1,1192 @@ +{ + "v": "5.7.5", + "fr": 100, + "ip": 0, + "op": 300, + "w": 180, + "h": 180, + "nm": "Comp 1", + "ddd": 0, + "metadata": { "backgroundColor": { "r": 255, "g": 255, "b": 255 } }, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "Bluetooth-logo.svg 1", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 0, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "hd": true, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "nm": "SVG", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [14.144503, 18.121092], + [18.520503, 13.745092], + [14.140603, 9.367192], + [14.144503, 18.121092], + [14.144503, 18.121092] + ], + "i": [ + [0, 0], + [-1.458666666666666, 1.4586666666666677], + [1.4599666666666682, 1.4593000000000007], + [-0.001300000000000523, -2.9179666666666666], + [0, 0] + ], + "o": [ + [1.458666666666666, -1.458666666666666], + [-1.4599666666666664, -1.4593000000000007], + [0.001300000000000523, 2.9179666666666666], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [14.140603, 36.172892], + [18.520503, 31.793992], + [14.144503, 27.417992], + [14.140603, 36.172892], + [14.140603, 36.172892] + ], + "i": [ + [0, 0], + [-1.4599666666666664, 1.4596333333333327], + [1.458666666666666, 1.458666666666666], + [0.001300000000000523, -2.918300000000002], + [0, 0] + ], + "o": [ + [1.4599666666666682, -1.4596333333333362], + [-1.458666666666666, -1.458666666666666], + [-0.001300000000000523, 2.9182999999999986], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [23.999003000000002, 13.713892], + [14.943403, 22.769492], + [23.999003000000002, 31.826192000000002], + [10.286103, 45.539092], + [10.286103, 27.460892], + [2.743163, 35.003892], + [0, 32.260791999999995], + [9.465823, 22.769492], + [0, 13.278291999999999], + [2.743163, 10.535191999999999], + [10.286103, 18.079092], + [10.286103, 0], + [23.999003000000002, 13.713892], + [23.999003000000002, 13.713892] + ], + "i": [ + [0, 0], + [3.018533333333334, -3.018533333333334], + [-3.018533333333334, -3.018900000000002], + [4.570966666666667, -4.5709666666666635], + [0, 6.026066666666665], + [2.5143133333333347, -2.514333333333333], + [0.9143876666666669, 0.9143666666666661], + [-3.1552743333333337, 3.1637666666666675], + [3.1552743333333333, 3.163733333333335], + [-0.9143876666666668, 0.9143666666666661], + [-2.514313333333334, -2.514633333333334], + [0, 6.026364], + [-4.570966666666667, -4.571297333333334], + [0, 0] + ], + "o": [ + [-3.018533333333334, 3.0185333333333357], + [3.018533333333334, 3.018900000000002], + [-4.570966666666667, 4.57096666666666], + [0, -6.026066666666665], + [-2.514313333333334, 2.514333333333333], + [-0.9143876666666668, -0.9143666666666661], + [3.1552743333333333, -3.163766666666664], + [-3.155274333333333, -3.163733333333333], + [0.9143876666666665, -0.9143666666666661], + [2.514313333333333, 2.5146333333333324], + [0, -6.026364000000001], + [4.570966666666667, 4.571297333333333], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.1568627450980392, 0.1568627450980392, + 0.1803921568627451 + ], + "ix": 2 + }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [12.403797149658203, 23.570756912231445], + "ix": 2 + }, + "a": { + "a": 0, + "k": [11.99950122833252, 22.769546508789062], + "ix": 2 + }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { + "a": 0, + "k": [12.403796672821045, 23.57075595855713], + "ix": 2 + }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 3, + "nm": "", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "Wave-1 1:0-stroke-mask", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "td": 1, + "ao": 0, + "parent": 2, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1 1", + "it": [ + { + "ty": "el", + "d": 1, + "s": { "a": 0, "k": [70, 70], "ix": 2 }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0, 0, 0], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 4, + "ty": 4, + "nm": "Wave-1 1:0", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "tt": 1, + "ao": 0, + "parent": 2, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1 1", + "it": [ + { + "ty": "el", + "d": 1, + "s": { "a": 0, "k": [70, 70], "ix": 2 }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 0.1568627450980392, 0.1568627450980392, 0.1803921568627451 + ], + "ix": 2 + }, + "o": { "a": 0, "k": 0, "ix": 2 }, + "w": { "a": 0, "k": 4, "ix": 2 }, + "lc": 1, + "lj": 1, + "ml": 4 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 5, + "ty": 3, + "nm": "", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 6, + "ty": 4, + "nm": "Wave-1 4:0-stroke-mask", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "td": 1, + "ao": 0, + "parent": 5, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1 4", + "it": [ + { + "ty": "el", + "d": 1, + "s": { + "a": 1, + "k": [ + { + "t": 85, + "s": [70, 70], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 175, + "s": [175, 175], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [88, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0, 0, 0], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 7, + "ty": 4, + "nm": "Wave-1 4:0", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "tt": 1, + "ao": 0, + "parent": 5, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1 4", + "it": [ + { + "ty": "el", + "d": 1, + "s": { + "a": 1, + "k": [ + { + "t": 85, + "s": [70, 70], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 175, + "s": [175, 175], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 0.1568627450980392, 0.1568627450980392, 0.1803921568627451 + ], + "ix": 2 + }, + "o": { + "a": 1, + "k": [ + { + "t": 85, + "s": [0], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 100, + "s": [20], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 175, + "s": [0], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "w": { "a": 0, "k": 4, "ix": 2 }, + "lc": 1, + "lj": 1, + "ml": 4 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [88, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 8, + "ty": 3, + "nm": "", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 9, + "ty": 4, + "nm": "Wave-1 3:0-stroke-mask", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "td": 1, + "ao": 0, + "parent": 8, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1 3", + "it": [ + { + "ty": "el", + "d": 1, + "s": { + "a": 1, + "k": [ + { + "t": 60, + "s": [70, 70], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 150, + "s": [175, 175], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0, 0, 0], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 10, + "ty": 4, + "nm": "Wave-1 3:0", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "tt": 1, + "ao": 0, + "parent": 8, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1 3", + "it": [ + { + "ty": "el", + "d": 1, + "s": { + "a": 1, + "k": [ + { + "t": 60, + "s": [70, 70], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 150, + "s": [175, 175], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 0.1568627450980392, 0.1568627450980392, 0.1803921568627451 + ], + "ix": 2 + }, + "o": { + "a": 1, + "k": [ + { + "t": 60, + "s": [0], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 75, + "s": [20], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 150, + "s": [0], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "w": { "a": 0, "k": 4, "ix": 2 }, + "lc": 1, + "lj": 1, + "ml": 4 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 11, + "ty": 3, + "nm": "", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 12, + "ty": 4, + "nm": "Wave-1 2:0-stroke-mask", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "td": 1, + "ao": 0, + "parent": 11, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1 2", + "it": [ + { + "ty": "el", + "d": 1, + "s": { + "a": 1, + "k": [ + { + "t": 35, + "s": [70, 70], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 125, + "s": [175, 175], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0, 0, 0], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 13, + "ty": 4, + "nm": "Wave-1 2:0", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "tt": 1, + "ao": 0, + "parent": 11, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1 2", + "it": [ + { + "ty": "el", + "d": 1, + "s": { + "a": 1, + "k": [ + { + "t": 35, + "s": [70, 70], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 125, + "s": [175, 175], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 0.1568627450980392, 0.1568627450980392, 0.1803921568627451 + ], + "ix": 2 + }, + "o": { + "a": 1, + "k": [ + { + "t": 35, + "s": [0], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 50, + "s": [20], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 125, + "s": [0], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "w": { "a": 0, "k": 4, "ix": 2 }, + "lc": 1, + "lj": 1, + "ml": 4 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 14, + "ty": 3, + "nm": "", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 15, + "ty": 4, + "nm": "Wave-1:0-stroke-mask", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "td": 1, + "ao": 0, + "parent": 14, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1", + "it": [ + { + "ty": "el", + "d": 1, + "s": { + "a": 1, + "k": [ + { + "t": 10, + "s": [70, 70], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 100, + "s": [175, 175], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0, 0, 0], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 16, + "ty": 4, + "nm": "Wave-1:0", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "tt": 1, + "ao": 0, + "parent": 14, + "shapes": [ + { + "ty": "gr", + "nm": "Wave-1", + "it": [ + { + "ty": "el", + "d": 1, + "s": { + "a": 1, + "k": [ + { + "t": 10, + "s": [70, 70], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 100, + "s": [175, 175], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "p": { "a": 0, "k": [0, 0], "ix": 2 } + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 0.1568627450980392, 0.1568627450980392, 0.1803921568627451 + ], + "ix": 2 + }, + "o": { + "a": 1, + "k": [ + { + "t": 10, + "s": [0], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 25, + "s": [20], + "o": { "x": [0], "y": [0] }, + "i": { "x": [0.58], "y": [1] } + }, + { + "t": 100, + "s": [0], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "w": { "a": 0, "k": 4, "ix": 2 }, + "lc": 1, + "lj": 1, + "ml": 4 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [90, 90], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 301, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} diff --git a/packages/core-mobile/app/new/features/ledger/components/AnimatedIconWithText.tsx b/packages/core-mobile/app/new/features/ledger/components/AnimatedIconWithText.tsx new file mode 100644 index 0000000000..f532dd8780 --- /dev/null +++ b/packages/core-mobile/app/new/features/ledger/components/AnimatedIconWithText.tsx @@ -0,0 +1,124 @@ +import React from 'react' +import { View } from 'react-native' +import LottieView from 'lottie-react-native' +import { Text, useTheme } from '@avalabs/k2-alpine' + +// Import animation at the top level +const connectWavesAnimation = require('assets/lotties/connect-waves.json') + +interface AnimatedIconWithTextProps { + /** The icon component to display */ + icon: React.ReactNode + /** The main title text */ + title: string + /** The subtitle/description text */ + subtitle: string + /** Whether to show the animation behind the icon */ + showAnimation?: boolean + /** Custom animation source (defaults to connect-waves.json) - Lottie animation JSON + * In React Native, require() for a Lottie JSON returns either a number (asset reference) or an object (parsed JSON). + * This union type matches what LottieView expects and removes the any lint error. + */ + animationSource?: number | object + /** Custom animation size (defaults to 220x220) */ + animationSize?: { width: number; height: number } + /** Custom icon positioning offset for animation centering */ + animationOffset?: { top: number; left: number } + /** Custom color for the animation (defaults to theme textPrimary) */ + animationColor?: string +} + +export const AnimatedIconWithText: React.FC = ({ + icon, + title, + subtitle, + showAnimation = false, + animationSource = connectWavesAnimation, + animationSize = { width: 220, height: 220 }, + animationColor +}) => { + const { + theme: { colors } + } = useTheme() + + // Calculate dynamic positioning based on animation size + const iconContainerHeight = 44 // Assuming standard icon size + const animationRadius = animationSize.width / 2 + const iconRadius = iconContainerHeight / 2 + + // Calculate animation offset to center it around the icon + const dynamicAnimationOffset = { + top: -(animationRadius - iconRadius), + left: -(animationRadius - iconRadius) + } + + // Calculate consistent text position regardless of animation state + const baseTopPosition = 160 // Base centering position + const textOverlapPosition = baseTopPosition + iconContainerHeight + 16 // Keep text close to icon for both states + + return ( + + + {showAnimation && ( + + )} + {icon} + + + + {title} + + + {subtitle} + + + + ) +} diff --git a/packages/core-mobile/app/new/features/ledger/components/DerivationPathSelector.tsx b/packages/core-mobile/app/new/features/ledger/components/DerivationPathSelector.tsx index 7863e863c3..b22311d29f 100644 --- a/packages/core-mobile/app/new/features/ledger/components/DerivationPathSelector.tsx +++ b/packages/core-mobile/app/new/features/ledger/components/DerivationPathSelector.tsx @@ -1,6 +1,6 @@ -import React, { useState, useCallback } from 'react' -import { View } from 'react-native' -import { Text, Button, useTheme, GroupList, Icons } from '@avalabs/k2-alpine' +import React from 'react' +import { View, TouchableOpacity } from 'react-native' +import { Text, useTheme, Icons } from '@avalabs/k2-alpine' import { ScrollScreen } from 'common/components/ScrollScreen' import { LedgerDerivationPathType } from 'services/ledger/types' @@ -17,15 +17,193 @@ interface DerivationPathOption { interface DerivationPathSelectorProps { onSelect: (derivationPathType: LedgerDerivationPathType) => void + onCancel?: () => void +} + +interface ListItemProps { + text: string + type: 'benefit' | 'consideration' + isFirst?: boolean +} + +const ListItem: React.FC = ({ text, type, isFirst = false }) => { + const { + theme: { colors } + } = useTheme() + + const iconProps = + type === 'benefit' + ? { + Icon: Icons.Custom.CheckSmall, + color: colors.$textSuccess + } + : { + Icon: Icons.Custom.RedExclamation, + color: colors.$textDanger, + width: 16, + height: 12 + } + + return ( + + + + {text} + + + ) +} + +interface OptionCardProps { + option: DerivationPathOption + onPress?: () => void +} + +const OptionCard: React.FC = ({ option, onPress }) => { + const { + theme: { colors } + } = useTheme() + + return ( + + + + + + + {option.title} + + + {option.subtitle} + + + + + + + {/* Divider that spans from text start to card end */} + + + + + Benefits + + {option.benefits.map((benefit, index) => ( + + ))} + + + {option.warnings.length > 0 && ( + + + Considerations + + {option.warnings.map((warning, index) => ( + + ))} + + )} + + ) } const derivationPathOptions: DerivationPathOption[] = [ { type: LedgerDerivationPathType.BIP44, - title: 'BIP44 (Recommended)', - subtitle: 'Standard approach for most users', + title: 'BIP44', + subtitle: 'Recommended for most users', benefits: [ - 'Faster setup (~15 seconds)', + 'Faster setup, about 15 seconds', 'Create new accounts without device', 'Industry standard approach', 'Better for multiple accounts' @@ -54,210 +232,25 @@ const derivationPathOptions: DerivationPathOption[] = [ export const DerivationPathSelector: React.FC = ({ onSelect }) => { - const { - theme: { colors } - } = useTheme() - const [selectedType, setSelectedType] = - useState(null) - - const selectedOption = derivationPathOptions.find( - option => option.type === selectedType - ) - - const renderFooter = useCallback(() => { - return ( - - - - ) - }, [selectedType, onSelect]) - - const groupListData = derivationPathOptions.map(option => ({ - title: option.title, - subtitle: ( - - {option.subtitle} - - ), - leftIcon: ( - - - - ), - accessory: - selectedType === option.type ? ( - - ) : ( - - ), - onPress: () => setSelectedType(option.type) - })) - return ( - - - + contentContainerStyle={{ + padding: 16, + gap: 16, + paddingBottom: 100 + }}> + - {selectedOption && ( - - {/* Recommendation badge */} - {selectedOption.recommended && ( - - - RECOMMENDED - - - )} - - {/* Quick stats */} - - - - - Setup Time - - - {selectedOption.setupTime} - - - - - New Accounts - - - {selectedOption.newAccountRequirement} - - - - - - {/* Benefits */} - - - ✓ Benefits - - {selectedOption.benefits.map((benefit, index) => ( - - - • - - - {benefit} - - - ))} - - - {/* Considerations */} - {selectedOption.warnings.length > 0 && ( - - - ⚠️ Considerations - - {selectedOption.warnings.map((warning, index) => ( - - - • - - - {warning} - - - ))} - - )} - - )} + {derivationPathOptions.map(option => ( + onSelect(option.type)} + /> + ))} ) } diff --git a/packages/core-mobile/app/new/features/ledger/components/EnhancedLedgerSetup.tsx b/packages/core-mobile/app/new/features/ledger/components/EnhancedLedgerSetup.tsx deleted file mode 100644 index 854d2260c3..0000000000 --- a/packages/core-mobile/app/new/features/ledger/components/EnhancedLedgerSetup.tsx +++ /dev/null @@ -1,391 +0,0 @@ -import React, { useState, useCallback, useEffect } from 'react' -import { View, Alert } from 'react-native' -import { Text, Button, useTheme, GroupList, Icons } from '@avalabs/k2-alpine' -import { ScrollScreen } from 'common/components/ScrollScreen' -import { LoadingState } from 'common/components/LoadingState' -import { - LedgerDerivationPathType, - WalletCreationOptions -} from 'services/ledger/types' -import { useLedgerWallet } from '../hooks/useLedgerWallet' -import { DerivationPathSelector } from './DerivationPathSelector' -import { LedgerSetupProgress } from './LedgerSetupProgress' -import { LedgerAppConnection } from './LedgerAppConnection' - -type SetupStep = - | 'path-selection' - | 'education' - | 'device-connection' - | 'app-connection' - | 'setup-progress' - | 'complete' - -interface EnhancedLedgerSetupProps { - onComplete: (walletId: string) => void - onCancel: () => void -} - -export const EnhancedLedgerSetup: React.FC = ({ - onComplete, - onCancel -}) => { - const { - theme: { colors } - } = useTheme() - const [currentStep, setCurrentStep] = useState('path-selection') - const [selectedDerivationPath, setSelectedDerivationPath] = - useState(null) - const [connectedDeviceId, setConnectedDeviceId] = useState( - null - ) - const [connectedDeviceName, setConnectedDeviceName] = - useState('Ledger Device') - const [isCreatingWallet, setIsCreatingWallet] = useState(false) - - const { - devices, - isScanning, - isConnecting, - transportState, - scanForDevices, - connectToDevice, - disconnectDevice, - getSolanaKeys, - getAvalancheKeys, - createLedgerWallet, - setupProgress, - keys - } = useLedgerWallet() - - // Check if keys are available and auto-progress to setup - useEffect(() => { - if ( - keys.avalancheKeys && - keys.solanaKeys.length > 0 && - currentStep === 'app-connection' - ) { - setCurrentStep('setup-progress') - } - }, [keys.avalancheKeys, keys.solanaKeys, currentStep]) - - // Handle device connection - const handleDeviceConnection = useCallback( - async (deviceId: string, deviceName: string) => { - try { - await connectToDevice(deviceId) - setConnectedDeviceId(deviceId) - setConnectedDeviceName(deviceName) - - // Move to app connection step instead of getting keys immediately - setCurrentStep('app-connection') - } catch (error) { - Alert.alert( - 'Connection Failed', - 'Failed to connect to Ledger device. Please try again.', - [{ text: 'OK' }] - ) - } - }, - [connectToDevice] - ) - - // Start wallet setup (called from setup-progress step) - const handleStartSetup = useCallback(async () => { - if (!connectedDeviceId || !selectedDerivationPath || isCreatingWallet) { - return - } - - try { - setIsCreatingWallet(true) - // We already have all the keys we need from the app connection step - // No need to retrieve keys again - just create the wallet with what we have - const walletCreationOptions: WalletCreationOptions = { - deviceId: connectedDeviceId, - deviceName: connectedDeviceName, - derivationPathType: selectedDerivationPath, - accountCount: 3 // Standard 3 accounts for both BIP44 and Ledger Live - } - - const walletId = await createLedgerWallet(walletCreationOptions) - setCurrentStep('complete') - onComplete(walletId) - } catch (error) { - // Wallet creation failed - Alert.alert( - 'Setup Failed', - 'Failed to create Ledger wallet. Please try again.', - [ - { - text: 'Try Again', - onPress: () => { - setIsCreatingWallet(false) - setCurrentStep('app-connection') - } - }, - { - text: 'Cancel', - onPress: () => { - setIsCreatingWallet(false) - onCancel?.() - } - } - ] - ) - } finally { - setIsCreatingWallet(false) - } - }, [ - connectedDeviceId, - connectedDeviceName, - selectedDerivationPath, - isCreatingWallet, - createLedgerWallet, - onComplete, - onCancel - ]) - - // Auto-start wallet creation when entering setup-progress step - useEffect(() => { - if ( - currentStep === 'setup-progress' && - selectedDerivationPath && - connectedDeviceId && - !isCreatingWallet - ) { - handleStartSetup() - } - }, [ - currentStep, - selectedDerivationPath, - connectedDeviceId, - isCreatingWallet, - handleStartSetup - ]) - - const handleDerivationPathSelect = useCallback( - (derivationPathType: LedgerDerivationPathType) => { - setSelectedDerivationPath(derivationPathType) - setCurrentStep('device-connection') - }, - [] - ) - - const handleCancel = useCallback(async () => { - if (connectedDeviceId) { - await disconnectDevice() - } - onCancel?.() - }, [connectedDeviceId, disconnectDevice, onCancel]) - - const renderCurrentStep = (): React.ReactNode => { - switch (currentStep) { - case 'path-selection': - return ( - - - - ) - - case 'device-connection': - return ( - - ) - - case 'app-connection': - return ( - setCurrentStep('setup-progress')} - onCancel={handleCancel} - getSolanaKeys={getSolanaKeys} - getAvalancheKeys={getAvalancheKeys} - deviceName={connectedDeviceName} - selectedDerivationPath={selectedDerivationPath} - /> - ) - - case 'setup-progress': - return setupProgress ? ( - - ) : ( - - Initializing setup... - - ) - - case 'complete': - return ( - - - 🎉 Wallet Created Successfully! - - - Your Ledger wallet has been set up and is ready to use. - - - - ) - - default: - return null - } - } - - return ( - - {renderCurrentStep()} - - ) -} - -interface LedgerDevice { - id: string - name: string -} - -// Enhanced device connection component -const DeviceConnectionStep: React.FC<{ - devices: LedgerDevice[] - isScanning: boolean - isConnecting: boolean - transportState: unknown - onScan: () => void - onConnect: (deviceId: string, deviceName: string) => void - onCancel: () => void -}> = ({ devices, isScanning, isConnecting, onScan, onConnect, onCancel }) => { - const { - theme: { colors } - } = useTheme() - - const renderFooter = useCallback(() => { - return ( - - - - - - ) - }, [isScanning, isConnecting, onScan, onCancel]) - - const deviceListData = devices.map(device => ({ - title: device.name || 'Ledger Device', - subtitle: ( - - {device.id ? `ID: ${device.id.slice(0, 8)}...` : 'Ready to connect'} - - ), - leftIcon: ( - - ), - accessory: ( - - ), - onPress: () => onConnect(device.id, device.name) - })) - - return ( - - {isScanning && ( - - - - Searching for Ledger devices... - - - )} - - {!isScanning && devices.length === 0 && ( - - - - No devices found. Make sure your Ledger is connected and unlocked. - - - )} - - {devices.length > 0 && ( - - - Available Devices - - - - )} - - ) -} diff --git a/packages/core-mobile/app/new/features/ledger/components/LedgerAppConnection.tsx b/packages/core-mobile/app/new/features/ledger/components/LedgerAppConnection.tsx index db9a6ab4f3..c97216a58f 100644 --- a/packages/core-mobile/app/new/features/ledger/components/LedgerAppConnection.tsx +++ b/packages/core-mobile/app/new/features/ledger/components/LedgerAppConnection.tsx @@ -22,6 +22,7 @@ interface LedgerAppConnectionProps { getAvalancheKeys: () => Promise deviceName: string selectedDerivationPath: LedgerDerivationPathType | null + isCreatingWallet?: boolean } export const LedgerAppConnection: React.FC = ({ @@ -30,7 +31,8 @@ export const LedgerAppConnection: React.FC = ({ getSolanaKeys, getAvalancheKeys, deviceName, - selectedDerivationPath + selectedDerivationPath, + isCreatingWallet = false }) => { const { theme: { colors } @@ -57,9 +59,12 @@ export const LedgerAppConnection: React.FC = ({ }, 2000) break case AppConnectionStep.COMPLETE: - timeoutId = setTimeout(() => { - onComplete() - }, 1500) + // Don't auto-navigate if wallet is being created + if (!isCreatingWallet) { + timeoutId = setTimeout(() => { + onComplete() + }, 1500) + } break } @@ -68,7 +73,7 @@ export const LedgerAppConnection: React.FC = ({ clearTimeout(timeoutId) } } - }, [currentStep, onComplete]) + }, [currentStep, onComplete, isCreatingWallet]) const handleConnectAvalanche = useCallback(async () => { try { @@ -327,37 +332,59 @@ export const LedgerAppConnection: React.FC = ({ case AppConnectionStep.COMPLETE: return ( - - - - - Apps Connected! - - - Both Avalanche and Solana apps are connected. Proceeding to wallet - setup... - + {isCreatingWallet ? ( + <> + + + Creating Wallet... + + + Setting up your Ledger wallet. This may take a moment... + + + ) : ( + <> + + + + + Apps Connected! + + + Both Avalanche and Solana apps are connected. Proceeding to + wallet setup... + + + )} ) diff --git a/packages/core-mobile/app/new/features/ledger/components/LedgerSetupProgress.tsx b/packages/core-mobile/app/new/features/ledger/components/LedgerSetupProgress.tsx deleted file mode 100644 index 41a91dad2a..0000000000 --- a/packages/core-mobile/app/new/features/ledger/components/LedgerSetupProgress.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import React from 'react' -import { View, ActivityIndicator, TouchableOpacity } from 'react-native' -import { Text, useTheme } from '@avalabs/k2-alpine' -import { LedgerDerivationPathType } from 'services/ledger/types' -import { SetupProgress } from 'services/ledger/types' - -interface LedgerSetupProgressProps { - progress: SetupProgress - derivationPathType: LedgerDerivationPathType - onCancel?: () => void -} - -const formatTime = (seconds: number): string => { - if (seconds < 60) { - return `${seconds}s` - } - const minutes = Math.floor(seconds / 60) - const remainingSeconds = seconds % 60 - return remainingSeconds > 0 - ? `${minutes}m ${remainingSeconds}s` - : `${minutes}m` -} - -const getSetupInstructions = ( - derivationPathType: LedgerDerivationPathType -): string[] => { - if (derivationPathType === LedgerDerivationPathType.BIP44) { - return [ - 'Please confirm each derivation path on your Ledger device', - 'This will take approximately 15 seconds', - 'Keep your device connected during setup' - ] - } else { - return [ - 'Please confirm each derivation path on your Ledger device', - 'This will take approximately 45 seconds', - 'Each account requires individual confirmation', - 'Keep your device connected during setup' - ] - } -} - -export const LedgerSetupProgress: React.FC = ({ - progress, - derivationPathType, - onCancel -}) => { - const { - theme: { colors } - } = useTheme() - const instructions = getSetupInstructions(derivationPathType) - - return ( - - {/* Header */} - - - - - - - Setting up your Ledger wallet - - - - {derivationPathType === LedgerDerivationPathType.BIP44 - ? 'BIP44 Setup' - : 'Ledger Live Setup'} - - - - {/* Progress section */} - - {/* Current step */} - - {progress.currentStep} - - - {/* Progress bar */} - - - - - - - {/* Progress stats */} - - - Step {Math.ceil((progress.progress / 100) * progress.totalSteps)} of{' '} - {progress.totalSteps} - - - {progress.estimatedTimeRemaining !== undefined && - progress.estimatedTimeRemaining > 0 && ( - - ~{formatTime(progress.estimatedTimeRemaining)} remaining - - )} - - - - {/* Instructions */} - - - 📱 Instructions - - {instructions.map((instruction, index) => ( - - • {instruction} - - ))} - - - {/* Device status indicators */} - - - - Device connected - - - - {/* Cancel button */} - {onCancel && ( - - - - Cancel Setup - - - - )} - - ) -} diff --git a/packages/core-mobile/app/new/features/ledger/components/index.ts b/packages/core-mobile/app/new/features/ledger/components/index.ts index ac676ca023..a35676bd0e 100644 --- a/packages/core-mobile/app/new/features/ledger/components/index.ts +++ b/packages/core-mobile/app/new/features/ledger/components/index.ts @@ -1,3 +1,3 @@ export { DerivationPathSelector } from './DerivationPathSelector' -export { LedgerSetupProgress } from './LedgerSetupProgress' -export { EnhancedLedgerSetup } from './EnhancedLedgerSetup' +export { LedgerAppConnection } from './LedgerAppConnection' +export { AnimatedIconWithText } from './AnimatedIconWithText' diff --git a/packages/core-mobile/app/new/features/ledger/contexts/LedgerSetupContext.tsx b/packages/core-mobile/app/new/features/ledger/contexts/LedgerSetupContext.tsx new file mode 100644 index 0000000000..4fafde6678 --- /dev/null +++ b/packages/core-mobile/app/new/features/ledger/contexts/LedgerSetupContext.tsx @@ -0,0 +1,167 @@ +import React, { + createContext, + useContext, + useState, + useCallback, + useMemo, + ReactNode +} from 'react' +import { + LedgerDerivationPathType, + WalletCreationOptions, + LedgerDevice, + SetupProgress, + LedgerTransportState +} from 'services/ledger/types' +import { + useLedgerWallet, + UseLedgerWalletReturn +} from '../hooks/useLedgerWallet' + +interface LedgerSetupContextValue { + // State values + selectedDerivationPath: LedgerDerivationPathType | null + connectedDeviceId: string | null + connectedDeviceName: string + isCreatingWallet: boolean + hasStartedSetup: boolean + + // State setters + setSelectedDerivationPath: (path: LedgerDerivationPathType) => void + setConnectedDevice: (deviceId: string, deviceName: string) => void + setIsCreatingWallet: (creating: boolean) => void + setHasStartedSetup: (started: boolean) => void + + // Ledger wallet hook values + devices: LedgerDevice[] + isScanning: boolean + isConnecting: boolean + transportState: LedgerTransportState + scanForDevices: () => void + connectToDevice: (deviceId: string) => Promise + disconnectDevice: () => Promise + getSolanaKeys: () => Promise + getAvalancheKeys: () => Promise + createLedgerWallet: (options: WalletCreationOptions) => Promise + setupProgress: SetupProgress | null + keys: UseLedgerWalletReturn['keys'] + + // Helper methods + resetSetup: () => void +} + +const LedgerSetupContext = createContext(null) + +interface LedgerSetupProviderProps { + children: ReactNode +} + +export const LedgerSetupProvider: React.FC = ({ + children +}) => { + const [selectedDerivationPath, setSelectedDerivationPath] = + useState(null) + const [connectedDeviceId, setConnectedDeviceId] = useState( + null + ) + const [connectedDeviceName, setConnectedDeviceName] = + useState('Ledger Device') + const [isCreatingWallet, setIsCreatingWallet] = useState(false) + const [hasStartedSetup, setHasStartedSetup] = useState(false) + + // Use the existing ledger wallet hook + const { + devices, + isScanning, + isConnecting, + transportState, + scanForDevices, + connectToDevice, + disconnectDevice, + getSolanaKeys, + getAvalancheKeys, + createLedgerWallet, + setupProgress, + keys + } = useLedgerWallet() + + const handleSetConnectedDevice = useCallback( + (deviceId: string, deviceName: string) => { + setConnectedDeviceId(deviceId) + setConnectedDeviceName(deviceName) + }, + [] + ) + + const resetSetup = useCallback(() => { + setSelectedDerivationPath(null) + setConnectedDeviceId(null) + setConnectedDeviceName('Ledger Device') + setIsCreatingWallet(false) + setHasStartedSetup(false) + }, []) + + const contextValue: LedgerSetupContextValue = useMemo( + () => ({ + selectedDerivationPath, + connectedDeviceId, + connectedDeviceName, + isCreatingWallet, + hasStartedSetup, + devices, + isScanning, + isConnecting, + transportState, + scanForDevices, + connectToDevice, + disconnectDevice, + getSolanaKeys, + getAvalancheKeys, + createLedgerWallet, + setupProgress, + keys, + setSelectedDerivationPath, + setConnectedDevice: handleSetConnectedDevice, + setIsCreatingWallet, + setHasStartedSetup, + resetSetup + }), + [ + selectedDerivationPath, + connectedDeviceId, + connectedDeviceName, + isCreatingWallet, + hasStartedSetup, + devices, + isScanning, + isConnecting, + transportState, + scanForDevices, + connectToDevice, + disconnectDevice, + getSolanaKeys, + getAvalancheKeys, + createLedgerWallet, + setupProgress, + keys, + handleSetConnectedDevice, + resetSetup + ] + ) + + return ( + + {children} + + ) +} + +export const useLedgerSetupContext = (): LedgerSetupContextValue => { + const context = useContext(LedgerSetupContext) + if (!context) { + throw new Error( + 'useLedgerSetupContext must be used within a LedgerSetupProvider' + ) + } + return context +} diff --git a/packages/core-mobile/app/new/features/ledger/hooks/useLedgerWallet.ts b/packages/core-mobile/app/new/features/ledger/hooks/useLedgerWallet.ts index 6c3891f8fe..290f121180 100644 --- a/packages/core-mobile/app/new/features/ledger/hooks/useLedgerWallet.ts +++ b/packages/core-mobile/app/new/features/ledger/hooks/useLedgerWallet.ts @@ -484,8 +484,11 @@ export function useLedgerWallet(): UseLedgerWalletReturn { `Creating ${derivationPathType} Ledger wallet with generated keys...` ) - if (!avalancheKeys || solanaKeys.length === 0 || !bitcoinAddress) { - throw new Error('Missing required keys for wallet creation') + if (!avalancheKeys) { + throw new Error('Missing Avalanche keys for wallet creation') + } + if (solanaKeys.length === 0) { + throw new Error('Missing Solana keys for wallet creation') } updateProgress('Generating wallet ID...') diff --git a/packages/core-mobile/app/new/features/ledger/index.ts b/packages/core-mobile/app/new/features/ledger/index.ts new file mode 100644 index 0000000000..18023eec97 --- /dev/null +++ b/packages/core-mobile/app/new/features/ledger/index.ts @@ -0,0 +1,2 @@ +export * from './components' +export * from './contexts/LedgerSetupContext' diff --git a/packages/core-mobile/app/new/features/ledger/screens/AppConnectionScreen.tsx b/packages/core-mobile/app/new/features/ledger/screens/AppConnectionScreen.tsx new file mode 100644 index 0000000000..57debd2c2e --- /dev/null +++ b/packages/core-mobile/app/new/features/ledger/screens/AppConnectionScreen.tsx @@ -0,0 +1,132 @@ +import React, { useCallback, useEffect, useState } from 'react' +import { useRouter } from 'expo-router' +import { Alert } from 'react-native' +import { LedgerAppConnection } from 'new/features/ledger/components/LedgerAppConnection' +import { useLedgerSetupContext } from 'new/features/ledger/contexts/LedgerSetupContext' + +export default function AppConnectionScreen(): JSX.Element { + const { push, back } = useRouter() + const [isCreatingWallet, setIsCreatingWallet] = useState(false) + + const { + getSolanaKeys, + getAvalancheKeys, + connectedDeviceId, + connectedDeviceName, + selectedDerivationPath, + keys, + resetSetup, + disconnectDevice, + createLedgerWallet + } = useLedgerSetupContext() + + // Check if keys are available and create wallet, then navigate to complete + useEffect(() => { + const createWalletAndNavigate = async () => { + if ( + keys.avalancheKeys && + keys.solanaKeys.length > 0 && + connectedDeviceId && + selectedDerivationPath && + !isCreatingWallet + ) { + setIsCreatingWallet(true) + + try { + await createLedgerWallet({ + deviceId: connectedDeviceId, + deviceName: connectedDeviceName, + derivationPathType: selectedDerivationPath + }) + + // Navigate to complete screen after wallet creation + // @ts-ignore TODO: make routes typesafe + push('/accountSettings/ledger/complete') + } catch (error) { + Alert.alert( + 'Wallet Creation Failed', + error instanceof Error + ? error.message + : 'Failed to create Ledger wallet. Please try again.', + [{ text: 'OK' }] + ) + setIsCreatingWallet(false) + } + } + } + + createWalletAndNavigate() + }, [ + keys.avalancheKeys, + keys.solanaKeys, + connectedDeviceId, + connectedDeviceName, + selectedDerivationPath, + createLedgerWallet, + push, + isCreatingWallet + ]) + + const handleComplete = useCallback(async () => { + // If wallet hasn't been created yet, create it now + if ( + keys.avalancheKeys && + keys.solanaKeys.length > 0 && + connectedDeviceId && + selectedDerivationPath && + !isCreatingWallet + ) { + setIsCreatingWallet(true) + + try { + await createLedgerWallet({ + deviceId: connectedDeviceId, + deviceName: connectedDeviceName, + derivationPathType: selectedDerivationPath + }) + + // @ts-ignore TODO: make routes typesafe + push('/accountSettings/ledger/complete') + } catch (error) { + Alert.alert( + 'Wallet Creation Failed', + error instanceof Error + ? error.message + : 'Failed to create Ledger wallet. Please try again.', + [{ text: 'OK' }] + ) + setIsCreatingWallet(false) + } + } else { + // @ts-ignore TODO: make routes typesafe + push('/accountSettings/ledger/complete') + } + }, [ + keys.avalancheKeys, + keys.solanaKeys, + connectedDeviceId, + connectedDeviceName, + selectedDerivationPath, + createLedgerWallet, + push, + isCreatingWallet + ]) + + const handleCancel = useCallback(async () => { + await disconnectDevice() + resetSetup() + back() + }, [disconnectDevice, resetSetup, back]) + + return ( + + ) +} diff --git a/packages/core-mobile/app/new/features/ledger/screens/CompleteScreen.tsx b/packages/core-mobile/app/new/features/ledger/screens/CompleteScreen.tsx new file mode 100644 index 0000000000..bc3247c698 --- /dev/null +++ b/packages/core-mobile/app/new/features/ledger/screens/CompleteScreen.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import { View } from 'react-native' +import { useRouter } from 'expo-router' +import { Text, Button, useTheme } from '@avalabs/k2-alpine' +import { useLedgerSetupContext } from 'new/features/ledger/contexts/LedgerSetupContext' + +export default function CompleteScreen(): JSX.Element { + const { push } = useRouter() + const { + theme: { colors } + } = useTheme() + + const { resetSetup } = useLedgerSetupContext() + + const handleComplete = (): void => { + resetSetup() + // Navigate to account management after successful wallet creation + // @ts-ignore TODO: make routes typesafe + push('/accountSettings/manageAccounts') + } + + return ( + + + 🎉 Wallet created successfully! + + + Your Ledger wallet has been set up and is ready to use. + + + + ) +} diff --git a/packages/core-mobile/app/new/features/ledger/screens/DeviceConnectionScreen.tsx b/packages/core-mobile/app/new/features/ledger/screens/DeviceConnectionScreen.tsx new file mode 100644 index 0000000000..dd8c0c4b58 --- /dev/null +++ b/packages/core-mobile/app/new/features/ledger/screens/DeviceConnectionScreen.tsx @@ -0,0 +1,177 @@ +import React, { useCallback } from 'react' +import { View, Alert, ActivityIndicator } from 'react-native' +import { useRouter } from 'expo-router' +import { Text, Button, useTheme, GroupList, Icons } from '@avalabs/k2-alpine' +import { ScrollScreen } from 'common/components/ScrollScreen' +import { useLedgerSetupContext } from 'new/features/ledger/contexts/LedgerSetupContext' +import { AnimatedIconWithText } from 'new/features/ledger/components/AnimatedIconWithText' + +interface LedgerDevice { + id: string + name: string +} + +export default function DeviceConnectionScreen(): JSX.Element { + const { push, back } = useRouter() + const { + theme: { colors } + } = useTheme() + + const { + devices, + isScanning, + isConnecting, + scanForDevices, + connectToDevice, + setConnectedDevice, + resetSetup + } = useLedgerSetupContext() + + // Handle device connection + const handleDeviceConnection = useCallback( + async (deviceId: string, deviceName: string) => { + try { + await connectToDevice(deviceId) + setConnectedDevice(deviceId, deviceName) + + // Navigate to app connection step + // @ts-ignore TODO: make routes typesafe + push('/accountSettings/ledger/appConnection') + } catch (error) { + Alert.alert( + 'Connection failed', + 'Failed to connect to Ledger device. Please try again.', + [{ text: 'OK' }] + ) + } + }, + [connectToDevice, setConnectedDevice, push] + ) + + const handleCancel = useCallback(() => { + resetSetup() + back() + }, [resetSetup, back]) + + const deviceListData = devices.map((device: LedgerDevice) => ({ + title: device.name || 'Ledger Device', + subtitle: ( + + Found over Bluetooth + + ), + leftIcon: ( + + + + ), + accessory: ( + + ), + onPress: () => handleDeviceConnection(device.id, device.name) + })) + + const renderFooter = useCallback(() => { + return ( + + {!isScanning && devices.length === 0 && ( + + )} + + {isScanning && devices.length === 0 && ( + + + + )} + + + + ) + }, [ + isScanning, + scanForDevices, + devices.length, + colors.$textPrimary, + handleCancel + ]) + + return ( + + {isScanning && ( + + } + title="Looking for devices..." + subtitle="Make sure your Ledger device is unlocked and the Avalanche app is open" + showAnimation={true} + /> + )} + + {!isScanning && devices.length === 0 && ( + + } + title="Get your Ledger ready" + subtitle="Make sure your Ledger device is unlocked and ready to connect" + showAnimation={false} + /> + )} + + {devices.length > 0 && ( + + + + )} + + ) +} diff --git a/packages/core-mobile/app/new/features/ledger/screens/PathSelectionScreen.tsx b/packages/core-mobile/app/new/features/ledger/screens/PathSelectionScreen.tsx new file mode 100644 index 0000000000..18c1681a49 --- /dev/null +++ b/packages/core-mobile/app/new/features/ledger/screens/PathSelectionScreen.tsx @@ -0,0 +1,32 @@ +import React, { useCallback } from 'react' +import { useRouter } from 'expo-router' +import { DerivationPathSelector } from 'new/features/ledger/components/DerivationPathSelector' +import { useLedgerSetupContext } from 'new/features/ledger/contexts/LedgerSetupContext' +import { LedgerDerivationPathType } from 'services/ledger/types' + +export default function PathSelectionScreen(): JSX.Element { + const { push, back } = useRouter() + const { setSelectedDerivationPath, resetSetup } = useLedgerSetupContext() + + const handleDerivationPathSelect = useCallback( + (derivationPathType: LedgerDerivationPathType) => { + setSelectedDerivationPath(derivationPathType) + // Navigate to device connection step + // @ts-ignore TODO: make routes typesafe + push('/accountSettings/ledger/deviceConnection') + }, + [setSelectedDerivationPath, push] + ) + + const handleCancel = useCallback(() => { + resetSetup() + back() + }, [resetSetup, back]) + + return ( + + ) +} diff --git a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/_layout.tsx b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/_layout.tsx index 9be54fb03a..14730cfecd 100644 --- a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/_layout.tsx +++ b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/_layout.tsx @@ -28,6 +28,7 @@ export default function AccountSettingsLayout(): JSX.Element { + diff --git a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/importWallet.tsx b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/importWallet.tsx index a0e1c173e5..d57a979592 100644 --- a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/importWallet.tsx +++ b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/importWallet.tsx @@ -72,7 +72,7 @@ const ImportWalletScreen = (): JSX.Element => { const handleImportLedger = (): void => { // @ts-ignore TODO: make routes typesafe - navigate({ pathname: '/accountSettings/ledger/enhancedSetup' }) + navigate({ pathname: '/accountSettings/ledger' }) } const baseData = [ @@ -125,7 +125,7 @@ const ImportWalletScreen = (): JSX.Element => { ), leftIcon: ( - + + + + + + + + ) +} diff --git a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/appConnection.tsx b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/appConnection.tsx new file mode 100644 index 0000000000..7720707500 --- /dev/null +++ b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/appConnection.tsx @@ -0,0 +1,3 @@ +import AppConnectionScreen from 'new/features/ledger/screens/AppConnectionScreen' + +export { AppConnectionScreen as default } diff --git a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/complete.tsx b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/complete.tsx new file mode 100644 index 0000000000..69d1e879cb --- /dev/null +++ b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/complete.tsx @@ -0,0 +1,3 @@ +import CompleteScreen from 'new/features/ledger/screens/CompleteScreen' + +export { CompleteScreen as default } diff --git a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/deviceConnection.tsx b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/deviceConnection.tsx new file mode 100644 index 0000000000..9b379969dc --- /dev/null +++ b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/deviceConnection.tsx @@ -0,0 +1,3 @@ +import DeviceConnectionScreen from 'new/features/ledger/screens/DeviceConnectionScreen' + +export { DeviceConnectionScreen as default } diff --git a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/enhancedSetup.tsx b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/enhancedSetup.tsx deleted file mode 100644 index 3f52c1286b..0000000000 --- a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/enhancedSetup.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { useRouter } from 'expo-router' -import { EnhancedLedgerSetup } from 'new/features/ledger/components' - -export default function EnhancedLedgerSetupScreen(): JSX.Element { - const router = useRouter() - - const handleComplete = (): void => { - // Navigate to account management after successful wallet creation - // @ts-ignore TODO: make routes typesafe - router.push('/accountSettings/manageAccounts') - } - - const handleCancel = (): void => { - router.back() - } - - return ( - - ) -} diff --git a/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/pathSelection.tsx b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/pathSelection.tsx new file mode 100644 index 0000000000..6e07736132 --- /dev/null +++ b/packages/core-mobile/app/new/routes/(signedIn)/(modals)/accountSettings/ledger/pathSelection.tsx @@ -0,0 +1,3 @@ +import PathSelectionScreen from 'new/features/ledger/screens/PathSelectionScreen' + +export { PathSelectionScreen as default } diff --git a/packages/k2-alpine/src/assets/icons/bluetooth.svg b/packages/k2-alpine/src/assets/icons/bluetooth.svg new file mode 100644 index 0000000000..b3563f99bf --- /dev/null +++ b/packages/k2-alpine/src/assets/icons/bluetooth.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/k2-alpine/src/assets/icons/ledger_logo.svg b/packages/k2-alpine/src/assets/icons/ledger_logo.svg new file mode 100644 index 0000000000..c8f53582a5 --- /dev/null +++ b/packages/k2-alpine/src/assets/icons/ledger_logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/k2-alpine/src/theme/tokens/Icons.ts b/packages/k2-alpine/src/theme/tokens/Icons.ts index 15b07845e0..36ba0b61ce 100644 --- a/packages/k2-alpine/src/theme/tokens/Icons.ts +++ b/packages/k2-alpine/src/theme/tokens/Icons.ts @@ -97,6 +97,7 @@ import IconAch from '../../assets/icons/ach.svg' import IconDownload from '../../assets/icons/download.svg' import IconEncrypted from '../../assets/icons/shield.svg' import IconSwapProviderAuto from '../../assets/icons/swap_auto.svg' +import IconLedger from '../../assets/icons/ledger_logo.svg' // Transaction types import IconTxTypeAdd from '../../assets/icons/tx-type-add.svg' import IconTxTypeAdvanceTime from '../../assets/icons/advance-time.svg' @@ -111,6 +112,7 @@ import IconTxTypeSubnet from '../../assets/icons/transaction-subnet.svg' import IconTxTypeUnwrap from '../../assets/icons/unwrap.svg' import IconTxTypeUnknown from '../../assets/icons/unknown.svg' import IconPsychiatry from '../../assets/icons/psychiatry.svg' +import IconBluetooth from '../../assets/icons/bluetooth.svg' // token logos import AAVE from '../../assets/tokenLogos/AAVE.svg' @@ -332,7 +334,9 @@ export const Icons = { ShopeePay: IconShopeePay, Ach: IconAch, Download: IconDownload, - SwapProviderAuto: IconSwapProviderAuto + SwapProviderAuto: IconSwapProviderAuto, + Ledger: IconLedger, + Bluetooth: IconBluetooth }, RecoveryMethod: { Passkey: IconPasskey,