Compare commits
664 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c16f5d2335 | |||
| 320d69920e | |||
| d4d1fb820b | |||
| 330a1a0be4 | |||
| 3df953b73d | |||
| 97c5d4d965 | |||
| b2300a8381 | |||
| 5accb619ac | |||
| ebb0b11774 | |||
| 3b0d72b776 | |||
| bf1f743382 | |||
| de5ee3c768 | |||
| 0d92dd8306 | |||
| 5b3eeaf747 | |||
| df4dee6db2 | |||
| 6a4961caa5 | |||
| bf951421ea | |||
| 34050e05be | |||
| ce7817d2fb | |||
| 0c7f61046c | |||
| 2c41458963 | |||
| 3560291329 | |||
| d073c66c5d | |||
| 15a18d9dd3 | |||
| 7c481c343e | |||
| 360322f475 | |||
| 18347bdb22 | |||
| bc46714ca5 | |||
| 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,21 @@ buck-out/
|
||||
# Ruby / CocoaPods
|
||||
/ios/Pods/
|
||||
/vendor/bundle/
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
junit.xml
|
||||
artifacts
|
||||
|
||||
.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,5 @@
|
||||
ios
|
||||
android
|
||||
src/third-party
|
||||
src/app.json
|
||||
public
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,157 @@
|
||||
import {RootStoreModel} from './../../../src/state/models/root-store'
|
||||
import {NavigationModel} from './../../../src/state/models/navigation'
|
||||
import * as flags from '../../../src/lib/build-flags'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {DEFAULT_SERVICE} from '../../../src/state'
|
||||
|
||||
describe('NavigationModel', () => {
|
||||
let model: NavigationModel
|
||||
let rootStore: RootStoreModel
|
||||
|
||||
beforeEach(() => {
|
||||
rootStore = new RootStoreModel(new AtpAgent({service: DEFAULT_SERVICE}))
|
||||
model = new NavigationModel(rootStore)
|
||||
model.setTitle('0-0', 'title')
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should clear() to the correct base state', async () => {
|
||||
await model.clear()
|
||||
expect(model.tabCount).toBe(3)
|
||||
expect(model.tab).toEqual({
|
||||
fixedTabPurpose: 0,
|
||||
history: [
|
||||
{
|
||||
id: expect.anything(),
|
||||
ts: expect.anything(),
|
||||
url: '/',
|
||||
},
|
||||
],
|
||||
id: expect.anything(),
|
||||
index: 0,
|
||||
isNewTab: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should call the navigate method', async () => {
|
||||
const navigateSpy = jest.spyOn(model.tab, 'navigate')
|
||||
await model.navigate('testurl', 'teststring')
|
||||
expect(navigateSpy).toHaveBeenCalledWith('testurl', 'teststring')
|
||||
})
|
||||
|
||||
it('should call the refresh method', async () => {
|
||||
const refreshSpy = jest.spyOn(model.tab, 'refresh')
|
||||
await model.refresh()
|
||||
expect(refreshSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should call the isCurrentScreen method', () => {
|
||||
expect(model.isCurrentScreen('11', 0)).toEqual(false)
|
||||
})
|
||||
|
||||
it('should call the tab getter', () => {
|
||||
expect(model.tab).toEqual({
|
||||
fixedTabPurpose: 0,
|
||||
history: [
|
||||
{
|
||||
id: expect.anything(),
|
||||
ts: expect.anything(),
|
||||
url: '/',
|
||||
},
|
||||
],
|
||||
id: expect.anything(),
|
||||
index: 0,
|
||||
isNewTab: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should call the tabCount getter', () => {
|
||||
expect(model.tabCount).toBe(3)
|
||||
})
|
||||
|
||||
describe('tabs not enabled', () => {
|
||||
jest.mock('../../../src/lib/build-flags', () => ({
|
||||
TABS_ENABLED: false,
|
||||
}))
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should not create new tabs', () => {
|
||||
// @ts-expect-error
|
||||
flags.TABS_ENABLED = false
|
||||
model.newTab('testurl')
|
||||
expect(model.tab.isNewTab).toBe(false)
|
||||
expect(model.tabIndex).toBe(0)
|
||||
})
|
||||
|
||||
it('should not change the active tab', () => {
|
||||
// @ts-expect-error
|
||||
flags.TABS_ENABLED = false
|
||||
model.setActiveTab(3)
|
||||
expect(model.tabIndex).toBe(0)
|
||||
})
|
||||
|
||||
it('should note close tabs', () => {
|
||||
// @ts-expect-error
|
||||
flags.TABS_ENABLED = false
|
||||
model.closeTab(0)
|
||||
expect(model.tabCount).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
// TODO restore when tabs get re-enabled
|
||||
// describe('tabs enabled', () => {
|
||||
// jest.mock('../../../src/build-flags', () => ({
|
||||
// TABS_ENABLED: true,
|
||||
// }))
|
||||
|
||||
// afterAll(() => {
|
||||
// jest.clearAllMocks()
|
||||
// })
|
||||
|
||||
// it('should create new tabs', () => {
|
||||
// // @ts-expect-error
|
||||
// flags.TABS_ENABLED = true
|
||||
|
||||
// model.newTab('testurl', 'title')
|
||||
// expect(model.tab.isNewTab).toBe(true)
|
||||
// expect(model.tabIndex).toBe(2)
|
||||
// })
|
||||
|
||||
// it('should change the current tab', () => {
|
||||
// // @ts-expect-error
|
||||
// flags.TABS_ENABLED = true
|
||||
|
||||
// model.setActiveTab(0)
|
||||
// expect(model.tabIndex).toBe(0)
|
||||
// })
|
||||
|
||||
// it('should close tabs', () => {
|
||||
// // @ts-expect-error
|
||||
// flags.TABS_ENABLED = true
|
||||
|
||||
// model.closeTab(0)
|
||||
// expect(model.tabs).toEqual([
|
||||
// {
|
||||
// fixedTabPurpose: 1,
|
||||
// history: [
|
||||
// {
|
||||
// id: expect.anything(),
|
||||
// ts: expect.anything(),
|
||||
// url: '/notifications',
|
||||
// },
|
||||
// ],
|
||||
// id: expect.anything(),
|
||||
// index: 0,
|
||||
// isNewTab: false,
|
||||
// },
|
||||
// ])
|
||||
// expect(model.tabIndex).toBe(0)
|
||||
// })
|
||||
// })
|
||||
})
|
||||
@@ -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.7.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"userInterfaceStyle": "light",
|
||||
"splash": {
|
||||
"image": "./assets/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,12 @@
|
||||
/** @type {import('@jest/types').Config.InitialOptions} */
|
||||
module.exports = {
|
||||
rootDir: '..',
|
||||
testMatch: ['<rootDir>/e2e/**/*.test.js'],
|
||||
testTimeout: 120000,
|
||||
maxWorkers: 1,
|
||||
globalSetup: 'detox/runners/jest/globalSetup',
|
||||
globalTeardown: 'detox/runners/jest/globalTeardown',
|
||||
reporters: ['detox/runners/jest/reporter'],
|
||||
testEnvironment: 'detox/runners/jest/testEnvironment',
|
||||
verbose: true,
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* eslint-env detox/detox */
|
||||
|
||||
describe('Happy paths', () => {
|
||||
async function grantAccessToUserWithValidCredentials(
|
||||
username,
|
||||
{takeScreenshots} = {takeScreenshots: false},
|
||||
) {
|
||||
await element(by.id('signInButton')).tap()
|
||||
if (takeScreenshots) {
|
||||
await device.takeScreenshot('1- opened sign-in screen')
|
||||
}
|
||||
await element(by.id('loginSelectServiceButton')).tap()
|
||||
if (takeScreenshots) {
|
||||
await device.takeScreenshot('2- opened service selector')
|
||||
}
|
||||
await element(by.id('localDevServerButton')).tap()
|
||||
if (takeScreenshots) {
|
||||
await device.takeScreenshot('3- selected local dev server')
|
||||
}
|
||||
await element(by.id('loginUsernameInput')).typeText(username)
|
||||
await element(by.id('loginPasswordInput')).typeText('hunter2')
|
||||
if (takeScreenshots) {
|
||||
await device.takeScreenshot('4- entered username and password')
|
||||
}
|
||||
await element(by.id('loginNextButton')).tap()
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await device.uninstallApp()
|
||||
await device.installApp()
|
||||
await device.launchApp({permissions: {notifications: 'YES'}})
|
||||
})
|
||||
|
||||
it('As Alice, I can login', async () => {
|
||||
await expect(element(by.id('signInButton'))).toBeVisible()
|
||||
await grantAccessToUserWithValidCredentials('alice', {
|
||||
takeScreenshots: true,
|
||||
})
|
||||
await device.takeScreenshot('5- opened home screen')
|
||||
})
|
||||
|
||||
it('As Alice, I can login, and post a text', async () => {
|
||||
await grantAccessToUserWithValidCredentials('alice')
|
||||
await element(by.id('composeFAB')).tap()
|
||||
await device.takeScreenshot('1- opened composer')
|
||||
await element(by.id('composerTextInput')).typeText(
|
||||
'Greetings earthlings, I come in peace... and to run some tests.',
|
||||
)
|
||||
await device.takeScreenshot('2- entered text')
|
||||
await element(by.id('composerPublishButton')).tap()
|
||||
await device.takeScreenshot('3- opened general section')
|
||||
await expect(element(by.id('composeFAB'))).toBeVisible()
|
||||
})
|
||||
|
||||
it('I can create a new account', async () => {
|
||||
await element(by.id('createAccountButton')).tap()
|
||||
await device.takeScreenshot('1- opened create account screen')
|
||||
await element(by.id('registerSelectServiceButton')).tap()
|
||||
await device.takeScreenshot('2- opened service selector')
|
||||
await element(by.id('localDevServerButton')).tap()
|
||||
await device.takeScreenshot('3- selected local dev server')
|
||||
await element(by.id('registerEmailInput')).typeText('example@test.com')
|
||||
await element(by.id('registerPasswordInput')).typeText('hunter2')
|
||||
await element(by.id('registerHandleInput')).typeText('e2e-test')
|
||||
await element(by.id('registerIs13Input')).tap()
|
||||
await device.takeScreenshot('4- entered account details')
|
||||
await element(by.id('createAccountButton')).tap()
|
||||
await expect(element(by.id('welcomeBanner'))).toBeVisible()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'react-native-gesture-handler' // must be first
|
||||
|
||||
import {LogBox} from 'react-native'
|
||||
LogBox.ignoreLogs(['Require cycle:']) // suppress require-cycle warnings, it's fine
|
||||
|
||||
import 'platform/polyfills'
|
||||
import {registerRootComponent} from 'expo'
|
||||
|
||||
import App from './src/App'
|
||||
|
||||
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||
// It also ensures that whether you load the app in Expo Go or in a native build,
|
||||
// the environment is set up appropriately
|
||||
registerRootComponent(App)
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {AppRegistry} from 'react-native'
|
||||
import App from './src/App'
|
||||
import {name as appName} from './src/app.json'
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App)
|
||||
@@ -0,0 +1,4 @@
|
||||
import 'platform/polyfills'
|
||||
import {registerRootComponent} from 'expo'
|
||||
import App from './src/App'
|
||||
registerRootComponent(App)
|
||||
@@ -0,0 +1,10 @@
|
||||
# This `.xcode.env` file is versioned and is used to source the environment
|
||||
# used when running script phases inside Xcode.
|
||||
# To customize your local environment, you can create an `.xcode.env.local`
|
||||
# file that is not versioned.
|
||||
# NODE_BINARY variable contains the PATH to the node executable.
|
||||
#
|
||||
# Customize the NODE_BINARY variable here.
|
||||
# For example, to use nvm with brew, add the following line
|
||||
# . "$(brew --prefix nvm)/nvm.sh" --no-use
|
||||
export NODE_BINARY=$(command -v node)
|
||||
@@ -0,0 +1 @@
|
||||
export NODE_BINARY="/Users/paulfrazee/.nvm/versions/node/v18.8.0/bin/node"
|
||||
@@ -1,5 +0,0 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
|
||||
@interface AppSecureRandomModule : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
@@ -1,27 +0,0 @@
|
||||
#import "AppSecureRandomModule.h"
|
||||
|
||||
@implementation AppSecureRandomModule
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
RCT_REMAP_METHOD(generateSecureRandomAsBase64,
|
||||
withLength:(int)length
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSMutableData* bytes = [NSMutableData dataWithLength:length];
|
||||
int result = SecRandomCopyBytes(kSecRandomDefault,length, [bytes mutableBytes]);
|
||||
if (result == errSecSuccess) {
|
||||
resolve([bytes base64EncodedStringWithOptions:0]);
|
||||
} else {
|
||||
NSError *error = [NSError errorWithDomain:@"RNSecureRandom" code:result userInfo: nil];
|
||||
reject(@"randombytes_error", @"Error generating random bytes", error);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,48 +1,91 @@
|
||||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
|
||||
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
|
||||
require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules")
|
||||
|
||||
platform :ios, '11.0'
|
||||
install! 'cocoapods', :deterministic_uuids => false
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
|
||||
|
||||
target 'app' do
|
||||
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
|
||||
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
|
||||
|
||||
platform :ios, podfile_properties['ios.deploymentTarget'] || '13.0'
|
||||
install! 'cocoapods',
|
||||
:deterministic_uuids => false
|
||||
|
||||
prepare_react_native_project!
|
||||
|
||||
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
|
||||
# because `react-native-flipper` depends on (FlipperKit,...), which will be excluded. To fix this,
|
||||
# you can also exclude `react-native-flipper` in `react-native.config.js`
|
||||
#
|
||||
# ```js
|
||||
# module.exports = {
|
||||
# dependencies: {
|
||||
# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
|
||||
# }
|
||||
# }
|
||||
# ```
|
||||
flipper_config = FlipperConfiguration.disabled
|
||||
if ENV['NO_FLIPPER'] == "1" || ENV['CI'] then
|
||||
# Explicitly disabled through environment variables
|
||||
flipper_config = FlipperConfiguration.disabled
|
||||
elsif podfile_properties.key?('ios.flipper') then
|
||||
# Configure Flipper in Podfile.properties.json
|
||||
if podfile_properties['ios.flipper'] == 'true' then
|
||||
flipper_config = FlipperConfiguration.enabled(["Debug", "Release"])
|
||||
elsif ppodfile_properties['ios.flipper'] != 'false' then
|
||||
flipper_config = FlipperConfiguration.enabled(["Debug", "Release"], { 'Flipper' => podfile_properties['ios.flipper'] })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
target 'bluesky' do
|
||||
use_expo_modules!
|
||||
config = use_native_modules!
|
||||
|
||||
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
|
||||
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
|
||||
|
||||
# Flags change depending on the env values.
|
||||
flags = get_default_flags()
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
# to enable hermes on iOS, change `false` to `true` and then install pods
|
||||
:hermes_enabled => flags[:hermes_enabled],
|
||||
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
|
||||
:fabric_enabled => flags[:fabric_enabled],
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/..",
|
||||
# Note that if you have use_frameworks! enabled, Flipper will not work if enabled
|
||||
:flipper_configuration => flipper_config
|
||||
)
|
||||
|
||||
target 'appTests' do
|
||||
inherit! :complete
|
||||
# Pods for testing
|
||||
end
|
||||
|
||||
# Enables Flipper.
|
||||
#
|
||||
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||
# you should disable the next line.
|
||||
#use_flipper!()
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(installer)
|
||||
react_native_post_install(
|
||||
installer,
|
||||
config[:reactNativePath],
|
||||
# Set `mac_catalyst_enabled` to `true` in order to apply patches
|
||||
# necessary for Mac Catalyst builds
|
||||
:mac_catalyst_enabled => false
|
||||
)
|
||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
|
||||
# iOS fix
|
||||
installer.pods_project.build_configurations.each do |config|
|
||||
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
|
||||
end
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES'
|
||||
# This is necessary for Xcode 14, because it signs resource bundles by default
|
||||
# when building for devices.
|
||||
installer.target_installation_results.pod_target_installation_results
|
||||
.each do |pod_name, target_installation_result|
|
||||
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
|
||||
resource_bundle_target.build_configurations.each do |config|
|
||||
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post_integrate do |installer|
|
||||
begin
|
||||
expo_patch_react_imports!(installer)
|
||||
rescue => e
|
||||
Pod::UI.warn e
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"expo.jsEngine": "hermes"
|
||||
}
|
||||
@@ -1,680 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
00E356F31AD99517003FC87E /* appTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* appTests.m */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
5CEAE7B7A55582F96F1D5952 /* libPods-app.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FCB672808307A6013805A3FE /* libPods-app.a */; };
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||
E4BD704B285AD57E00A8FED9 /* AppSecureRandomModule.m in Sources */ = {isa = PBXBuildFile; fileRef = E4BD704A285AD57E00A8FED9 /* AppSecureRandomModule.m */; };
|
||||
E4BD704C285AD57E00A8FED9 /* AppSecureRandomModule.m in Sources */ = {isa = PBXBuildFile; fileRef = E4BD704A285AD57E00A8FED9 /* AppSecureRandomModule.m */; };
|
||||
FEB90D21557517F9279AECA4 /* libPods-app-appTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BAD3BC60FA05CF2D4F6F9BA2 /* libPods-app-appTests.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
||||
remoteInfo = app;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
00E356EE1AD99517003FC87E /* appTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = appTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
00E356F21AD99517003FC87E /* appTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = appTests.m; sourceTree = "<group>"; };
|
||||
03F7088487057F17D25D2F4B /* Pods-app-appTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app-appTests.release.xcconfig"; path = "Target Support Files/Pods-app-appTests/Pods-app-appTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = app.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = app/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = app/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = app/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = app/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = app/main.m; sourceTree = "<group>"; };
|
||||
19C43486502F2DE23E091FE3 /* Pods-app-appTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app-appTests.debug.xcconfig"; path = "Target Support Files/Pods-app-appTests/Pods-app-appTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = app/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
970005155A83960D1D13380F /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = "<group>"; };
|
||||
BAD3BC60FA05CF2D4F6F9BA2 /* libPods-app-appTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app-appTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E4437C9E28581FA7006DA9E7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = app.entitlements; path = app/app.entitlements; sourceTree = "<group>"; };
|
||||
E4BD7049285AD54000A8FED9 /* AppSecureRandomModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppSecureRandomModule.h; sourceTree = "<group>"; };
|
||||
E4BD704A285AD57E00A8FED9 /* AppSecureRandomModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppSecureRandomModule.m; sourceTree = "<group>"; };
|
||||
ED22CAA45207BC18E75DB44B /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
FCB672808307A6013805A3FE /* libPods-app.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
00E356EB1AD99517003FC87E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FEB90D21557517F9279AECA4 /* libPods-app-appTests.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CEAE7B7A55582F96F1D5952 /* libPods-app.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
00E356EF1AD99517003FC87E /* appTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
00E356F21AD99517003FC87E /* appTests.m */,
|
||||
00E356F01AD99517003FC87E /* Supporting Files */,
|
||||
);
|
||||
path = appTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
00E356F01AD99517003FC87E /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
00E356F11AD99517003FC87E /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
13B07FAE1A68108700A75B9A /* app */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4437C9E28581FA7006DA9E7 /* app.entitlements */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
E4BD7049285AD54000A8FED9 /* AppSecureRandomModule.h */,
|
||||
E4BD704A285AD57E00A8FED9 /* AppSecureRandomModule.m */,
|
||||
);
|
||||
name = app;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
FCB672808307A6013805A3FE /* libPods-app.a */,
|
||||
BAD3BC60FA05CF2D4F6F9BA2 /* libPods-app-appTests.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FAE1A68108700A75B9A /* app */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
00E356EF1AD99517003FC87E /* appTests */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
usesTabs = 0;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07F961A680F5B00A75B9A /* app.app */,
|
||||
00E356EE1AD99517003FC87E /* appTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ED22CAA45207BC18E75DB44B /* Pods-app.debug.xcconfig */,
|
||||
970005155A83960D1D13380F /* Pods-app.release.xcconfig */,
|
||||
19C43486502F2DE23E091FE3 /* Pods-app-appTests.debug.xcconfig */,
|
||||
03F7088487057F17D25D2F4B /* Pods-app-appTests.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
00E356ED1AD99517003FC87E /* appTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "appTests" */;
|
||||
buildPhases = (
|
||||
F309585457BFF312EF73FD95 /* [CP] Check Pods Manifest.lock */,
|
||||
00E356EA1AD99517003FC87E /* Sources */,
|
||||
00E356EB1AD99517003FC87E /* Frameworks */,
|
||||
00E356EC1AD99517003FC87E /* Resources */,
|
||||
2772CBCCE24036517E86445D /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
00E356F51AD99517003FC87E /* PBXTargetDependency */,
|
||||
);
|
||||
name = appTests;
|
||||
productName = appTests;
|
||||
productReference = 00E356EE1AD99517003FC87E /* appTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
13B07F861A680F5B00A75B9A /* app */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "app" */;
|
||||
buildPhases = (
|
||||
4D45C29570A58E7A8A8D177D /* [CP] Check Pods Manifest.lock */,
|
||||
FD10A7F022414F080027D42C /* Start Packager */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
9CEFF2A5EA3D60C120442CA6 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = app;
|
||||
productName = app;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* app.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1340;
|
||||
TargetAttributes = {
|
||||
00E356ED1AD99517003FC87E = {
|
||||
CreatedOnToolsVersion = 6.2;
|
||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1120;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */;
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* app */,
|
||||
00E356ED1AD99517003FC87E /* appTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
00E356EC1AD99517003FC87E /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Bundle React Native code and images";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -e\n\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
|
||||
};
|
||||
2772CBCCE24036517E86445D /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app-appTests/Pods-app-appTests-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app-appTests/Pods-app-appTests-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app-appTests/Pods-app-appTests-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
4D45C29570A58E7A8A8D177D /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-app-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9CEFF2A5EA3D60C120442CA6 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F309585457BFF312EF73FD95 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-app-appTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
FD10A7F022414F080027D42C /* Start Packager */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Start Packager";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
00E356EA1AD99517003FC87E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
00E356F31AD99517003FC87E /* appTests.m in Sources */,
|
||||
E4BD704C285AD57E00A8FED9 /* AppSecureRandomModule.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||
E4BD704B285AD57E00A8FED9 /* AppSecureRandomModule.m in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 13B07F861A680F5B00A75B9A /* app */;
|
||||
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
00E356F61AD99517003FC87E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 19C43486502F2DE23E091FE3 /* Pods-app-appTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = appTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
"$(inherited)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/app.app/app";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
00E356F71AD99517003FC87E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 03F7088487057F17D25D2F4B /* Pods-app-appTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
INFOPLIST_FILE = appTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
"$(inherited)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/app.app/app";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = ED22CAA45207BC18E75DB44B /* Pods-app.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = app/app.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = A6GRZHF6BV;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
INFOPLIST_FILE = app/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.blueskyweb.pubsq;
|
||||
PRODUCT_NAME = app;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 970005155A83960D1D13380F /* Pods-app.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = app/app.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = A6GRZHF6BV;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
INFOPLIST_FILE = app/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.blueskyweb.pubsq;
|
||||
PRODUCT_NAME = app;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
/usr/lib/swift,
|
||||
"$(inherited)",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||
"\"$(inherited)\"",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-DFOLLY_NO_CONFIG",
|
||||
"-DFOLLY_MOBILE=1",
|
||||
"-DFOLLY_USE_LIBCPP=1",
|
||||
);
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
83CBBA211A601CBA00E9B192 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
/usr/lib/swift,
|
||||
"$(inherited)",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||
"\"$(inherited)\"",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-DFOLLY_NO_CONFIG",
|
||||
"-DFOLLY_MOBILE=1",
|
||||
"-DFOLLY_USE_LIBCPP=1",
|
||||
);
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "appTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
00E356F61AD99517003FC87E /* Debug */,
|
||||
00E356F71AD99517003FC87E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "app" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
13B07F941A680F5B00A75B9A /* Debug */,
|
||||
13B07F951A680F5B00A75B9A /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
83CBBA201A601CBA00E9B192 /* Debug */,
|
||||
83CBBA211A601CBA00E9B192 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#import <React/RCTBridgeDelegate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIWindow *window;
|
||||
|
||||
@end
|
||||
@@ -1,126 +0,0 @@
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#import <React/RCTAppSetupUtils.h>
|
||||
|
||||
// universal links
|
||||
#import <React/RCTLinkingManager.h>
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
#import <React/RCTCxxBridgeDelegate.h>
|
||||
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
|
||||
#import <React/RCTSurfacePresenter.h>
|
||||
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||
|
||||
#import <react/config/ReactNativeConfig.h>
|
||||
|
||||
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
|
||||
RCTTurboModuleManager *_turboModuleManager;
|
||||
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
|
||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||
facebook::react::ContextContainer::Shared _contextContainer;
|
||||
}
|
||||
@end
|
||||
#endif
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTAppSetupPrepareApp(application);
|
||||
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
||||
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
||||
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
|
||||
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
|
||||
#endif
|
||||
|
||||
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"xyz.blueskyweb.pubsq", nil);
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
} else {
|
||||
rootView.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [UIViewController new];
|
||||
rootViewController.view = rootView;
|
||||
self.window.rootViewController = rootViewController;
|
||||
[self.window makeKeyAndVisible];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
#if DEBUG
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
||||
#else
|
||||
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
}
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
|
||||
#pragma mark - RCTCxxBridgeDelegate
|
||||
|
||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
|
||||
delegate:self
|
||||
jsInvoker:bridge.jsCallInvoker];
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
|
||||
}
|
||||
|
||||
#pragma mark RCTTurboModuleManagerDelegate
|
||||
|
||||
- (Class)getModuleClassFromName:(const char *)name
|
||||
{
|
||||
return RCTCoreModulesClassProvider(name);
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
||||
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
||||
initParams:
|
||||
(const facebook::react::ObjCTurboModule::InitParams &)params
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
|
||||
{
|
||||
return RCTAppSetupDefaultModuleFromClass(moduleClass);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// universal links
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
||||
{
|
||||
return [RCTLinkingManager application:application openURL:url options:options];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
continueUserActivity:(nonnull NSUserActivity *)userActivity
|
||||
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
|
||||
{
|
||||
return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,53 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>PubSQ</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>xyz.blueskyweb.pubsq</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>pubsqapp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||