You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
6.3 KiB

  1. import loadPolyfills from '../mastodon/load_polyfills';
  2. import ready from '../mastodon/ready';
  3. import { start } from '../mastodon/common';
  4. start();
  5. window.addEventListener('message', e => {
  6. const data = e.data || {};
  7. if (!window.parent || data.type !== 'setHeight') {
  8. return;
  9. }
  10. ready(() => {
  11. window.parent.postMessage({
  12. type: 'setHeight',
  13. id: data.id,
  14. height: document.getElementsByTagName('html')[0].scrollHeight,
  15. }, '*');
  16. });
  17. });
  18. function main() {
  19. const { length } = require('stringz');
  20. const IntlMessageFormat = require('intl-messageformat').default;
  21. const { timeAgoString } = require('../mastodon/components/relative_timestamp');
  22. const { delegate } = require('rails-ujs');
  23. const emojify = require('../mastodon/features/emoji/emoji').default;
  24. const { getLocale } = require('../mastodon/locales');
  25. const { messages } = getLocale();
  26. const React = require('react');
  27. const ReactDOM = require('react-dom');
  28. const Rellax = require('rellax');
  29. const createHistory = require('history').createBrowserHistory;
  30. ready(() => {
  31. const locale = document.documentElement.lang;
  32. const dateTimeFormat = new Intl.DateTimeFormat(locale, {
  33. year: 'numeric',
  34. month: 'long',
  35. day: 'numeric',
  36. hour: 'numeric',
  37. minute: 'numeric',
  38. });
  39. [].forEach.call(document.querySelectorAll('.emojify'), (content) => {
  40. content.innerHTML = emojify(content.innerHTML);
  41. });
  42. [].forEach.call(document.querySelectorAll('time.formatted'), (content) => {
  43. const datetime = new Date(content.getAttribute('datetime'));
  44. const formattedDate = dateTimeFormat.format(datetime);
  45. content.title = formattedDate;
  46. content.textContent = formattedDate;
  47. });
  48. [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
  49. const datetime = new Date(content.getAttribute('datetime'));
  50. const now = new Date();
  51. content.title = dateTimeFormat.format(datetime);
  52. content.textContent = timeAgoString({
  53. formatMessage: ({ id, defaultMessage }, values) => (new IntlMessageFormat(messages[id] || defaultMessage, locale)).format(values),
  54. formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
  55. }, datetime, now, datetime.getFullYear());
  56. });
  57. const reactComponents = document.querySelectorAll('[data-component]');
  58. if (reactComponents.length > 0) {
  59. import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container')
  60. .then(({ default: MediaContainer }) => {
  61. const content = document.createElement('div');
  62. ReactDOM.render(<MediaContainer locale={locale} components={reactComponents} />, content);
  63. document.body.appendChild(content);
  64. })
  65. .catch(error => console.error(error));
  66. }
  67. const parallaxComponents = document.querySelectorAll('.parallax');
  68. if (parallaxComponents.length > 0 ) {
  69. new Rellax('.parallax', { speed: -1 });
  70. }
  71. const history = createHistory();
  72. const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status');
  73. const location = history.location;
  74. if (detailedStatuses.length === 1 && (!location.state || !location.state.scrolledToDetailedStatus)) {
  75. detailedStatuses[0].scrollIntoView();
  76. history.replace(location.pathname, { ...location.state, scrolledToDetailedStatus: true });
  77. }
  78. });
  79. delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
  80. if (button !== 0) {
  81. return true;
  82. }
  83. window.location.href = target.href;
  84. return false;
  85. });
  86. delegate(document, '.status__content__spoiler-link', 'click', ({ target }) => {
  87. const contentEl = target.parentNode.parentNode.querySelector('.e-content');
  88. if (contentEl.style.display === 'block') {
  89. contentEl.style.display = 'none';
  90. target.parentNode.style.marginBottom = 0;
  91. } else {
  92. contentEl.style.display = 'block';
  93. target.parentNode.style.marginBottom = null;
  94. }
  95. return false;
  96. });
  97. delegate(document, '.modal-button', 'click', e => {
  98. e.preventDefault();
  99. let href;
  100. if (e.target.nodeName !== 'A') {
  101. href = e.target.parentNode.href;
  102. } else {
  103. href = e.target.href;
  104. }
  105. window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
  106. });
  107. delegate(document, '#account_display_name', 'input', ({ target }) => {
  108. const nameCounter = document.querySelector('.name-counter');
  109. const name = document.querySelector('.card .display-name strong');
  110. if (nameCounter) {
  111. nameCounter.textContent = 30 - length(target.value);
  112. }
  113. if (name) {
  114. name.innerHTML = emojify(target.value);
  115. }
  116. });
  117. delegate(document, '#account_note', 'input', ({ target }) => {
  118. const noteCounter = document.querySelector('.note-counter');
  119. if (noteCounter) {
  120. noteCounter.textContent = 160 - length(target.value);
  121. }
  122. });
  123. delegate(document, '#account_avatar', 'change', ({ target }) => {
  124. const avatar = document.querySelector('.card .avatar img');
  125. const [file] = target.files || [];
  126. const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
  127. avatar.src = url;
  128. });
  129. delegate(document, '#account_header', 'change', ({ target }) => {
  130. const header = document.querySelector('.card .card__img img');
  131. const [file] = target.files || [];
  132. const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
  133. header.src = url;
  134. });
  135. delegate(document, '#account_locked', 'change', ({ target }) => {
  136. const lock = document.querySelector('.card .display-name i');
  137. if (target.checked) {
  138. lock.style.display = 'inline';
  139. } else {
  140. lock.style.display = 'none';
  141. }
  142. });
  143. delegate(document, '.input-copy input', 'click', ({ target }) => {
  144. target.select();
  145. });
  146. delegate(document, '.input-copy button', 'click', ({ target }) => {
  147. const input = target.parentNode.querySelector('input');
  148. input.focus();
  149. input.select();
  150. try {
  151. if (document.execCommand('copy')) {
  152. input.blur();
  153. target.parentNode.classList.add('copied');
  154. setTimeout(() => {
  155. target.parentNode.classList.remove('copied');
  156. }, 700);
  157. }
  158. } catch (err) {
  159. console.error(err);
  160. }
  161. });
  162. }
  163. loadPolyfills().then(main).catch(error => {
  164. console.error(error);
  165. });