@shoutem/ui 6.3.2 → 6.3.3

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.
@@ -13,6 +13,8 @@ import autoBindReact from 'auto-bind/react';
13
13
  import _ from 'lodash';
14
14
  import PropTypes from 'prop-types';
15
15
  import { connectStyle } from '@shoutem/theme';
16
+ import VideoRenderer from '@shoutem/ui/html/components/VideoRenderer';
17
+ import { videoModel } from '@shoutem/ui/html/services/HTMLElementModels';
16
18
  import { View } from '../../components/View';
17
19
  import { resolveMaxWidth } from '../services/Dimensions';
18
20
  import { onElement } from '../services/DomVisitors';
@@ -38,10 +40,14 @@ class SimpleHtml extends PureComponent {
38
40
  const {
39
41
  style,
40
42
  body,
41
- customTagStyles,
42
43
  unsupportedVideoFormatMessage,
43
44
  attachments,
44
- domVisitors,
45
+ customDomVisitors,
46
+ customCustomRenderers,
47
+ customTagStyles,
48
+ customHtmlElementModels,
49
+ customIgnoredStyles,
50
+ customRendererProps,
45
51
  ...otherProps
46
52
  } = this.props;
47
53
 
@@ -76,6 +82,8 @@ class SimpleHtml extends PureComponent {
76
82
  style={style}
77
83
  />
78
84
  ),
85
+ video: VideoRenderer,
86
+ ...customCustomRenderers,
79
87
  };
80
88
 
