Fix Video Unavailable problem at YouTube preview (#26980)

* Fix YouTube preview shows Video Unavailable

* Fix typo on iframe property

* Remove duplicated property

* fix lint error (double-quotes, unknown property)

* renew snapshot for youtube_video.tsx

* fix double quotes and newline error on snapshot for youtube_video.tsx

* fix blank on snapshot for youtube_video.tsx

* Add YouTube Shorts Embed Preview

* Revert "Add YouTube Shorts Embed Preview"

This reverts commit b5fb7a4076ef73774a0e50157f371516b9d383b2.

* Add setting for Youtube Referrer Policy

* fix test code error about Youtube Referrer Policy

* remove mistake changes on `webpack.config.js`

* add test and snapshot about `youtubeReferrerPolicy = true`

* fix errors on ci

* update description of YouTube Referrer Policy

* remove unnecessary whitespace in default_config.ts

* remove ko.json changes to prevent conflict with translate tool

* update snapshot of `youtube_video.test.tsx`

* referrerPolicy on `youtube_video` didn't follow global policy value

https://github.com/mattermost/mattermost/pull/26980#discussion_r1672524152

* update snapshot of `youtube_video.test.tsx`

* Remove obsolete snapshot

* fix typo on index.ts @ youtube_video

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
Этот коммит содержится в:
Yoo Dongryul
2024-08-22 18:35:04 +09:00
коммит произвёл GitHub
родитель 0d6b1070a2
Коммит 283f59b479
11 изменённых файлов: 86 добавлений и 1 удалений

Просмотреть файл

@@ -580,6 +580,7 @@ const defaultServerConfig: AdminConfig = {
DisableAppBar: false,
DisableRefetchingOnBrowserFocus: false,
DelayChannelAutocomplete: false,
YoutubeReferrerPolicy: false,
},
AnalyticsSettings: {
MaxUsersForStatistics: 2500,

Просмотреть файл

@@ -142,6 +142,7 @@ func GenerateClientConfig(c *model.Config, telemetryID string, license *model.Li
props["PersistentNotificationMaxRecipients"] = strconv.FormatInt(int64(*c.ServiceSettings.PersistentNotificationMaxRecipients), 10)
props["AllowSyncedDrafts"] = strconv.FormatBool(*c.ServiceSettings.AllowSyncedDrafts)
props["DelayChannelAutocomplete"] = strconv.FormatBool(*c.ExperimentalSettings.DelayChannelAutocomplete)
props["YoutubeReferrerPolicy"] = strconv.FormatBool(*c.ExperimentalSettings.YoutubeReferrerPolicy)
props["UniqueEmojiReactionLimitPerPost"] = strconv.FormatInt(int64(*c.ServiceSettings.UniqueEmojiReactionLimitPerPost), 10)
props["WranglerPermittedWranglerRoles"] = strings.Join(c.WranglerSettings.PermittedWranglerRoles, ",")

Просмотреть файл

@@ -778,6 +778,7 @@ func (ts *TelemetryService) trackConfig() {
"enable_app_bar": !*cfg.ExperimentalSettings.DisableAppBar,
"disable_refetching_on_browser_focus": *cfg.ExperimentalSettings.DisableRefetchingOnBrowserFocus,
"delay_channel_autocomplete": *cfg.ExperimentalSettings.DelayChannelAutocomplete,
"youtube_referrer_policy": *cfg.ExperimentalSettings.YoutubeReferrerPolicy,
})
ts.SendTelemetry(TrackConfigAnalytics, map[string]any{

Просмотреть файл

@@ -1075,6 +1075,7 @@ type ExperimentalSettings struct {
DelayChannelAutocomplete *bool `access:"experimental_features"`
DisableWakeUpReconnectHandler *bool `access:"experimental_features"`
UsersStatusAndProfileFetchingPollIntervalMilliseconds *int64 `access:"experimental_features"`
YoutubeReferrerPolicy *bool `access:"experimental_features"`
}
func (s *ExperimentalSettings) SetDefaults() {
@@ -1121,6 +1122,10 @@ func (s *ExperimentalSettings) SetDefaults() {
if s.UsersStatusAndProfileFetchingPollIntervalMilliseconds == nil {
s.UsersStatusAndProfileFetchingPollIntervalMilliseconds = NewPointer(int64(ExperimentalSettingsDefaultUsersStatusAndProfileFetchingPollIntervalMilliseconds))
}
if s.YoutubeReferrerPolicy == nil {
s.YoutubeReferrerPolicy = NewBool(false)
}
}
type AnalyticsSettings struct {

Просмотреть файл

@@ -6256,6 +6256,13 @@ const AdminDefinition: AdminDefinitionType = {
help_text: defineMessage({id: 'admin.experimental.delayChannelAutocomplete.desc', defaultMessage: 'When true, the autocomplete for channel links (such as ~town-square) will only trigger after typing a tilde followed by a couple letters. When false, the autocomplete will appear as soon as the user types a tilde.'}),
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)),
},
{
type: 'bool',
key: 'ExperimentalSettings.YoutubeReferrerPolicy',
label: defineMessage({id: 'admin.experimental.youtubeReferrerPolicy.title', defaultMessage: 'YouTube Referrer Policy:'}),
help_text: defineMessage({id: 'admin.experimental.youtubeReferrerPolicy.desc', defaultMessage: 'When true, the referrer policy for embedded YouTube videos will be set to "strict-origin-when-cross-origin" which resolves issues where YouTube video previews display as unavailable, while balancing the need to protect user privacy with some degree of referral data to support web functionalities, like analytics, logging, and third-party integrations. When false, the referrer policy will be set to "no-referrer" which enhances user privacy by not disclosing the source URL, but limits the ability to track user engagement and traffic sources in analytics tools.'}),
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)),
},
],
},
},

