1 | 'use strict';
|
2 |
|
3 | import {
|
4 | NativeModules,
|
5 | PixelRatio,
|
6 | Platform,
|
7 | } from 'react-native';
|
8 |
|
9 | import AssetRegistry from 'react-native/Libraries/Image/AssetRegistry';
|
10 | import AssetSourceResolver from 'react-native/Libraries/Image/AssetSourceResolver';
|
11 | import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
|
12 |
|
13 | import { manifest } from './Constants';
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | const pickScale = (meta) => {
|
19 |
|
20 |
|
21 |
|
22 | const scale = meta.scales.length > 1 ?
|
23 | AssetSourceResolver.pickScale(meta.scales, PixelRatio.get()) :
|
24 | 1;
|
25 | const index = meta.scales.findIndex(s => s === scale);
|
26 | const hash = meta.fileHashes[index] || meta.fileHashes[0];
|
27 |
|
28 | if (manifest.xde) {
|
29 |
|
30 | const suffix = scale === 1 ? '' : '@' + scale + 'x';
|
31 | return {
|
32 | uri: manifest.bundleUrl.match(/^https?:\/\/.*?\//)[0] +
|
33 | meta.httpServerLocation.replace(/^\/?/, '') +
|
34 | '/' + meta.name + suffix + '.' + meta.type +
|
35 | '?platform=' + Platform.OS + '&hash=' + meta.hash,
|
36 | hash,
|
37 | };
|
38 | }
|
39 |
|
40 |
|
41 | return {
|
42 | uri: 'https://d1wp6m56sqw74a.cloudfront.net/~assets/' + hash,
|
43 | hash,
|
44 | };
|
45 | };
|
46 |
|
47 | export default class Asset {
|
48 | static byModule = {};
|
49 |
|
50 | constructor({ name, type, hash, uri, width, height }) {
|
51 | this.name = name;
|
52 | this.type = type;
|
53 | this.hash = hash;
|
54 | this.uri = uri;
|
55 | if (typeof width === 'number') {
|
56 | this.width = width;
|
57 | }
|
58 | if (typeof height === 'number') {
|
59 | this.height = height;
|
60 | }
|
61 |
|
62 | this.downloading = false;
|
63 | this.downloaded = false;
|
64 | this.downloadCallbacks = [];
|
65 | }
|
66 |
|
67 | static fromModule(moduleId) {
|
68 | if (Asset.byModule[moduleId]) {
|
69 | return Asset.byModule[moduleId];
|
70 | }
|
71 |
|
72 |
|
73 |
|
74 | const meta = AssetRegistry.getAssetByID(moduleId);
|
75 | meta.moduleId = moduleId;
|
76 | const { uri, hash } = pickScale(meta);
|
77 |
|
78 | const asset = new Asset({
|
79 | name: meta.name,
|
80 | type: meta.type,
|
81 | hash,
|
82 | uri,
|
83 | width: meta.width,
|
84 | height: meta.height,
|
85 | });
|
86 | Asset.byModule[moduleId] = asset;
|
87 | return asset;
|
88 | }
|
89 |
|
90 | async downloadAsync() {
|
91 | if (this.downloaded) {
|
92 | return;
|
93 | }
|
94 | if (this.downloading) {
|
95 | await new Promise((resolve, reject) =>
|
96 | this.downloadCallbacks.push({ resolve, reject }));
|
97 | return;
|
98 | }
|
99 | this.downloading = true;
|
100 |
|
101 | try {
|
102 | const path = `ExponentAsset-${this.hash}.${this.type}`;
|
103 | let exists, md5, uri;
|
104 | ({ exists, md5, uri } = await NativeModules.ExponentFileSystem.getInfoAsync(
|
105 | path, { cache: true, md5: true }));
|
106 | if (!exists || md5 !== this.hash) {
|
107 | ({ md5, uri } = await NativeModules.ExponentFileSystem.downloadAsync(
|
108 | this.uri, path, { cache: true, md5: true }));
|
109 | if (md5 !== this.hash) {
|
110 | throw new Error(`Downloaded file for asset '${this.name}.${this.type}' ` +
|
111 | `failed MD5 integrity check`);
|
112 | }
|
113 | }
|
114 | this.localUri = uri;
|
115 | this.downloaded = true;
|
116 | this.downloadCallbacks.forEach(({ resolve }) => resolve());
|
117 | } catch (e) {
|
118 | this.downloadCallbacks.forEach(({ reject }) => reject(e));
|
119 | throw e;
|
120 | } finally {
|
121 | this.downloading = false;
|
122 | this.downloadCallbacks = [];
|
123 | }
|
124 | }
|
125 | }
|
126 |
|
127 |
|
128 | resolveAssetSource.setCustomSourceTransformer((resolver) => {
|
129 | if (!resolver.asset.moduleId) {
|
130 | return resolver.fromSource(pickScale(resolver.asset).uri);
|
131 | }
|
132 | const asset = Asset.fromModule(resolver.asset.moduleId);
|
133 | return resolver.fromSource(asset.downloaded ? asset.localUri : asset.uri);
|
134 | });
|
135 |
|