diff --git a/.gitignore b/.gitignore index 445c45263..b89dcf032 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ vendor/.nimble *.AppImage tmp nimcache +.DS_Store \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 348e6671e..0960e643d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,17 @@ [submodule "vendor/nim-serialization"] path = vendor/nim-serialization url = https://github.com/status-im/nim-serialization/ +[submodule "vendor/nimcrypto"] + path = vendor/nimcrypto + url = https://github.com/cheatfate/nimcrypto +[submodule "vendor/uuids"] + path = vendor/uuids + url = https://github.com/pragmagic/uuids +[submodule "vendor/isaac"] + path = vendor/isaac + url = https://github.com/pragmagic/isaac +[submodule "vendor/eventemitter"] + path = vendor/eventemitter + url = https://github.com/al-bimani/eventemitter + ignore = dirty + branch = master diff --git a/nim_status_client.nimble b/nim_status_client.nimble index b39851f4c..ab5837bb3 100644 --- a/nim_status_client.nimble +++ b/nim_status_client.nimble @@ -10,4 +10,4 @@ skipExt = @["nim"] # Deps -requires "nim >= 1.0.0", " nimqml >= 0.7.0", "stint" +requires "nim >= 1.0.0", " nimqml >= 0.7.0", "stint", "nimcrypto >= 0.4.11", "uuids >= 0.1.10" diff --git a/src/constants/constants.nim b/src/constants/constants.nim new file mode 100644 index 000000000..67792ac4d --- /dev/null +++ b/src/constants/constants.nim @@ -0,0 +1,172 @@ +import json + +const PATH_WALLET_ROOT* = "m/44'/60'/0'/0" +# EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived +const PATH_EIP_1581* = "m/43'/60'/1581'" +# BIP44-0 Wallet key, the default wallet key +const PATH_DEFAULT_WALLET* = PATH_WALLET_ROOT & "/0" +# EIP1581 Chat Key 0, the default whisper key +const PATH_WHISPER* = PATH_EIP_1581 & "/0'/0" + +let DEFAULT_NETWORKS* = %* [ + { + "id": "testnet_rpc", + "etherscan-link": "https://ropsten.etherscan.io/address/", + "name": "Ropsten with upstream RPC", + "config": { + "NetworkId": 3, + "DataDir": "/ethereum/testnet_rpc", + "UpstreamConfig": { + "Enabled": true, + "URL": "https://ropsten.infura.io/v3/f315575765b14720b32382a61a89341a" + } + } + }, + { + "id": "rinkeby_rpc", + "etherscan-link": "https://rinkeby.etherscan.io/address/", + "name": "Rinkeby with upstream RPC", + "config": { + "NetworkId": 4, + "DataDir": "/ethereum/rinkeby_rpc", + "UpstreamConfig": { + "Enabled": true, + "URL": "https://rinkeby.infura.io/v3/f315575765b14720b32382a61a89341a" + } + } + }, + { + "id": "goerli_rpc", + "etherscan-link": "https://goerli.etherscan.io/address/", + "name": "Goerli with upstream RPC", + "config": { + "NetworkId": 5, + "DataDir": "/ethereum/goerli_rpc", + "UpstreamConfig": { + "Enabled": true, + "URL": "https://goerli.blockscout.com/" + } + } + }, + { + "id": "mainnet_rpc", + "etherscan-link": "https://etherscan.io/address/", + "name": "Mainnet with upstream RPC", + "config": { + "NetworkId": 1, + "DataDir": "/ethereum/mainnet_rpc", + "UpstreamConfig": { + "Enabled": true, + "URL": "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a" + } + } + }, + { + "id": "xdai_rpc", + "name": "xDai Chain", + "config": { + "NetworkId": 100, + "DataDir": "/ethereum/xdai_rpc", + "UpstreamConfig": { + "Enabled": true, + "URL": "https://dai.poa.network" + } + } + }, + { + "id": "poa_rpc", + "name": "POA Network", + "config": { + "NetworkId": 99, + "DataDir": "/ethereum/poa_rpc", + "UpstreamConfig": { + "Enabled": true, + "URL": "https://core.poa.network" + } + } + } +] + +let NODE_CONFIG* = %* { + "BrowsersConfig": { + "Enabled": true + }, + "ClusterConfig": { + "BootNodes": [ + "enode://23d0740b11919358625d79d4cac7d50a34d79e9c69e16831c5c70573757a1f5d7d884510bc595d7ee4da3c1508adf87bbc9e9260d804ef03f8c1e37f2fb2fc69@47.52.106.107:443", + "enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@178.128.140.188:443", + "enode://6e6554fb3034b211398fcd0f0082cbb6bd13619e1a7e76ba66e1809aaa0c5f1ac53c9ae79cf2fd4a7bacb10d12010899b370c75fed19b991d9c0cdd02891abad@47.75.99.169:443", + "enode://5405c509df683c962e7c9470b251bb679dd6978f82d5b469f1f6c64d11d50fbd5dd9f7801c6ad51f3b20a5f6c7ffe248cc9ab223f8bcbaeaf14bb1c0ef295fd0@35.223.215.156:443" + ], + "Enabled": true, + "Fleet": "eth.prod", + "RendezvousNodes": [ + "/ip4/34.70.75.208/tcp/30703/ethv4/16Uiu2HAm6ZsERLx2BwVD2UM9SVPnnMU6NBycG8XPtu8qKys5awsU", + "/ip4/178.128.140.188/tcp/30703/ethv4/16Uiu2HAmLqTXuY4Sb6G28HNooaFUXUKzpzKXCcgyJxgaEE2i5vnf", + "/ip4/47.52.106.107/tcp/30703/ethv4/16Uiu2HAmEHiptiDDd9gqNY8oQqo8hHUWMHJzfwt5aLRdD6W2zcXR" + ], + "StaticNodes": [ + "enode://887cbd92d95afc2c5f1e227356314a53d3d18855880ac0509e0c0870362aee03939d4074e6ad31365915af41d34320b5094bfcc12a67c381788cd7298d06c875@178.128.141.0:443", + "enode://fbeddac99d396b91d59f2c63a3cb5fc7e0f8a9f7ce6fe5f2eed5e787a0154161b7173a6a73124a4275ef338b8966dc70a611e9ae2192f0f2340395661fad81c0@34.67.230.193:443" + ], + "TrustedMailServers": [ + "enode://2c8de3cbb27a3d30cbb5b3e003bc722b126f5aef82e2052aaef032ca94e0c7ad219e533ba88c70585ebd802de206693255335b100307645ab5170e88620d2a81@47.244.221.14:443", + "enode://ee2b53b0ace9692167a410514bca3024695dbf0e1a68e1dff9716da620efb195f04a4b9e873fb9b74ac84de801106c465b8e2b6c4f0d93b8749d1578bfcaf03e@104.197.238.144:443", + "enode://8a64b3c349a2e0ef4a32ea49609ed6eb3364be1110253c20adc17a3cebbc39a219e5d3e13b151c0eee5d8e0f9a8ba2cd026014e67b41a4ab7d1d5dd67ca27427@178.128.142.94:443", + "enode://7aa648d6e855950b2e3d3bf220c496e0cae4adfddef3e1e6062e6b177aec93bc6cdcf1282cb40d1656932ebfdd565729da440368d7c4da7dbd4d004b1ac02bf8@178.128.142.26:443", + "enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@178.128.142.54:443", + "enode://30211cbd81c25f07b03a0196d56e6ce4604bb13db773ff1c0ea2253547fafd6c06eae6ad3533e2ba39d59564cfbdbb5e2ce7c137a5ebb85e99dcfc7a75f99f55@23.236.58.92:443" + ] + }, + "DataDir": "./ethereum/mainnet", + "EnableNTPSync": true, + "KeyStoreDir": "./keystore", + "ListenAddr": ":30304", + "LogEnabled": true, + "LogFile": "geth.log", + "LogLevel": "INFO", + "MailserversConfig": { + "Enabled": true + }, + "Name": "StatusIM", + "NetworkId": 1, + "NoDiscovery": false, + "PermissionsConfig": { + "Enabled": true + }, + "Rendezvous": true, + "RequireTopics": { + "whisper": { + "Max": 2, + "Min": 2 + } + }, + "ShhextConfig": { + "BackupDisabledDataDir": "./", + "DataSyncEnabled": true, + "InstallationID": "aef27732-8d86-5039-a32e-bdbe094d8791", + "MailServerConfirmations": true, + "MaxMessageDeliveryAttempts": 6, + "PFSEnabled": true, + "VerifyENSContractAddress": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + "VerifyENSURL": "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a", + "VerifyTransactionChainID": 1, + "VerifyTransactionURL": "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a" + }, + "StatusAccountsConfig": { + "Enabled": true + }, + "UpstreamConfig": { + "Enabled": true, + "URL": "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a" + }, + "WakuConfig": { + "BloomFilterMode": nil, + "Enabled": true, + "LightClient": true, + "MinimumPoW": 0.001 + }, + "WalletConfig": { + "Enabled": true + } +} \ No newline at end of file diff --git a/src/constants/default_networks.json b/src/constants/default_networks.json new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/constants/default_networks.json @@ -0,0 +1 @@ + diff --git a/src/constants/node_config.json b/src/constants/node_config.json new file mode 100644 index 000000000..d2a49c511 --- /dev/null +++ b/src/constants/node_config.json @@ -0,0 +1,83 @@ +{ + "BrowsersConfig": { + "Enabled": true + }, + "ClusterConfig": { + "BootNodes": [ + "enode://23d0740b11919358625d79d4cac7d50a34d79e9c69e16831c5c70573757a1f5d7d884510bc595d7ee4da3c1508adf87bbc9e9260d804ef03f8c1e37f2fb2fc69@47.52.106.107:443", + "enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@178.128.140.188:443", + "enode://6e6554fb3034b211398fcd0f0082cbb6bd13619e1a7e76ba66e1809aaa0c5f1ac53c9ae79cf2fd4a7bacb10d12010899b370c75fed19b991d9c0cdd02891abad@47.75.99.169:443", + "enode://5405c509df683c962e7c9470b251bb679dd6978f82d5b469f1f6c64d11d50fbd5dd9f7801c6ad51f3b20a5f6c7ffe248cc9ab223f8bcbaeaf14bb1c0ef295fd0@35.223.215.156:443" + ], + "Enabled": true, + "Fleet": "eth.prod", + "RendezvousNodes": [ + "/ip4/34.70.75.208/tcp/30703/ethv4/16Uiu2HAm6ZsERLx2BwVD2UM9SVPnnMU6NBycG8XPtu8qKys5awsU", + "/ip4/178.128.140.188/tcp/30703/ethv4/16Uiu2HAmLqTXuY4Sb6G28HNooaFUXUKzpzKXCcgyJxgaEE2i5vnf", + "/ip4/47.52.106.107/tcp/30703/ethv4/16Uiu2HAmEHiptiDDd9gqNY8oQqo8hHUWMHJzfwt5aLRdD6W2zcXR" + ], + "StaticNodes": [ + "enode://887cbd92d95afc2c5f1e227356314a53d3d18855880ac0509e0c0870362aee03939d4074e6ad31365915af41d34320b5094bfcc12a67c381788cd7298d06c875@178.128.141.0:443", + "enode://fbeddac99d396b91d59f2c63a3cb5fc7e0f8a9f7ce6fe5f2eed5e787a0154161b7173a6a73124a4275ef338b8966dc70a611e9ae2192f0f2340395661fad81c0@34.67.230.193:443" + ], + "TrustedMailServers": [ + "enode://2c8de3cbb27a3d30cbb5b3e003bc722b126f5aef82e2052aaef032ca94e0c7ad219e533ba88c70585ebd802de206693255335b100307645ab5170e88620d2a81@47.244.221.14:443", + "enode://ee2b53b0ace9692167a410514bca3024695dbf0e1a68e1dff9716da620efb195f04a4b9e873fb9b74ac84de801106c465b8e2b6c4f0d93b8749d1578bfcaf03e@104.197.238.144:443", + "enode://8a64b3c349a2e0ef4a32ea49609ed6eb3364be1110253c20adc17a3cebbc39a219e5d3e13b151c0eee5d8e0f9a8ba2cd026014e67b41a4ab7d1d5dd67ca27427@178.128.142.94:443", + "enode://7aa648d6e855950b2e3d3bf220c496e0cae4adfddef3e1e6062e6b177aec93bc6cdcf1282cb40d1656932ebfdd565729da440368d7c4da7dbd4d004b1ac02bf8@178.128.142.26:443", + "enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@178.128.142.54:443", + "enode://30211cbd81c25f07b03a0196d56e6ce4604bb13db773ff1c0ea2253547fafd6c06eae6ad3533e2ba39d59564cfbdbb5e2ce7c137a5ebb85e99dcfc7a75f99f55@23.236.58.92:443" + ] + }, + "DataDir": "./ethereum/mainnet", + "EnableNTPSync": true, + "KeyStoreDir": "./keystore", + "ListenAddr": ":30304", + "LogEnabled": true, + "LogFile": "geth.log", + "LogLevel": "INFO", + "MailserversConfig": { + "Enabled": true + }, + "Name": "StatusIM", + "NetworkId": 1, + "NoDiscovery": false, + "PermissionsConfig": { + "Enabled": true + }, + "Rendezvous": true, + "RequireTopics": { + "whisper": { + "Max": 2, + "Min": 2 + } + }, + "ShhextConfig": { + "BackupDisabledDataDir": "./", + "DataSyncEnabled": true, + "InstallationID": "aef27732-8d86-5039-a32e-bdbe094d8791", + "MailServerConfirmations": true, + "MaxMessageDeliveryAttempts": 6, + "PFSEnabled": true, + "VerifyENSContractAddress": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + "VerifyENSURL": "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a", + "VerifyTransactionChainID": 1, + "VerifyTransactionURL": "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a" + }, + "StatusAccountsConfig": { + "Enabled": true + }, + "UpstreamConfig": { + "Enabled": true, + "URL": "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a" + }, + "WakuConfig": { + "BloomFilterMode": null, + "Enabled": true, + "LightClient": true, + "MinimumPoW": 0.001 + }, + "WalletConfig": { + "Enabled": true + } +} diff --git a/src/constants/signing_phrases.nim b/src/constants/signing_phrases.nim new file mode 100644 index 000000000..e459823bf --- /dev/null +++ b/src/constants/signing_phrases.nim @@ -0,0 +1,623 @@ +const phrases*: seq[string] = @[ + "acid", + "alto", + "apse", + "arch", + "area", + "army", + "atom", + "aunt", + "babe", + "baby", + "back", + "bail", + "bait", + "bake", + "ball", + "band", + "bank", + "barn", + "base", + "bass", + "bath", + "bead", + "beak", + "beam", + "bean", + "bear", + "beat", + "beef", + "beer", + "beet", + "bell", + "belt", + "bend", + "bike", + "bill", + "bird", + "bite", + "blow", + "blue", + "boar", + "boat", + "body", + "bolt", + "bomb", + "bone", + "book", + "boot", + "bore", + "boss", + "bowl", + "brow", + "bulb", + "bull", + "burn", + "bush", + "bust", + "cafe", + "cake", + "calf", + "call", + "calm", + "camp", + "cane", + "cape", + "card", + "care", + "carp", + "cart", + "case", + "cash", + "cast", + "cave", + "cell", + "cent", + "chap", + "chef", + "chin", + "chip", + "chop", + "chub", + "chug", + "city", + "clam", + "clef", + "clip", + "club", + "clue", + "coal", + "coat", + "code", + "coil", + "coin", + "coke", + "cold", + "colt", + "comb", + "cone", + "cook", + "cope", + "copy", + "cord", + "cork", + "corn", + "cost", + "crab", + "craw", + "crew", + "crib", + "crop", + "crow", + "curl", + "cyst", + "dame", + "dare", + "dark", + "dart", + "dash", + "data", + "date", + "dead", + "deal", + "dear", + "debt", + "deck", + "deep", + "deer", + "desk", + "dhow", + "diet", + "dill", + "dime", + "dirt", + "dish", + "disk", + "dock", + "doll", + "door", + "dory", + "drag", + "draw", + "drop", + "drug", + "drum", + "duck", + "dump", + "dust", + "duty", + "ease", + "east", + "eave", + "eddy", + "edge", + "envy", + "epee", + "exam", + "exit", + "face", + "fact", + "fail", + "fall", + "fame", + "fang", + "farm", + "fawn", + "fear", + "feed", + "feel", + "feet", + "file", + "fill", + "film", + "find", + "fine", + "fire", + "fish", + "flag", + "flat", + "flax", + "flow", + "foam", + "fold", + "font", + "food", + "foot", + "fork", + "form", + "fort", + "fowl", + "frog", + "fuel", + "full", + "gain", + "gale", + "galn", + "game", + "garb", + "gate", + "gear", + "gene", + "gift", + "girl", + "give", + "glad", + "glen", + "glue", + "glut", + "goal", + "goat", + "gold", + "golf", + "gong", + "good", + "gown", + "grab", + "gram", + "gray", + "grey", + "grip", + "grit", + "gyro", + "hail", + "hair", + "half", + "hall", + "hand", + "hang", + "harm", + "harp", + "hate", + "hawk", + "head", + "heat", + "heel", + "hell", + "helo", + "help", + "hemp", + "herb", + "hide", + "high", + "hill", + "hire", + "hive", + "hold", + "hole", + "home", + "hood", + "hoof", + "hook", + "hope", + "hops", + "horn", + "hose", + "host", + "hour", + "hunt", + "hurt", + "icon", + "idea", + "inch", + "iris", + "iron", + "item", + "jail", + "jeep", + "jeff", + "joey", + "join", + "joke", + "judo", + "jump", + "junk", + "jury", + "jute", + "kale", + "keep", + "kick", + "kill", + "kilt", + "kind", + "king", + "kiss", + "kite", + "knee", + "knot", + "lace", + "lack", + "lady", + "lake", + "lamb", + "lamp", + "land", + "lark", + "lava", + "lawn", + "lead", + "leaf", + "leek", + "lier", + "life", + "lift", + "lily", + "limo", + "line", + "link", + "lion", + "lisa", + "list", + "load", + "loaf", + "loan", + "lock", + "loft", + "long", + "look", + "loss", + "lout", + "love", + "luck", + "lung", + "lute", + "lynx", + "lyre", + "maid", + "mail", + "main", + "make", + "male", + "mall", + "manx", + "many", + "mare", + "mark", + "mask", + "mass", + "mate", + "math", + "meal", + "meat", + "meet", + "menu", + "mess", + "mice", + "midi", + "mile", + "milk", + "mime", + "mind", + "mine", + "mini", + "mint", + "miss", + "mist", + "moat", + "mode", + "mole", + "mood", + "moon", + "most", + "moth", + "move", + "mule", + "mutt", + "nail", + "name", + "neat", + "neck", + "need", + "neon", + "nest", + "news", + "node", + "nose", + "note", + "oboe", + "okra", + "open", + "oval", + "oven", + "oxen", + "pace", + "pack", + "page", + "pail", + "pain", + "pair", + "palm", + "pard", + "park", + "part", + "pass", + "past", + "path", + "peak", + "pear", + "peen", + "peer", + "pelt", + "perp", + "pest", + "pick", + "pier", + "pike", + "pile", + "pimp", + "pine", + "ping", + "pink", + "pint", + "pipe", + "piss", + "pith", + "plan", + "play", + "plot", + "plow", + "poem", + "poet", + "pole", + "polo", + "pond", + "pony", + "poof", + "pool", + "port", + "post", + "prow", + "pull", + "puma", + "pump", + "pupa", + "push", + "quit", + "race", + "rack", + "raft", + "rage", + "rail", + "rain", + "rake", + "rank", + "rate", + "read", + "rear", + "reef", + "rent", + "rest", + "rice", + "rich", + "ride", + "ring", + "rise", + "risk", + "road", + "robe", + "rock", + "role", + "roll", + "roof", + "room", + "root", + "rope", + "rose", + "ruin", + "rule", + "rush", + "ruth", + "sack", + "safe", + "sage", + "sail", + "sale", + "salt", + "sand", + "sari", + "sash", + "save", + "scow", + "seal", + "seat", + "seed", + "self", + "sell", + "shed", + "shin", + "ship", + "shoe", + "shop", + "shot", + "show", + "sick", + "side", + "sign", + "silk", + "sill", + "silo", + "sing", + "sink", + "site", + "size", + "skin", + "sled", + "slip", + "smog", + "snob", + "snow", + "soap", + "sock", + "soda", + "sofa", + "soft", + "soil", + "song", + "soot", + "sort", + "soup", + "spot", + "spur", + "stag", + "star", + "stay", + "stem", + "step", + "stew", + "stop", + "stud", + "suck", + "suit", + "swan", + "swim", + "tail", + "tale", + "talk", + "tank", + "tard", + "task", + "taxi", + "team", + "tear", + "teen", + "tell", + "temp", + "tent", + "term", + "test", + "text", + "thaw", + "tile", + "till", + "time", + "tire", + "toad", + "toga", + "togs", + "tone", + "tool", + "toot", + "tote", + "tour", + "town", + "tram", + "tray", + "tree", + "trim", + "trip", + "tuba", + "tube", + "tuna", + "tune", + "turn", + "tutu", + "twig", + "type", + "unit", + "user", + "vane", + "vase", + "vast", + "veal", + "veil", + "vein", + "vest", + "vibe", + "view", + "vise", + "wait", + "wake", + "walk", + "wall", + "wash", + "wasp", + "wave", + "wear", + "weed", + "week", + "well", + "west", + "whip", + "wife", + "will", + "wind", + "wine", + "wing", + "wire", + "wish", + "wolf", + "wood", + "wool", + "word", + "work", + "worm", + "wrap", + "wren", + "yard", + "yarn", + "yawl", + "year", + "yoga", + "yoke", + "yurt", + "zinc", + "zone"] \ No newline at end of file diff --git a/src/models/accounts.nim b/src/models/accounts.nim new file mode 100644 index 000000000..fbaa719a1 --- /dev/null +++ b/src/models/accounts.nim @@ -0,0 +1,10 @@ +import json + +type + GeneratedAccount* = object + publicKey*: string + address*: string + id*: string + keyUid*: string + mnemonic*: string + derived*: JsonNode \ No newline at end of file diff --git a/src/nim_status_client.nim b/src/nim_status_client.nim index 8551cf4f6..e08f489d3 100644 --- a/src/nim_status_client.nim +++ b/src/nim_status_client.nim @@ -6,6 +6,10 @@ import app/node/core as node import app/profile/core as profile import app/signals/core as signals import state +import onboarding +import status/utils +import strformat +import strutils import strformat import strutils import json @@ -16,16 +20,43 @@ import status/types as types import status/wallet as status_wallet import status/libstatus import state +import status/libstatusqml +import status/types +import eventemitter +import os var signalsQObjPointer: pointer logScope: topics = "main" +proc ensureDir(dirname: string) = + if not existsDir(dirname): + # removeDir(dirname) + createDir(dirname) + +proc initNode(): string = + const datadir = "./data/" + const keystoredir = "./data/keystore/" + const nobackupdir = "./noBackup/" + + ensureDir(datadir) + ensureDir(keystoredir) + ensureDir(nobackupdir) + + # 1 + result = $libstatus.initKeystore(keystoredir); + + # 2 + result = $libstatus.openAccounts(datadir); + proc mainProc() = + discard initNode() + let app = newQApplication() let engine = newQQmlApplicationEngine() let signalController = signals.newController(app) + let events = createEventEmitter() defer: # Defer will run this just before mainProc() function ends app.delete() @@ -42,10 +73,12 @@ proc mainProc() = var accounts = status_test.setupNewAccount() debug "Accounts", accounts0 = parseJSON(accounts)[0], accounts1 = parseJSON(accounts)[1] - status_chat.startMessenger() + events.on("node:ready") do(a: Args): + status_chat.startMessenger() var wallet = wallet.newController() - wallet.init() + events.on("node:ready") do(a: Args): + wallet.init() engine.setRootContextProperty("assetsModel", wallet.variant) var chat = chat.newController() @@ -54,7 +87,23 @@ proc mainProc() = var node = node.newController() node.init() + engine.setRootContextProperty("nodeModel", node.variant) + + var onboarding = newOnboarding(events); + defer: onboarding.delete + + let onboardingVariant = newQVariant(onboarding) + defer: onboardingVariant.delete + + engine.setRootContextProperty("onboardingLogic", onboardingVariant) + + # TODO: figure out a way to prevent this from breaking Qt Creator + # var initLibStatusQml = proc(): LibStatusQml = + # let libStatus = newLibStatusQml(); + # return libStatus; + + # discard qmlRegisterSingletonType[LibStatusQml]("im.status.desktop.Status", 1, 0, "Status", initLibStatusQml) var profile = profile.newController() profile.init(accounts) # TODO: use correct account @@ -73,9 +122,14 @@ proc mainProc() = chat.join(channel.name) ) - appState.addChannel("test") - appState.addChannel("test2") + events.on("node:ready") do(a: Args): + appState.addChannel("test") + appState.addChannel("test2") + + + + engine.load("../ui/main.qml") # Please note that this must use the `cdecl` calling convention because diff --git a/src/onboarding.nim b/src/onboarding.nim new file mode 100644 index 000000000..ee6e537d4 --- /dev/null +++ b/src/onboarding.nim @@ -0,0 +1,147 @@ +import NimQml +import json +import status/accounts +import nimcrypto +import status/utils +import status/libstatus +import models/accounts as Models +import constants/constants +import uuids +import eventemitter +import status/test as status_test + +# Probably all QT classes will look like this: +QtObject: + type Onboarding* = ref object of QObject + m_generatedAddresses: string + events: EventEmitter + + # ¯\_(ツ)_/¯ dunno what is this + proc setup(self: Onboarding) = + self.QObject.setup + + # ¯\_(ツ)_/¯ seems to be a method for garbage collection + proc delete*(self: Onboarding) = + self.QObject.delete + + # Constructor + proc newOnboarding*(events: EventEmitter): Onboarding = + new(result, delete) + result.events = events + result.setup() + + # Read more about slots and signals here: https://doc.qt.io/qt-5/signalsandslots.html + + # Accesors + proc getGeneratedAddresses*(self: Onboarding): string {.slot.} = + result = self.m_generatedAddresses + + proc generatedAddressesChanged*(self: Onboarding, + generatedAddresses: string) {.signal.} + + proc setGeneratedAddresses*(self: Onboarding, generatedAddresses: string) {.slot.} = + if self.m_generatedAddresses == generatedAddresses: + return + self.m_generatedAddresses = generatedAddresses + self.generatedAddressesChanged(generatedAddresses) + + QtProperty[string]generatedAddresses: + read = getGeneratedAddresses + write = setGeneratedAddresses + notify = generatedAddressesChanged + + # QML functions + proc generateAddresses*(self: Onboarding) {.slot.} = + self.setGeneratedAddresses(generateAddresses()) + + proc generateAlias*(self: Onboarding, publicKey: string): string {.slot.} = + result = $libstatus.generateAlias(publicKey.toGoString) + + proc identicon*(self: Onboarding, publicKey: string): string {.slot.} = + result = $libstatus.identicon(publicKey.toGoString) + + proc storeAccountAndLogin(self: Onboarding, selectedAccount: string, password: string): string {.slot.} = + let account = to(json.parseJson(selectedAccount), Models.GeneratedAccount) + let password = "0x" & $keccak_256.digest(password) + let multiAccount = %* { + "accountID": account.id, + "paths": [constants.PATH_WALLET_ROOT, constants.PATH_EIP_1581, constants.PATH_WHISPER, + constants.PATH_DEFAULT_WALLET], + "password": password + } + let storeResult = $libstatus.multiAccountStoreDerivedAccounts($multiAccount); + let multiAccounts = storeResult.parseJson + let whisperPubKey = account.derived[constants.PATH_WHISPER]["publicKey"].getStr + let alias = $libstatus.generateAlias(whisperPubKey.toGoString) + let identicon = $libstatus.identicon(whisperPubKey.toGoString) + let accountData = %* { + "name": alias, + "address": account.address, + "photo-path": identicon, + "key-uid": account.keyUid, + "keycard-pairing": nil + } + var nodeConfig = constants.NODE_CONFIG + let defaultNetworks = constants.DEFAULT_NETWORKS + let settingsJSON = %* { + "key-uid": account.keyUid, + "mnemonic": account.mnemonic, + "public-key": multiAccounts[constants.PATH_WHISPER]["publicKey"].getStr, + "name": alias, + "address": account.address, + "eip1581-address": multiAccounts[constants.PATH_EIP_1581]["address"].getStr, + "dapps-address": multiAccounts[constants.PATH_DEFAULT_WALLET]["address"].getStr, + "wallet-root-address": multiAccounts[constants.PATH_WALLET_ROOT]["address"].getStr, + "preview-privacy?": true, + "signing-phrase": generateSigningPhrase(3), + "log-level": "INFO", + "latest-derived-path": 0, + "networks/networks": $defaultNetworks, + "currency": "usd", + "photo-path": identicon, + "waku-enabled": true, + "wallet/visible-tokens": { + "mainnet": ["SNT"] + }, + "appearance": 0, + "networks/current-network": "mainnet_rpc", + "installation-id": $genUUID() + } + + let subaccountData = %* [ + { + "public-key": multiAccounts[constants.PATH_DEFAULT_WALLET]["publicKey"], + "address": multiAccounts[constants.PATH_DEFAULT_WALLET]["address"], + "color": "#4360df", + "wallet": true, + "path": constants.PATH_DEFAULT_WALLET, + "name": "Status account" + }, + { + "public-key": multiAccounts[constants.PATH_WHISPER]["publicKey"], + "address": multiAccounts[constants.PATH_WHISPER]["address"], + "name": alias, + "photo-path": identicon, + "path": constants.PATH_WHISPER, + "chat": true + } + ] + + result = $libstatus.saveAccountAndLogin($accountData, password, $settingsJSON, + $nodeConfig, $subaccountData) + + let saveResult = result.parseJson + + if saveResult["error"].getStr == "": + self.events.emit("node:ready", Args()) + echo "Account saved succesfully" + + proc generateRandomAccountAndLogin*(self: Onboarding) {.slot.} = + status_test.setupNewAccount() + self.events.emit("node:ready", Args()) + + + + + # This class has the metaObject property available which lets + # access all the QProperties which are stored as QVariants diff --git a/src/status/accounts.nim b/src/status/accounts.nim new file mode 100644 index 000000000..2a30af38a --- /dev/null +++ b/src/status/accounts.nim @@ -0,0 +1,15 @@ +import libstatus +import json +import utils + +proc generateAddresses*(): string = + let multiAccountConfig = %* { + "n": 5, + "mnemonicPhraseLength": 12, + "bip39Passphrase": "", + "paths": ["m/43'/60'/1581'/0'/0", "m/44'/60'/0'/0/0"] + } + result = $libstatus.multiAccountGenerateAndDeriveAddresses($multiAccountConfig) + +proc generateAlias*(publicKey: string): string = + result = $libstatus.generateAlias(publicKey.toGoString) diff --git a/src/status/libstatus.nim b/src/status/libstatus.nim index 55874c3dc..b2b500dce 100644 --- a/src/status/libstatus.nim +++ b/src/status/libstatus.nim @@ -21,3 +21,7 @@ proc addPeer*(peer: cstring): cstring {.importc: "AddPeer".} proc setSignalEventCallback*(callback: SignalCallback) {.importc: "SetSignalEventCallback".} proc sendTransaction*(jsonArgs: cstring, password: cstring): cstring {.importc: "SendTransaction".} + +proc generateAlias*(p0: GoString): cstring {.importc: "GenerateAlias".} + +proc identicon*(p0: GoString): cstring {.importc: "Identicon".} diff --git a/src/status/libstatusqml.nim b/src/status/libstatusqml.nim new file mode 100644 index 000000000..86825d7c4 --- /dev/null +++ b/src/status/libstatusqml.nim @@ -0,0 +1,51 @@ +import NimQml +import libstatus +import utils + +QtObject: + type + LibStatusQml* = ref object of QObject + + + # ¯\_(ツ)_/¯ dunno what is this + proc setup(self: LibStatusQml) = + self.QObject.setup + + # ¯\_(ツ)_/¯ seems to be a method for garbage collection + proc delete*(self: LibStatusQml) = + self.QObject.delete + + # Constructor + proc newLibStatusQml*(): LibStatusQml = + new(result, delete) + result.setup + + proc hashMessage*(self: LibStatusQml, p0: string): string {.slot.} = + return $libstatus.hashMessage(p0) + + proc initKeystore*(self: LibStatusQml, keydir: string): string {.slot.} = + return $libstatus.initKeystore(keydir) + + proc openAccounts*(self: LibStatusQml, datadir: string): string {.slot.} = + return $libstatus.openAccounts(datadir) + + proc multiAccountGenerateAndDeriveAddresses*(self: LibStatusQml, paramsJSON: string): string {.slot.} = + return $libstatus.multiAccountGenerateAndDeriveAddresses(paramsJSON) + + proc multiAccountStoreDerivedAccounts*(self: LibStatusQml, paramsJSON: string): string {.slot.} = + return $libstatus.multiAccountStoreDerivedAccounts(paramsJSON) + + proc saveAccountAndLogin*(self: LibStatusQml, accountData: string, password: string, settingsJSON: string, configJSON: string, subaccountData: string): string {.slot.} = + return $libstatus.saveAccountAndLogin(accountData, password, settingsJSON, configJSON, subaccountData) + + proc callRPC*(self: LibStatusQml, inputJSON: string): string {.slot.} = + return $libstatus.callRPC(inputJSON) + + proc callPrivateRPC*(self: LibStatusQml, inputJSON: string): string {.slot.} = + return $libstatus.callPrivateRPC(inputJSON) + + proc addPeer*(self: LibStatusQml, peer: string): string {.slot.} = + return $libstatus.addPeer(peer) + + proc generateAlias*(self: LibStatusQml, p0: string): string {.slot.} = + return $libstatus.generateAlias(p0.toGoString) \ No newline at end of file diff --git a/src/status/test.nim b/src/status/test.nim index 7a8acda94..fe9a216f0 100644 --- a/src/status/test.nim +++ b/src/status/test.nim @@ -31,17 +31,17 @@ proc queryAccounts*(): string = proc setupNewAccount*(): string = # Deleting directories - recreateDir(datadir) - recreateDir(keystoredir) - recreateDir(nobackupdir) + # recreateDir(datadir) + # recreateDir(keystoredir) + # recreateDir(nobackupdir) var result: string - # 1 - result = $libstatus.initKeystore(keystoredir); + # # 1 + # result = $libstatus.initKeystore(keystoredir); - # 2 - result = $libstatus.openAccounts(datadir); + # # 2 + # result = $libstatus.openAccounts(datadir); # 3 let multiAccountConfig = %* { diff --git a/src/status/types.nim b/src/status/types.nim index 2c52fd35d..d75c3a506 100644 --- a/src/status/types.nim +++ b/src/status/types.nim @@ -14,3 +14,8 @@ type SignalType* {.pure.} = enum SubscriptionsError = "subscriptions.error" WhisperFilterAdded = "whisper.filter.added" Unknown + +type + GoString* = object + str*: cstring + length*: cint diff --git a/src/status/utils.nim b/src/status/utils.nim index b6a4782d4..5b79ca19e 100644 --- a/src/status/utils.nim +++ b/src/status/utils.nim @@ -1,4 +1,9 @@ +import json +import types +import random +from times import getTime, toUnix, nanosecond import strutils +import ../constants/signing_phrases proc isWakuEnabled(): bool = true # TODO: @@ -8,4 +13,22 @@ proc prefix*(methodName: string): string = result = result & methodName proc isOneToOneChat*(chatId: string): bool = - result = chatId.startsWith("0x") # There is probably a better way to do this \ No newline at end of file + result = chatId.startsWith("0x") # There is probably a better way to do this + +proc keys*(obj: JsonNode): seq[string] = + result = newSeq[string]() + for k, _ in obj: + result.add k + +proc toGoString*(str: string): GoString = + result = GoString(str: str, length: cint(str.len)) + +proc generateSigningPhrase*(count: int): string = + let now = getTime() + var rng = initRand(now.toUnix * 1000000000 + now.nanosecond) + var phrases: seq[string] = @[] + + for i in 1..count: + phrases.add(rng.sample(signing_phrases.phrases)) + + result = phrases.join(" ") \ No newline at end of file diff --git a/ui/main.qml b/ui/main.qml index 13769244c..11d0fb1e5 100644 --- a/ui/main.qml +++ b/ui/main.qml @@ -30,27 +30,18 @@ ApplicationWindow { } } - Rectangle { - id: rctAppBg - color: "#FFFFFF" - Layout.fillHeight: true - Layout.fillWidth: true + OnboardingMain { + id: onboarding + visible: !app.visible anchors.fill: parent - border.width: 0 + } - Intro { - id: onboarding - visible: !app.visible - anchors.fill: parent - } - - AppMain { - id: app - // TODO: Set this to a logic result determining when we need to show the onboarding screens - // Set to true to hide the onboarding screens manually - // Set to false to show the onboarding screens manually - visible: false // logic.accountResult !== "" - anchors.fill: parent - } + AppMain { + id: app + // TODO: Set this to a logic result determining when we need to show the onboarding screens + // Set to true to hide the onboarding screens manually + // Set to false to show the onboarding screens manually + visible: false // logic.accountResult !== "" + anchors.fill: parent } } diff --git a/ui/nim-status-client.pro b/ui/nim-status-client.pro index d1a7a0b75..f39e0c352 100644 --- a/ui/nim-status-client.pro +++ b/ui/nim-status-client.pro @@ -70,7 +70,11 @@ DISTFILES += \ app/img/walletActive.svg \ app/qmldir \ imports/qmldir \ + onboarding/ExistingKey.qml \ + onboarding/GenKey.qml \ onboarding/Intro.qml \ + onboarding/KeysMain.qml \ + onboarding/OnboardingMain.qml \ onboarding/img/browser-dark@2x.jpg \ onboarding/img/browser-dark@3x.jpg \ onboarding/img/browser@2x.jpg \ @@ -79,11 +83,14 @@ DISTFILES += \ onboarding/img/chat-dark@3x.jpg \ onboarding/img/chat@2x.jpg \ onboarding/img/chat@3x.jpg \ + onboarding/img/key.png \ + onboarding/img/key@2x.png \ onboarding/img/next.svg \ onboarding/img/wallet-dark@2x.jpg \ onboarding/img/wallet-dark@3x.jpg \ onboarding/img/wallet@2x.jpg \ onboarding/img/wallet@3x.jpg \ onboarding/qmldir \ + shared/StyledButton.qml \ shared/RoundedIcon.qml \ shared/qmldir diff --git a/ui/onboarding/ExistingKey.qml b/ui/onboarding/ExistingKey.qml new file mode 100644 index 000000000..8923ee003 --- /dev/null +++ b/ui/onboarding/ExistingKey.qml @@ -0,0 +1,18 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtQuick.Window 2.11 +import QtQuick.Dialogs 1.3 + + + +Text { + text: "Existing key flow [COMING SOON]" + font.pointSize: 36 + anchors.centerIn: parent +} +/*##^## +Designer { + D{i:0;autoSize:true;height:480;width:640} +} +##^##*/ diff --git a/ui/onboarding/GenKey.qml b/ui/onboarding/GenKey.qml new file mode 100644 index 000000000..e9be40cbf --- /dev/null +++ b/ui/onboarding/GenKey.qml @@ -0,0 +1,241 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtQuick.Window 2.11 +import QtQuick.Dialogs 1.3 + +SwipeView { + id: swipeView + anchors.fill: parent + currentIndex: 0 + + property string strGeneratedAccounts: onboardingLogic.generatedAddresses + property var generatedAccounts: {} + signal storeAccountAndLoginResult(response: var) + + onCurrentItemChanged: { + currentItem.txtPassword.focus = true; + } + + ListModel { + id: generatedAccountsModel + } + + Item { + id: wizardStep2 + property int selectedIndex: 0 + + Text { + text: "Generated accounts" + font.pointSize: 36 + anchors.top: parent.top + anchors.topMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + } + + Item { + anchors.top: parent.top + anchors.topMargin: 50 + + Column { + spacing: 10 + ButtonGroup { + id: accountGroup + } + + Repeater { + model: generatedAccountsModel + Rectangle { + height: 32 + width: 32 + anchors.leftMargin: 20 + anchors.rightMargin: 20 + Row { + RadioButton { + checked: index == 0 ? true : false + ButtonGroup.group: accountGroup + onClicked: { + wizardStep2.selectedIndex = index; + } + } + Column { + Image { + source: identicon + } + } + Column { + Text { + text: alias + } + Text { + text: publicKey + width: 160 + elide: Text.ElideMiddle + } + + } + } + } + } + } + + + } + + Button { + text: "Select" + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + onClicked: { + console.log("button: " + wizardStep2.selectedIndex); + + swipeView.incrementCurrentIndex(); + } + } + } + + Item { + id: wizardStep3 + property Item txtPassword: txtPassword + + Text { + text: "Enter password" + font.pointSize: 36 + anchors.top: parent.top + anchors.topMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + + } + + Rectangle { + color: "#EEEEEE" + anchors.left: parent.left + anchors.right: parent.right + anchors.centerIn: parent + height: 32 + width: parent.width - 40 + TextInput { + id: txtPassword + anchors.fill: parent + focus: true + echoMode: TextInput.Password + selectByMouse: true + } + } + + Button { + text: "Next" + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + onClicked: { + console.log("password: " + txtPassword.text); + + swipeView.incrementCurrentIndex(); + } + } + } + + Item { + id: wizardStep4 + property Item txtPassword: txtConfirmPassword + + Text { + text: "Confirm password" + font.pointSize: 36 + anchors.top: parent.top + anchors.topMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + } + + Rectangle { + color: "#EEEEEE" + anchors.left: parent.left + anchors.right: parent.right + anchors.centerIn: parent + height: 32 + width: parent.width - 40 + + TextInput { + id: txtConfirmPassword + anchors.fill: parent + focus: true + echoMode: TextInput.Password + selectByMouse: true + } + } + + MessageDialog { + id: passwordsDontMatchError + title: "Error" + text: "Passwords don't match" + icon: StandardIcon.Warning + standardButtons: StandardButton.Ok + onAccepted: { + txtConfirmPassword.clear(); + swipeView.currentIndex = 1 + txtPassword.focus = true + } + } + + MessageDialog { + id: storeAccountAndLoginError + title: "Error storing account and logging in" + text: "An error occurred while storing your account and logging in: " + icon: StandardIcon.Error + standardButtons: StandardButton.Ok + } + + Button { + text: "Finish" + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + onClicked: { + console.log("confirm clicked " + txtConfirmPassword.text + " : " + txtPassword.text); + + if (txtConfirmPassword.text != txtPassword.text) { + passwordsDontMatchError.open(); + } + else { + const selectedAccount = swipeView.generatedAccounts[wizardStep2.selectedIndex]; + const storeResponse = onboardingLogic.storeAccountAndLogin(JSON.stringify(selectedAccount), txtPassword.text) + const response = JSON.parse(storeResponse); + if (response.error) { + storeAccountAndLoginError.text += response.error; + return storeAccountAndLoginError.open(); + } + swipeView.storeAccountAndLoginResult(response); + } + } + } + } + + // handle the serialised result coming from node and deserialise into JSON + // TODO: maybe we should figure out a clever to avoid this? + onStrGeneratedAccountsChanged: { + if (generatedAccounts === null || generatedAccounts === "") { + return; + } + swipeView.generatedAccounts = JSON.parse(strGeneratedAccounts); + } + + // handle deserialised data coming from the node + onGeneratedAccountsChanged: { + generatedAccountsModel.clear(); + generatedAccounts.forEach(acc => { + generatedAccountsModel.append({ + publicKey: acc.publicKey, + alias: onboardingLogic.generateAlias(acc.publicKey), + identicon: onboardingLogic.identicon(acc.publicKey) + }); + }); + } +} +/*##^## +Designer { + D{i:0;autoSize:true;height:480;width:640} +} +##^##*/ + diff --git a/ui/onboarding/Intro.qml b/ui/onboarding/Intro.qml index b103f15e8..476c96dd0 100644 --- a/ui/onboarding/Intro.qml +++ b/ui/onboarding/Intro.qml @@ -2,8 +2,11 @@ import QtQuick 2.3 import QtQuick.Controls 1.3 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 +import "../shared" RowLayout { + property alias btnGetStarted: btnGetStarted + id: obLayout anchors.fill: parent Layout.fillWidth: true @@ -26,6 +29,21 @@ RowLayout { Item { id: itmSlide1 + StyledButton { + id: btnGenRandomAcct + width: 250 + height: 50 + anchors.top: parent.top + anchors.topMargin: 50 + anchors.left: parent.left + anchors.leftMargin: 15 + label: "Generate random acct and login" + onClicked: { + onboardingLogic.generateRandomAccountAndLogin() + app.visible = true + } + } + Image { id: img1 anchors.horizontalCenter: parent.horizontalCenter @@ -301,32 +319,15 @@ RowLayout { } } - Button { + StyledButton { id: btnGetStarted - rightPadding: 32 - leftPadding: 32 - bottomPadding: 11 - topPadding: 11 - width: 146 - height: 44 + label: "Get started" anchors.top: rctPageIndicator.bottom anchors.topMargin: 87 anchors.horizontalCenter: parent.horizontalCenter - onClicked: app.visible = true - background: Rectangle { - color: "#ECEFFC" - radius: 8 - } - - Text { - id: txtGetStarted - color: "#4360DF" - text: qsTr("Get started") - font.weight: Font.DemiBold - font.pointSize: 15 - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - } +// onClicked: app.visible = true + width: 146 + height: 44 } Text { diff --git a/ui/onboarding/KeysMain.qml b/ui/onboarding/KeysMain.qml new file mode 100644 index 000000000..0db14d340 --- /dev/null +++ b/ui/onboarding/KeysMain.qml @@ -0,0 +1,91 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.3 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import "../shared" + +Page { + property alias btnExistingKey: btnExistingKey + property alias btnGenKey: btnGenKey + + Image { + id: img1 + anchors.horizontalCenter: parent.horizontalCenter + sourceSize.width: 160 + sourceSize.height: 160 + anchors.topMargin: 24 + anchors.top: parent.top + fillMode: Image.PreserveAspectFit + source: "img/key@2x.png" + } + + Text { + id: txtTitle1 + text: qsTr("Get your keys") + anchors.right: parent.right + anchors.rightMargin: 177 + anchors.left: parent.left + anchors.leftMargin: 177 + anchors.top: img1.bottom + anchors.topMargin: 16 + font.letterSpacing: -0.2 + font.weight: Font.Bold + lineHeight: 1 + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + transformOrigin: Item.Center + font.bold: true + font.pixelSize: 22 + font.kerning: true + + } + + Text { + id: txtDesc1 + x: 772 + color: "#939BA1" + text: qsTr("A set of keys controls your account. Your keys live on\nyour device, so only you can use them.") + horizontalAlignment: Text.AlignHCenter + font.weight: Font.Normal + style: Text.Normal + anchors.horizontalCenterOffset: 0 + anchors.top: txtTitle1.bottom + anchors.topMargin: 14 + font.bold: true + font.family: "Inter" + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 15 + } + + StyledButton { + id: btnExistingKey + label: "Access existing key" + anchors.top: txtDesc1.bottom + anchors.topMargin: 87 + anchors.horizontalCenter: parent.horizontalCenter + // onClicked: logic.generateAddresses() + width: 142 + height: 44 + } + + StyledButton { + id: btnGenKey + width: 194 + height: 44 + anchors.top: btnExistingKey.bottom + anchors.topMargin: 19 + anchors.horizontalCenter: parent.horizontalCenter + label: "I'm new, generate me a key" + background: Rectangle {color: "transparent"} + onClicked: onboardingLogic.generateAddresses() + } + +} + + + +/*##^## +Designer { + D{i:0;autoSize:true;height:480;width:720} +} +##^##*/ diff --git a/ui/onboarding/OnboardingMain.qml b/ui/onboarding/OnboardingMain.qml new file mode 100644 index 000000000..5a2b92735 --- /dev/null +++ b/ui/onboarding/OnboardingMain.qml @@ -0,0 +1,101 @@ +import QtQuick 2.3 +import QtQml.StateMachine 1.14 as DSM +import QtQuick.Controls 2.3 + +Page { + id: onboardingMain + property string state + anchors.fill: parent + + DSM.StateMachine { + id: stateMachine + initialState: stateIntro + running: onboardingMain.visible + + DSM.State { + id: stateIntro + onEntered: intro.visible = true + onExited: intro.visible = false + + DSM.SignalTransition { + targetState: keysMainState + signal: intro.btnGetStarted.clicked + } + } + + DSM.State { + id: keysMainState + onEntered: keysMain.visible = true + onExited: keysMain.visible = false + + DSM.SignalTransition { + targetState: existingKeyState + signal: keysMain.btnExistingKey.clicked + } + + DSM.SignalTransition { + targetState: genKeyState + signal: keysMain.btnGenKey.clicked + } + } + + DSM.State { + id: existingKeyState + onEntered: existingKey.visible = true + onExited: existingKey.visible = false + +// DSM.SignalTransition { +// targetState: keysMainState +// signal: keysMain.btnExistingKey.clicked +// } + } + + DSM.State { + id: genKeyState + onEntered: genKey.visible = true + onExited: genKey.visible = false + + DSM.SignalTransition { + targetState: appState + signal: genKey.storeAccountAndLoginResult + guard: !response.error + } + } + + DSM.FinalState { + id: appState + onEntered: app.visible = true + onExited: app.visible = false + } + } + + Intro { + id: intro + anchors.fill: parent + visible: true + } + + KeysMain { + id: keysMain + anchors.fill: parent + visible: false + } + + ExistingKey { + id: existingKey + anchors.fill: parent + visible: false + } + + GenKey { + id: genKey + anchors.fill: parent + visible: false + } +} + +/*##^## +Designer { + D{i:0;autoSize:true;height:770;width:1232} +} +##^##*/ diff --git a/ui/onboarding/img/key.png b/ui/onboarding/img/key.png new file mode 100644 index 000000000..7d09df17a Binary files /dev/null and b/ui/onboarding/img/key.png differ diff --git a/ui/onboarding/img/key@2x.png b/ui/onboarding/img/key@2x.png new file mode 100644 index 000000000..1bb1d41be Binary files /dev/null and b/ui/onboarding/img/key@2x.png differ diff --git a/ui/onboarding/qmldir b/ui/onboarding/qmldir index 27c23c7c7..d3cdc39b0 100644 --- a/ui/onboarding/qmldir +++ b/ui/onboarding/qmldir @@ -1 +1,5 @@ -Intro 1.0 Intro.qml \ No newline at end of file +ExistingKey 1.0 ExistingKey.qml +GenKey 1.0 GenKey.qml +Intro 1.0 Intro.qml +KeysMain 1.0 KeysMain.qml +OnboardingMain 1.0 OnboardingMain.qml \ No newline at end of file diff --git a/ui/shared/StyledButton.qml b/ui/shared/StyledButton.qml new file mode 100644 index 000000000..4299c5f23 --- /dev/null +++ b/ui/shared/StyledButton.qml @@ -0,0 +1,34 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.3 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import QtQml 2.14 + + + +Button { + property alias label: txtBtnLabel.text + font.weight: Font.Medium + + id: btnStyled + rightPadding: 32 + leftPadding: 32 + bottomPadding: 11 + topPadding: 11 + + background: Rectangle { + color: "#ECEFFC" + radius: 8 + } + + Text { + id: txtBtnLabel + color: "#4360DF" + font.family: "Inter" + font.pointSize: 15 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + text: "Get started" + } +} + diff --git a/ui/shared/qmldir b/ui/shared/qmldir index fb19b06a5..94e1b6e5c 100644 --- a/ui/shared/qmldir +++ b/ui/shared/qmldir @@ -1 +1,2 @@ +StyledButton 1.0 StyledButton.qml RoundedIcon 1.0 RoundedIcon.qml diff --git a/vendor/eventemitter b/vendor/eventemitter new file mode 160000 index 000000000..49cfa2f31 --- /dev/null +++ b/vendor/eventemitter @@ -0,0 +1 @@ +Subproject commit 49cfa2f3135139c3488b68fdd061cc069d31d651 diff --git a/vendor/isaac b/vendor/isaac new file mode 160000 index 000000000..45a5cbbd5 --- /dev/null +++ b/vendor/isaac @@ -0,0 +1 @@ +Subproject commit 45a5cbbd54ff59ba3ed94242620c818b9aad1b5b diff --git a/vendor/nimcrypto b/vendor/nimcrypto new file mode 160000 index 000000000..30d0ceaba --- /dev/null +++ b/vendor/nimcrypto @@ -0,0 +1 @@ +Subproject commit 30d0ceaba02c0b966515f98873a0404786fbf796 diff --git a/vendor/uuids b/vendor/uuids new file mode 160000 index 000000000..c5039c1cc --- /dev/null +++ b/vendor/uuids @@ -0,0 +1 @@ +Subproject commit c5039c1cc6a8a93fc2f3c03a206372eb4412e63b