81
89
  const htmlProps = {
@@ -85,7 +93,12 @@ class SimpleHtml extends PureComponent {
85
93
  tagsStyles: _.omitBy(tagStyles, tagStyle => !tagStyle),
86
94
  systemFonts: [...defaultSystemFonts, style.baseFont.fontFamily],
87
95
  baseStyle: style.baseFont,
88
- ignoredStyles: ['fontFamily', 'letterSpacing', 'transform'],
96
+ ignoredStyles: [
97
+ 'fontFamily',
98
+ 'letterSpacing',
99
+ 'transform',
100
+ ...customIgnoredStyles,
101
+ ],
89
102
  renderers: customRenderers,
90
103
  renderersProps: {
91
104
  table: {
@@ -97,14 +110,17 @@ class SimpleHtml extends PureComponent {
97
110
  renderToHardwareTextureAndroid: true,
98
111
  },
99
112
  },
113
+ ...customRendererProps,
100
114
  },
101
115
  customHTMLElementModels: {
102
116
  table: tableModel,
103
117
  iframe: iframeModel,
118
+ video: videoModel,
119
+ ...customHtmlElementModels,
104
120
  },
105
121
  WebView,
106
122
  ignoredTags: IGNORED_TAGS,
107
- domVisitors: { onElement },
123
+ domVisitors: { onElement, ...customDomVisitors },
108
124
  };
109
125
 
110
126
  return (
@@ -119,8 +135,12 @@ SimpleHtml.propTypes = {
119
135
  style: PropTypes.object.isRequired,
120
136
  attachments: PropTypes.array,
121
137
  body: PropTypes.string,
122
- customAlterNode: PropTypes.func,
138
+ customCustomRenderers: PropTypes.object,
139
+ customDomVisitors: PropTypes.object,
123
140
  customHandleLinkPress: PropTypes.func,
141
+ customHtmlElementModels: PropTypes.object,
142
+ customIgnoredStyles: PropTypes.arrayOf(PropTypes.string),
143
+ customRendererProps: PropTypes.object,
124
144
  customTagStyles: PropTypes.object,
125
145
  domVisitors: PropTypes.object,
126
146
  unsupportedVideoFormatMessage: PropTypes.string,
@@ -129,11 +149,15 @@ SimpleHtml.propTypes = {
129
149
  SimpleHtml.defaultProps = {
130
150
  attachments: undefined,
131
151
  body: undefined,
132
- customAlterNode: undefined,
133
152
  customHandleLinkPress: undefined,
134
- customTagStyles: undefined,
153
+ customTagStyles: {},
135
154
  unsupportedVideoFormatMessage: undefined,
136
155
  domVisitors: { onElement },
156
+ customDomVisitors: {},
157
+ customCustomRenderers: {},
158
+ customHtmlElementModels: {},
159
+ customIgnoredStyles: [],
160
+ customRendererProps: {},
137
161
  };
138
162
 
139
163
  export default connectStyle('shoutem.ui.SimpleHtml')(SimpleHtml);
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import {
3
+ useComputeMaxWidthForTag,
4
+ useContentWidth,
5
+ } from 'react-native-render-html';
6
+ import WebView from 'react-native-webview';
7
+ import { findOne } from 'domutils';
8
+ import PropTypes from 'prop-types';
9
+
10
+ function findSource(tnode) {
11
+ if (tnode.attributes.src) {
12
+ return tnode.attributes.src;
13
+ }
14
+
15
+ const sourceElms = findOne(
16
+ elm => elm.tagName === 'source',
17
+ tnode.domNode.children,
18
+ );
19
+
20
+ return sourceElms ? sourceElms.attribs.src : '';
21
+ }
22
+
23
+ /**
24
+ *
25
+ * Generate video html with only selected attributes and parameters.
26
+ * @returns HTML video element
27
+ */
28
+ function generateVideoHtml(tnode) {
29
+ const url = findSource(tnode);
30
+
31
+ const containsTimeParameter = url.match(/(#|\?)t=[^&$]*/g, '');
32
+ // If video has no time parameter, we want to add time parameter at 0.1, just to be able to
33
+ // display the image, first frame of the video. Video tags that don't have autoplay attribute
34
+ // are not showing first frame without this solution.
35
+ // Otherwise, if user has defined time parameter, keep it as is.
36
+ const resolvedUrl = `${url}${containsTimeParameter ? '' : '#t=0.1'}`;
37
+
38
+ return `<video controls src="${resolvedUrl}" style="width: 100%; height: 100%;"/>`;
39
+ }
40
+
41
+ export const VideoRenderer = ({ tnode, style }) => {
42
+ const computeMaxWidth = useComputeMaxWidthForTag('video');
43
+ const width = computeMaxWidth(useContentWidth());
44
+
45
+ // Using uri as a Webview source is not an option, because we don't have as much control of video.
46
+ // E.g. if video from uri has autoplay=true, we can't prevent it from auto-playing when Webview renders.
47
+ // Instead, create video HTML element with only picked attributes and parameters.
48
+ const html = generateVideoHtml(tnode);
49
+
50
+ return (
51
+ <WebView
52
+ scrollEnabled={false}
53
+ source={{
54
+ html,
55
+ }}
56
+ style={[{ aspectRatio: 16 / 9 }, style, { width }]}
57
+ />
58
+ );
59
+ };
60
+
61
+ VideoRenderer.propTypes = {
62
+ tnode: PropTypes.object.isRequired,
63
+ style: PropTypes.object,
64
+ };
65
+
66
+ VideoRenderer.defaultProps = {
67
+ style: {},
68
+ };
69
+
70
+ export default VideoRenderer;
@@ -1,16 +1,19 @@
1
1
  import _ from 'lodash';
2
2
 
3
- // Shoutem RTE component wraps any video attachment (hosted inside iframe) with <figure>
3
+ // Shoutem RTE component wraps any video attachment (hosted inside iframe or video) with <figure>
4
4
  // element. That element contains style that breaks the SimpleHtml UI - enormous blank
5
5
  // space below attachment.
6
6
  // This function removes height and padding-bottom styles from figure to fix this UI issue.
7
7
  export const removeShoutemRteVideoAttachmentWrapper = element => {
8
8
  if (
9
- element.name === 'figure' &&
9
+ (element.name === 'figure' || element.name === 'video') &&
10
10
  element.children.length > 0 &&
11
11
  // <figure> can have multiple generated children too - type:text. Probably caused by empty space between
12
12
  // figure and iframe elements in generated HTML.
13
- _.some(element.children, ({ name }) => name === 'iframe')
13
+ _.some(
14
+ element.children,
15
+ ({ name }) => name === 'iframe' || name === 'video',
16
+ )
14
17
  ) {
15
18
  // Remove height and padding-bottom styles because they break the UI
16
19
  element.attribs.style = element.attribs.style
@@ -0,0 +1,7 @@
1
+ import { HTMLContentModel, HTMLElementModel } from 'react-native-render-html';
2
+
3
+ export const videoModel = HTMLElementModel.fromCustomModel({
4
+ contentModel: HTMLContentModel.block,
5
+ tagName: 'video',
6
+ isOpaque: true,
7
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shoutem/ui",
3
- "version": "6.3.2",
3
+ "version": "6.3.3",
4
4
  "description": "Styleable set of components for React Native applications",
5
5
  "scripts": {
6
6
  "lint": "eslint .",