Compare commits
689 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d12dab5c5 | |||
| 343c9754a0 | |||
| 4ef113e8ea | |||
| 1309250973 | |||
| 4b784c0d55 | |||
| 0e4325a403 | |||
| f751ce532b | |||
| d03c88fbc7 | |||
| b35f195b08 | |||
| ce15b63aa1 | |||
| 7816be7bec | |||
| ea41b2c810 | |||
| 021dad5355 | |||
| b4965b6255 | |||
| 2ea2b9c333 | |||
| c26c1ddb09 | |||
| 1ecee683e4 | |||
| 6adf8c1ff8 | |||
| e31e910446 | |||
| c1628b4b17 | |||
| c7a0e33de5 | |||
| f16fb43d6c | |||
| 7af6238929 | |||
| 8a8bafc8cc | |||
| 96f09439f3 | |||
| a04d133743 | |||
| 5710494ad3 | |||
| 2bf3cecd06 | |||
| 63539bcde5 | |||
| 83223caf47 | |||
| f646c1855e | |||
| 189f6f1fa5 | |||
| be831b3cde | |||
| e4cb16d1d1 | |||
| b54319ebdf | |||
| 8f584010ba | |||
| 7d6d8ed341 | |||
| 3ee2b1d0be | |||
| f434a1af05 | |||
| 83cdf4b383 | |||
| 2cebf079a5 | |||
| 2ffb26f1ea | |||
| 2c04999c26 | |||
| 7e98ee4c9a | |||
| 391f12b69b | |||
| 4777d293f7 | |||
| e6bb5ef4b5 | |||
| 8eb0898dc5 | |||
| 2f46d52c1f | |||
| 9a471eedd5 | |||
| 54d3dc472c | |||
| 874a528fcc | |||
| 68268312ae | |||
| 150db9bcc3 | |||
| f297ad6946 | |||
| 30b01ade2c | |||
| 1044c8bb8b | |||
| d6568c33e0 | |||
| 42f88d13bc | |||
| 77db31b913 | |||
| 75f34c5bb8 | |||
| e574c8404a | |||
| be4bb79502 | |||
| 9f29bd78c9 | |||
| 87ac7c42cd | |||
| c6d1aa2c9c | |||
| 7417ea3714 | |||
| 4e42d05f56 | |||
| d5e3fa2d4a | |||
| 44cc4c58dd | |||
| 33be8a1d59 | |||
| 6488aade12 | |||
| c9441963a5 | |||
| d7b8394b14 | |||
| 98e3b96b23 | |||
| 401739a4b0 | |||
| c1126bf5d5 | |||
| 645aad0833 | |||
| 08178f01b1 | |||
| ee7fca7381 | |||
| 5a59784fe7 | |||
| 34cc7cb5f8 | |||
| 9e9a446c03 | |||
| d1dacfa8b0 | |||
| 9bbf9ae7f4 | |||
| 1561ff64c4 | |||
| d53caa390b | |||
| a6e9ebec6e | |||
| aca5cfd202 | |||
| 6885bccdff | |||
| 23bbfebfda | |||
| a2c8f727b9 | |||
| dd00191782 | |||
| 9b78718276 | |||
| 9450694742 | |||
| 7712cd2ea0 | |||
| 26031d4ac3 | |||
| f460e39e71 | |||
| 52cefa2f92 | |||
| f0c71b1790 | |||
| 274fa773df | |||
| 8322aa9f4d | |||
| d97c82a0b9 | |||
| 8b67d26a2d | |||
| 8ea64e9564 | |||
| 0ef938ff49 | |||
| f38225243b | |||
| 4d08884e0e | |||
| 742dff16eb | |||
| 30d895fa2e | |||
| 629ac2d426 | |||
| 6dd10c1b91 | |||
| 3c6046c7b7 | |||
| b86959b9af | |||
| d84f3a10f1 | |||
| 63fb4d2805 | |||
| 1f96e33827 | |||
| f55c25feb7 | |||
| 60c25cef98 | |||
| b0109448d8 | |||
| df4c31d602 | |||
| 560c6183fb | |||
| 4a468d3513 | |||
| e6d22bdf90 | |||
| cfd563c782 | |||
| 8ef6b108a3 | |||
| f42e08008f | |||
| 879e5ca11b | |||
| 392c1083c8 | |||
| b3dd3e29dc | |||
| 23b7e2d8e0 | |||
| a8defc4c63 | |||
| 871a6621f6 | |||
| b5efaa1de5 | |||
| cdcce39776 | |||
| 83efaa189b | |||
| 72f1ca3954 | |||
| c1681b40ff | |||
| 6a098cf4a8 | |||
| 4ccb10c09c | |||
| eb71c77533 | |||
| c4fb2cdee5 | |||
| bd8c7ab106 | |||
| 6b457d8d04 | |||
| 9282df591a | |||
| 50b272c636 | |||
| 0627a7d1a7 | |||
| 3a6c0624ab | |||
| e7452f8065 | |||
| 280339ae37 | |||
| 759f022351 | |||
| 59b096196c | |||
| f39a27ed66 | |||
| d0bca3c576 | |||
| 03b44c0e45 | |||
| 10c9b26475 | |||
| 3e6a6960d4 | |||
| 5635bde707 | |||
| 1a8c6ac71e | |||
| 3bb220f8be | |||
| 8187a07aee | |||
| d054a69bcf | |||
| 4b1fe49132 | |||
| b2c139dbc0 | |||
| 02488f06d8 | |||
| b0d367a3b8 | |||
| 19c9ffa352 | |||
| cfc7d17db3 | |||
| 71b24952c7 | |||
| d7aed0b199 | |||
| 2823524388 | |||
| fd8083350c | |||
| 1411f33393 | |||
| 8840293c92 | |||
| 83fa99a4fe | |||
| 64473f5c98 | |||
| a45424fdb1 | |||
| 14d3389a63 | |||
| 311c7b52c8 | |||
| a8fd747187 | |||
| 259610ab37 | |||
| 44b4ec22d7 | |||
| f3dd6a89a1 | |||
| a60473b93f | |||
| c8c5ab05cd | |||
| 38219721c9 | |||
| 9d6e23b6e7 | |||
| ce69d13ec4 | |||
| 4875c2edaf | |||
| e39759a9a4 | |||
| 547b663bb6 | |||
| 6ab5776f95 | |||
| 9b40d4c8cc | |||
| a0a81da164 | |||
| 0220adaefb | |||
| 481dee52b1 | |||
| 78d74a3d7c | |||
| 83ca9059fa | |||
| 8da491cc3b | |||
| 8d1b3adb6a | |||
| 626f85651f | |||
| 2acc7ed107 | |||
| 14a2be33af | |||
| a642a3fe8f | |||
| b90f7eaefb | |||
| 455515cae1 | |||
| e3fc3b997a | |||
| 3c529e743f | |||
| 9d7f968cdd | |||
| b5769f9aed | |||
| ac98b322ca | |||
| c499f5f96a | |||
| d719b502d3 | |||
| 02e24c23a6 | |||
| c57ae4cea4 | |||
| 2536d6bf10 | |||
| 060ce2c725 | |||
| a8d2935cf2 | |||
| d6fb1528a0 | |||
| 2e3d170d1e | |||
| 4690e28cde | |||
| 66a754ad22 | |||
| 83a74f0f6e | |||
| 811b8cfc4c | |||
| a81d9cfca6 | |||
| c7ef5cd741 | |||
| 41f6afc528 | |||
| 37cb821d6b | |||
| 203bfc4c7c | |||
| 779556f36d | |||
| cf575cca68 | |||
| 97e6bdbe40 | |||
| 5f0101a360 | |||
| a3bb305229 | |||
| 808b33e7a7 | |||
| ac917ba829 | |||
| 6c3f2d3505 | |||
| 742a43658f | |||
| 0307e2e932 | |||
| 7c1add25a1 | |||
| feeab9205b | |||
| 1023653d97 | |||
| 077f2fe093 | |||
| d636ec6339 | |||
| 98c9a6ad32 | |||
| 33a4d2cbe5 | |||
| 8a5bc60819 | |||
| f492fd4d41 | |||
| ed7204ab71 | |||
| 9aeaa38f3b | |||
| b3f9ed7981 | |||
| b8ab064fcc | |||
| 58bf9cb8cd | |||
| 8a67e8d102 | |||
| 5d6fc56349 | |||
| 77ed2129d3 | |||
| fcebe8e0b0 | |||
| 6df617003a | |||
| 6953bf9d22 | |||
| 9396595595 | |||
| cf13b5cd4c | |||
| e24ab48b73 | |||
| bbee08eb22 | |||
| c68f291128 | |||
| 32b3575074 | |||
| 11f972c640 | |||
| 61cf754fd5 | |||
| 00b50c71b5 | |||
| 66eb6f2888 | |||
| bc571ee998 | |||
| c758996034 | |||
| 1f26494c94 | |||
| 74bfc10e36 | |||
| e194e5de91 | |||
| 09f96da982 | |||
| 6e48d63dda | |||
| eec636897c | |||
| 777f30562b | |||
| 16e4bc66bd | |||
| 98c5312a29 | |||
| 9971f1f035 | |||
| dec9d97171 | |||
| 355749e605 | |||
| 6268736bfa | |||
| a270d750c8 | |||
| 0eb4c7495e | |||
| b438318b92 | |||
| 01aaa6123f | |||
| 486a1f082c | |||
| f943cbd04a | |||
| c54fa5b2e4 | |||
| 54c0115181 | |||
| bb3a2fddfc | |||
| 5135a13812 | |||
| 059c3b362d | |||
| 090ceb4c53 | |||
| 7796203c25 | |||
| 44ae514d89 | |||
| 12d8e06a1e | |||
| de65d9198a | |||
| 41bf8e15cd | |||
| adeb1d5cec | |||
| 61511d5161 | |||
| aee205b0ab | |||
| e59763b50a | |||
| 5168d44926 | |||
| def3156c33 | |||
| a290891f70 | |||
| a96562e89c | |||
| 279d41fcd1 | |||
| 4932505c5e | |||
| 629b7d8c9b | |||
| 5c71f8e32a | |||
| 395a8838be | |||
| 17903b817d | |||
| 54f05bfcf6 | |||
| b1a058d098 | |||
| 27657541f2 | |||
| 73ea67f5c0 | |||
| d065b62f52 | |||
| eb03170736 | |||
| 6f734f7978 | |||
| 9235d6c13b | |||
| bc54c8d7c6 | |||
| 3b8dbbff12 | |||
| e8bb545023 | |||
| f0b30e26d4 | |||
| cbfa9f62a4 | |||
| fefd8cc8a0 | |||
| 09765a5d36 | |||
| e325b72c62 | |||
| 055a4d8e98 | |||
| 1e28ae6d2a | |||
| 16343a2936 | |||
| 18cc2183cc | |||
| 5e711c799b | |||
| ca4b9d3a60 | |||
| ebd03ffe2a | |||
| ee4f0673f1 | |||
| 33145f2e71 | |||
| 721fd84a7e | |||
| a06fd7dfd4 | |||
| bd6bb4df06 | |||
| bb4dc3c3a5 | |||
| 7b7fe7ccd8 | |||
| 316679ae68 | |||
| b34cae6db4 | |||
| 52e31394ab | |||
| 84f18e03fa | |||
| 30761594ec | |||
| 87258a2439 | |||
| 6e31e928a9 | |||
| d0587c81f8 | |||
| 29018f06b9 | |||
| 171cb6b3d3 | |||
| 3e37c27310 | |||
| 06c3ebe2d4 | |||
| d336cd58b1 | |||
| 684f67a8e0 | |||
| a7b23613d3 | |||
| a6db1a9ee3 | |||
| 0f065b2c27 | |||
| 7399e37bba | |||
| 7ce4c821e5 | |||
| 68dd96128a | |||
| 6e55149281 | |||
| 3aca890da6 | |||
| 4798d44110 | |||
| 37b2b40aa1 | |||
| f9e287b12c | |||
| 96dc9e398b | |||
| 2f0157dd33 | |||
| f608712308 | |||
| 3807f216fe | |||
| 6b1a5374ae | |||
| 71b7158ff9 | |||
| 26f06d768c | |||
| 383a40bc03 | |||
| 8413fccdb5 | |||
| 3161703d8a | |||
| 4c097d89c5 | |||
| 0b50ae3e61 | |||
| 063583bb6f | |||
| f2da9f2d0a | |||
| fc7fa8a354 | |||
| 709a970cdc | |||
| 5d7814897c | |||
| 1e2ee001a8 | |||
| 11423fc25c | |||
| 160fc31b47 | |||
| 3389e0dc4a | |||
| ae8162974e | |||
| b33269a535 | |||
| d8280b6ad9 | |||
| 935cc8f675 | |||
| 3bd6e9c84c | |||
| f1b9114af0 | |||
| 95e5b169c8 | |||
| 09791a3b3c | |||
| c7a5642e48 | |||
| 6c23972620 | |||
| 211af98aaf | |||
| bc404572cb | |||
| b824ac710b | |||
| 372cd80175 | |||
| d0c9f95c7e | |||
| 91a06f2390 | |||
| ffd333164b | |||
| a6e8bb39b8 | |||
| 8d87fbd56a | |||
| a129ddba27 | |||
| 211b9fc2f2 | |||
| 8b6bb6999d | |||
| 524cb062a2 | |||
| be71989e45 | |||
| edb5a8ddb2 | |||
| fb461c0344 | |||
| a8608d0170 | |||
| 628f69605c | |||
| 537770b39d | |||
| 44eca8a550 | |||
| 3a79e46a4a | |||
| 6f56a8c539 | |||
| 2ac28aa2de | |||
| 53c55b2d7a | |||
| 31e0fa898a | |||
| 1ec46a00e2 | |||
| d98fbbbe4d | |||
| ef02ec9ab1 | |||
| 72fe122005 | |||
| 4b960bc320 | |||
| 090ea1a3f4 | |||
| 983f2349e0 | |||
| 8af98b4a52 | |||
| d821803050 | |||
| 35ead0155e | |||
| 5b908f5358 | |||
| 099fea6624 | |||
| a2ef0b5a68 | |||
| 38c17e06bb | |||
| c7c0c04b66 | |||
| bc7364acca | |||
| 1e9d265403 | |||
| 02689c5533 | |||
| d0f6da2a51 | |||
| 2e9a858e3d | |||
| 8e46204617 | |||
| b844e479a1 | |||
| f7ccf14211 | |||
| 814ddb194a | |||
| d7103412e5 | |||
| ad4c413ae4 | |||
| f0ed5d7c28 | |||
| e8304bb77d | |||
| 5ae10f17c5 | |||
| 4b8de11046 | |||
| 27f94052e9 | |||
| 8004d038ab | |||
| ff9d7346ec | |||
| c0298dc7b7 | |||
| ab0f4f8288 | |||
| 148fa29875 | |||
| f13c42c078 | |||
| a079605049 | |||
| 56d98abe64 | |||
| dab5a36a58 | |||
| 65e80556e8 | |||
| b60406f960 | |||
| 4d2c2c5f2a | |||
| deb7c1bba7 | |||
| 8b1134dc75 | |||
| a83da7367c | |||
| ec3ba2ebc8 | |||
| ab4f828865 | |||
| c72e1b6635 | |||
| 4e245f8471 | |||
| 652362b0c2 | |||
| abd6ffdbf5 | |||
| 0dc1c904ed | |||
| a8bdb4a288 | |||
| 9ec2712728 | |||
| 64609575e7 | |||
| 09411967e8 | |||
| fbec25412b | |||
| ffe51591e8 | |||
| f11ef7ba0b | |||
| 0439674aa9 | |||
| e609f8dadb | |||
| 6af2e1d162 | |||
| 35e46a5b60 | |||
| 14ca7602e9 | |||
| 7c719f3af8 | |||
| 547965b528 | |||
| 85a425bf3e | |||
| d9d9344fcc | |||
| 67fb6b23da | |||
| 2a4391af97 | |||
| fec3aa2e2f | |||
| 4fcc0accc7 | |||
| 3da90128f0 | |||
| 038556cc58 | |||
| f42ff8e4cf | |||
| d073049ed2 | |||
| b165d076f1 | |||
| c16e1b8fb1 | |||
| a527c1086a | |||
| 6be082e728 | |||
| f99cdba6dd | |||
| 397ace5c11 | |||
| 924c76f11b | |||
| d875933412 | |||
| c5a81dcc71 | |||
| 28a9fe85d5 | |||
| 29d5cef912 | |||
| 0e935a50e7 | |||
| 61de9aaea6 | |||
| 83a533a0dc | |||
| 2e7da95e5b | |||
| 93a7667c83 | |||
| 51f78cef9a | |||
| bf7581e8e3 | |||
| aa7a5964f2 | |||
| 30c0a5f4b1 | |||
| a3c3b1cd27 | |||
| cc775dcdc0 | |||
| 5d9c21c2a5 | |||
| 9a19607f3f | |||
| 3d0eeb01f8 | |||
| 310ec3a574 | |||
| 96d12df021 | |||
| f382d6f3a5 | |||
| 6de99fd964 | |||
| bc76c7ffb8 | |||
| 20ed8da853 | |||
| aaf10bfdbf | |||
| 36b9020f36 | |||
| 5ec8fcf350 | |||
| 194c6dc0fd | |||
| aadec58087 | |||
| 1b1883aa30 | |||
| 020a0bc882 | |||
| f2bf75e85e | |||
| 957f42698e | |||
| fd436dcd9b | |||
| 9e32b64d57 | |||
| 7d39cb5f6b | |||
| a8cd419b08 | |||
| c20d941a77 | |||
| 93741ac50e | |||
| f83739fdb9 | |||
| daf91112f5 | |||
| d406b3a5c2 | |||
| 09b8ae0fed | |||
| 683a456b5c | |||
| 265af0d720 | |||
| 30f6636a95 | |||
| 03980afe98 | |||
| a3f2c28c13 | |||
| 2850887c71 | |||
| 8a403407cb | |||
| 73f1f464d8 | |||
| 16c06cd997 | |||
| 0f1a59641a | |||
| bd62ab2f92 | |||
| 4727fb25ce | |||
| 0947970514 | |||
| 04b081bbd7 | |||
| 3a6bb19cad | |||
| 77650f328b | |||
| 424a08c4a0 | |||
| 18d96ce0ce | |||
| 60f4d64af6 | |||
| 5664ecbd0c | |||
| 4fa74c0d38 | |||
| 6b3780f6d1 | |||
| 7d90638188 | |||
| 28c2591e0b | |||
| 2b58550d36 | |||
| 22e619d0ea | |||
| a32b067057 | |||
| 482f848281 | |||
| 6f97f8a84b | |||
| 0168efd737 | |||
| bb1d0ab0d9 | |||
| 0fae45e8df | |||
| 34203fcbce | |||
| e64fe9d36e | |||
| 11c5000b5b | |||
| 1e5046c679 | |||
| 9344f7f20d | |||
| b724b0d3eb | |||
| 6703f0c068 | |||
| 88345d7cb5 | |||
| 266cd69348 | |||
| 1ae5318af0 | |||
| 992b79384f | |||
| f9fa6229b2 | |||
| 2c5b988de0 | |||
| feac39e657 | |||
| 1fa4419da1 | |||
| f49f8b6495 | |||
| c122f8a9d7 | |||
| 91dd25f7b1 | |||
| 0d1cdb7a38 | |||
| 9238504db9 | |||
| 774a3a653e | |||
| 497155b84c | |||
| b03cc5862b | |||
| b76a6109cf | |||
| 97d8b7cf23 | |||
| 48158e05cb | |||
| 20890227c9 | |||
| ed86125d87 | |||
| 4b8795bfea | |||
| 07a234e0f2 | |||
| bf42123576 | |||
| 37016ccf9f | |||
| 47b8b93c2c | |||
| c5f744870f | |||
| 993a273d76 | |||
| 6ee93cdbc4 | |||
| 9a57f5de5e | |||
| 94f365f564 | |||
| 01515de17d | |||
| f074d6017d | |||
| 5b96be04b4 | |||
| de7951074a | |||
| 990b3c7084 | |||
| 7de013f467 | |||
| ba417935f0 | |||
| a77988a984 | |||
| 5b8d0957b3 | |||
| a913745b8f | |||
| 2f7e3da138 | |||
| 13ce232455 | |||
| a67b4847ca | |||
| 49beea6cc8 | |||
| ee81540c64 | |||
| ccd3492e1e | |||
| 0a37c34704 | |||
| 60c2805c01 | |||
| debfb16520 | |||
| 76782b6ff1 | |||
| 1ccb0e8123 | |||
| d9cf32f0ac | |||
| 521e691138 | |||
| be9d89e0cc | |||
| b93b57c83e | |||
| 6bad17e353 | |||
| eaabfb8250 | |||
| 1001c5b4c4 | |||
| 2441cbfde6 | |||
| b2b6df7a7e | |||
| d954f6d905 | |||
| 3269f048b3 | |||
| 5c07a74c55 | |||
| 744a21ec57 | |||
| c55711a1d9 | |||
| ddcc804518 | |||
| 56f983fd4e | |||
| d85751caf0 | |||
| 79a5064072 | |||
| cb083f3836 | |||
| 3677f3be4b | |||
| fc835be1e6 | |||
| 40fdd27698 | |||
| d46de5b819 | |||
| 9627e9981d | |||
| 5852f17bed | |||
| 80c4566baf | |||
| 8b286a2179 | |||
| 9abeb86168 | |||
| 0963e67325 | |||
| 8dba18b67c | |||
| 4e56f11d06 | |||
| 39f13f01bc | |||
| 2ed9ea85c6 | |||
| 61b5a0cbe9 | |||
| c3831bd799 | |||
| 53deb48e2d | |||
| 2eac309f86 | |||
| 558f8010eb | |||
| 36e3697fd6 | |||
| 42f1c5d45d | |||
| 00c4940352 | |||
| 3075ded77f | |||
| 94fd697148 | |||
| a3df278f7d | |||
| 135a0456a2 |
@@ -0,0 +1,85 @@
|
||||
/** @type {Detox.DetoxConfig} */
|
||||
module.exports = {
|
||||
testRunner: {
|
||||
args: {
|
||||
$0: 'jest',
|
||||
config: 'e2e/jest.config.js',
|
||||
},
|
||||
jest: {
|
||||
setupTimeout: 120000,
|
||||
},
|
||||
},
|
||||
apps: {
|
||||
'ios.debug': {
|
||||
type: 'ios.app',
|
||||
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/app.app',
|
||||
build:
|
||||
'xcodebuild -workspace ios/app.xcworkspace -scheme app -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
|
||||
},
|
||||
'ios.release': {
|
||||
type: 'ios.app',
|
||||
binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/app.app',
|
||||
build:
|
||||
'xcodebuild -workspace ios/app.xcworkspace -scheme app -configuration Release -sdk iphonesimulator -derivedDataPath ios/build',
|
||||
},
|
||||
'android.debug': {
|
||||
type: 'android.apk',
|
||||
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
|
||||
build:
|
||||
'cd android ; ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug ; cd -',
|
||||
reversePorts: [8081],
|
||||
},
|
||||
'android.release': {
|
||||
type: 'android.apk',
|
||||
binaryPath: 'android/app/build/outputs/apk/release/app-release.apk',
|
||||
build:
|
||||
'cd android ; ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release ; cd -',
|
||||
},
|
||||
},
|
||||
devices: {
|
||||
simulator: {
|
||||
type: 'ios.simulator',
|
||||
device: {
|
||||
type: 'iPhone 14',
|
||||
},
|
||||
},
|
||||
attached: {
|
||||
type: 'android.attached',
|
||||
device: {
|
||||
adbName: '.*',
|
||||
},
|
||||
},
|
||||
emulator: {
|
||||
type: 'android.emulator',
|
||||
device: {
|
||||
avdName: 'Pixel_3a_API_30_x86',
|
||||
},
|
||||
},
|
||||
},
|
||||
configurations: {
|
||||
'ios.sim.debug': {
|
||||
device: 'simulator',
|
||||
app: 'ios.debug',
|
||||
},
|
||||
'ios.sim.release': {
|
||||
device: 'simulator',
|
||||
app: 'ios.release',
|
||||
},
|
||||
'android.att.debug': {
|
||||
device: 'attached',
|
||||
app: 'android.debug',
|
||||
},
|
||||
'android.att.release': {
|
||||
device: 'attached',
|
||||
app: 'android.release',
|
||||
},
|
||||
'android.emu.debug': {
|
||||
device: 'emulator',
|
||||
app: 'android.debug',
|
||||
},
|
||||
'android.emu.release': {
|
||||
device: 'emulator',
|
||||
app: 'android.release',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -2,14 +2,18 @@ module.exports = {
|
||||
root: true,
|
||||
extends: '@react-native-community',
|
||||
parser: '@typescript-eslint/parser',
|
||||
// plugins: ['@typescript-eslint'],
|
||||
plugins: ['@typescript-eslint', 'detox'],
|
||||
ignorePatterns: [
|
||||
'**/__mocks__/*.ts',
|
||||
'src/third-party',
|
||||
'ios',
|
||||
'android',
|
||||
'coverage',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.js', '*.mjs', '*.ts', '*.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-shadow': 'off',
|
||||
'no-shadow': 'off',
|
||||
'no-undef': 'off',
|
||||
semi: [2, 'never'],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
linting:
|
||||
name: Run linters
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Yarn install
|
||||
run: yarn
|
||||
- name: Lint Reporter
|
||||
uses: wearerequired/lint-action@v2.2.0
|
||||
with:
|
||||
eslint: true
|
||||
prettier: true
|
||||
- name: Typescript & Lint check
|
||||
run: yarn lint
|
||||
testing:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install node 18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Yarn install
|
||||
run: yarn
|
||||
- name: Run tests
|
||||
run: |
|
||||
yarn test --forceExit
|
||||
@@ -59,3 +59,25 @@ buck-out/
|
||||
# Ruby / CocoaPods
|
||||
/ios/Pods/
|
||||
/vendor/bundle/
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
junit.xml
|
||||
artifacts
|
||||
|
||||
# Web bundle outputs
|
||||
/dist/
|
||||
|
||||
# Expo
|
||||
.expo/
|
||||
dist/
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
*.orig.*
|
||||
web-build/
|
||||
|
||||
# Temporary files created by Metro to check the health of the file watcher
|
||||
.metro-health-check*
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
ios
|
||||
android
|
||||
src/third-party
|
||||
src/app.json
|
||||
public
|
||||
/bskyweb/templates
|
||||
/dist/
|
||||
/.watchmanconfig
|
||||
@@ -1 +1 @@
|
||||
2.7.4
|
||||
2.7.6
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||
ruby '2.7.4'
|
||||
ruby File.read(File.join(__dir__, '.ruby-version')).strip
|
||||
|
||||
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
|
||||
gem 'cocoapods', '~> 1.11', '>= 1.11.3'
|
||||
@@ -1,28 +1,19 @@
|
||||
# Social App
|
||||
|
||||
In-progress social app.
|
||||
|
||||
Uses:
|
||||
|
||||
- [React Native](https://reactnative.dev)
|
||||
- [React Native for Web](https://necolas.github.io/react-native-web/)
|
||||
- [React Navigation](https://reactnative.dev/docs/navigation#react-navigation)
|
||||
- [MobX](https://mobx.js.org/README.html)
|
||||
- [Async Storage](https://github.com/react-native-async-storage/async-storage)
|
||||
|
||||
## TODOs
|
||||
|
||||
- Handle the "unauthed" state better than changing route definitions
|
||||
- Currently it's possible to get a 404 if the auth state changes
|
||||
# Bluesky
|
||||
|
||||
## Build instructions
|
||||
|
||||
- Setup your environment [using the react native instructions](https://reactnative.dev/docs/environment-setup).
|
||||
- Setup your environment [for e2e testing using detox](https://wix.github.io/Detox/docs/introduction/getting-started):
|
||||
- yarn global add detox-cli
|
||||
- brew tap wix/brew
|
||||
- brew install applesimutils
|
||||
- After initial setup:
|
||||
- `cd ios ; pod install`
|
||||
- Start the dev servers
|
||||
- `yarn dev-pds`
|
||||
- `yarn dev-wallet`
|
||||
- `git clone git@github.com:bluesky-social/atproto.git`
|
||||
- `cd atproto`
|
||||
- `yarn`
|
||||
- `cd packages/dev-env && yarn start`
|
||||
- Run the dev app
|
||||
- iOS: `yarn ios`
|
||||
- Android: `yarn android`
|
||||
@@ -37,34 +28,19 @@ Uses:
|
||||
|
||||
## Various notes
|
||||
|
||||
### Env vars
|
||||
### Debugging
|
||||
|
||||
Set using the `.env` file or using bash.
|
||||
- Note that since 0.70, debugging using the old debugger (which shows up using CMD+D) doesn't work anymore. Follow the instructions below to debug the code: https://reactnative.dev/docs/next/hermes#debugging-js-on-hermes-using-google-chromes-devtools
|
||||
|
||||
```
|
||||
REACT_APP_AUTH_LOBBY = 'http://localhost:3001'
|
||||
```
|
||||
### Running E2E Tests
|
||||
|
||||
### Build behaviors
|
||||
|
||||
The `metro.config.js` file rewrites a couple of imports. This is partly to work around missing features in Metro, and partly to patch the bundle. Affected imports include:
|
||||
|
||||
- ucans
|
||||
- one-webcrypto
|
||||
|
||||
### Cryptography
|
||||
|
||||
For native builds, we must provide a polyfill of `webcrypto`. We use a custom native module AppSecureRandom (based on [react-native-securerandom](https://github.com/robhogan/react-native-securerandom)) for the CRNG and [msrcrypto](https://github.com/microsoft/MSR-JavaScript-Crypto) for the cryptography.
|
||||
|
||||
**NOTE** Keys are not currently stored securely.
|
||||
- Make sure you've setup your environment following above
|
||||
- Make sure Metro and the dev server are running
|
||||
- Run `yarn e2e`
|
||||
- Find the artifacts in the `artifact` folder
|
||||
|
||||
### Polyfills
|
||||
|
||||
`./platform/polyfills.*.ts` adds polyfills to the environment. Currently this includes:
|
||||
|
||||
- webcrypto
|
||||
- TextEncoder / TextDecoder
|
||||
|
||||
### Auth flow
|
||||
|
||||
The auth flow is based on a browser app which is specified by the `REACT_APP_AUTH_LOBBY` env var. The app redirects to that location with the UCAN request, and then waits for a redirect back. In the native platforms with proper support, it will do this using an in-app browser. In native without in-app browser, or in the Web platform, it will handle this with redirects. The ucan is extracted from the hash fragment of the "return url" which is provided either by the in-app browser in response or detected during initial setup in the case of redirects.
|
||||
@@ -0,0 +1,57 @@
|
||||
import React from 'react'
|
||||
import {View, ScrollView, Modal, FlatList, TextInput} from 'react-native'
|
||||
|
||||
const BottomSheetModalContext = React.createContext(null)
|
||||
|
||||
const BottomSheetModalProvider = (props: any) => {
|
||||
return <BottomSheetModalContext.Provider {...props} value={{}} />
|
||||
}
|
||||
class BottomSheet extends React.Component<{onClose?: () => void}> {
|
||||
snapToIndex() {}
|
||||
snapToPosition() {}
|
||||
expand() {}
|
||||
collapse() {}
|
||||
close() {
|
||||
this.props.onClose?.()
|
||||
}
|
||||
forceClose() {}
|
||||
|
||||
render() {
|
||||
return <View>{this.props.children}</View>
|
||||
}
|
||||
}
|
||||
const BottomSheetModal = (props: any) => <Modal {...props} />
|
||||
|
||||
const BottomSheetBackdrop = (props: any) => <View {...props} />
|
||||
const BottomSheetHandle = (props: any) => <View {...props} />
|
||||
const BottomSheetFooter = (props: any) => <View {...props} />
|
||||
const BottomSheetScrollView = (props: any) => <ScrollView {...props} />
|
||||
const BottomSheetFlatList = (props: any) => <FlatList {...props} />
|
||||
const BottomSheetTextInput = (props: any) => <TextInput {...props} />
|
||||
|
||||
const useBottomSheet = jest.fn()
|
||||
const useBottomSheetModal = jest.fn()
|
||||
const useBottomSheetSpringConfigs = jest.fn()
|
||||
const useBottomSheetTimingConfigs = jest.fn()
|
||||
const useBottomSheetInternal = jest.fn()
|
||||
const useBottomSheetDynamicSnapPoints = jest.fn()
|
||||
|
||||
export {useBottomSheet}
|
||||
export {useBottomSheetModal}
|
||||
export {useBottomSheetSpringConfigs}
|
||||
export {useBottomSheetTimingConfigs}
|
||||
export {useBottomSheetInternal}
|
||||
export {useBottomSheetDynamicSnapPoints}
|
||||
|
||||
export {
|
||||
BottomSheetModalProvider,
|
||||
BottomSheetBackdrop,
|
||||
BottomSheetHandle,
|
||||
BottomSheetModal,
|
||||
BottomSheetFooter,
|
||||
BottomSheetScrollView,
|
||||
BottomSheetFlatList,
|
||||
BottomSheetTextInput,
|
||||
}
|
||||
|
||||
export default BottomSheet
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
requestPermission: jest.fn(),
|
||||
onForegroundEvent: jest.fn(),
|
||||
setBadgeCount: jest.fn(),
|
||||
displayNotification: jest.fn(),
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export const CameraRoll = {
|
||||
getPhotos: jest.fn().mockResolvedValue({
|
||||
edges: [
|
||||
{node: {image: {uri: 'path/to/image1.jpg'}}},
|
||||
{node: {image: {uri: 'path/to/image2.jpg'}}},
|
||||
{node: {image: {uri: 'path/to/image3.jpg'}}},
|
||||
],
|
||||
}),
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export default {
|
||||
configure: jest.fn().mockResolvedValue(0),
|
||||
finish: jest.fn(),
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export default {}
|
||||
@@ -0,0 +1,9 @@
|
||||
export const openPicker = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve({uri: ''}))
|
||||
export const openCamera = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve({uri: ''}))
|
||||
export const openCropper = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve({uri: ''}))
|
||||
@@ -0,0 +1,67 @@
|
||||
// @flow
|
||||
|
||||
// https://github.com/FormidableLabs/react-native-svg-mock
|
||||
import React from 'react'
|
||||
|
||||
const createComponent = function (name) {
|
||||
return class extends React.Component {
|
||||
// overwrite the displayName, since this is a class created dynamically
|
||||
static displayName = name
|
||||
|
||||
render() {
|
||||
return React.createElement(name, this.props, this.props.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mock all react-native-svg exports
|
||||
// from https://github.com/magicismight/react-native-svg/blob/master/index.js
|
||||
const Svg = createComponent('Svg')
|
||||
const Circle = createComponent('Circle')
|
||||
const Ellipse = createComponent('Ellipse')
|
||||
const G = createComponent('G')
|
||||
const Text = createComponent('Text')
|
||||
const TextPath = createComponent('TextPath')
|
||||
const TSpan = createComponent('TSpan')
|
||||
const Path = createComponent('Path')
|
||||
const Polygon = createComponent('Polygon')
|
||||
const Polyline = createComponent('Polyline')
|
||||
const Line = createComponent('Line')
|
||||
const Rect = createComponent('Rect')
|
||||
const Use = createComponent('Use')
|
||||
const Image = createComponent('Image')
|
||||
const Symbol = createComponent('Symbol')
|
||||
const Defs = createComponent('Defs')
|
||||
const LinearGradient = createComponent('LinearGradient')
|
||||
const RadialGradient = createComponent('RadialGradient')
|
||||
const Stop = createComponent('Stop')
|
||||
const ClipPath = createComponent('ClipPath')
|
||||
const Pattern = createComponent('Pattern')
|
||||
const Mask = createComponent('Mask')
|
||||
|
||||
export {
|
||||
Svg,
|
||||
Circle,
|
||||
Ellipse,
|
||||
G,
|
||||
Text,
|
||||
TextPath,
|
||||
TSpan,
|
||||
Path,
|
||||
Polygon,
|
||||
Polyline,
|
||||
Line,
|
||||
Rect,
|
||||
Use,
|
||||
Image,
|
||||
Symbol,
|
||||
Defs,
|
||||
LinearGradient,
|
||||
RadialGradient,
|
||||
Stop,
|
||||
ClipPath,
|
||||
Pattern,
|
||||
Mask,
|
||||
}
|
||||
|
||||
export default Svg
|
||||
@@ -0,0 +1,10 @@
|
||||
jest.mock('rn-fetch-blob', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: {
|
||||
fs: {
|
||||
unlink: jest.fn(),
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
import 'react-native'
|
||||
import React from 'react'
|
||||
import App from '../src/App'
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer'
|
||||
|
||||
it('renders correctly', () => {
|
||||
renderer.act(() => {
|
||||
renderer.create(<App />)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
export const exampleComHtml = `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Example Domain</title>
|
||||
<meta name="description" content="An example website">
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: #f0f0f2;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
|
||||
}
|
||||
div {
|
||||
width: 600px;
|
||||
margin: 5em auto;
|
||||
padding: 2em;
|
||||
background-color: #fdfdff;
|
||||
border-radius: 0.5em;
|
||||
box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
|
||||
}
|
||||
a:link, a:visited {
|
||||
color: #38488f;
|
||||
text-decoration: none;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
div {
|
||||
margin: 0 auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Example Domain</h1>
|
||||
<p>This domain is for use in illustrative examples in documents. You may use this
|
||||
domain in literature without prior coordination or asking for permission.</p>
|
||||
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>`
|
||||
@@ -0,0 +1,47 @@
|
||||
import {bundleAsync} from '../../../src/lib/async/bundle'
|
||||
|
||||
describe('bundle', () => {
|
||||
it('bundles multiple simultaneous calls into one execution', async () => {
|
||||
let calls = 0
|
||||
const fn = bundleAsync(async () => {
|
||||
calls++
|
||||
await new Promise(r => setTimeout(r, 1))
|
||||
return 'hello'
|
||||
})
|
||||
const [res1, res2, res3] = await Promise.all([fn(), fn(), fn()])
|
||||
expect(calls).toEqual(1)
|
||||
expect(res1).toEqual('hello')
|
||||
expect(res2).toEqual('hello')
|
||||
expect(res3).toEqual('hello')
|
||||
})
|
||||
it('does not bundle non-simultaneous calls', async () => {
|
||||
let calls = 0
|
||||
const fn = bundleAsync(async () => {
|
||||
calls++
|
||||
await new Promise(r => setTimeout(r, 1))
|
||||
return 'hello'
|
||||
})
|
||||
const res1 = await fn()
|
||||
const res2 = await fn()
|
||||
const res3 = await fn()
|
||||
expect(calls).toEqual(3)
|
||||
expect(res1).toEqual('hello')
|
||||
expect(res2).toEqual('hello')
|
||||
expect(res3).toEqual('hello')
|
||||
})
|
||||
it('is not affected by rejections', async () => {
|
||||
let calls = 0
|
||||
const fn = bundleAsync(async () => {
|
||||
calls++
|
||||
await new Promise(r => setTimeout(r, 1))
|
||||
throw new Error()
|
||||
})
|
||||
const res1 = await fn().catch(() => 'reject')
|
||||
const res2 = await fn().catch(() => 'reject')
|
||||
const res3 = await fn().catch(() => 'reject')
|
||||
expect(calls).toEqual(3)
|
||||
expect(res1).toEqual('reject')
|
||||
expect(res2).toEqual('reject')
|
||||
expect(res3).toEqual('reject')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import {isNetworkError} from '../../src/lib/strings/errors'
|
||||
|
||||
describe('isNetworkError', () => {
|
||||
const inputs = [
|
||||
'TypeError: Network request failed',
|
||||
'Uncaught TypeError: Cannot read property x of undefined',
|
||||
'Uncaught RangeError',
|
||||
'Error: Aborted',
|
||||
]
|
||||
const outputs = [true, false, false, true]
|
||||
|
||||
it('correctly distinguishes network errors', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i]
|
||||
const result = isNetworkError(input)
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,134 @@
|
||||
import {extractHtmlMeta} from '../../src/lib/link-meta/html'
|
||||
import {exampleComHtml} from './__mocks__/exampleComHtml'
|
||||
import {youtubeHTML} from './__mocks__/youtubeHtml'
|
||||
import {tiktokHtml} from './__mocks__/tiktokHtml'
|
||||
import {youtubeChannelHtml} from './__mocks__/youtubeChannelHtml'
|
||||
|
||||
describe('extractHtmlMeta', () => {
|
||||
const cases = [
|
||||
['', {}],
|
||||
['nothing', {}],
|
||||
['<title>title</title>', {title: 'title'}],
|
||||
['<title> aSd!@#AC </title>', {title: 'aSd!@#AC'}],
|
||||
['<title>\n title\n </title>', {title: 'title'}],
|
||||
['<meta name="title" content="meta title">', {title: 'meta title'}],
|
||||
[
|
||||
'<meta name="description" content="meta description">',
|
||||
{description: 'meta description'},
|
||||
],
|
||||
['<meta property="og:title" content="og title">', {title: 'og title'}],
|
||||
[
|
||||
'<meta property="og:description" content="og description">',
|
||||
{description: 'og description'},
|
||||
],
|
||||
[
|
||||
'<meta property="og:image" content="https://ogimage.com/foo.png">',
|
||||
{image: 'https://ogimage.com/foo.png'},
|
||||
],
|
||||
[
|
||||
'<meta property="twitter:title" content="twitter title">',
|
||||
{title: 'twitter title'},
|
||||
],
|
||||
[
|
||||
'<meta property="twitter:description" content="twitter description">',
|
||||
{description: 'twitter description'},
|
||||
],
|
||||
[
|
||||
'<meta property="twitter:image" content="https://twitterimage.com/foo.png">',
|
||||
{image: 'https://twitterimage.com/foo.png'},
|
||||
],
|
||||
['<meta\n name="title"\n content="meta title"\n>', {title: 'meta title'}],
|
||||
]
|
||||
|
||||
it.each(cases)(
|
||||
'given the html tag %p, returns %p',
|
||||
// @ts-ignore not worth fixing -prf
|
||||
(input, expectedResult) => {
|
||||
const output = extractHtmlMeta({html: input as string, hostname: ''})
|
||||
expect(output).toEqual(expectedResult)
|
||||
},
|
||||
)
|
||||
|
||||
it('extracts title and description from a generic HTML page', () => {
|
||||
const input = exampleComHtml
|
||||
const expectedOutput = {
|
||||
title: 'Example Domain',
|
||||
description: 'An example website',
|
||||
}
|
||||
const output = extractHtmlMeta({html: input, hostname: 'example.com'})
|
||||
expect(output).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('extracts title and description from a Tiktok HTML page', () => {
|
||||
const input = tiktokHtml
|
||||
const expectedOutput = {
|
||||
title:
|
||||
'Coca-Cola and Mentos! Super Reaction! #cocacola #mentos #reaction #bal... | TikTok',
|
||||
description:
|
||||
'5.5M Likes, 20.8K Comments. TikTok video from Power Vision Tests (@_powervision_): "Coca-Cola and Mentos! Super Reaction! #cocacola #mentos #reaction #balloon #sciencemoment #scienceexperiment #experiment #test #amazing #pvexp". оригинальный звук - Power Vision Tests.',
|
||||
}
|
||||
const output = extractHtmlMeta({html: input, hostname: 'tiktok.com'})
|
||||
expect(output).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('extracts title and description from a generic youtube page', () => {
|
||||
const input = youtubeHTML
|
||||
const expectedOutput = {
|
||||
title: 'HD Video (1080p) with Relaxing Music of Native American Shamans',
|
||||
description:
|
||||
'Stunning HD Video ( 1080p ) of Patagonian Nature with Relaxing Native American Shamanic Music. HD footage used from ',
|
||||
image: 'https://i.ytimg.com/vi/x6UITRjhijI/sddefault.jpg',
|
||||
}
|
||||
const output = extractHtmlMeta({html: input, hostname: 'youtube.com'})
|
||||
expect(output).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('extracts avatar from a youtube channel', () => {
|
||||
const input = youtubeChannelHtml
|
||||
const expectedOutput = {
|
||||
title: 'penguinz0',
|
||||
description:
|
||||
'Clips channel: https://www.youtube.com/channel/UC4EQHfzIbkL_Skit_iKt1aA\n\nTwitter: https://twitter.com/MoistCr1TiKaL\n\nInstagram: https://www.instagram.com/bigmoistcr1tikal/?hl=en\n\nTwitch: https://www.twitch.tv/moistcr1tikal\n\nSnapchat: Hugecharles\n\nTik Tok: Hugecharles\n\nI don't have any other public accounts.',
|
||||
image:
|
||||
'https://yt3.googleusercontent.com/ytc/AL5GRJWOhJOuUC6C2b7gP-5D2q6ypXbcOOckyAE1En4RUQ=s176-c-k-c0x00ffffff-no-rj',
|
||||
}
|
||||
const output = extractHtmlMeta({html: input, hostname: 'youtube.com'})
|
||||
expect(output).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('extracts username from the url a twitter profile page', () => {
|
||||
const expectedOutput = {
|
||||
title: '@bluesky on Twitter',
|
||||
}
|
||||
const output = extractHtmlMeta({
|
||||
html: '',
|
||||
hostname: 'twitter.com',
|
||||
pathname: '/bluesky',
|
||||
})
|
||||
expect(output).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('extracts username from the url a tweet', () => {
|
||||
const expectedOutput = {
|
||||
title: 'Tweet by @bluesky',
|
||||
}
|
||||
const output = extractHtmlMeta({
|
||||
html: '',
|
||||
hostname: 'twitter.com',
|
||||
pathname: '/bluesky/status/1582437529969917953',
|
||||
})
|
||||
expect(output).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it("does not extract username from the url when it's not a tweet or profile page", () => {
|
||||
const expectedOutput = {
|
||||
title: 'Twitter',
|
||||
}
|
||||
const output = extractHtmlMeta({
|
||||
html: '',
|
||||
hostname: 'twitter.com',
|
||||
pathname: '/i/articles/follows/-1675653703?time_window=24',
|
||||
})
|
||||
expect(output).toEqual(expectedOutput)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,121 @@
|
||||
import {
|
||||
downloadAndResize,
|
||||
DownloadAndResizeOpts,
|
||||
} from '../../src/lib/media/manip'
|
||||
import ImageResizer from '@bam.tech/react-native-image-resizer'
|
||||
import RNFetchBlob from 'rn-fetch-blob'
|
||||
|
||||
describe('downloadAndResize', () => {
|
||||
const errorSpy = jest.spyOn(global.console, 'error')
|
||||
|
||||
const mockResizedImage = {
|
||||
path: jest.fn().mockReturnValue('file://resized-image.jpg'),
|
||||
size: 100,
|
||||
width: 50,
|
||||
height: 50,
|
||||
mime: 'image/jpeg',
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const mockedCreateResizedImage =
|
||||
ImageResizer.createResizedImage as jest.Mock
|
||||
mockedCreateResizedImage.mockResolvedValue(mockResizedImage)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should return resized image for valid URI and options', async () => {
|
||||
const mockedFetch = RNFetchBlob.fetch as jest.Mock
|
||||
mockedFetch.mockResolvedValueOnce({
|
||||
path: jest.fn().mockReturnValue('file://downloaded-image.jpg'),
|
||||
flush: jest.fn(),
|
||||
})
|
||||
|
||||
const opts: DownloadAndResizeOpts = {
|
||||
uri: 'https://example.com/image.jpg',
|
||||
width: 100,
|
||||
height: 100,
|
||||
maxSize: 500000,
|
||||
mode: 'cover',
|
||||
timeout: 10000,
|
||||
}
|
||||
|
||||
const result = await downloadAndResize(opts)
|
||||
expect(result).toEqual(mockResizedImage)
|
||||
expect(RNFetchBlob.config).toHaveBeenCalledWith({
|
||||
fileCache: true,
|
||||
appendExt: 'jpeg',
|
||||
})
|
||||
expect(RNFetchBlob.fetch).toHaveBeenCalledWith(
|
||||
'GET',
|
||||
'https://example.com/image.jpg',
|
||||
)
|
||||
expect(ImageResizer.createResizedImage).toHaveBeenCalledWith(
|
||||
'file://downloaded-image.jpg',
|
||||
100,
|
||||
100,
|
||||
'JPEG',
|
||||
100,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
{mode: 'cover'},
|
||||
)
|
||||
})
|
||||
|
||||
it('should return undefined for invalid URI', async () => {
|
||||
const opts: DownloadAndResizeOpts = {
|
||||
uri: 'invalid-uri',
|
||||
width: 100,
|
||||
height: 100,
|
||||
maxSize: 500000,
|
||||
mode: 'cover',
|
||||
timeout: 10000,
|
||||
}
|
||||
|
||||
const result = await downloadAndResize(opts)
|
||||
expect(errorSpy).toHaveBeenCalled()
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should return undefined for unsupported file type', async () => {
|
||||
const mockedFetch = RNFetchBlob.fetch as jest.Mock
|
||||
mockedFetch.mockResolvedValueOnce({
|
||||
path: jest.fn().mockReturnValue('file://downloaded-image'),
|
||||
flush: jest.fn(),
|
||||
})
|
||||
|
||||
const opts: DownloadAndResizeOpts = {
|
||||
uri: 'https://example.com/image',
|
||||
width: 100,
|
||||
height: 100,
|
||||
maxSize: 500000,
|
||||
mode: 'cover',
|
||||
timeout: 10000,
|
||||
}
|
||||
|
||||
const result = await downloadAndResize(opts)
|
||||
expect(result).toEqual(mockResizedImage)
|
||||
expect(RNFetchBlob.config).toHaveBeenCalledWith({
|
||||
fileCache: true,
|
||||
appendExt: 'jpeg',
|
||||
})
|
||||
expect(RNFetchBlob.fetch).toHaveBeenCalledWith(
|
||||
'GET',
|
||||
'https://example.com/image',
|
||||
)
|
||||
expect(ImageResizer.createResizedImage).toHaveBeenCalledWith(
|
||||
'file://downloaded-image',
|
||||
100,
|
||||
100,
|
||||
'JPEG',
|
||||
100,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
{mode: 'cover'},
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,118 @@
|
||||
import {
|
||||
LikelyType,
|
||||
getLinkMeta,
|
||||
getLikelyType,
|
||||
} from '../../src/lib/link-meta/link-meta'
|
||||
import {exampleComHtml} from './__mocks__/exampleComHtml'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {DEFAULT_SERVICE, RootStoreModel} from '../../src/state'
|
||||
|
||||
describe('getLinkMeta', () => {
|
||||
let rootStore: RootStoreModel
|
||||
|
||||
beforeEach(() => {
|
||||
rootStore = new RootStoreModel(new AtpAgent({service: DEFAULT_SERVICE}))
|
||||
})
|
||||
|
||||
const inputs = [
|
||||
'',
|
||||
'httpbadurl',
|
||||
'https://example.com',
|
||||
'https://example.com/index.html',
|
||||
'https://example.com/image.png',
|
||||
'https://example.com/video.avi',
|
||||
'https://example.com/audio.ogg',
|
||||
'https://example.com/text.txt',
|
||||
'https://example.com/javascript.js',
|
||||
'https://bsky.app/',
|
||||
'https://bsky.app/index.html',
|
||||
]
|
||||
const outputs = [
|
||||
{
|
||||
error: 'Invalid URL',
|
||||
likelyType: LikelyType.Other,
|
||||
url: '',
|
||||
},
|
||||
{
|
||||
error: 'Invalid URL',
|
||||
likelyType: LikelyType.Other,
|
||||
url: 'httpbadurl',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.HTML,
|
||||
url: 'https://example.com',
|
||||
title: 'Example Domain',
|
||||
description: 'An example website',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.HTML,
|
||||
url: 'https://example.com/index.html',
|
||||
title: 'Example Domain',
|
||||
description: 'An example website',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.Image,
|
||||
url: 'https://example.com/image.png',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.Video,
|
||||
url: 'https://example.com/video.avi',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.Audio,
|
||||
url: 'https://example.com/audio.ogg',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.Text,
|
||||
url: 'https://example.com/text.txt',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.Other,
|
||||
url: 'https://example.com/javascript.js',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.AtpData,
|
||||
url: '/',
|
||||
title: 'Bluesky',
|
||||
description: 'A new kind of social network',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.AtpData,
|
||||
url: '/index.html',
|
||||
title: 'Not found',
|
||||
},
|
||||
{
|
||||
likelyType: LikelyType.Other,
|
||||
url: '',
|
||||
title: '',
|
||||
},
|
||||
]
|
||||
it('correctly handles a set of text inputs', async () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
global.fetch = jest.fn().mockImplementationOnce(() => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
resolve({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: () => exampleComHtml,
|
||||
})
|
||||
})
|
||||
})
|
||||
const input = inputs[i]
|
||||
const output = await getLinkMeta(rootStore, input)
|
||||
expect(output).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('getLikelyType', () => {
|
||||
it('correctly handles non-parsed url', async () => {
|
||||
const output = await getLikelyType('https://example.com')
|
||||
expect(output).toEqual(LikelyType.HTML)
|
||||
})
|
||||
|
||||
it('handles non-string urls without crashing', async () => {
|
||||
const output = await getLikelyType('123')
|
||||
expect(output).toEqual(LikelyType.Other)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
import {clamp} from '../../src/lib/numbers'
|
||||
|
||||
describe('clamp', () => {
|
||||
const inputs: [number, number, number][] = [
|
||||
[100, 0, 200],
|
||||
[100, 0, 100],
|
||||
[0, 0, 100],
|
||||
[100, 0, -1],
|
||||
[4, 1, 1],
|
||||
[100, -100, 0],
|
||||
[400, 100, -100],
|
||||
[70, -1, 1],
|
||||
[Infinity, Infinity, Infinity],
|
||||
]
|
||||
const outputs = [100, 100, 0, -1, 1, 0, -100, 1, Infinity]
|
||||
|
||||
it('correctly clamps any given number and range', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i]
|
||||
const result = clamp(...input)
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,516 @@
|
||||
import {
|
||||
getYoutubeVideoId,
|
||||
makeRecordUri,
|
||||
toNiceDomain,
|
||||
toShortUrl,
|
||||
toShareUrl,
|
||||
} from '../../src/lib/strings/url-helpers'
|
||||
import {pluralize, enforceLen} from '../../src/lib/strings/helpers'
|
||||
import {ago} from '../../src/lib/strings/time'
|
||||
import {
|
||||
extractEntities,
|
||||
detectLinkables,
|
||||
} from '../../src/lib/strings/rich-text-detection'
|
||||
import {makeValidHandle, createFullHandle} from '../../src/lib/strings/handles'
|
||||
import {cleanError} from '../../src/lib/strings/errors'
|
||||
|
||||
describe('extractEntities', () => {
|
||||
const knownHandles = new Set(['handle.com', 'full123.test-of-chars'])
|
||||
const inputs = [
|
||||
'no mention',
|
||||
'@handle.com middle end',
|
||||
'start @handle.com end',
|
||||
'start middle @handle.com',
|
||||
'@handle.com @handle.com @handle.com',
|
||||
'@full123.test-of-chars',
|
||||
'not@right',
|
||||
'@handle.com!@#$chars',
|
||||
'@handle.com\n@handle.com',
|
||||
'parenthetical (@handle.com)',
|
||||
'start https://middle.com end',
|
||||
'start https://middle.com/foo/bar end',
|
||||
'start https://middle.com/foo/bar?baz=bux end',
|
||||
'start https://middle.com/foo/bar?baz=bux#hash end',
|
||||
'https://start.com/foo/bar?baz=bux#hash middle end',
|
||||
'start middle https://end.com/foo/bar?baz=bux#hash',
|
||||
'https://newline1.com\nhttps://newline2.com',
|
||||
'start middle.com end',
|
||||
'start middle.com/foo/bar end',
|
||||
'start middle.com/foo/bar?baz=bux end',
|
||||
'start middle.com/foo/bar?baz=bux#hash end',
|
||||
'start.com/foo/bar?baz=bux#hash middle end',
|
||||
'start middle end.com/foo/bar?baz=bux#hash',
|
||||
'newline1.com\nnewline2.com',
|
||||
'not.. a..url ..here',
|
||||
'e.g.',
|
||||
'something-cool.jpg',
|
||||
'website.com.jpg',
|
||||
'e.g./foo',
|
||||
'website.com.jpg/foo',
|
||||
'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
|
||||
'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/ ',
|
||||
'https://foo.com https://bar.com/whatever https://baz.com',
|
||||
'punctuation https://foo.com, https://bar.com/whatever; https://baz.com.',
|
||||
'parenthentical (https://foo.com)',
|
||||
'except for https://foo.com/thing_(cool)',
|
||||
]
|
||||
interface Output {
|
||||
type: string
|
||||
value: string
|
||||
noScheme?: boolean
|
||||
}
|
||||
const outputs: Output[][] = [
|
||||
[],
|
||||
[{type: 'mention', value: 'handle.com'}],
|
||||
[{type: 'mention', value: 'handle.com'}],
|
||||
[{type: 'mention', value: 'handle.com'}],
|
||||
[
|
||||
{type: 'mention', value: 'handle.com'},
|
||||
{type: 'mention', value: 'handle.com'},
|
||||
{type: 'mention', value: 'handle.com'},
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'mention',
|
||||
value: 'full123.test-of-chars',
|
||||
},
|
||||
],
|
||||
[],
|
||||
[{type: 'mention', value: 'handle.com'}],
|
||||
[
|
||||
{type: 'mention', value: 'handle.com'},
|
||||
{type: 'mention', value: 'handle.com'},
|
||||
],
|
||||
[{type: 'mention', value: 'handle.com'}],
|
||||
[{type: 'link', value: 'https://middle.com'}],
|
||||
[{type: 'link', value: 'https://middle.com/foo/bar'}],
|
||||
[{type: 'link', value: 'https://middle.com/foo/bar?baz=bux'}],
|
||||
[{type: 'link', value: 'https://middle.com/foo/bar?baz=bux#hash'}],
|
||||
[{type: 'link', value: 'https://start.com/foo/bar?baz=bux#hash'}],
|
||||
[{type: 'link', value: 'https://end.com/foo/bar?baz=bux#hash'}],
|
||||
[
|
||||
{type: 'link', value: 'https://newline1.com'},
|
||||
{type: 'link', value: 'https://newline2.com'},
|
||||
],
|
||||
[{type: 'link', value: 'middle.com', noScheme: true}],
|
||||
[{type: 'link', value: 'middle.com/foo/bar', noScheme: true}],
|
||||
[{type: 'link', value: 'middle.com/foo/bar?baz=bux', noScheme: true}],
|
||||
[{type: 'link', value: 'middle.com/foo/bar?baz=bux#hash', noScheme: true}],
|
||||
[{type: 'link', value: 'start.com/foo/bar?baz=bux#hash', noScheme: true}],
|
||||
[{type: 'link', value: 'end.com/foo/bar?baz=bux#hash', noScheme: true}],
|
||||
[
|
||||
{type: 'link', value: 'newline1.com', noScheme: true},
|
||||
{type: 'link', value: 'newline2.com', noScheme: true},
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
{
|
||||
type: 'link',
|
||||
value:
|
||||
'https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'link',
|
||||
value:
|
||||
'https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
|
||||
},
|
||||
],
|
||||
[
|
||||
{type: 'link', value: 'https://foo.com'},
|
||||
{type: 'link', value: 'https://bar.com/whatever'},
|
||||
{type: 'link', value: 'https://baz.com'},
|
||||
],
|
||||
[
|
||||
{type: 'link', value: 'https://foo.com'},
|
||||
{type: 'link', value: 'https://bar.com/whatever'},
|
||||
{type: 'link', value: 'https://baz.com'},
|
||||
],
|
||||
[{type: 'link', value: 'https://foo.com'}],
|
||||
[{type: 'link', value: 'https://foo.com/thing_(cool)'}],
|
||||
]
|
||||
it('correctly handles a set of text inputs', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i]
|
||||
const result = extractEntities(input, knownHandles)
|
||||
if (!outputs[i].length) {
|
||||
expect(result).toBeFalsy()
|
||||
} else if (outputs[i].length && !result) {
|
||||
expect(result).toBeTruthy()
|
||||
} else if (result) {
|
||||
expect(result.length).toBe(outputs[i].length)
|
||||
for (let j = 0; j < outputs[i].length; j++) {
|
||||
expect(result[j].type).toEqual(outputs[i][j].type)
|
||||
if (outputs[i][j].noScheme) {
|
||||
expect(result[j].value).toEqual(`https://${outputs[i][j].value}`)
|
||||
} else {
|
||||
expect(result[j].value).toEqual(outputs[i][j].value)
|
||||
}
|
||||
if (outputs[i]?.[j].type === 'mention') {
|
||||
expect(
|
||||
input.slice(result[j].index.start, result[j].index.end),
|
||||
).toBe(`@${result[j].value}`)
|
||||
} else {
|
||||
if (!outputs[i]?.[j].noScheme) {
|
||||
expect(
|
||||
input.slice(result[j].index.start, result[j].index.end),
|
||||
).toBe(result[j].value)
|
||||
} else {
|
||||
expect(
|
||||
input.slice(result[j].index.start, result[j].index.end),
|
||||
).toBe(result[j].value.slice('https://'.length))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('detectLinkables', () => {
|
||||
const inputs = [
|
||||
'no linkable',
|
||||
'@start middle end',
|
||||
'start @middle end',
|
||||
'start middle @end',
|
||||
'@start @middle @end',
|
||||
'@full123.test-of-chars',
|
||||
'not@right',
|
||||
'@bad!@#$chars',
|
||||
'@newline1\n@newline2',
|
||||
'parenthetical (@handle)',
|
||||
'start https://middle.com end',
|
||||
'start https://middle.com/foo/bar end',
|
||||
'start https://middle.com/foo/bar?baz=bux end',
|
||||
'start https://middle.com/foo/bar?baz=bux#hash end',
|
||||
'https://start.com/foo/bar?baz=bux#hash middle end',
|
||||
'start middle https://end.com/foo/bar?baz=bux#hash',
|
||||
'https://newline1.com\nhttps://newline2.com',
|
||||
'start middle.com end',
|
||||
'start middle.com/foo/bar end',
|
||||
'start middle.com/foo/bar?baz=bux end',
|
||||
'start middle.com/foo/bar?baz=bux#hash end',
|
||||
'start.com/foo/bar?baz=bux#hash middle end',
|
||||
'start middle end.com/foo/bar?baz=bux#hash',
|
||||
'newline1.com\nnewline2.com',
|
||||
'not.. a..url ..here',
|
||||
'e.g.',
|
||||
'e.g. real.com fake.notreal',
|
||||
'something-cool.jpg',
|
||||
'website.com.jpg',
|
||||
'e.g./foo',
|
||||
'website.com.jpg/foo',
|
||||
'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
|
||||
'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/ ',
|
||||
'https://foo.com https://bar.com/whatever https://baz.com',
|
||||
'punctuation https://foo.com, https://bar.com/whatever; https://baz.com.',
|
||||
'parenthentical (https://foo.com)',
|
||||
'except for https://foo.com/thing_(cool)',
|
||||
]
|
||||
const outputs = [
|
||||
['no linkable'],
|
||||
[{link: '@start'}, ' middle end'],
|
||||
['start ', {link: '@middle'}, ' end'],
|
||||
['start middle ', {link: '@end'}],
|
||||
[{link: '@start'}, ' ', {link: '@middle'}, ' ', {link: '@end'}],
|
||||
[{link: '@full123.test-of-chars'}],
|
||||
['not@right'],
|
||||
[{link: '@bad'}, '!@#$chars'],
|
||||
[{link: '@newline1'}, '\n', {link: '@newline2'}],
|
||||
['parenthetical (', {link: '@handle'}, ')'],
|
||||
['start ', {link: 'https://middle.com'}, ' end'],
|
||||
['start ', {link: 'https://middle.com/foo/bar'}, ' end'],
|
||||
['start ', {link: 'https://middle.com/foo/bar?baz=bux'}, ' end'],
|
||||
['start ', {link: 'https://middle.com/foo/bar?baz=bux#hash'}, ' end'],
|
||||
[{link: 'https://start.com/foo/bar?baz=bux#hash'}, ' middle end'],
|
||||
['start middle ', {link: 'https://end.com/foo/bar?baz=bux#hash'}],
|
||||
[{link: 'https://newline1.com'}, '\n', {link: 'https://newline2.com'}],
|
||||
['start ', {link: 'middle.com'}, ' end'],
|
||||
['start ', {link: 'middle.com/foo/bar'}, ' end'],
|
||||
['start ', {link: 'middle.com/foo/bar?baz=bux'}, ' end'],
|
||||
['start ', {link: 'middle.com/foo/bar?baz=bux#hash'}, ' end'],
|
||||
[{link: 'start.com/foo/bar?baz=bux#hash'}, ' middle end'],
|
||||
['start middle ', {link: 'end.com/foo/bar?baz=bux#hash'}],
|
||||
[{link: 'newline1.com'}, '\n', {link: 'newline2.com'}],
|
||||
['not.. a..url ..here'],
|
||||
['e.g.'],
|
||||
['e.g. ', {link: 'real.com'}, ' fake.notreal'],
|
||||
['something-cool.jpg'],
|
||||
['website.com.jpg'],
|
||||
['e.g./foo'],
|
||||
['website.com.jpg/foo'],
|
||||
[
|
||||
'Classic article ',
|
||||
{
|
||||
link: 'https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
|
||||
},
|
||||
],
|
||||
[
|
||||
'Classic article ',
|
||||
{
|
||||
link: 'https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
|
||||
},
|
||||
' ',
|
||||
],
|
||||
[
|
||||
{link: 'https://foo.com'},
|
||||
' ',
|
||||
{link: 'https://bar.com/whatever'},
|
||||
' ',
|
||||
{link: 'https://baz.com'},
|
||||
],
|
||||
[
|
||||
'punctuation ',
|
||||
{link: 'https://foo.com'},
|
||||
', ',
|
||||
{link: 'https://bar.com/whatever'},
|
||||
'; ',
|
||||
{link: 'https://baz.com'},
|
||||
'.',
|
||||
],
|
||||
['parenthentical (', {link: 'https://foo.com'}, ')'],
|
||||
['except for ', {link: 'https://foo.com/thing_(cool)'}],
|
||||
]
|
||||
it('correctly handles a set of text inputs', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i]
|
||||
const output = detectLinkables(input)
|
||||
expect(output).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('pluralize', () => {
|
||||
const inputs: [number, string, string?][] = [
|
||||
[1, 'follower'],
|
||||
[1, 'member'],
|
||||
[100, 'post'],
|
||||
[1000, 'repost'],
|
||||
[10000, 'upvote'],
|
||||
[100000, 'other'],
|
||||
[2, 'man', 'men'],
|
||||
]
|
||||
const outputs = [
|
||||
'follower',
|
||||
'member',
|
||||
'posts',
|
||||
'reposts',
|
||||
'upvotes',
|
||||
'others',
|
||||
'men',
|
||||
]
|
||||
|
||||
it('correctly pluralizes a set of words', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i]
|
||||
const output = pluralize(...input)
|
||||
expect(output).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('makeRecordUri', () => {
|
||||
const inputs: [string, string, string][] = [
|
||||
['alice.test', 'app.bsky.feed.post', '3jk7x4irgv52r'],
|
||||
]
|
||||
const outputs = ['at://alice.test/app.bsky.feed.post/3jk7x4irgv52r']
|
||||
|
||||
it('correctly builds a record URI', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i]
|
||||
const result = makeRecordUri(...input)
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('ago', () => {
|
||||
const inputs = [
|
||||
1671461038,
|
||||
'04 Dec 1995 00:12:00 GMT',
|
||||
new Date(),
|
||||
new Date().setMinutes(new Date().getMinutes() - 10),
|
||||
new Date().setHours(new Date().getHours() - 1),
|
||||
new Date().setDate(new Date().getDate() - 1),
|
||||
new Date().setMonth(new Date().getMonth() - 1),
|
||||
]
|
||||
const outputs = [
|
||||
new Date(1671461038).toLocaleDateString(),
|
||||
new Date('04 Dec 1995 00:12:00 GMT').toLocaleDateString(),
|
||||
'0s',
|
||||
'10m',
|
||||
'1h',
|
||||
'1d',
|
||||
'1mo',
|
||||
]
|
||||
|
||||
it('correctly calculates how much time passed, in a string', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const result = ago(inputs[i])
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('makeValidHandle', () => {
|
||||
const inputs = [
|
||||
'test-handle-123',
|
||||
'test!"#$%&/()=?_',
|
||||
'this-handle-should-be-too-big',
|
||||
]
|
||||
const outputs = ['test-handle-123', 'test', 'this-handle-should-b']
|
||||
|
||||
it('correctly parses and corrects handles', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const result = makeValidHandle(inputs[i])
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('createFullHandle', () => {
|
||||
const inputs: [string, string][] = [
|
||||
['test-handle-123', 'test'],
|
||||
['.test.handle', 'test.test.'],
|
||||
['test.handle.', '.test.test'],
|
||||
]
|
||||
const outputs = [
|
||||
'test-handle-123.test',
|
||||
'.test.handle.test.test.',
|
||||
'test.handle.test.test',
|
||||
]
|
||||
|
||||
it('correctly parses and corrects handles', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i]
|
||||
const result = createFullHandle(...input)
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('enforceLen', () => {
|
||||
const inputs: [string, number][] = [
|
||||
['Hello World!', 5],
|
||||
['Hello World!', 20],
|
||||
['', 5],
|
||||
]
|
||||
const outputs = ['Hello', 'Hello World!', '']
|
||||
|
||||
it('correctly enforces defined length on a given string', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i]
|
||||
const result = enforceLen(...input)
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('cleanError', () => {
|
||||
const inputs = [
|
||||
'TypeError: Network request failed',
|
||||
'Error: Aborted',
|
||||
'Error: TypeError "x" is not a function',
|
||||
'Error: SyntaxError unexpected token "export"',
|
||||
'Some other error',
|
||||
]
|
||||
const outputs = [
|
||||
'Unable to connect. Please check your internet connection and try again.',
|
||||
'Unable to connect. Please check your internet connection and try again.',
|
||||
'TypeError "x" is not a function',
|
||||
'SyntaxError unexpected token "export"',
|
||||
'Some other error',
|
||||
]
|
||||
|
||||
it('removes extra content from error message', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const result = cleanError(inputs[i])
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('toNiceDomain', () => {
|
||||
const inputs = [
|
||||
'https://example.com/index.html',
|
||||
'https://bsky.app',
|
||||
'https://bsky.social',
|
||||
'#123123123',
|
||||
]
|
||||
const outputs = ['example.com', 'bsky.app', 'Bluesky Social', '#123123123']
|
||||
|
||||
it("displays the url's host in a easily readable manner", () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const result = toNiceDomain(inputs[i])
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('toShortUrl', () => {
|
||||
const inputs = [
|
||||
'https://bsky.app',
|
||||
'https://bsky.app/3jk7x4irgv52r',
|
||||
'https://bsky.app/3jk7x4irgv52r2313y182h9',
|
||||
]
|
||||
const outputs = [
|
||||
'bsky.app',
|
||||
'bsky.app/3jk7x4irgv52r',
|
||||
'bsky.app/3jk7x4irgv52r2313y...',
|
||||
]
|
||||
|
||||
it('shortens the url', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const result = toShortUrl(inputs[i])
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('toShareUrl', () => {
|
||||
const inputs = ['https://bsky.app', '/3jk7x4irgv52r', 'item/test/123']
|
||||
const outputs = [
|
||||
'https://bsky.app',
|
||||
'https://bsky.app/3jk7x4irgv52r',
|
||||
'https://bsky.app/item/test/123',
|
||||
]
|
||||
|
||||
it('appends https, when not present', () => {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const result = toShareUrl(inputs[i])
|
||||
expect(result).toEqual(outputs[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('getYoutubeVideoId', () => {
|
||||
it(' should return undefined for invalid youtube links', () => {
|
||||
expect(getYoutubeVideoId('')).toBeUndefined()
|
||||
expect(getYoutubeVideoId('https://www.google.com')).toBeUndefined()
|
||||
expect(getYoutubeVideoId('https://www.youtube.com')).toBeUndefined()
|
||||
expect(
|
||||
getYoutubeVideoId('https://www.youtube.com/channelName'),
|
||||
).toBeUndefined()
|
||||
expect(
|
||||
getYoutubeVideoId('https://www.youtube.com/channel/channelName'),
|
||||
).toBeUndefined()
|
||||
})
|
||||
|
||||
it('getYoutubeVideoId should return video id for valid youtube links', () => {
|
||||
expect(getYoutubeVideoId('https://www.youtube.com/watch?v=videoId')).toBe(
|
||||
'videoId',
|
||||
)
|
||||
expect(
|
||||
getYoutubeVideoId(
|
||||
'https://www.youtube.com/watch?v=videoId&feature=share',
|
||||
),
|
||||
).toBe('videoId')
|
||||
expect(getYoutubeVideoId('https://youtu.be/videoId')).toBe('videoId')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
getMentionAt,
|
||||
insertMentionAt,
|
||||
} from '../../../src/lib/strings/mention-manip'
|
||||
|
||||
describe('getMentionAt', () => {
|
||||
type Case = [string, number, string | undefined]
|
||||
const cases: Case[] = [
|
||||
['hello @alice goodbye', 0, undefined],
|
||||
['hello @alice goodbye', 1, undefined],
|
||||
['hello @alice goodbye', 2, undefined],
|
||||
['hello @alice goodbye', 3, undefined],
|
||||
['hello @alice goodbye', 4, undefined],
|
||||
['hello @alice goodbye', 5, undefined],
|
||||
['hello @alice goodbye', 6, 'alice'],
|
||||
['hello @alice goodbye', 7, 'alice'],
|
||||
['hello @alice goodbye', 8, 'alice'],
|
||||
['hello @alice goodbye', 9, 'alice'],
|
||||
['hello @alice goodbye', 10, 'alice'],
|
||||
['hello @alice goodbye', 11, 'alice'],
|
||||
['hello @alice goodbye', 12, 'alice'],
|
||||
['hello @alice goodbye', 13, undefined],
|
||||
['hello @alice goodbye', 14, undefined],
|
||||
['@alice', 0, 'alice'],
|
||||
['@alice hello', 0, 'alice'],
|
||||
['@alice hello', 1, 'alice'],
|
||||
['@alice hello', 2, 'alice'],
|
||||
['@alice hello', 3, 'alice'],
|
||||
['@alice hello', 4, 'alice'],
|
||||
['@alice hello', 5, 'alice'],
|
||||
['@alice hello', 6, 'alice'],
|
||||
['@alice hello', 7, undefined],
|
||||
['alice@alice', 0, undefined],
|
||||
['alice@alice', 6, undefined],
|
||||
]
|
||||
|
||||
it.each(cases)(
|
||||
'given input string %p and cursor position %p, returns %p',
|
||||
(str, cursorPos, expected) => {
|
||||
const output = getMentionAt(str, cursorPos)
|
||||
expect(output?.value).toEqual(expected)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
describe('insertMentionAt', () => {
|
||||
type Case = [string, number, string]
|
||||
const cases: Case[] = [
|
||||
['hello @alice goodbye', 0, 'hello @alice goodbye'],
|
||||
['hello @alice goodbye', 1, 'hello @alice goodbye'],
|
||||
['hello @alice goodbye', 2, 'hello @alice goodbye'],
|
||||
['hello @alice goodbye', 3, 'hello @alice goodbye'],
|
||||
['hello @alice goodbye', 4, 'hello @alice goodbye'],
|
||||
['hello @alice goodbye', 5, 'hello @alice goodbye'],
|
||||
['hello @alice goodbye', 6, 'hello @alice.com goodbye'],
|
||||
['hello @alice goodbye', 7, 'hello @alice.com goodbye'],
|
||||
['hello @alice goodbye', 8, 'hello @alice.com goodbye'],
|
||||
['hello @alice goodbye', 9, 'hello @alice.com goodbye'],
|
||||
['hello @alice goodbye', 10, 'hello @alice.com goodbye'],
|
||||
['hello @alice goodbye', 11, 'hello @alice.com goodbye'],
|
||||
['hello @alice goodbye', 12, 'hello @alice.com goodbye'],
|
||||
['hello @alice goodbye', 13, 'hello @alice goodbye'],
|
||||
['hello @alice goodbye', 14, 'hello @alice goodbye'],
|
||||
['@alice', 0, '@alice.com '],
|
||||
['@alice hello', 0, '@alice.com hello'],
|
||||
['@alice hello', 1, '@alice.com hello'],
|
||||
['@alice hello', 2, '@alice.com hello'],
|
||||
['@alice hello', 3, '@alice.com hello'],
|
||||
['@alice hello', 4, '@alice.com hello'],
|
||||
['@alice hello', 5, '@alice.com hello'],
|
||||
['@alice hello', 6, '@alice.com hello'],
|
||||
['@alice hello', 7, '@alice hello'],
|
||||
['alice@alice', 0, 'alice@alice'],
|
||||
['alice@alice', 6, 'alice@alice'],
|
||||
]
|
||||
|
||||
it.each(cases)(
|
||||
'given input string %p and cursor position %p, returns %p',
|
||||
(str, cursorPos, expected) => {
|
||||
const output = insertMentionAt(str, cursorPos, 'alice.com')
|
||||
expect(output).toEqual(expected)
|
||||
},
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,123 @@
|
||||
import {AppBskyFeedPost} from '@atproto/api'
|
||||
type Entity = AppBskyFeedPost.Entity
|
||||
import {RichText} from '../../../src/lib/strings/rich-text'
|
||||
import {removeExcessNewlines} from '../../../src/lib/strings/rich-text-sanitize'
|
||||
|
||||
describe('removeExcessNewlines', () => {
|
||||
it('removes more than two consecutive new lines', () => {
|
||||
const input = new RichText(
|
||||
'test\n\n\n\n\ntest\n\n\n\n\n\n\ntest\n\n\n\n\n\n\ntest\n\n\n\n\n\n\ntest',
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
})
|
||||
|
||||
it('removes more than two consecutive new lines with spaces', () => {
|
||||
const input = new RichText(
|
||||
'test\n\n\n\n\ntest\n \n \n \n \n\n\ntest\n\n\n\n\n\n\ntest\n\n\n\n\n \n\ntest',
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
})
|
||||
|
||||
it('returns original string if there are no consecutive new lines', () => {
|
||||
const input = new RichText('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual(input.text)
|
||||
})
|
||||
|
||||
it('returns original string if there are no new lines', () => {
|
||||
const input = new RichText('test test test test test')
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual(input.text)
|
||||
})
|
||||
|
||||
it('returns empty string if input is empty', () => {
|
||||
const input = new RichText('')
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('')
|
||||
})
|
||||
|
||||
it('works with different types of new line characters', () => {
|
||||
const input = new RichText(
|
||||
'test\r\ntest\n\rtest\rtest\n\n\n\ntest\n\r \n \n \n \n\n\ntest',
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('test\r\ntest\n\rtest\rtest\n\ntest\n\ntest')
|
||||
})
|
||||
|
||||
it('removes more than two consecutive new lines with zero width space', () => {
|
||||
const input = new RichText(
|
||||
'test\n\n\n\n\ntest\n\u200B\u200B\n\n\n\ntest\n \u200B\u200B \n\n\n\ntest\n\n\n\n\n\n\ntest',
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
})
|
||||
|
||||
it('removes more than two consecutive new lines with zero width non-joiner', () => {
|
||||
const input = new RichText(
|
||||
'test\n\n\n\n\ntest\n\u200C\u200C\n\n\n\ntest\n \u200C\u200C \n\n\n\ntest\n\n\n\n\n\n\ntest',
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
})
|
||||
|
||||
it('removes more than two consecutive new lines with zero width joiner', () => {
|
||||
const input = new RichText(
|
||||
'test\n\n\n\n\ntest\n\u200D\u200D\n\n\n\ntest\n \u200D\u200D \n\n\n\ntest\n\n\n\n\n\n\ntest',
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
})
|
||||
|
||||
it('removes more than two consecutive new lines with soft hyphen', () => {
|
||||
const input = new RichText(
|
||||
'test\n\n\n\n\ntest\n\u00AD\u00AD\n\n\n\ntest\n \u00AD\u00AD \n\n\n\ntest\n\n\n\n\n\n\ntest',
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
})
|
||||
|
||||
it('removes more than two consecutive new lines with word joiner', () => {
|
||||
const input = new RichText(
|
||||
'test\n\n\n\n\ntest\n\u2060\u2060\n\n\n\ntest\n \u2060\u2060 \n\n\n\ntest\n\n\n\n\n\n\ntest',
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(output.text).toEqual('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
})
|
||||
})
|
||||
|
||||
describe('removeExcessNewlines w/entities', () => {
|
||||
it('preserves entities as expected', () => {
|
||||
const input = new RichText(
|
||||
'test\n\n\n\n\ntest\n\n\n\n\n\n\ntest\n\n\n\n\n\n\ntest\n\n\n\n\n\n\ntest',
|
||||
[
|
||||
{index: {start: 0, end: 13}, type: '', value: ''},
|
||||
{index: {start: 13, end: 24}, type: '', value: ''},
|
||||
{index: {start: 9, end: 15}, type: '', value: ''},
|
||||
{index: {start: 4, end: 9}, type: '', value: ''},
|
||||
],
|
||||
)
|
||||
const output = removeExcessNewlines(input)
|
||||
expect(entToStr(input.text, input.entities?.[0])).toEqual(
|
||||
'test\n\n\n\n\ntest',
|
||||
)
|
||||
expect(entToStr(input.text, input.entities?.[1])).toEqual(
|
||||
'\n\n\n\n\n\n\ntest',
|
||||
)
|
||||
expect(entToStr(input.text, input.entities?.[2])).toEqual('test\n\n')
|
||||
expect(entToStr(input.text, input.entities?.[3])).toEqual('\n\n\n\n\n')
|
||||
expect(output.text).toEqual('test\n\ntest\n\ntest\n\ntest\n\ntest')
|
||||
expect(entToStr(output.text, output.entities?.[0])).toEqual('test\n\ntest')
|
||||
expect(entToStr(output.text, output.entities?.[1])).toEqual('test')
|
||||
expect(entToStr(output.text, output.entities?.[2])).toEqual('test')
|
||||
expect(output.entities?.[3]).toEqual(undefined)
|
||||
})
|
||||
})
|
||||
|
||||
function entToStr(str: string, ent?: Entity) {
|
||||
if (!ent) {
|
||||
return ''
|
||||
}
|
||||
return str.slice(ent.index.start, ent.index.end)
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import {RichText} from '../../../src/lib/strings/rich-text'
|
||||
|
||||
describe('richText.insert', () => {
|
||||
const input = new RichText('hello world', [
|
||||
{index: {start: 2, end: 7}, type: '', value: ''},
|
||||
])
|
||||
|
||||
it('correctly adjusts entities (scenario A - before)', () => {
|
||||
const output = input.clone().insert(0, 'test')
|
||||
expect(output.text).toEqual('testhello world')
|
||||
expect(output.entities?.[0].index.start).toEqual(6)
|
||||
expect(output.entities?.[0].index.end).toEqual(11)
|
||||
expect(
|
||||
output.text.slice(
|
||||
output.entities?.[0].index.start,
|
||||
output.entities?.[0].index.end,
|
||||
),
|
||||
).toEqual('llo w')
|
||||
})
|
||||
|
||||
it('correctly adjusts entities (scenario B - inner)', () => {
|
||||
const output = input.clone().insert(4, 'test')
|
||||
expect(output.text).toEqual('helltesto world')
|
||||
expect(output.entities?.[0].index.start).toEqual(2)
|
||||
expect(output.entities?.[0].index.end).toEqual(11)
|
||||
expect(
|
||||
output.text.slice(
|
||||
output.entities?.[0].index.start,
|
||||
output.entities?.[0].index.end,
|
||||
),
|
||||
).toEqual('lltesto w')
|
||||
})
|
||||
|
||||
it('correctly adjusts entities (scenario C - after)', () => {
|
||||
const output = input.clone().insert(8, 'test')
|
||||
expect(output.text).toEqual('hello wotestrld')
|
||||
expect(output.entities?.[0].index.start).toEqual(2)
|
||||
expect(output.entities?.[0].index.end).toEqual(7)
|
||||
expect(
|
||||
output.text.slice(
|
||||
output.entities?.[0].index.start,
|
||||
output.entities?.[0].index.end,
|
||||
),
|
||||
).toEqual('llo w')
|
||||
})
|
||||
})
|
||||
|
||||
describe('richText.delete', () => {
|
||||
const input = new RichText('hello world', [
|
||||
{index: {start: 2, end: 7}, type: '', value: ''},
|
||||
])
|
||||
|
||||
it('correctly adjusts entities (scenario A - entirely outer)', () => {
|
||||
const output = input.clone().delete(0, 9)
|
||||
expect(output.text).toEqual('ld')
|
||||
expect(output.entities?.length).toEqual(0)
|
||||
})
|
||||
|
||||
it('correctly adjusts entities (scenario B - entirely after)', () => {
|
||||
const output = input.clone().delete(7, 11)
|
||||
expect(output.text).toEqual('hello w')
|
||||
expect(output.entities?.[0].index.start).toEqual(2)
|
||||
expect(output.entities?.[0].index.end).toEqual(7)
|
||||
expect(
|
||||
output.text.slice(
|
||||
output.entities?.[0].index.start,
|
||||
output.entities?.[0].index.end,
|
||||
),
|
||||
).toEqual('llo w')
|
||||
})
|
||||
|
||||
it('correctly adjusts entities (scenario C - partially after)', () => {
|
||||
const output = input.clone().delete(4, 11)
|
||||
expect(output.text).toEqual('hell')
|
||||
expect(output.entities?.[0].index.start).toEqual(2)
|
||||
expect(output.entities?.[0].index.end).toEqual(4)
|
||||
expect(
|
||||
output.text.slice(
|
||||
output.entities?.[0].index.start,
|
||||
output.entities?.[0].index.end,
|
||||
),
|
||||
).toEqual('ll')
|
||||
})
|
||||
|
||||
it('correctly adjusts entities (scenario D - entirely inner)', () => {
|
||||
const output = input.clone().delete(3, 5)
|
||||
expect(output.text).toEqual('hel world')
|
||||
expect(output.entities?.[0].index.start).toEqual(2)
|
||||
expect(output.entities?.[0].index.end).toEqual(5)
|
||||
expect(
|
||||
output.text.slice(
|
||||
output.entities?.[0].index.start,
|
||||
output.entities?.[0].index.end,
|
||||
),
|
||||
).toEqual('l w')
|
||||
})
|
||||
|
||||
it('correctly adjusts entities (scenario E - partially before)', () => {
|
||||
const output = input.clone().delete(1, 5)
|
||||
expect(output.text).toEqual('h world')
|
||||
expect(output.entities?.[0].index.start).toEqual(1)
|
||||
expect(output.entities?.[0].index.end).toEqual(3)
|
||||
expect(
|
||||
output.text.slice(
|
||||
output.entities?.[0].index.start,
|
||||
output.entities?.[0].index.end,
|
||||
),
|
||||
).toEqual(' w')
|
||||
})
|
||||
|
||||
it('correctly adjusts entities (scenario F - entirely before)', () => {
|
||||
const output = input.clone().delete(0, 2)
|
||||
expect(output.text).toEqual('llo world')
|
||||
expect(output.entities?.[0].index.start).toEqual(0)
|
||||
expect(output.entities?.[0].index.end).toEqual(5)
|
||||
expect(
|
||||
output.text.slice(
|
||||
output.entities?.[0].index.start,
|
||||
output.entities?.[0].index.end,
|
||||
),
|
||||
).toEqual('llo w')
|
||||
})
|
||||
})
|
||||
@@ -1,55 +0,0 @@
|
||||
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||
# To run your application with Buck:
|
||||
# - install Buck
|
||||
# - `npm start` - to start the packager
|
||||
# - `cd android`
|
||||
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||
# - `buck install -r android/app` - compile, install and run application
|
||||
#
|
||||
|
||||
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
|
||||
|
||||
lib_deps = []
|
||||
|
||||
create_aar_targets(glob(["libs/*.aar"]))
|
||||
|
||||
create_jar_targets(glob(["libs/*.jar"]))
|
||||
|
||||
android_library(
|
||||
name = "all-libs",
|
||||
exported_deps = lib_deps,
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = "app-code",
|
||||
srcs = glob([
|
||||
"src/main/java/**/*.java",
|
||||
]),
|
||||
deps = [
|
||||
":all-libs",
|
||||
":build_config",
|
||||
":res",
|
||||
],
|
||||
)
|
||||
|
||||
android_build_config(
|
||||
name = "build_config",
|
||||
package = "xyz.blueskyweb.pubsq",
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = "res",
|
||||
package = "xyz.blueskyweb.pubsq",
|
||||
res = "src/main/res",
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = "app",
|
||||
keystore = "//android/keystores:debug",
|
||||
manifest = "src/main/AndroidManifest.xml",
|
||||
package_type = "debug",
|
||||
deps = [
|
||||
":app-code",
|
||||
],
|
||||
)
|
||||
@@ -1,128 +1,107 @@
|
||||
apply plugin: "com.android.application"
|
||||
apply plugin: "com.facebook.react"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
|
||||
def expoDebuggableVariants = ['debug']
|
||||
// Override `debuggableVariants` for expo-updates debugging
|
||||
if (System.getenv('EX_UPDATES_NATIVE_DEBUG') == "1") {
|
||||
react {
|
||||
expoDebuggableVariants = []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation. If none specified and
|
||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||
* // for example: to disable dev mode in the staging build type (if configured)
|
||||
* devDisabledInStaging: true,
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||
* // 'devDisabledIn${buildType}'
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"],
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
* This is the configuration block to customize your React Native Android app.
|
||||
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
||||
*/
|
||||
react {
|
||||
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
|
||||
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
||||
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
|
||||
debuggableVariants = expoDebuggableVariants
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: false, // clean and rebuild if changing
|
||||
]
|
||||
/* Folders */
|
||||
// The root of your project, i.e. where "package.json" lives. Default is '..'
|
||||
// root = file("../")
|
||||
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
|
||||
// reactNativeDir = file("../node_modules/react-native")
|
||||
// The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen
|
||||
// codegenDir = file("../node_modules/react-native-codegen")
|
||||
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
|
||||
// cliFile = file("../node_modules/react-native/cli.js")
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
/* Variants */
|
||||
// The list of variants to that are debuggable. For those we're going to
|
||||
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
||||
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
||||
// debuggableVariants = ["liteDebug", "prodDebug"]
|
||||
|
||||
/* Bundling */
|
||||
// A list containing the node command and its flags. Default is just 'node'.
|
||||
// nodeExecutableAndArgs = ["node"]
|
||||
//
|
||||
// The command to run when bundling. By default is 'bundle'
|
||||
// bundleCommand = "ram-bundle"
|
||||
//
|
||||
// The path to the CLI configuration file. Default is empty.
|
||||
// bundleConfig = file(../rn-cli.config.js)
|
||||
//
|
||||
// The name of the generated asset file containing your JS bundle
|
||||
// bundleAssetName = "MyApplication.android.bundle"
|
||||
//
|
||||
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
||||
// entryFile = file("../js/MyApplication.android.js")
|
||||
//
|
||||
// A list of extra flags to pass to the 'bundle' commands.
|
||||
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
||||
// extraPackagerArgs = []
|
||||
|
||||
/* Hermes Commands */
|
||||
// The hermes compiler command to run. By default it is 'hermesc'
|
||||
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
||||
//
|
||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||
// hermesFlags = ["-O", "-output-source-map"]
|
||||
}
|
||||
|
||||
// Override `hermesEnabled` by `expo.jsEngine`
|
||||
ext {
|
||||
hermesEnabled = (findProperty('expo.jsEngine') ?: "hermes") == "hermes"
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
* Set this to true to create four separate APKs instead of one,
|
||||
* one for each native architecture. This is useful if you don't
|
||||
* use App Bundles (https://developer.android.com/guide/app-bundle/)
|
||||
* and want to have separate APKs to upload to the Play Store.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean()
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
* The preferred build flavor of JavaScriptCore (JSC)
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and that value will be read here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
|
||||
/**
|
||||
* Architectures to build native code for.
|
||||
* Private function to get the list of Native Architectures you want to build.
|
||||
* This reads the value from reactNativeArchitectures in your gradle.properties
|
||||
* file and works together with the --active-arch-only flag of react-native run-android.
|
||||
*/
|
||||
def reactNativeArchitectures() {
|
||||
def value = project.getProperties().get("reactNativeArchitectures")
|
||||
@@ -134,84 +113,13 @@ android {
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
namespace 'xyz.blueskyweb.app'
|
||||
defaultConfig {
|
||||
applicationId "xyz.blueskyweb.pubsq"
|
||||
applicationId 'xyz.blueskyweb.app'
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// We configure the NDK build only if you decide to opt-in for the New Architecture.
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments "APP_PLATFORM=android-21",
|
||||
"APP_STL=c++_shared",
|
||||
"NDK_TOOLCHAIN_VERSION=clang",
|
||||
"GENERATED_SRC_DIR=$buildDir/generated/source",
|
||||
"PROJECT_BUILD_DIR=$buildDir",
|
||||
"REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
|
||||
"REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build"
|
||||
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
|
||||
cppFlags "-std=c++17"
|
||||
// Make sure this target name is the same you specify inside the
|
||||
// src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
|
||||
targets "app_appmodules"
|
||||
|
||||
// Fix for windows limit on number of character in file paths and in command lines
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
arguments "NDK_APP_SHORT_COMMANDS=true"
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!enableSeparateBuildPerCPUArchitecture) {
|
||||
ndk {
|
||||
abiFilters (*reactNativeArchitectures())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// We configure the NDK build only if you decide to opt-in for the New Architecture.
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path "$projectDir/src/main/jni/Android.mk"
|
||||
}
|
||||
}
|
||||
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
|
||||
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
|
||||
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
|
||||
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
||||
into("$buildDir/react-ndk/exported")
|
||||
}
|
||||
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
|
||||
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
|
||||
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
||||
into("$buildDir/react-ndk/exported")
|
||||
}
|
||||
afterEvaluate {
|
||||
// If you wish to add a custom TurboModule or component locally,
|
||||
// you should uncomment this line.
|
||||
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
|
||||
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
|
||||
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
|
||||
|
||||
// Due to a bug inside AGP, we have to explicitly set a dependency
|
||||
// between configureNdkBuild* tasks and the preBuild tasks.
|
||||
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
|
||||
configureNdkBuildRelease.dependsOn(preReleaseBuild)
|
||||
configureNdkBuildDebug.dependsOn(preDebugBuild)
|
||||
reactNativeArchitectures().each { architecture ->
|
||||
tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
|
||||
dependsOn("preDebugBuild")
|
||||
}
|
||||
tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
|
||||
dependsOn("preReleaseBuild")
|
||||
}
|
||||
}
|
||||
}
|
||||
versionName "1.0.0"
|
||||
}
|
||||
|
||||
splits {
|
||||
@@ -260,61 +168,69 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply static values from `gradle.properties` to the `android.packagingOptions`
|
||||
// Accepts values in comma delimited lists, example:
|
||||
// android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini
|
||||
["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop ->
|
||||
// Split option: 'foo,bar' -> ['foo', 'bar']
|
||||
def options = (findProperty("android.packagingOptions.$prop") ?: "").split(",");
|
||||
// Trim all elements in place.
|
||||
for (i in 0..<options.size()) options[i] = options[i].trim();
|
||||
// `[] - ""` is essentially `[""].filter(Boolean)` removing all empty strings.
|
||||
options -= ""
|
||||
|
||||
if (options.length > 0) {
|
||||
println "android.packagingOptions.$prop += $options ($options.length)"
|
||||
// Ex: android.packagingOptions.pickFirsts += '**/SCCS/**'
|
||||
options.each {
|
||||
android.packagingOptions[prop] += it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
|
||||
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
|
||||
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
|
||||
def frescoVersion = rootProject.ext.frescoVersion
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
// If your app supports Android versions before Ice Cream Sandwich (API level 14)
|
||||
if (isGifEnabled || isWebpEnabled) {
|
||||
implementation("com.facebook.fresco:fresco:${frescoVersion}")
|
||||
implementation("com.facebook.fresco:imagepipeline-okhttp3:${frescoVersion}")
|
||||
}
|
||||
|
||||
if (isGifEnabled) {
|
||||
// For animated gif support
|
||||
implementation("com.facebook.fresco:animated-gif:${frescoVersion}")
|
||||
}
|
||||
|
||||
if (isWebpEnabled) {
|
||||
// For webp support
|
||||
implementation("com.facebook.fresco:webpsupport:${frescoVersion}")
|
||||
if (isWebpAnimatedEnabled) {
|
||||
// Animated webp support
|
||||
implementation("com.facebook.fresco:animated-webp:${frescoVersion}")
|
||||
}
|
||||
}
|
||||
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
|
||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||
}
|
||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
|
||||
if (enableHermes) {
|
||||
def hermesPath = "../../node_modules/hermes-engine/android/";
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// If new architecture is enabled, we let you build RN from source
|
||||
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
|
||||
// This will be applied to all the imported transtitive dependency.
|
||||
configurations.all {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(module("com.facebook.react:react-native"))
|
||||
.using(project(":ReactAndroid")).because("On New Architecture we're building React Native from source")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.implementation
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
def isNewArchitectureEnabled() {
|
||||
// To opt-in for the New Architecture, you can either:
|
||||
// - Set `newArchEnabled` to true inside the `gradle.properties` file
|
||||
// - Invoke gradle with `-newArchEnabled=true`
|
||||
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
|
||||
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
||||
}
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
|
||||
applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
"""Helper definitions to glob .aar and .jar targets"""
|
||||
|
||||
def create_aar_targets(aarfiles):
|
||||
for aarfile in aarfiles:
|
||||
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
|
||||
lib_deps.append(":" + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
def create_jar_targets(jarfiles):
|
||||
for jarfile in jarfiles:
|
||||
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
|
||||
lib_deps.append(":" + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
||||
@@ -8,3 +8,7 @@
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# react-native-reanimated
|
||||
-keep class com.swmansion.reanimated.** { *; }
|
||||
-keep class com.facebook.react.turbomodule.** { *; }
|
||||
@@ -1,13 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="28"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
|
||||
</application>
|
||||
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
|
||||
</manifest>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package com.app;
|
||||
package xyz.blueskyweb.app;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
@@ -17,7 +17,6 @@ import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||
import com.facebook.react.ReactInstanceEventListener;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
@@ -25,13 +24,16 @@ import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
/**
|
||||
* Class responsible of loading Flipper inside your React Native application. This is the debug
|
||||
* flavor of it. Here you can add your own plugins and customize the Flipper setup.
|
||||
*/
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||
|
||||
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||
client.addPlugin(new ReactFlipperPlugin());
|
||||
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||
@@ -1,44 +1,35 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="xyz.blueskyweb.pubsq">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<!-- deep linking via web links -->
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<!-- debug host: https://pubsq.pfrazee.com -->
|
||||
<data android:scheme="https" />
|
||||
<data android:host="pubsq.pfrazee.com" />
|
||||
</intent-filter>
|
||||
<!-- deep linking via custom links -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<!-- debug scheme: pubsq://app -->
|
||||
<data android:scheme="pubsq" />
|
||||
<data android:host="app" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="xyz.blueskyweb.app">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="https"/>
|
||||
</intent>
|
||||
</queries>
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">
|
||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="48.0.0"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@arrygoo/bluesky"/> <!-- TODO -->
|
||||
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="xyz.blueskyweb.app"/>
|
||||
<data android:scheme="exp+bluesky"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,68 @@
|
||||
package xyz.blueskyweb.app;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
||||
|
||||
import expo.modules.ReactActivityDelegateWrapper;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// Set the theme to AppTheme BEFORE onCreate to support
|
||||
// coloring the background, status bar, and navigation bar.
|
||||
// This is required for expo-splash-screen.
|
||||
setTheme(R.style.AppTheme);
|
||||
super.onCreate(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "main";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
|
||||
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
|
||||
* (aka React 18) with two boolean flags.
|
||||
*/
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, new DefaultReactActivityDelegate(
|
||||
this,
|
||||
getMainComponentName(),
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
|
||||
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
|
||||
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the back button behavior with Android S
|
||||
* where moving root activities to background instead of finishing activities.
|
||||
* @see <a href="https://developer.android.com/reference/android/app/Activity#onBackPressed()">onBackPressed</a>
|
||||
*/
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
|
||||
if (!moveTaskToBack(false)) {
|
||||
// For non-root activities, use the default implementation to finish them.
|
||||
super.invokeDefaultOnBackPressed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the default back button implementation on Android S
|
||||
// because it's doing more than {@link Activity#moveTaskToBack} in fact.
|
||||
super.invokeDefaultOnBackPressed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package xyz.blueskyweb.app;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.res.Configuration;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import expo.modules.ApplicationLifecycleDispatcher;
|
||||
import expo.modules.ReactNativeHostWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNewArchEnabled() {
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean isHermesEnabled() {
|
||||
return BuildConfig.IS_HERMES_ENABLED;
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package xyz.blueskyweb.pubsq;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import android.util.Base64;
|
||||
|
||||
public class AppSecureRandomModule extends ReactContextBaseJavaModule {
|
||||
public AppSecureRandomModule(ReactApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void generateSecureRandomAsBase64(int length, Promise promise) {
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
byte[] buffer = new byte[length];
|
||||
secureRandom.nextBytes(buffer);
|
||||
promise.resolve(Base64.encodeToString(buffer, Base64.NO_WRAP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "AppSecureRandomModule";
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package xyz.blueskyweb.pubsq;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AppSecureRandomPackage implements ReactPackage {
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
modules.add(new AppSecureRandomModule(reactContext));
|
||||
return modules;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package xyz.blueskyweb.pubsq;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
||||
* rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "xyz.blueskyweb.pubsq";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
|
||||
* you can specify the rendered you wish to use (Fabric or the older renderer).
|
||||
*/
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new MainActivityDelegate(this, getMainComponentName());
|
||||
}
|
||||
|
||||
public static class MainActivityDelegate extends ReactActivityDelegate {
|
||||
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
|
||||
super(activity, mainComponentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactRootView createRootView() {
|
||||
ReactRootView reactRootView = new ReactRootView(getContext());
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
|
||||
return reactRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package xyz.blueskyweb.pubsq;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import xyz.blueskyweb.pubsq.newarchitecture.MainApplicationReactNativeHost;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
new ReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
packages.add(new AppSecureRandomPackage());
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
};
|
||||
|
||||
private final ReactNativeHost mNewArchitectureNativeHost =
|
||||
new MainApplicationReactNativeHost(this);
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
return mNewArchitectureNativeHost;
|
||||
} else {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// If you opted-in for the New Architecture, we enable the TurboModule system
|
||||
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
||||
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
*
|
||||
* @param context
|
||||
* @param reactInstanceManager
|
||||
*/
|
||||
private static void initializeFlipper(
|
||||
Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
/*
|
||||
We use reflection here to pick up the class that initializes Flipper,
|
||||
since Flipper library is not available in release mode
|
||||
*/
|
||||
Class<?> aClass = Class.forName("com.app.ReactNativeFlipper");
|
||||
aClass
|
||||
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
|
||||
.invoke(null, context, reactInstanceManager);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package xyz.blueskyweb.pubsq.newarchitecture;
|
||||
|
||||
import android.app.Application;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
|
||||
import com.facebook.react.bridge.JSIModulePackage;
|
||||
import com.facebook.react.bridge.JSIModuleProvider;
|
||||
import com.facebook.react.bridge.JSIModuleSpec;
|
||||
import com.facebook.react.bridge.JSIModuleType;
|
||||
import com.facebook.react.bridge.JavaScriptContextHolder;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.UIManager;
|
||||
import com.facebook.react.fabric.ComponentFactory;
|
||||
import com.facebook.react.fabric.CoreComponentsRegistry;
|
||||
import com.facebook.react.fabric.EmptyReactNativeConfig;
|
||||
import com.facebook.react.fabric.FabricJSIModuleProvider;
|
||||
import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||
import xyz.blueskyweb.pubsq.BuildConfig;
|
||||
import xyz.blueskyweb.pubsq.newarchitecture.components.MainComponentsRegistry;
|
||||
import xyz.blueskyweb.pubsq.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
|
||||
* TurboModule delegates and the Fabric Renderer.
|
||||
*
|
||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
||||
* `newArchEnabled` property). Is ignored otherwise.
|
||||
*/
|
||||
public class MainApplicationReactNativeHost extends ReactNativeHost {
|
||||
public MainApplicationReactNativeHost(Application application) {
|
||||
super(application);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
// TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
|
||||
// packages.add(new TurboReactPackage() { ... });
|
||||
// If you have custom Fabric Components, their ViewManagers should also be loaded here
|
||||
// inside a ReactPackage.
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ReactPackageTurboModuleManagerDelegate.Builder
|
||||
getReactPackageTurboModuleManagerDelegateBuilder() {
|
||||
// Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
|
||||
// for the new architecture and to use TurboModules correctly.
|
||||
return new MainApplicationTurboModuleManagerDelegate.Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSIModulePackage getJSIModulePackage() {
|
||||
return new JSIModulePackage() {
|
||||
@Override
|
||||
public List<JSIModuleSpec> getJSIModules(
|
||||
final ReactApplicationContext reactApplicationContext,
|
||||
final JavaScriptContextHolder jsContext) {
|
||||
final List<JSIModuleSpec> specs = new ArrayList<>();
|
||||
|
||||
// Here we provide a new JSIModuleSpec that will be responsible of providing the
|
||||
// custom Fabric Components.
|
||||
specs.add(
|
||||
new JSIModuleSpec() {
|
||||
@Override
|
||||
public JSIModuleType getJSIModuleType() {
|
||||
return JSIModuleType.UIManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
|
||||
final ComponentFactory componentFactory = new ComponentFactory();
|
||||
CoreComponentsRegistry.register(componentFactory);
|
||||
|
||||
// Here we register a Components Registry.
|
||||
// The one that is generated with the template contains no components
|
||||
// and just provides you the one from React Native core.
|
||||
MainComponentsRegistry.register(componentFactory);
|
||||
|
||||
final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
|
||||
|
||||
ViewManagerRegistry viewManagerRegistry =
|
||||
new ViewManagerRegistry(
|
||||
reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
|
||||
|
||||
return new FabricJSIModuleProvider(
|
||||
reactApplicationContext,
|
||||
componentFactory,
|
||||
new EmptyReactNativeConfig(),
|
||||
viewManagerRegistry);
|
||||
}
|
||||
});
|
||||
return specs;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package xyz.blueskyweb.pubsq.newarchitecture.components;
|
||||
|
||||
import com.facebook.jni.HybridData;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.fabric.ComponentFactory;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
/**
|
||||
* Class responsible to load the custom Fabric Components. This class has native methods and needs a
|
||||
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
|
||||
* folder for you).
|
||||
*
|
||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
||||
* `newArchEnabled` property). Is ignored otherwise.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class MainComponentsRegistry {
|
||||
static {
|
||||
SoLoader.loadLibrary("fabricjni");
|
||||
}
|
||||
|
||||
@DoNotStrip private final HybridData mHybridData;
|
||||
|
||||
@DoNotStrip
|
||||
private native HybridData initHybrid(ComponentFactory componentFactory);
|
||||
|
||||
@DoNotStrip
|
||||
private MainComponentsRegistry(ComponentFactory componentFactory) {
|
||||
mHybridData = initHybrid(componentFactory);
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
public static MainComponentsRegistry register(ComponentFactory componentFactory) {
|
||||
return new MainComponentsRegistry(componentFactory);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package xyz.blueskyweb.pubsq.newarchitecture.modules;
|
||||
|
||||
import com.facebook.jni.HybridData;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class responsible to load the TurboModules. This class has native methods and needs a
|
||||
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
|
||||
* folder for you).
|
||||
*
|
||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
||||
* `newArchEnabled` property). Is ignored otherwise.
|
||||
*/
|
||||
public class MainApplicationTurboModuleManagerDelegate
|
||||
extends ReactPackageTurboModuleManagerDelegate {
|
||||
|
||||
private static volatile boolean sIsSoLibraryLoaded;
|
||||
|
||||
protected MainApplicationTurboModuleManagerDelegate(
|
||||
ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
|
||||
super(reactApplicationContext, packages);
|
||||
}
|
||||
|
||||
protected native HybridData initHybrid();
|
||||
|
||||
native boolean canCreateTurboModule(String moduleName);
|
||||
|
||||
public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
|
||||
protected MainApplicationTurboModuleManagerDelegate build(
|
||||
ReactApplicationContext context, List<ReactPackage> packages) {
|
||||
return new MainApplicationTurboModuleManagerDelegate(context, packages);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void maybeLoadOtherSoLibraries() {
|
||||
if (!sIsSoLibraryLoaded) {
|
||||
// If you change the name of your application .so file in the Android.mk file,
|
||||
// make sure you update the name here as well.
|
||||
SoLoader.loadLibrary("app_appmodules");
|
||||
sIsSoLibraryLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
THIS_DIR := $(call my-dir)
|
||||
|
||||
include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
|
||||
|
||||
# If you wish to add a custom TurboModule or Fabric component in your app you
|
||||
# will have to include the following autogenerated makefile.
|
||||
# include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_PATH := $(THIS_DIR)
|
||||
|
||||
# You can customize the name of your application .so file here.
|
||||
LOCAL_MODULE := app_appmodules
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
||||
|
||||
# If you wish to add a custom TurboModule or Fabric component in your app you
|
||||
# will have to uncomment those lines to include the generated source
|
||||
# files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni)
|
||||
#
|
||||
# LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
|
||||
# LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
|
||||
# LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
|
||||
|
||||
# Here you should add any native library you wish to depend on.
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libfabricjni \
|
||||
libfbjni \
|
||||
libfolly_futures \
|
||||
libfolly_json \
|
||||
libglog \
|
||||
libjsi \
|
||||
libreact_codegen_rncore \
|
||||
libreact_debug \
|
||||
libreact_nativemodule_core \
|
||||
libreact_render_componentregistry \
|
||||
libreact_render_core \
|
||||
libreact_render_debug \
|
||||
libreact_render_graphics \
|
||||
librrc_view \
|
||||
libruntimeexecutor \
|
||||
libturbomodulejsijni \
|
||||
libyoga
|
||||
|
||||
LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -1,24 +0,0 @@
|
||||
#include "MainApplicationModuleProvider.h"
|
||||
|
||||
#include <rncore.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
|
||||
const std::string moduleName,
|
||||
const JavaTurboModule::InitParams ¶ms) {
|
||||
// Here you can provide your own module provider for TurboModules coming from
|
||||
// either your application or from external libraries. The approach to follow
|
||||
// is similar to the following (for a library called `samplelibrary`:
|
||||
//
|
||||
// auto module = samplelibrary_ModuleProvider(moduleName, params);
|
||||
// if (module != nullptr) {
|
||||
// return module;
|
||||
// }
|
||||
// return rncore_ModuleProvider(moduleName, params);
|
||||
return rncore_ModuleProvider(moduleName, params);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <ReactCommon/JavaTurboModule.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
|
||||
const std::string moduleName,
|
||||
const JavaTurboModule::InitParams ¶ms);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@@ -1,45 +0,0 @@
|
||||
#include "MainApplicationTurboModuleManagerDelegate.h"
|
||||
#include "MainApplicationModuleProvider.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
jni::local_ref<MainApplicationTurboModuleManagerDelegate::jhybriddata>
|
||||
MainApplicationTurboModuleManagerDelegate::initHybrid(
|
||||
jni::alias_ref<jhybridobject>) {
|
||||
return makeCxxInstance();
|
||||
}
|
||||
|
||||
void MainApplicationTurboModuleManagerDelegate::registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod(
|
||||
"initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
|
||||
makeNativeMethod(
|
||||
"canCreateTurboModule",
|
||||
MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule>
|
||||
MainApplicationTurboModuleManagerDelegate::getTurboModule(
|
||||
const std::string name,
|
||||
const std::shared_ptr<CallInvoker> jsInvoker) {
|
||||
// Not implemented yet: provide pure-C++ NativeModules here.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule>
|
||||
MainApplicationTurboModuleManagerDelegate::getTurboModule(
|
||||
const std::string name,
|
||||
const JavaTurboModule::InitParams ¶ms) {
|
||||
return MainApplicationModuleProvider(name, params);
|
||||
}
|
||||
|
||||
bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
|
||||
std::string name) {
|
||||
return getTurboModule(name, nullptr) != nullptr ||
|
||||
getTurboModule(name, {.moduleName = name}) != nullptr;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@@ -1,38 +0,0 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <ReactCommon/TurboModuleManagerDelegate.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class MainApplicationTurboModuleManagerDelegate
|
||||
: public jni::HybridClass<
|
||||
MainApplicationTurboModuleManagerDelegate,
|
||||
TurboModuleManagerDelegate> {
|
||||
public:
|
||||
// Adapt it to the package you used for your Java class.
|
||||
static constexpr auto kJavaDescriptor =
|
||||
"Lcom/app/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
|
||||
|
||||
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
|
||||
|
||||
static void registerNatives();
|
||||
|
||||
std::shared_ptr<TurboModule> getTurboModule(
|
||||
const std::string name,
|
||||
const std::shared_ptr<CallInvoker> jsInvoker) override;
|
||||
std::shared_ptr<TurboModule> getTurboModule(
|
||||
const std::string name,
|
||||
const JavaTurboModule::InitParams ¶ms) override;
|
||||
|
||||
/**
|
||||
* Test-only method. Allows user to verify whether a TurboModule can be
|
||||
* created by instances of this class.
|
||||
*/
|
||||
bool canCreateTurboModule(std::string name);
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@@ -1,61 +0,0 @@
|
||||
#include "MainComponentsRegistry.h"
|
||||
|
||||
#include <CoreComponentsRegistry.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
||||
#include <react/renderer/components/rncore/ComponentDescriptors.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
|
||||
|
||||
std::shared_ptr<ComponentDescriptorProviderRegistry const>
|
||||
MainComponentsRegistry::sharedProviderRegistry() {
|
||||
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
|
||||
|
||||
// Custom Fabric Components go here. You can register custom
|
||||
// components coming from your App or from 3rd party libraries here.
|
||||
//
|
||||
// providerRegistry->add(concreteComponentDescriptorProvider<
|
||||
// AocViewerComponentDescriptor>());
|
||||
return providerRegistry;
|
||||
}
|
||||
|
||||
jni::local_ref<MainComponentsRegistry::jhybriddata>
|
||||
MainComponentsRegistry::initHybrid(
|
||||
jni::alias_ref<jclass>,
|
||||
ComponentFactory *delegate) {
|
||||
auto instance = makeCxxInstance(delegate);
|
||||
|
||||
auto buildRegistryFunction =
|
||||
[](EventDispatcher::Weak const &eventDispatcher,
|
||||
ContextContainer::Shared const &contextContainer)
|
||||
-> ComponentDescriptorRegistry::Shared {
|
||||
auto registry = MainComponentsRegistry::sharedProviderRegistry()
|
||||
->createComponentDescriptorRegistry(
|
||||
{eventDispatcher, contextContainer});
|
||||
|
||||
auto mutableRegistry =
|
||||
std::const_pointer_cast<ComponentDescriptorRegistry>(registry);
|
||||
|
||||
mutableRegistry->setFallbackComponentDescriptor(
|
||||
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
|
||||
ComponentDescriptorParameters{
|
||||
eventDispatcher, contextContainer, nullptr}));
|
||||
|
||||
return registry;
|
||||
};
|
||||
|
||||
delegate->buildRegistryFunction = buildRegistryFunction;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void MainComponentsRegistry::registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <ComponentFactory.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class MainComponentsRegistry
|
||||
: public facebook::jni::HybridClass<MainComponentsRegistry> {
|
||||
public:
|
||||
// Adapt it to the package you used for your Java class.
|
||||
constexpr static auto kJavaDescriptor =
|
||||
"Lcom/app/newarchitecture/components/MainComponentsRegistry;";
|
||||
|
||||
static void registerNatives();
|
||||
|
||||
MainComponentsRegistry(ComponentFactory *delegate);
|
||||
|
||||
private:
|
||||
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
|
||||
sharedProviderRegistry();
|
||||
|
||||
static jni::local_ref<jhybriddata> initHybrid(
|
||||
jni::alias_ref<jclass>,
|
||||
ComponentFactory *delegate);
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@@ -1,11 +0,0 @@
|
||||
#include <fbjni/fbjni.h>
|
||||
#include "MainApplicationTurboModuleManagerDelegate.h"
|
||||
#include "MainComponentsRegistry.h"
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
|
||||
return facebook::jni::initialize(vm, [] {
|
||||
facebook::react::MainApplicationTurboModuleManagerDelegate::
|
||||
registerNatives();
|
||||
facebook::react::MainComponentsRegistry::registerNatives();
|
||||
});
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,3 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">pubsq</string>
|
||||
<string name="app_name">bsky</string>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package xyz.blueskyweb.app;
|
||||
import android.content.Context;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
/**
|
||||
* Class responsible of loading Flipper inside your React Native application. This is the release
|
||||
* flavor of it so it's empty as we don't want to load Flipper.
|
||||
*/
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
// Do nothing as we don't want to initialize Flipper on Release.
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 31
|
||||
targetSdkVersion = 31
|
||||
|
||||
if (System.properties['os.arch'] == "aarch64") {
|
||||
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
||||
ndkVersion = "24.0.8215888"
|
||||
} else {
|
||||
// Otherwise we default to the side-by-side NDK version from AGP.
|
||||
ndkVersion = "21.4.7075529"
|
||||
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '33.0.0'
|
||||
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '21')
|
||||
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '33')
|
||||
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '33')
|
||||
if (findProperty('android.kotlinVersion')) {
|
||||
kotlinVersion = findProperty('android.kotlinVersion')
|
||||
}
|
||||
frescoVersion = findProperty('expo.frescoVersion') ?: '2.5.0'
|
||||
|
||||
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
||||
ndkVersion = "23.1.7779620"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath('com.android.tools.build:gradle:7.2.1')
|
||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||
classpath("de.undercouch:gradle-download-task:4.1.2")
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath('com.android.tools.build:gradle:7.3.1')
|
||||
classpath('com.facebook.react:react-native-gradle-plugin')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,20 +28,15 @@ allprojects {
|
||||
repositories {
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url("$rootDir/../node_modules/react-native/android")
|
||||
url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url("$rootDir/../node_modules/jsc-android/dist")
|
||||
}
|
||||
mavenCentral {
|
||||
// We don't want to fetch react-native from Maven Central as there are
|
||||
// older versions over there.
|
||||
content {
|
||||
excludeGroup "com.facebook.react"
|
||||
}
|
||||
url(new File(['node', '--print', "require.resolve('jsc-android/package.json')"].execute(null, rootDir).text.trim(), '../dist'))
|
||||
}
|
||||
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
|
||||
@@ -38,3 +39,15 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
# to write custom TurboModules/Fabric components OR use libraries that
|
||||
# are providing them.
|
||||
newArchEnabled=false
|
||||
|
||||
# The hosted JavaScript engine
|
||||
# Supported values: expo.jsEngine = "hermes" | "jsc"
|
||||
expo.jsEngine=hermes
|
||||
|
||||
# Enable GIF support in React Native images (~200 B increase)
|
||||
expo.gif.enabled=true
|
||||
# Enable webp support in React Native images (~85 KB increase)
|
||||
expo.webp.enabled=true
|
||||
# Enable animated webp support (~3.4 MB increase)
|
||||
# Disabled by default because iOS doesn't support animated webp
|
||||
expo.webp.animated=false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -205,6 +205,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,7 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
rootProject.name = 'app'
|
||||
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||
include ':app'
|
||||
includeBuild('../node_modules/react-native-gradle-plugin')
|
||||
rootProject.name = 'bluesky'
|
||||
|
||||
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
|
||||
include(":ReactAndroid")
|
||||
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
|
||||
}
|
||||
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
|
||||
useExpoModules()
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
|
||||
applyNativeModulesSettingsGradle(settings)
|
||||
|
||||
include ':app'
|
||||
includeBuild(new File(["node", "--print", "require.resolve('react-native-gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile())
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "bluesky",
|
||||
"slug": "bluesky",
|
||||
"version": "1.8.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"userInterfaceStyle": "light",
|
||||
"splash": {
|
||||
"image": "./assets/cloud-splash.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"ios": {
|
||||
"supportsTablet": false,
|
||||
"bundleIdentifier": "xyz.blueskyweb.app"
|
||||
},
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"package": "xyz.blueskyweb.app"
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 696 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 78 KiB |
@@ -1,19 +1,34 @@
|
||||
module.exports = {
|
||||
presets: ['module:metro-react-native-babel-preset'],
|
||||
plugins: [
|
||||
[
|
||||
'module:react-native-dotenv',
|
||||
{
|
||||
// envName: 'APP_ENV',
|
||||
moduleName: '@env',
|
||||
path: '.env',
|
||||
blocklist: null,
|
||||
allowlist: null,
|
||||
safe: false,
|
||||
allowUndefined: true,
|
||||
verbose: false,
|
||||
},
|
||||
module.exports = function (api) {
|
||||
api.cache(true)
|
||||
return {
|
||||
presets: ['babel-preset-expo'],
|
||||
plugins: [
|
||||
[
|
||||
'module:react-native-dotenv',
|
||||
{
|
||||
envName: 'APP_ENV',
|
||||
moduleName: '@env',
|
||||
path: '.env',
|
||||
blocklist: null,
|
||||
allowlist: null,
|
||||
safe: false,
|
||||
allowUndefined: true,
|
||||
verbose: false,
|
||||
},
|
||||
],
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
alias: {
|
||||
// This needs to be mirrored in tsconfig.json
|
||||
lib: './src/lib',
|
||||
platform: './src/platform',
|
||||
state: './src/state',
|
||||
view: './src/view',
|
||||
},
|
||||
},
|
||||
],
|
||||
'react-native-reanimated/plugin', // NOTE: this plugin MUST be last
|
||||
],
|
||||
'react-native-reanimated/plugin', // NOTE: this plugin MUST be last
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
/static/bundle.web.js
|
||||
/bskyweb
|
||||
.env
|
||||
@@ -0,0 +1,38 @@
|
||||
## Build / Develop
|
||||
|
||||
### SPA Bundle (monolithic static javascript file)
|
||||
|
||||
To build the SPA bundle (`bundle.web.js`), first get a Javascript development
|
||||
environment set up. Either follow the top-level README, or something quick
|
||||
like:
|
||||
|
||||
# install nodejs 18 (specifically)
|
||||
nvm install 18
|
||||
nvm use 18
|
||||
npm install --global yarn
|
||||
|
||||
# setup tools and deps (in top level of this repo)
|
||||
yarn install --frozen-lockfile
|
||||
|
||||
# run yarn web dev server, if you wanted
|
||||
yarn web
|
||||
|
||||
Then build and copy over the big 'ol `bundle.web.js` file:
|
||||
|
||||
# in the top level of this repo
|
||||
yarn build-web
|
||||
|
||||
### Golang Daemon
|
||||
|
||||
Install golang. We are generally using v1.19+.
|
||||
|
||||
In this directory (`bskyweb/`):
|
||||
|
||||
# re-build and run daemon
|
||||
go run ./cmd/bskyweb serve
|
||||
|
||||
# build and output a binary
|
||||
go build -o bskyweb ./cmd/bskyweb/
|
||||
|
||||
The easiest way to configure the daemon is to copy `example.env` to `.env` and
|
||||
fill in auth values there.
|
||||
@@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var log = logging.Logger("bskyweb")
|
||||
|
||||
func init() {
|
||||
logging.SetAllLoggers(logging.LevelDebug)
|
||||
//logging.SetAllLoggers(logging.LevelWarn)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// only try dotenv if it exists
|
||||
if _, err := os.Stat(".env"); err == nil {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
}
|
||||
|
||||
run(os.Args)
|
||||
}
|
||||
|
||||
func run(args []string) {
|
||||
|
||||
app := cli.App{
|
||||
Name: "bskyweb",
|
||||
Usage: "web server for bsky.app web app (SPA)",
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "pds-host",
|
||||
Usage: "method, hostname, and port of PDS instance",
|
||||
Value: "http://localhost:4849",
|
||||
EnvVars: []string{"ATP_PDS_HOST"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "handle",
|
||||
Usage: "for PDS login",
|
||||
Required: true,
|
||||
EnvVars: []string{"ATP_AUTH_HANDLE"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "for PDS login",
|
||||
Required: true,
|
||||
EnvVars: []string{"ATP_AUTH_PASSWORD"},
|
||||
},
|
||||
// TODO: local IP/port to bind on
|
||||
}
|
||||
|
||||
app.Commands = []*cli.Command{
|
||||
&cli.Command{
|
||||
Name: "serve",
|
||||
Usage: "run the server",
|
||||
Action: serve,
|
||||
},
|
||||
}
|
||||
app.RunAndExitOnError()
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
comatproto "github.com/bluesky-social/indigo/api/atproto"
|
||||
appbsky "github.com/bluesky-social/indigo/api/bsky"
|
||||
cliutil "github.com/bluesky-social/indigo/cmd/gosky/util"
|
||||
"github.com/bluesky-social/indigo/xrpc"
|
||||
|
||||
"github.com/flosch/pongo2/v6"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// TODO: embed templates in executable
|
||||
|
||||
type Renderer struct {
|
||||
Debug bool
|
||||
}
|
||||
|
||||
func (r Renderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
|
||||
var ctx pongo2.Context
|
||||
|
||||
if data != nil {
|
||||
var ok bool
|
||||
ctx, ok = data.(pongo2.Context)
|
||||
|
||||
if !ok {
|
||||
return errors.New("no pongo2.Context data was passed...")
|
||||
}
|
||||
}
|
||||
|
||||
var t *pongo2.Template
|
||||
var err error
|
||||
|
||||
if r.Debug {
|
||||
t, err = pongo2.FromFile(name)
|
||||
} else {
|
||||
t, err = pongo2.FromCache(name)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.ExecuteWriter(ctx, w)
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
xrpcc *xrpc.Client
|
||||
}
|
||||
|
||||
func serve(cctx *cli.Context) error {
|
||||
|
||||
// create a new session
|
||||
// TODO: does this work with no auth at all?
|
||||
xrpcc := &xrpc.Client{
|
||||
Client: cliutil.NewHttpClient(),
|
||||
Host: cctx.String("pds-host"),
|
||||
Auth: &xrpc.AuthInfo{
|
||||
Handle: cctx.String("handle"),
|
||||
},
|
||||
}
|
||||
|
||||
auth, err := comatproto.SessionCreate(context.TODO(), xrpcc, &comatproto.SessionCreate_Input{
|
||||
Identifier: &xrpcc.Auth.Handle,
|
||||
Password: cctx.String("password"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
xrpcc.Auth.AccessJwt = auth.AccessJwt
|
||||
xrpcc.Auth.RefreshJwt = auth.RefreshJwt
|
||||
xrpcc.Auth.Did = auth.Did
|
||||
xrpcc.Auth.Handle = auth.Handle
|
||||
|
||||
server := Server{xrpcc}
|
||||
|
||||
e := echo.New()
|
||||
e.HideBanner = true
|
||||
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
||||
Format: "method=${method} path=${uri} status=${status} latency=${latency_human}\n",
|
||||
}))
|
||||
e.Renderer = Renderer{Debug: true}
|
||||
e.HTTPErrorHandler = customHTTPErrorHandler
|
||||
|
||||
// configure routes
|
||||
e.File("/robots.txt", "static/robots.txt")
|
||||
e.Static("/static", "static")
|
||||
e.Static("/static/js", "../web-build/static/js")
|
||||
|
||||
e.GET("/", server.WebHome)
|
||||
|
||||
// generic routes
|
||||
e.GET("/search", server.WebGeneric)
|
||||
e.GET("/notifications", server.WebGeneric)
|
||||
e.GET("/settings", server.WebGeneric)
|
||||
e.GET("/sys/debug", server.WebGeneric)
|
||||
e.GET("/sys/log", server.WebGeneric)
|
||||
e.GET("/support", server.WebGeneric)
|
||||
e.GET("/support/privacy", server.WebGeneric)
|
||||
|
||||
// profile endpoints; only first populates info
|
||||
e.GET("/profile/:handle", server.WebProfile)
|
||||
e.GET("/profile/:handle/follows", server.WebGeneric)
|
||||
e.GET("/profile/:handle/followers", server.WebGeneric)
|
||||
|
||||
// post endpoints; only first populates info
|
||||
e.GET("/profile/:handle/post/:rkey", server.WebPost)
|
||||
e.GET("/profile/:handle/post/:rkey/upvoted-by", server.WebGeneric)
|
||||
e.GET("/profile/:handle/post/:rkey/downvoted-by", server.WebGeneric)
|
||||
e.GET("/profile/:handle/post/:rkey/reposted-by", server.WebGeneric)
|
||||
|
||||
bind := "localhost:8100"
|
||||
log.Infof("starting server bind=%s", bind)
|
||||
return e.Start(bind)
|
||||
}
|
||||
|
||||
func customHTTPErrorHandler(err error, c echo.Context) {
|
||||
code := http.StatusInternalServerError
|
||||
if he, ok := err.(*echo.HTTPError); ok {
|
||||
code = he.Code
|
||||
}
|
||||
c.Logger().Error(err)
|
||||
data := pongo2.Context{
|
||||
"statusCode": code,
|
||||
}
|
||||
c.Render(code, "templates/error.html", data)
|
||||
}
|
||||
|
||||
// handler for endpoint that have no specific server-side handling
|
||||
func (srv *Server) WebGeneric(c echo.Context) error {
|
||||
data := pongo2.Context{}
|
||||
return c.Render(http.StatusOK, "templates/base.html", data)
|
||||
}
|
||||
|
||||
func (srv *Server) WebHome(c echo.Context) error {
|
||||
data := pongo2.Context{}
|
||||
return c.Render(http.StatusOK, "templates/home.html", data)
|
||||
}
|
||||
|
||||
func (srv *Server) WebPost(c echo.Context) error {
|
||||
data := pongo2.Context{}
|
||||
handle := c.Param("handle")
|
||||
rkey := c.Param("rkey")
|
||||
// sanity check argument
|
||||
if len(handle) > 4 && len(handle) < 128 && len(rkey) > 0 {
|
||||
ctx := context.TODO()
|
||||
// requires two fetches: first fetch profile (!)
|
||||
pv, err := appbsky.ActorGetProfile(ctx, srv.xrpcc, handle)
|
||||
if err != nil {
|
||||
log.Warnf("failed to fetch handle: %s\t%v", handle, err)
|
||||
} else {
|
||||
did := pv.Did
|
||||
data["did"] = did
|
||||
|
||||
// then fetch the post thread (with extra context)
|
||||
uri := fmt.Sprintf("at://%s/app.bsky.feed.post/%s", did, rkey)
|
||||
tpv, err := appbsky.FeedGetPostThread(ctx, srv.xrpcc, 1, uri)
|
||||
if err != nil {
|
||||
log.Warnf("failed to fetch post: %s\t%v", uri, err)
|
||||
} else {
|
||||
data["postView"] = tpv.Thread.FeedGetPostThread_ThreadViewPost.Post
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return c.Render(http.StatusOK, "templates/post.html", data)
|
||||
}
|
||||
|
||||
func (srv *Server) WebProfile(c echo.Context) error {
|
||||
data := pongo2.Context{}
|
||||
handle := c.Param("handle")
|
||||
// sanity check argument
|
||||
if len(handle) > 4 && len(handle) < 128 {
|
||||
ctx := context.TODO()
|
||||
pv, err := appbsky.ActorGetProfile(ctx, srv.xrpcc, handle)
|
||||
if err != nil {
|
||||
log.Warnf("failed to fetch handle: %s\t%v", handle, err)
|
||||
} else {
|
||||
data["profileView"] = pv
|
||||
}
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "templates/profile.html", data)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
GOLOG_LOG_LEVEL=info
|
||||
ATP_PDS_HOST=https://bsky.social
|
||||
ATP_AUTH_HANDLE=TODO
|
||||
ATP_AUTH_PASSWORD=TODO
|
||||
@@ -0,0 +1,72 @@
|
||||
module github.com/bluesky-social/social-app/bskyweb
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/bluesky-social/indigo v0.0.0-20230307000525-294e33e70185
|
||||
github.com/ipfs/go-log v1.0.5
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/urfave/cli/v2 v2.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||
github.com/flosch/pongo2/v6 v6.0.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/goccy/go-json v0.10.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/ipfs/go-cid v0.3.2 // indirect
|
||||
github.com/ipfs/go-log/v2 v2.5.1 // indirect
|
||||
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.2.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httprc v1.0.4 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.8 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.1.1 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.1 // indirect
|
||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa // indirect
|
||||
github.com/whyrusleeping/go-did v0.0.0-20230301193428-2146016fc220 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
go.opentelemetry.io/otel v1.11.2 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.11.2 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.6.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
gorm.io/driver/postgres v1.4.6 // indirect
|
||||
gorm.io/driver/sqlite v1.4.4 // indirect
|
||||
gorm.io/gorm v1.24.3 // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
)
|
||||
@@ -0,0 +1,287 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bluesky-social/indigo v0.0.0-20230306194356-5958f14d5152 h1:7fHM+tQHJN5lsMU8FvV4bNuWpD0Dd+pAUSuoLYdcYIQ=
|
||||
github.com/bluesky-social/indigo v0.0.0-20230306194356-5958f14d5152/go.mod h1:xy2hI4NMC6fgUefSJcCst6E0yo9Xbfd97aF27lgHyHE=
|
||||
github.com/bluesky-social/indigo v0.0.0-20230307000525-294e33e70185 h1:WnaOpRFWE8Tmw0IeXEEthsqBZtNG6/niokmWANv/aEU=
|
||||
github.com/bluesky-social/indigo v0.0.0-20230307000525-294e33e70185/go.mod h1:xy2hI4NMC6fgUefSJcCst6E0yo9Xbfd97aF27lgHyHE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
|
||||
github.com/flosch/pongo2/v6 v6.0.0 h1:lsGru8IAzHgIAw6H2m4PCyleO58I40ow6apih0WprMU=
|
||||
github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
|
||||
github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc=
|
||||
github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw=
|
||||
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
|
||||
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
|
||||
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
|
||||
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
|
||||
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
|
||||
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52 h1:QG4CGBqCeuBo6aZlGAamSkxWdgWfZGeE49eUOWJPA4c=
|
||||
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52/go.mod h1:fdg+/X9Gg4AsAIzWpEHwnqd+QY3b7lajxyjE1m4hkq4=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
|
||||
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||
github.com/jackc/puddle/v2 v2.1.2/go.mod h1:2lpufsF5mRHO6SuZkm0fNYxM6SWHfvyFj62KwNzgels=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
|
||||
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
|
||||
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
|
||||
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
|
||||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||
github.com/lestrrat-go/jwx v1.2.25 h1:tAx93jN2SdPvFn08fHNAhqFJazn5mBBOB8Zli0g0otA=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.8 h1:jCFT8oc0hEDVjgUgsBy1F9cbjsjAVZSXNi7JaU9HR/Q=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.8/go.mod h1:zLxnyv9rTlEvOUHbc48FAfIL8iYu2hHvIRaTFGc8mT0=
|
||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
|
||||
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
|
||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
|
||||
github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI=
|
||||
github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8=
|
||||
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
|
||||
github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
|
||||
github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
|
||||
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
||||
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
||||
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8=
|
||||
github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa h1:EyA027ZAkuaCLoxVX4r1TZMPy1d31fM6hbfQ4OU4I5o=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
|
||||
github.com/whyrusleeping/go-did v0.0.0-20230301193428-2146016fc220 h1:EO/9z3yDvx1van1/0esdcqhalZZQGRj3I1BPTWr5k3A=
|
||||
github.com/whyrusleeping/go-did v0.0.0-20230301193428-2146016fc220/go.mod h1:qPtRyexGM5XMHFIfjH+EiA/A/1n2JakWEdMPC53pJAE=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0=
|
||||
go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI=
|
||||
go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0=
|
||||
go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.4.6 h1:1FPESNXqIKG5JmraaH2bfCVlMQ7paLoCreFxDtqzwdc=
|
||||
gorm.io/driver/postgres v1.4.6/go.mod h1:UJChCNLFKeBqQRE+HrkFUbKbq9idPXmTOk2u4Wok8S4=
|
||||
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
|
||||
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.3 h1:WL2ifUmzR/SLp85CSURAfybcHnGZ+yLSGSxgYXlFBHg=
|
||||
gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 316 B |
|
After Width: | Height: | Size: 769 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
@@ -0,0 +1 @@
|
||||
# hello friends!
|
||||
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,149 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover">
|
||||
<meta name="referrer" content="origin-when-cross-origin">
|
||||
<title>{%- block head_title -%}Bluesky{%- endblock -%}</title>
|
||||
|
||||
<!-- Hello Humans! API docs at https://atproto.com -->
|
||||
|
||||
<style>
|
||||
/**
|
||||
* Extend the react-native-web reset:
|
||||
* https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
|
||||
*/
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
width: 100%;
|
||||
/* To smooth any scrolling behavior */
|
||||
-webkit-overflow-scrolling: touch;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
/* Allows content to fill the viewport and go beyond the bottom */
|
||||
min-height: 100%;
|
||||
}
|
||||
#root {
|
||||
flex-shrink: 0;
|
||||
flex-basis: auto;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
/* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
height: calc(100% + env(safe-area-inset-top));
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
/* Allows you to scroll below the viewport; default value is visible */
|
||||
overflow-y: auto;
|
||||
overscroll-behavior-y: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
/* Enable for apps that support dark-theme */
|
||||
/*@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
}*/
|
||||
|
||||
/* Remove focus state on inputs */
|
||||
*:focus {
|
||||
outline: 0;
|
||||
}
|
||||
/* Remove default link styling */
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
a[role="link"]:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* ProseMirror */
|
||||
.ProseMirror {
|
||||
font: 18px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
min-height: 140px;
|
||||
}
|
||||
.ProseMirror p {
|
||||
margin: 0;
|
||||
}
|
||||
.ProseMirror p.is-editor-empty:first-child::before {
|
||||
color: #8d8e96;
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.ProseMirror .mention {
|
||||
color: #0085ff;
|
||||
}
|
||||
.ProseMirror a {
|
||||
color: #0085ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tippy-content .items {
|
||||
border-radius: 6px;
|
||||
background: #F3F3F8;
|
||||
border: 1px solid #e0d9d9;
|
||||
padding: 3px 3px;
|
||||
}
|
||||
.tippy-content .items .item {
|
||||
display: block;
|
||||
background: transparent;
|
||||
color: #8a8c9a;
|
||||
border: 0;
|
||||
font: 17px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
padding: 7px 10px 8px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
.tippy-content .items .item.is-selected {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
{% include "scripts.html" %}
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png"/>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png"/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png"/>
|
||||
{% block head_page_meta -%}
|
||||
<meta property="og:title" content="Bluesky Social"/>
|
||||
<meta property="og:type" content="article"/>
|
||||
<meta property="og:image" content="/static/default-social-card.png"/>
|
||||
<meta name="twitter:title" content="Bluesky Social"/>
|
||||
<meta name="twitter:description" content="See what's next."/>
|
||||
<meta name="twitter:image" content="/static/default-social-card.png"/>
|
||||
<meta name="twitter:card" content="summary_large_image"/>
|
||||
<meta name="twitter:site" content="@bluesky"/>
|
||||
{%- endblock %}
|
||||
<!-- TODO: link rel=canonical -->
|
||||
<!-- TODO: analytics code -->
|
||||
<!-- TODO: could put <link rel="preload"> tags here -->
|
||||
<meta name="generator" name="bskyweb">
|
||||
{% block head_metadata %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{%- block body_all %}
|
||||
<div id="root"></div>
|
||||
<noscript>
|
||||
{%- block noscript_extra %}{% endblock -%}
|
||||
<h1>Javascript Required</h1>
|
||||
<p>This is a heavily interactive web application, and Javascript is required. Simple HTML interfaces are possible, but that is not what this is.
|
||||
<p>Learn more about Bluesky at <a href="https://blueskyweb.xyz">blueskyweb.xyz</a> and <a href="https://atproto.com">atproto.com</a>.
|
||||
</noscript>
|
||||
{% endblock -%}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head_title %}Error {{ statusCode }} - Bluesky{% endblock %}
|
||||
|
||||
{% block noscript_extra %}
|
||||
{%- if statusCode == 404 %}
|
||||
<h1>404: Not Found</h1>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{# don't include the bundle on non-404 error pages #}
|
||||
{% block head_bundle %}
|
||||
{% if statusCode == 404 %}
|
||||
{{ super() }}
|
||||
{% else %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{%- block body_all %}
|
||||
{% if statusCode == 404 %}
|
||||
{{ super() }}
|
||||
{% else %}
|
||||
<h1>{{ statusCode }}: Server Error</h1>
|
||||
<p>Sorry about that! Our <a href="https://bluesky.statuspage.io/">Status Page</a> might have more context.
|
||||
{% endif %}
|
||||
{% endblock -%}
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head_title %}Bluesky{% endblock %}
|
||||
|
||||
{% block noscript_extra %}
|
||||
<p>This is the home page.
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,25 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head_page_meta -%}
|
||||
<!-- TODO: "same as" indication with at:// URI? -->
|
||||
{%- if postView -%}
|
||||
<meta property="og:type" content="article"/>
|
||||
<meta name="twitter:card" content="summary"/>
|
||||
{%- if postView.Author.DisplayName -%}
|
||||
<meta property="og:title" content="{{ postView.Author.DisplayName }} / {{ postView.Author.Handle }}"/>
|
||||
<meta name="twitter:title" content="{{ postView.Author.DisplayName }} / {{ postView.Author.Handle }}"/>
|
||||
{%- else -%}
|
||||
<meta property="og:title" content="{{ postView.Author.Handle }}"/>
|
||||
<meta name="twitter:title" content="{{ postView.Author.Handle }}"/>
|
||||
{%- endif -%}
|
||||
{%- if postView.Record.Text -%}
|
||||
<meta name="twitter:description" content="{{ postView.Record.Text }}"/>
|
||||
<!-- TODO: could put any images in here, or author avatar -->
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endblock %}
|
||||
|
||||
{% block noscript_extra -%}
|
||||
<p>{{ postView.Author.DisplayName }} / {{ postView.Author.Handle }}
|
||||
<p>{{ postView.Record.Text }}
|
||||
{%- endblock %}
|
||||