@onekeyfe/react-native-image 6.7.0-alpha.414 → 6.7.0-alpha.418
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/android/build.gradle +3 -1
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyIcon.kt +93 -0
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageGlideRegistry.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImagePackage.kt +2 -1
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyNamedSVG.kt +217 -0
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeySVG.kt +31 -0
- package/ios/OneKeyIcon.swift +90 -0
- package/ios/OneKeyImageCoderBridge.h +4 -0
- package/ios/OneKeyImageCoderBridge.m +9 -0
- package/ios/OneKeyNamedSVG.swift +282 -0
- package/ios/tests/OneKeyIconTests.swift +484 -0
- package/lib/module/OneKeyIcon.nitro.js +4 -0
- package/lib/module/icon/assetUrls.js +13 -0
- package/lib/module/icon/dom.js +21 -0
- package/lib/module/icon/index.js +29 -0
- package/lib/module/icon/index.web.js +32 -0
- package/lib/module/icon/props.js +22 -0
- package/lib/module/package.json +6 -1
- package/lib/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.cpp +67 -0
- package/lib/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.hpp +66 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.cpp +79 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.hpp +54 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyIconSpec.kt +65 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconManager.kt +96 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconStateUpdater.kt +23 -0
- package/lib/nitrogen/generated/android/onekeyimage+autolinking.cmake +4 -0
- package/lib/nitrogen/generated/android/onekeyimageOnLoad.cpp +18 -0
- package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.cpp +17 -0
- package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.hpp +32 -15
- package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Umbrella.hpp +5 -0
- package/lib/nitrogen/generated/ios/OneKeyImageAutolinking.mm +8 -0
- package/lib/nitrogen/generated/ios/OneKeyImageAutolinking.swift +12 -0
- package/lib/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.cpp +11 -0
- package/lib/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.hpp +89 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridOneKeyIconComponent.mm +150 -0
- package/lib/nitrogen/generated/ios/swift/HybridOneKeyIconSpec.swift +56 -0
- package/lib/nitrogen/generated/ios/swift/HybridOneKeyIconSpec_cxx.swift +193 -0
- package/lib/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.cpp +24 -0
- package/lib/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.hpp +66 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.cpp +36 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.hpp +88 -0
- package/lib/nitrogen/generated/shared/json/OneKeyIconConfig.json +11 -0
- package/lib/typescript/src/OneKeyIcon.nitro.d.ts +7 -0
- package/lib/typescript/src/icon/assetUrls.d.ts +2 -0
- package/lib/typescript/src/icon/dom.d.ts +3 -0
- package/lib/typescript/src/icon/index.d.ts +140 -0
- package/lib/typescript/src/icon/index.web.d.ts +134 -0
- package/lib/typescript/src/icon/props.d.ts +8 -0
- package/nitro.json +10 -0
- package/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.cpp +67 -0
- package/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.cpp +79 -0
- package/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.hpp +54 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyIconSpec.kt +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconManager.kt +96 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconStateUpdater.kt +23 -0
- package/nitrogen/generated/android/onekeyimage+autolinking.cmake +4 -0
- package/nitrogen/generated/android/onekeyimageOnLoad.cpp +18 -0
- package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.hpp +32 -15
- package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Umbrella.hpp +5 -0
- package/nitrogen/generated/ios/OneKeyImageAutolinking.mm +8 -0
- package/nitrogen/generated/ios/OneKeyImageAutolinking.swift +12 -0
- package/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.hpp +89 -0
- package/nitrogen/generated/ios/c++/views/HybridOneKeyIconComponent.mm +150 -0
- package/nitrogen/generated/ios/swift/HybridOneKeyIconSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridOneKeyIconSpec_cxx.swift +193 -0
- package/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.cpp +24 -0
- package/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.hpp +66 -0
- package/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.cpp +36 -0
- package/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.hpp +88 -0
- package/nitrogen/generated/shared/json/OneKeyIconConfig.json +11 -0
- package/package.json +40 -7
- package/scripts/configure-icon-imports.mjs +7 -0
- package/scripts/icon-fixtures.test.mjs +398 -0
- package/src/OneKeyIcon.nitro.ts +8 -0
- package/src/icon/assetUrls.ts +13 -0
- package/src/icon/dom.ts +21 -0
- package/src/icon/index.tsx +38 -0
- package/src/icon/index.web.tsx +36 -0
- package/src/icon/props.ts +38 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// HybridOneKeyIconComponent.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <NitroModules/ReactProp.hpp>
|
|
11
|
+
#include <NitroModules/ViewComponentDescriptor.hpp>
|
|
12
|
+
#include <NitroModules/ViewPropsHolderState.hpp>
|
|
13
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
14
|
+
#include <react/renderer/components/view/ViewProps.h>
|
|
15
|
+
#include <react/renderer/core/PropsParserContext.h>
|
|
16
|
+
#include <react/renderer/core/RawProps.h>
|
|
17
|
+
|
|
18
|
+
#include <string>
|
|
19
|
+
|
|
20
|
+
#include <string>
|
|
21
|
+
#include <optional>
|
|
22
|
+
#include <memory>
|
|
23
|
+
#include "HybridOneKeyIconSpec.hpp"
|
|
24
|
+
#include <functional>
|
|
25
|
+
|
|
26
|
+
namespace margelo::nitro::onekeyimage::views {
|
|
27
|
+
|
|
28
|
+
using namespace facebook;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The name of the actual native View.
|
|
32
|
+
*/
|
|
33
|
+
extern const char HybridOneKeyIconComponentName[];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Props for the "OneKeyIcon" View.
|
|
37
|
+
*/
|
|
38
|
+
class HybridOneKeyIconProps final: public react::ViewProps {
|
|
39
|
+
public:
|
|
40
|
+
HybridOneKeyIconProps() = default;
|
|
41
|
+
HybridOneKeyIconProps(const react::PropsParserContext& context,
|
|
42
|
+
const HybridOneKeyIconProps& sourceProps,
|
|
43
|
+
const react::RawProps& rawProps);
|
|
44
|
+
|
|
45
|
+
public:
|
|
46
|
+
nitro::ReactProp<std::optional<std::string>> iconName;
|
|
47
|
+
nitro::ReactProp<std::optional<std::string>> resolvedColor;
|
|
48
|
+
nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridOneKeyIconSpec>& /* ref */)>>> hybridRef;
|
|
49
|
+
|
|
50
|
+
[[nodiscard]]
|
|
51
|
+
bool hasSameProps(const HybridOneKeyIconProps& other) const noexcept {
|
|
52
|
+
return iconName.hasSameValue(other.iconName) &&
|
|
53
|
+
resolvedColor.hasSameValue(other.resolvedColor) &&
|
|
54
|
+
hybridRef.hasSameValue(other.hybridRef);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
[[nodiscard]]
|
|
58
|
+
bool hasAnyProvidedProps() const noexcept {
|
|
59
|
+
return iconName.isProvided() ||
|
|
60
|
+
resolvedColor.isProvided() ||
|
|
61
|
+
hybridRef.isProvided();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private:
|
|
65
|
+
static bool filterObjectKeys(const std::string& propName);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* State for the "OneKeyIcon" View.
|
|
70
|
+
*/
|
|
71
|
+
using HybridOneKeyIconState = nitro::ViewPropsHolderState<HybridOneKeyIconProps>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The Shadow Node for the "OneKeyIcon" View.
|
|
75
|
+
*/
|
|
76
|
+
using HybridOneKeyIconShadowNode = react::ConcreteViewShadowNode<HybridOneKeyIconComponentName /* "HybridOneKeyIcon" */,
|
|
77
|
+
HybridOneKeyIconProps /* custom props */,
|
|
78
|
+
react::ViewEventEmitter /* default */,
|
|
79
|
+
HybridOneKeyIconState /* custom state */>;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The Component Descriptor for the "OneKeyIcon" View.
|
|
83
|
+
*/
|
|
84
|
+
using HybridOneKeyIconComponentDescriptor = nitro::ViewComponentDescriptor<HybridOneKeyIconShadowNode>;
|
|
85
|
+
|
|
86
|
+
/* The actual view for "OneKeyIcon" needs to be implemented in platform-specific code. */
|
|
87
|
+
|
|
88
|
+
} // namespace margelo::nitro::onekeyimage::views
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-image",
|
|
3
|
-
"version": "6.7.0-alpha.
|
|
3
|
+
"version": "6.7.0-alpha.418",
|
|
4
4
|
"description": "High-performance native image view for OneKey",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -10,7 +10,27 @@
|
|
|
10
10
|
"types": "./lib/typescript/src/index.d.ts",
|
|
11
11
|
"default": "./lib/module/index.js"
|
|
12
12
|
},
|
|
13
|
-
"./package.json": "./package.json"
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
"./icon": {
|
|
15
|
+
"browser": {
|
|
16
|
+
"source": "./src/icon/index.web.tsx",
|
|
17
|
+
"types": "./lib/typescript/src/icon/index.d.ts",
|
|
18
|
+
"default": "./lib/module/icon/index.web.js"
|
|
19
|
+
},
|
|
20
|
+
"react-native": {
|
|
21
|
+
"source": "./src/icon/index.tsx",
|
|
22
|
+
"types": "./lib/typescript/src/icon/index.d.ts",
|
|
23
|
+
"default": "./lib/module/icon/index.js"
|
|
24
|
+
},
|
|
25
|
+
"types": "./lib/typescript/src/icon/index.d.ts",
|
|
26
|
+
"source": "./src/icon/index.tsx",
|
|
27
|
+
"default": "./lib/module/icon/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./icon/dom": {
|
|
30
|
+
"source": "./src/icon/dom.ts",
|
|
31
|
+
"types": "./lib/typescript/src/icon/dom.d.ts",
|
|
32
|
+
"default": "./lib/module/icon/dom.js"
|
|
33
|
+
}
|
|
14
34
|
},
|
|
15
35
|
"files": [
|
|
16
36
|
"src",
|
|
@@ -30,16 +50,19 @@
|
|
|
30
50
|
"!android/local.properties",
|
|
31
51
|
"!**/__tests__",
|
|
32
52
|
"!**/.*",
|
|
33
|
-
"!**/*.map"
|
|
53
|
+
"!**/*.map",
|
|
54
|
+
"scripts"
|
|
34
55
|
],
|
|
35
56
|
"scripts": {
|
|
36
57
|
"clean": "del-cli android/build lib",
|
|
37
|
-
"prepare": "rm -rf lib && bob build && cp -r nitrogen lib/nitrogen",
|
|
58
|
+
"prepare": "rm -rf lib && bob build && cp -r nitrogen lib/nitrogen && node scripts/configure-icon-imports.mjs",
|
|
38
59
|
"nitrogen": "nitrogen",
|
|
39
60
|
"typecheck": "tsc",
|
|
40
61
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
41
62
|
"test": "jest",
|
|
42
|
-
"release": "yarn nitrogen && yarn prepare && npm whoami && npm publish --access public"
|
|
63
|
+
"release": "yarn nitrogen && yarn prepare && npm whoami && npm publish --access public",
|
|
64
|
+
"icons:check": "node scripts/icon-fixtures.test.mjs",
|
|
65
|
+
"icons:test": "node scripts/icon-fixtures.test.mjs"
|
|
43
66
|
},
|
|
44
67
|
"keywords": [
|
|
45
68
|
"react-native",
|
|
@@ -85,7 +108,7 @@
|
|
|
85
108
|
"typescript": "^5.9.2"
|
|
86
109
|
},
|
|
87
110
|
"peerDependencies": {
|
|
88
|
-
"@onekeyfe/react-native-skeleton": "6.7.0-alpha.
|
|
111
|
+
"@onekeyfe/react-native-skeleton": "6.7.0-alpha.418",
|
|
89
112
|
"react": "*",
|
|
90
113
|
"react-native": "*",
|
|
91
114
|
"react-native-nitro-modules": "0.37.0"
|
|
@@ -127,6 +150,16 @@
|
|
|
127
150
|
"preset": "react-native",
|
|
128
151
|
"modulePathIgnorePatterns": [
|
|
129
152
|
"<rootDir>/lib/"
|
|
130
|
-
]
|
|
153
|
+
],
|
|
154
|
+
"moduleNameMapper": {
|
|
155
|
+
"^#onekey-icon-assets$": "<rootDir>/../react-native-image/src/icon/assetUrls.ts"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"imports": {
|
|
159
|
+
"#onekey-icon-assets": {
|
|
160
|
+
"types": "./src/icon/assetUrls.ts",
|
|
161
|
+
"source": "./src/icon/assetUrls.ts",
|
|
162
|
+
"default": "./lib/module/icon/assetUrls.js"
|
|
163
|
+
}
|
|
131
164
|
}
|
|
132
165
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
|
|
3
|
+
// Bob creates a nested package scope; preserve the provider fallback there too.
|
|
4
|
+
const file = new URL('../lib/module/package.json', import.meta.url);
|
|
5
|
+
const metadata = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
6
|
+
metadata.imports = { '#onekey-icon-assets': './icon/assetUrls.js' };
|
|
7
|
+
fs.writeFileSync(file, JSON.stringify(metadata, null, 2) + '\n');
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import test from 'node:test';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const packageRoot = path.resolve(
|
|
11
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
12
|
+
'..'
|
|
13
|
+
);
|
|
14
|
+
const fixtureRoot = path.resolve(
|
|
15
|
+
packageRoot,
|
|
16
|
+
'../../example/react-native/icon-fixtures'
|
|
17
|
+
);
|
|
18
|
+
const nativeRoot = path.join(fixtureRoot, 'native/icons');
|
|
19
|
+
const webRoot = path.join(fixtureRoot, 'web/assets');
|
|
20
|
+
const manifest = JSON.parse(
|
|
21
|
+
fs.readFileSync(path.join(nativeRoot, 'manifest.json'), 'utf8')
|
|
22
|
+
);
|
|
23
|
+
const names = Object.keys(manifest).sort();
|
|
24
|
+
|
|
25
|
+
test('SDK fixtures have paired raw and external symbol resources with verified identities', () => {
|
|
26
|
+
assert.equal(names.length, 63);
|
|
27
|
+
for (const root of [nativeRoot, webRoot])
|
|
28
|
+
assert.deepEqual(
|
|
29
|
+
fs
|
|
30
|
+
.readdirSync(root)
|
|
31
|
+
.filter((name) => name.endsWith('.svg'))
|
|
32
|
+
.sort(),
|
|
33
|
+
names.map((name) => name + '.svg')
|
|
34
|
+
);
|
|
35
|
+
const urls = fs.readFileSync(
|
|
36
|
+
path.join(fixtureRoot, 'web/assetUrls.ts'),
|
|
37
|
+
'utf8'
|
|
38
|
+
);
|
|
39
|
+
assert.deepEqual(
|
|
40
|
+
[...urls.matchAll(/^ ([A-Z][A-Za-z0-9]+):/gm)].map((match) => match[1]),
|
|
41
|
+
names
|
|
42
|
+
);
|
|
43
|
+
const nativeListWebBuiltins = [
|
|
44
|
+
'ArrowBottomOutline',
|
|
45
|
+
'ArrowTopOutline',
|
|
46
|
+
'CoinsOutline',
|
|
47
|
+
'Copy3Outline',
|
|
48
|
+
'CryptoCoinOutline',
|
|
49
|
+
'Document2Outline',
|
|
50
|
+
'DotHorOutline',
|
|
51
|
+
'ImageSquareWavesOutline',
|
|
52
|
+
'ShieldCheckDoneOutline',
|
|
53
|
+
'SwitchHorOutline',
|
|
54
|
+
'UnlockedOutline',
|
|
55
|
+
];
|
|
56
|
+
for (const name of nativeListWebBuiltins) assert.ok(manifest[name], name);
|
|
57
|
+
const modes = new Set();
|
|
58
|
+
for (const name of names) {
|
|
59
|
+
const entry = manifest[name];
|
|
60
|
+
assert.match(name, /^[A-Z][A-Za-z0-9]+$/);
|
|
61
|
+
assert.match(
|
|
62
|
+
entry.source,
|
|
63
|
+
/^(?:brand|colored|custom|illus|outline|solid)\/[a-z0-9-]+\.svg$/
|
|
64
|
+
);
|
|
65
|
+
assert.equal(entry.viewBox.length, 4);
|
|
66
|
+
assert.ok(entry.viewBox.every(Number.isFinite));
|
|
67
|
+
assert.ok(entry.viewBox[2] > 0 && entry.viewBox[3] > 0);
|
|
68
|
+
assert.ok(
|
|
69
|
+
['fixed', 'single', 'multiple', 'raster'].includes(entry.recolorMode)
|
|
70
|
+
);
|
|
71
|
+
modes.add(entry.recolorMode);
|
|
72
|
+
const raw = fs.readFileSync(path.join(nativeRoot, name + '.svg'), 'utf8');
|
|
73
|
+
assert.equal(
|
|
74
|
+
crypto.createHash('sha256').update(raw).digest('hex'),
|
|
75
|
+
entry.digest
|
|
76
|
+
);
|
|
77
|
+
const symbol = fs.readFileSync(path.join(webRoot, name + '.svg'), 'utf8');
|
|
78
|
+
assert.ok(symbol.includes(`id="onekey-${name}"`));
|
|
79
|
+
for (const match of symbol.matchAll(/(?:id="|url\(#)([^"\)]+)/g))
|
|
80
|
+
assert.ok(match[1].startsWith(`onekey-${name}`));
|
|
81
|
+
}
|
|
82
|
+
assert.deepEqual([...modes].sort(), [
|
|
83
|
+
'fixed',
|
|
84
|
+
'multiple',
|
|
85
|
+
'raster',
|
|
86
|
+
'single',
|
|
87
|
+
]);
|
|
88
|
+
assert.notEqual(
|
|
89
|
+
manifest.BadgeVerifiedSolid.digest,
|
|
90
|
+
manifest.BadgeRecognizedSolid.digest
|
|
91
|
+
);
|
|
92
|
+
assert.ok(manifest.EditOutline);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('production resource inputs contain no named SVG or example fixture dependency', () => {
|
|
96
|
+
assert.equal(fs.existsSync(path.join(packageRoot, 'resources/icons')), false);
|
|
97
|
+
assert.equal(fs.existsSync(path.join(packageRoot, 'src/icon/assets')), false);
|
|
98
|
+
const pod = fs.readFileSync(
|
|
99
|
+
path.join(packageRoot, 'OneKeyImage.podspec'),
|
|
100
|
+
'utf8'
|
|
101
|
+
);
|
|
102
|
+
assert.doesNotMatch(pod, /s\.resource_bundles|s\.resources\s*=/);
|
|
103
|
+
const gradle = fs.readFileSync(
|
|
104
|
+
path.join(packageRoot, 'android/build.gradle'),
|
|
105
|
+
'utf8'
|
|
106
|
+
);
|
|
107
|
+
assert.doesNotMatch(gradle, /assets\.srcDirs|icon-fixtures/);
|
|
108
|
+
const stub = fs.readFileSync(
|
|
109
|
+
path.join(packageRoot, 'src/icon/assetUrls.ts'),
|
|
110
|
+
'utf8'
|
|
111
|
+
);
|
|
112
|
+
assert.doesNotMatch(stub, /new URL|\.svg/);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('cross-module native consumers share OneKeyNamedSVG metadata and onekey-icon:// scheme', () => {
|
|
116
|
+
const iosSvg = fs.readFileSync(
|
|
117
|
+
path.join(packageRoot, 'ios/OneKeyNamedSVG.swift'),
|
|
118
|
+
'utf8'
|
|
119
|
+
);
|
|
120
|
+
assert.match(iosSvg, /public func iconMetadata\(forName name: String\)/);
|
|
121
|
+
assert.match(iosSvg, /@objc\(OneKeyNamedSVGBridge\)/);
|
|
122
|
+
|
|
123
|
+
const androidSvg = fs.readFileSync(
|
|
124
|
+
path.join(
|
|
125
|
+
packageRoot,
|
|
126
|
+
'android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyNamedSVG.kt'
|
|
127
|
+
),
|
|
128
|
+
'utf8'
|
|
129
|
+
);
|
|
130
|
+
assert.match(
|
|
131
|
+
androidSvg,
|
|
132
|
+
/fun iconMetadata\(assets: AssetManager, name: String\)/
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const nativeListIos = fs.readFileSync(
|
|
136
|
+
path.resolve(
|
|
137
|
+
packageRoot,
|
|
138
|
+
'../react-native-native-list/ios/NativeListDesignAssets.swift'
|
|
139
|
+
),
|
|
140
|
+
'utf8'
|
|
141
|
+
);
|
|
142
|
+
assert.match(
|
|
143
|
+
nativeListIos,
|
|
144
|
+
/OneKeyNamedSVG\.shared\.iconMetadata\(forName: name\)/
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const nativeListAndroid = fs.readFileSync(
|
|
148
|
+
path.resolve(
|
|
149
|
+
packageRoot,
|
|
150
|
+
'../react-native-native-list/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt'
|
|
151
|
+
),
|
|
152
|
+
'utf8'
|
|
153
|
+
);
|
|
154
|
+
assert.match(
|
|
155
|
+
nativeListAndroid,
|
|
156
|
+
/OneKeyNamedSVG\.iconMetadata\(context\.applicationContext\.assets, iconName\)/
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const bottomTabsJs = fs.readFileSync(
|
|
160
|
+
path.resolve(
|
|
161
|
+
packageRoot,
|
|
162
|
+
'../react-native-bottom-tabs/lib/module/BottomTabs.js'
|
|
163
|
+
),
|
|
164
|
+
'utf8'
|
|
165
|
+
);
|
|
166
|
+
assert.match(bottomTabsJs, /onekey-icon:\/\//);
|
|
167
|
+
|
|
168
|
+
const bottomTabsIos = fs.readFileSync(
|
|
169
|
+
path.resolve(
|
|
170
|
+
packageRoot,
|
|
171
|
+
'../react-native-bottom-tabs/ios/RCTBottomTabsContainerView.swift'
|
|
172
|
+
),
|
|
173
|
+
'utf8'
|
|
174
|
+
);
|
|
175
|
+
assert.match(bottomTabsIos, /onekey-icon:\/\//);
|
|
176
|
+
assert.match(bottomTabsIos, /OneKeyNamedSVGLoader\.request/);
|
|
177
|
+
|
|
178
|
+
const bottomTabsAndroid = fs.readFileSync(
|
|
179
|
+
path.resolve(
|
|
180
|
+
packageRoot,
|
|
181
|
+
'../react-native-bottom-tabs/android/src/main/java/com/onekey/bottomtabs/BottomTabsView.kt'
|
|
182
|
+
),
|
|
183
|
+
'utf8'
|
|
184
|
+
);
|
|
185
|
+
assert.match(bottomTabsAndroid, /onekey-icon:\/\//);
|
|
186
|
+
assert.match(bottomTabsAndroid, /OneKeyNamedSVG\.request/);
|
|
187
|
+
|
|
188
|
+
if (process.platform === 'darwin') {
|
|
189
|
+
const tempDir = fs.mkdtempSync(
|
|
190
|
+
path.join(os.tmpdir(), 'bottom-tabs-icon-ordering-')
|
|
191
|
+
);
|
|
192
|
+
try {
|
|
193
|
+
const sdkPath = execFileSync(
|
|
194
|
+
'xcrun',
|
|
195
|
+
['--sdk', 'macosx', '--show-sdk-path'],
|
|
196
|
+
{ encoding: 'utf8' }
|
|
197
|
+
).trim();
|
|
198
|
+
const harnessPath = path.join(tempDir, 'test_harness.swift');
|
|
199
|
+
const binPath = path.join(tempDir, 'bottom-tabs-ordering');
|
|
200
|
+
const bottomTabsIosDir = path.resolve(
|
|
201
|
+
packageRoot,
|
|
202
|
+
'../react-native-bottom-tabs/ios'
|
|
203
|
+
);
|
|
204
|
+
fs.writeFileSync(
|
|
205
|
+
harnessPath,
|
|
206
|
+
`import Foundation
|
|
207
|
+
import UIKit
|
|
208
|
+
|
|
209
|
+
public typealias RCTDirectEventBlock = ([AnyHashable: Any]?) -> Void
|
|
210
|
+
|
|
211
|
+
@objc public enum RCTResizeMode: Int {
|
|
212
|
+
case cover = 1
|
|
213
|
+
case contain = 2
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@objc public protocol RCTImageLoaderProtocol: NSObjectProtocol {
|
|
217
|
+
@discardableResult
|
|
218
|
+
func loadImage(
|
|
219
|
+
with imageURL: URLRequest?,
|
|
220
|
+
size: CGSize,
|
|
221
|
+
scale: CGFloat,
|
|
222
|
+
clipped: Bool,
|
|
223
|
+
resizeMode: RCTResizeMode,
|
|
224
|
+
progressBlock: @escaping (Int64, Int64) -> Void,
|
|
225
|
+
partialLoad: @escaping (UIImage?) -> Void,
|
|
226
|
+
completionBlock: @escaping (Error?, UIImage?) -> Void
|
|
227
|
+
) -> (() -> Void)?
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public enum RCTFont {
|
|
231
|
+
public static func update(
|
|
232
|
+
_ font: UIFont?,
|
|
233
|
+
withFamily family: String?,
|
|
234
|
+
size: NSNumber?,
|
|
235
|
+
weight: String?,
|
|
236
|
+
style: String?,
|
|
237
|
+
variant: [Any]?,
|
|
238
|
+
scaleMultiplier: CGFloat
|
|
239
|
+
) -> UIFont {
|
|
240
|
+
UIFont.systemFont(ofSize: CGFloat(size?.doubleValue ?? 12))
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
extension UIView {
|
|
245
|
+
public func reactViewController() -> UIViewController? { nil }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@objc(OneKeyNamedSVGBridge)
|
|
249
|
+
final class OneKeyNamedSVGBridge: NSObject {
|
|
250
|
+
static var pending: [(owner: AnyObject, name: String, complete: (UIImage?) -> Void)] = []
|
|
251
|
+
static var cancelledOwners: [ObjectIdentifier] = []
|
|
252
|
+
|
|
253
|
+
@objc(cancelWithOwner:)
|
|
254
|
+
static func cancel(owner: AnyObject) {
|
|
255
|
+
cancelledOwners.append(ObjectIdentifier(owner))
|
|
256
|
+
pending.removeAll { $0.owner === owner }
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
@objc(requestWithOwner:name:color:width:height:scale:complete:)
|
|
260
|
+
static func request(
|
|
261
|
+
owner: AnyObject,
|
|
262
|
+
name: String,
|
|
263
|
+
color: String,
|
|
264
|
+
width: Int,
|
|
265
|
+
height: Int,
|
|
266
|
+
scale: Double,
|
|
267
|
+
complete: @escaping (UIImage?) -> Void
|
|
268
|
+
) {
|
|
269
|
+
pending.append((owner: owner, name: name, complete: complete))
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
final class ControlledImageLoader: NSObject, RCTImageLoaderProtocol {
|
|
274
|
+
var pending: [(url: String, complete: (Error?, UIImage?) -> Void)] = []
|
|
275
|
+
|
|
276
|
+
func loadImage(
|
|
277
|
+
with imageURL: URLRequest?,
|
|
278
|
+
size: CGSize,
|
|
279
|
+
scale: CGFloat,
|
|
280
|
+
clipped: Bool,
|
|
281
|
+
resizeMode: RCTResizeMode,
|
|
282
|
+
progressBlock: @escaping (Int64, Int64) -> Void,
|
|
283
|
+
partialLoad: @escaping (UIImage?) -> Void,
|
|
284
|
+
completionBlock: @escaping (Error?, UIImage?) -> Void
|
|
285
|
+
) -> (() -> Void)? {
|
|
286
|
+
pending.append((url: imageURL?.url?.absoluteString ?? "", complete: completionBlock))
|
|
287
|
+
return nil
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
func solidImage(_ color: UIColor, width: CGFloat) -> UIImage {
|
|
292
|
+
let renderer = UIGraphicsImageRenderer(size: CGSize(width: width, height: 27))
|
|
293
|
+
return renderer.image { ctx in
|
|
294
|
+
color.setFill()
|
|
295
|
+
ctx.fill(CGRect(x: 0, y: 0, width: width, height: 27))
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
func inspectLoadedMap(_ view: RCTBottomTabsContainerView, label: String) -> [Int: UIImage] {
|
|
300
|
+
for child in Mirror(reflecting: view).children where child.label == label {
|
|
301
|
+
if let dict = child.value as? [Int: UIImage] {
|
|
302
|
+
return dict
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
fatalError("Missing field \\(label)")
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
@main
|
|
309
|
+
struct TestRunner {
|
|
310
|
+
static func main() {
|
|
311
|
+
let view = RCTBottomTabsContainerView(frame: CGRect(x: 0, y: 0, width: 320, height: 80))
|
|
312
|
+
let loader = ControlledImageLoader()
|
|
313
|
+
view.setImageLoader(loader)
|
|
314
|
+
|
|
315
|
+
// Case 1: Ordinary URL starts first, switches to named icon, named finishes first, stale ordinary finishes second.
|
|
316
|
+
view.icons = [["uri": "https://example.com/old.png", "width": 27.0, "height": 27.0, "scale": 1.0]]
|
|
317
|
+
view.selectedIcons = [["uri": "https://example.com/old-selected.png", "width": 27.0, "height": 27.0, "scale": 1.0]]
|
|
318
|
+
precondition(loader.pending.count == 2)
|
|
319
|
+
|
|
320
|
+
view.icons = [["uri": "onekey-icon://Wallet4Outline", "width": 27.0, "height": 27.0, "scale": 1.0]]
|
|
321
|
+
view.selectedIcons = [["uri": "onekey-icon://Wallet4Solid", "width": 27.0, "height": 27.0, "scale": 1.0]]
|
|
322
|
+
precondition(OneKeyNamedSVGBridge.pending.count == 2)
|
|
323
|
+
|
|
324
|
+
OneKeyNamedSVGBridge.pending[0].complete(solidImage(.blue, width: 41))
|
|
325
|
+
OneKeyNamedSVGBridge.pending[1].complete(solidImage(.green, width: 43))
|
|
326
|
+
OneKeyNamedSVGBridge.pending.removeAll()
|
|
327
|
+
|
|
328
|
+
precondition(inspectLoadedMap(view, label: "loadedIcons")[0]?.size.width == 41)
|
|
329
|
+
precondition(inspectLoadedMap(view, label: "loadedSelectedIcons")[0]?.size.width == 43)
|
|
330
|
+
|
|
331
|
+
let staleOrdinary = solidImage(.red, width: 27)
|
|
332
|
+
for item in loader.pending {
|
|
333
|
+
item.complete(nil, staleOrdinary)
|
|
334
|
+
}
|
|
335
|
+
loader.pending.removeAll()
|
|
336
|
+
RunLoop.main.run(until: Date().addingTimeInterval(0.05))
|
|
337
|
+
|
|
338
|
+
precondition(inspectLoadedMap(view, label: "loadedIcons")[0]?.size.width == 41)
|
|
339
|
+
precondition(inspectLoadedMap(view, label: "loadedSelectedIcons")[0]?.size.width == 43)
|
|
340
|
+
|
|
341
|
+
// Case 2: Named icon starts first, switches to ordinary URL, ordinary finishes first, stale named callback fires second.
|
|
342
|
+
view.icons = [["uri": "onekey-icon://CoinsOutline", "width": 27.0, "height": 27.0, "scale": 1.0]]
|
|
343
|
+
view.selectedIcons = [["uri": "onekey-icon://CoinsSolid", "width": 27.0, "height": 27.0, "scale": 1.0]]
|
|
344
|
+
precondition(OneKeyNamedSVGBridge.pending.count == 2)
|
|
345
|
+
let staleNamedCallbacks = OneKeyNamedSVGBridge.pending
|
|
346
|
+
|
|
347
|
+
view.icons = [["uri": "https://example.com/new.png", "width": 27.0, "height": 27.0, "scale": 1.0]]
|
|
348
|
+
view.selectedIcons = [["uri": "https://example.com/new-selected.png", "width": 27.0, "height": 27.0, "scale": 1.0]]
|
|
349
|
+
precondition(loader.pending.count == 2)
|
|
350
|
+
|
|
351
|
+
for item in loader.pending {
|
|
352
|
+
item.complete(nil, solidImage(.yellow, width: 27))
|
|
353
|
+
}
|
|
354
|
+
loader.pending.removeAll()
|
|
355
|
+
RunLoop.main.run(until: Date().addingTimeInterval(0.05))
|
|
356
|
+
precondition(inspectLoadedMap(view, label: "loadedIcons")[0]?.size.width == 27)
|
|
357
|
+
precondition(inspectLoadedMap(view, label: "loadedSelectedIcons")[0]?.size.width == 27)
|
|
358
|
+
|
|
359
|
+
staleNamedCallbacks[0].complete(solidImage(.purple, width: 51))
|
|
360
|
+
staleNamedCallbacks[1].complete(solidImage(.orange, width: 53))
|
|
361
|
+
precondition(inspectLoadedMap(view, label: "loadedIcons")[0]?.size.width == 27)
|
|
362
|
+
precondition(inspectLoadedMap(view, label: "loadedSelectedIcons")[0]?.size.width == 27)
|
|
363
|
+
|
|
364
|
+
print("RCTBottomTabsContainerViewOrdering: all assertions passed")
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
`
|
|
368
|
+
);
|
|
369
|
+
execFileSync('xcrun', [
|
|
370
|
+
'--sdk',
|
|
371
|
+
'macosx',
|
|
372
|
+
'swiftc',
|
|
373
|
+
'-target',
|
|
374
|
+
'arm64-apple-ios16.4-macabi',
|
|
375
|
+
'-I',
|
|
376
|
+
`${sdkPath}/System/iOSSupport/usr/lib/swift`,
|
|
377
|
+
'-F',
|
|
378
|
+
`${sdkPath}/System/iOSSupport/System/Library/Frameworks`,
|
|
379
|
+
'-module-cache-path',
|
|
380
|
+
path.join(tempDir, 'module-cache'),
|
|
381
|
+
harnessPath,
|
|
382
|
+
path.join(bottomTabsIosDir, 'Extensions.swift'),
|
|
383
|
+
path.join(bottomTabsIosDir, 'TabBarFontSize.swift'),
|
|
384
|
+
path.join(bottomTabsIosDir, 'RCTBottomAccessoryContainerView.swift'),
|
|
385
|
+
path.join(bottomTabsIosDir, 'RCTBottomTabsContainerView.swift'),
|
|
386
|
+
'-o',
|
|
387
|
+
binPath,
|
|
388
|
+
]);
|
|
389
|
+
const stdout = execFileSync(binPath, [], { encoding: 'utf8' });
|
|
390
|
+
assert.match(
|
|
391
|
+
stdout,
|
|
392
|
+
/RCTBottomTabsContainerViewOrdering: all assertions passed/
|
|
393
|
+
);
|
|
394
|
+
} finally {
|
|
395
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Hosts provide named artwork through a static build alias.
|
|
2
|
+
let warnedMissingHostAlias = false;
|
|
3
|
+
|
|
4
|
+
export function iconResourceHref(name: string): string | undefined {
|
|
5
|
+
// Plain web bundlers may not define __DEV__; treat it as production there.
|
|
6
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__ && !warnedMissingHostAlias) {
|
|
7
|
+
warnedMissingHostAlias = true;
|
|
8
|
+
console.warn(
|
|
9
|
+
`[OneKeyIcon] "#onekey-icon-assets" resolved to the SDK fallback, so named icons such as "${name}" render blank. Alias it to the host's generated icon table in the web build.`
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
package/src/icon/dom.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { iconResourceHref } from '#onekey-icon-assets';
|
|
2
|
+
|
|
3
|
+
/** Native SDK DOM consumers own name coverage, styling and accessible labels. */
|
|
4
|
+
export function createNamedIcon(
|
|
5
|
+
document: Document,
|
|
6
|
+
name: string
|
|
7
|
+
): SVGSVGElement {
|
|
8
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
9
|
+
svg.setAttribute('width', '24');
|
|
10
|
+
svg.setAttribute('height', '24');
|
|
11
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
12
|
+
svg.setAttribute('focusable', 'false');
|
|
13
|
+
const href = iconResourceHref(name);
|
|
14
|
+
if (href) {
|
|
15
|
+
const use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
|
16
|
+
use.setAttribute('href', href);
|
|
17
|
+
svg.append(use);
|
|
18
|
+
}
|
|
19
|
+
return svg;
|
|
20
|
+
}
|
|
21
|
+
/// <reference lib="dom" />
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
getHostComponent,
|
|
4
|
+
type HybridViewMethods,
|
|
5
|
+
} from 'react-native-nitro-modules';
|
|
6
|
+
import type { OneKeyIconNativeProps } from '../OneKeyIcon.nitro';
|
|
7
|
+
import { isIconName, normalizeIconColor, type OneKeyIconProps } from './props';
|
|
8
|
+
|
|
9
|
+
const config = require('../../nitrogen/generated/shared/json/OneKeyIconConfig.json');
|
|
10
|
+
const NativeIcon = getHostComponent<OneKeyIconNativeProps, HybridViewMethods>(
|
|
11
|
+
'OneKeyIcon',
|
|
12
|
+
() => config
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export type { OneKeyIconProps } from './props';
|
|
16
|
+
|
|
17
|
+
export function OneKeyIcon({
|
|
18
|
+
name,
|
|
19
|
+
color,
|
|
20
|
+
style,
|
|
21
|
+
...viewProps
|
|
22
|
+
}: OneKeyIconProps) {
|
|
23
|
+
const resolvedColor = normalizeIconColor(color);
|
|
24
|
+
if (__DEV__ && !resolvedColor) {
|
|
25
|
+
// Only hex, rgb() and rgba() are supported; anything else renders blank.
|
|
26
|
+
console.warn(
|
|
27
|
+
`[OneKeyIcon] Unsupported color ${JSON.stringify(
|
|
28
|
+
color
|
|
29
|
+
)} for "${name}"; expected #hex, rgb() or rgba().`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return createElement(NativeIcon, {
|
|
33
|
+
...viewProps,
|
|
34
|
+
iconName: resolvedColor && isIconName(name) ? name : '',
|
|
35
|
+
resolvedColor: resolvedColor ?? '',
|
|
36
|
+
style: [{ width: 24, height: 24 }, style],
|
|
37
|
+
});
|
|
38
|
+
}
|