Просмотреть файл

@@ -30,6 +30,7 @@ exports[`YoutubeVideo should match init snapshot 1`] = `
}
postId="post_id_1"
show={true}
youtubeReferrerPolicy={false}
>
<div
className="post__embed-container"
@@ -131,10 +132,56 @@ exports[`YoutubeVideo should match snapshot for playing state 1`] = `
onClick={[Function]}
>
<iframe
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen={true}
frameBorder="0"
height="360px"
referrerPolicy="no-referrer"
sandbox="allow-scripts allow-same-origin allow-popups allow-presentation"
src="https://www.youtube.com/embed/xqCoNej8Zxo?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1"
title="Youtube title"
width="480px"
/>
</div>
</div>
</div>
`;
exports[`YoutubeVideo should match snapshot for playing state and \`youtubeReferrerPolicy = true\` 1`] = `
<div
className="post__embed-container"
>
<div>
<h4>
<span
className="video-type"
>
YouTube -
</span>
<span
className="video-title"
>
<ForwardRef
href="https://www.youtube.com/watch?v=xqCoNej8Zxo"
location="youtube_video"
>
Youtube title
</ForwardRef>
</span>
</h4>
<div
className="video-div embed-responsive-item"
onClick={[Function]}
>
<iframe
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen={true}
frameBorder="0"
height="360px"
referrerPolicy="strict-origin-when-cross-origin"
sandbox="allow-scripts allow-same-origin allow-popups allow-presentation"
src="https://www.youtube.com/embed/xqCoNej8Zxo?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1"
title="Youtube title"
width="480px"
/>
</div>

Просмотреть файл

@@ -24,6 +24,7 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
googleDeveloperKey: config.GoogleDeveloperKey,
hasImageProxy: config.HasImageProxy === 'true',
metadata: getOpenGraphMetadataForUrl(state, ownProps.postId, ownProps.link),
youtubeReferrerPolicy: config.YoutubeReferrerPolicy === 'true',
};
}

Просмотреть файл

@@ -31,6 +31,7 @@ describe('YoutubeVideo', () => {
url: 'linkForThumbnail',
}],
},
youtubeReferrerPolicy: false,
};
const initialState: DeepPartial<GlobalState> = {
@@ -65,6 +66,17 @@ describe('YoutubeVideo', () => {
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot for playing state and `youtubeReferrerPolicy = true`', () => {
const wrapper = shallow(
<YoutubeVideo
{...baseProps}
youtubeReferrerPolicy={true}
/>,
);
wrapper.setState({playing: true});
expect(wrapper).toMatchSnapshot();
});
test('should use url if secure_url is not present', () => {
const props = {
...baseProps,

Просмотреть файл

@@ -15,6 +15,7 @@ type Props = {
link: string;
show: boolean;
metadata?: OpenGraphMetadata;
youtubeReferrerPolicy?: boolean;
}
type State = {
@@ -49,6 +50,7 @@ export default class YoutubeVideo extends React.PureComponent<Props, State> {
const {metadata, link} = this.props;
const videoId = getVideoId(link);
const videoTitle = metadata?.title || 'unknown';
const time = handleYoutubeTime(link);
const header = (
@@ -59,7 +61,7 @@ export default class YoutubeVideo extends React.PureComponent<Props, State> {
href={this.props.link}
location='youtube_video'
>
{metadata?.title || 'unknown'}
{videoTitle}
</ExternalLink>
</span>
</h4>
@@ -75,6 +77,10 @@ export default class YoutubeVideo extends React.PureComponent<Props, State> {
height='360px'
frameBorder='0'
allowFullScreen={true}
allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'
referrerPolicy={this.props.youtubeReferrerPolicy ? 'strict-origin-when-cross-origin' : 'no-referrer'}
title={videoTitle}
sandbox='allow-scripts allow-same-origin allow-popups allow-presentation'
/>
);
} else {

Просмотреть файл

@@ -992,6 +992,8 @@
"admin.experimental.userStatusAwayTimeout.desc": "This setting defines the number of seconds after which the users status indicator changes to \"Away\", when they are away from Mattermost.",
"admin.experimental.userStatusAwayTimeout.example": "E.g.: \"300\"",
"admin.experimental.userStatusAwayTimeout.title": "User Status Away Timeout:",
"admin.experimental.youtubeReferrerPolicy.desc": "When true, the referrer policy for embedded YouTube videos will be set to \"strict-origin-when-cross-origin\" which resolves issues where YouTube video previews display as unavailable, while balancing the need to protect user privacy with some degree of referral data to support web functionalities, like analytics, logging, and third-party integrations. When false, the referrer policy will be set to \"no-referrer\" which enhances user privacy by not disclosing the source URL, but limits the ability to track user engagement and traffic sources in analytics tools.",
"admin.experimental.youtubeReferrerPolicy.title": "YouTube Referrer Policy:",
"admin.exportStorage.dedicatedExportStore": "Enable Dedicated Export Store:",
"admin.exportStorage.dedicatedExportStoreDescription": "When enabled, Mattermost will use a dedicated export storage bucket for all export operations. This is required for Mattermost Cloud deployments.",
"admin.exportStorage.exportDirectory": "Export Directory",

Просмотреть файл

@@ -210,6 +210,7 @@ export type ClientConfig = {
ServiceEnvironment: string;
UniqueEmojiReactionLimitPerPost: string;
UsersStatusAndProfileFetchingPollIntervalMilliseconds: string;
YoutubeReferrerPolicy: 'true' | 'false';
};
export type License = {
@@ -785,6 +786,7 @@ export type ExperimentalSettings = {
DisableAppBar: boolean;
DisableRefetchingOnBrowserFocus: boolean;
DelayChannelAutocomplete: boolean;
YoutubeReferrerPolicy: boolean;
};
export type AnalyticsSettings = {