highline (2.0.0)
hiredis (0.6.3)
hkdf (0.3.0)
+ html2text (0.2.1)
+ nokogiri (~> 1.6)
htmlentities (4.3.4)
+ http-2 (0.10.1)
http (3.3.0)
addressable (~> 2.3)
http-cookie (~> 1.0)
end
def thumbnail
- instance_presenter.thumbnail ? full_asset_url(instance_presenter.thumbnail.file.url) : full_pack_url('preview.jpg')
+ instance_presenter.thumbnail ? full_asset_url(instance_presenter.thumbnail.file.url) : full_pack_url('media/images/preview.jpg')
end
+ def max_toot_chars
+ StatusLengthValidator::MAX_CHARS
+ end
+
+ def poll_limits
+ {
+ max_options: PollValidator::MAX_OPTIONS,
+ max_option_chars: PollValidator::MAX_OPTION_CHARS,
+ min_expiration: PollValidator::MIN_EXPIRATION,
+ max_expiration: PollValidator::MAX_EXPIRATION,
+ }
+ end
+
def stats
{
user_count: instance_presenter.user_count,
return reblog unless reblog.nil?
- reblog = account.statuses.create!(reblog: reblogged_status, text: '')
+ reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: options[:visibility] || account.user&.setting_default_privacy)
DistributionWorker.perform_async(reblog.id)
- Pubsubhubbub::DistributionWorker.perform_async(reblog.stream_entry.id)
- ActivityPub::DistributionWorker.perform_async(reblog.id)
+
+ unless reblogged_status.local_only?
+ Pubsubhubbub::DistributionWorker.perform_async(reblog.stream_entry.id)
+ ActivityPub::DistributionWorker.perform_async(reblog.id)
+ end
create_notification(reblog)
bump_potential_friendship(account, reblog)
%meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key}
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
- = javascript_pack_tag 'application', integrity: true, crossorigin: 'anonymous'
-
.app-holder#mastodon{ data: { props: Oj.dump(default_props) } }
%noscript
- = image_tag asset_pack_path('logo.svg'), alt: 'Mastodon'
+ = image_pack_tag 'logo.svg', alt: 'Mastodon'
%div
= t('errors.noscript_html', apps_path: 'https://joinmastodon.org/apps')
edit_profile: Edit profile
export: Data export
featured_tags: Featured hashtags
- followers: Authorized followers
+ flavours: Flavours
import: Import
migrate: Account migration
notifications: Notifications
edit_profile: プロフィールを編集
export: データのエクスポート
featured_tags: 注目のハッシュタグ
- followers: 信頼済みのサーバー
+ flavours: フレーバー
import: データのインポート
migrate: アカウントの引っ越し
notifications: 通知
edit_profile: Edytuj profil
export: Eksportowanie danych
featured_tags: Wyróżnione hashtagi
- followers: Autoryzowani śledzący
+ flavours: Odmiany
import: Importowanie danych
migrate: Migracja konta
notifications: Powiadomienia
settings.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url
settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url
settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url
- settings.item :follower_domains, safe_join([fa_icon('users fw'), t('settings.followers')]), settings_follower_domains_url
end
+ primary.item :flavours, safe_join([fa_icon('paint-brush fw'), t('settings.flavours')]), settings_flavours_url do |flavours|
+ Themes.instance.flavours.each do |flavour|
+ flavours.item flavour.to_sym, safe_join([fa_icon('star fw'), t("flavours.#{flavour}.name", default: flavour)]), settings_flavour_url(flavour)
+ end
+ end
+
+ primary.item :relationships, safe_join([fa_icon('users fw'), t('settings.relationships')]), relationships_url
primary.item :filters, safe_join([fa_icon('filter fw'), t('filters.index.title')]), filters_path, highlights_on: %r{/filters}
primary.item :invites, safe_join([fa_icon('user-plus fw'), t('invites.title')]), invites_path, if: proc { Setting.min_invite_role == 'user' }
// Common configuration for webpacker loaded from config/webpacker.yml
-const { resolve } = require('path');
+const { basename, dirname, extname, join, resolve } = require('path');
const { env } = require('process');
const { safeLoad } = require('js-yaml');
-const { readFileSync } = require('fs');
+const { lstatSync, readFileSync } = require('fs');
+const glob = require('glob');
const configPath = resolve('config', 'webpacker.yml');
- const loadersDir = join(__dirname, 'loaders');
const settings = safeLoad(readFileSync(configPath), 'utf8')[env.RAILS_ENV || env.NODE_ENV];
+const flavourFiles = glob.sync('app/javascript/flavours/*/theme.yml');
+const skinFiles = glob.sync('app/javascript/skins/*/*');
+const flavours = {};
-const themePath = resolve('config', 'themes.yml');
-const themes = safeLoad(readFileSync(themePath), 'utf8');
+const core = function () {
+ const coreFile = resolve('app', 'javascript', 'core', 'theme.yml');
+ const data = safeLoad(readFileSync(coreFile), 'utf8');
+ if (!data.pack_directory) {
+ data.pack_directory = dirname(coreFile);
+ }
+ return data.pack ? data : {};
+}();
+
+for (let i = 0; i < flavourFiles.length; i++) {
+ const flavourFile = flavourFiles[i];
+ const data = safeLoad(readFileSync(flavourFile), 'utf8');
+ data.name = basename(dirname(flavourFile));
+ data.skin = {};
+ if (!data.pack_directory) {
+ data.pack_directory = dirname(flavourFile);
+ }
+ if (data.locales) {
+ data.locales = join(dirname(flavourFile), data.locales);
+ }
+ if (data.pack && typeof data.pack === 'object') {
+ flavours[data.name] = data;
+ }
+}
+
+for (let i = 0; i < skinFiles.length; i++) {
+ const skinFile = skinFiles[i];
+ let skin = basename(skinFile);
+ const name = basename(dirname(skinFile));
+ if (!flavours[name]) {
+ continue;
+ }
+ const data = flavours[name].skin;
+ if (lstatSync(skinFile).isDirectory()) {
+ data[skin] = {};
+ const skinPacks = glob.sync(join(skinFile, '*.{css,scss}'));
+ for (let j = 0; j < skinPacks.length; j++) {
+ const pack = skinPacks[j];
+ data[skin][basename(pack, extname(pack))] = pack;
+ }
+ } else if ((skin = skin.match(/^(.*)\.s?css$/i))) {
+ data[skin[1]] = { common: skinFile };
+ }
+}
function removeOuterSlashes(string) {
return string.replace(/^\/*/, '').replace(/\/*$/, '');
--- /dev/null
+ const { join, resolve } = require('path');
+ const { env, settings } = require('../configuration');
+
+ module.exports = {
+ test: /\.(js|jsx|mjs)$/,
+ include: [
+ settings.source_path,
+ ...settings.resolved_paths,
+ ].map(p => resolve(p)),
+ exclude: /node_modules/,
+ use: [
+ {
+ loader: 'babel-loader',
+ options: {
++ sourceRoot: 'app/javascript',
+ cacheDirectory: join(settings.cache_path, 'babel-loader'),
+ cacheCompression: env.NODE_ENV === 'production',
+ compact: env.NODE_ENV === 'production',
+ },
+ },
+ ],
+ };
const { sync } = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const AssetsManifestPlugin = require('webpack-assets-manifest');
- const { env, settings, core, flavours, output, loadersDir } = require('./configuration.js');
-const extname = require('path-complete-extname');
-const { env, settings, themes, output } = require('./configuration');
++const { env, settings, core, flavours, output } = require('./configuration.js');
+ const rules = require('./rules');
-const localePackPaths = require('./generateLocalePacks');
+const localePacks = require('./generateLocalePacks');
+
+function reducePacks (data, into = {}) {
+ if (!data.pack) {
+ return into;
+ }
+ Object.keys(data.pack).reduce((map, entry) => {
+ const pack = data.pack[entry];
+ if (!pack) {
+ return map;
+ }
+ const packFile = typeof pack === 'string' ? pack : pack.filename;
+ if (packFile) {
+ map[data.name ? `flavours/${data.name}/${entry}` : `core/${entry}`] = resolve(data.pack_directory, packFile);
+ }
+ return map;
+ }, into);
+ if (data.name) {
+ Object.keys(data.skin).reduce((map, entry) => {
+ const skin = data.skin[entry];
+ const skinName = entry;
+ if (!skin) {
+ return map;
+ }
+ Object.keys(skin).reduce((map, entry) => {
+ const packFile = skin[entry];
+ if (!packFile) {
+ return map;
+ }
+ map[`skins/${data.name}/${skinName}/${entry}`] = resolve(packFile);
+ return map;
+ }, into);
+ return map;
+ }, into);
+ }
+ return into;
+}
+
+const entries = Object.assign(
+ { locales: resolve('app', 'javascript', 'locales') },
+ localePacks,
+ reducePacks(core),
+ Object.keys(flavours).reduce((map, entry) => reducePacks(flavours[entry], map), {})
+);
-const extensionGlob = `**/*{${settings.extensions.join(',')}}*`;
-const entryPath = join(settings.source_path, settings.source_entry_path);
-const packPaths = sync(join(entryPath, extensionGlob));
module.exports = {
- entry: Object.assign(
- packPaths.reduce((map, entry) => {
- const localMap = map;
- const namespace = relative(join(entryPath), dirname(entry));
- localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry);
- return localMap;
- }, {}),
- localePackPaths.reduce((map, entry) => {
- const localMap = map;
- localMap[basename(entry, extname(entry, extname(entry)))] = resolve(entry);
- return localMap;
- }, {}),
- Object.keys(themes).reduce((themePaths, name) => {
- themePaths[name] = resolve(join(settings.source_path, themes[name]));
- return themePaths;
- }, {})
- ),
+ entry: entries,
output: {
- filename: '[name].js',
- chunkFilename: '[name].js',
+ filename: 'js/[name]-[chunkhash].js',
+ chunkFilename: 'js/[name]-[chunkhash].chunk.js',
+ hotUpdateChunkFilename: 'js/[id]-[hash].hot-update.js',
path: output.path,
publicPath: output.publicPath,
},
"iOS >= 9",
"not dead"
],
- "<rootDir>/tmp/"
+ "jest": {
+ "projects": [
+ "<rootDir>/app/javascript/mastodon"
+ ],
+ "testPathIgnorePatterns": [
+ "<rootDir>/node_modules/",
+ "<rootDir>/vendor/",
+ "<rootDir>/config/",
+ "<rootDir>/log/",
+ "<rootDir>/public/",
++ "<rootDir>/tmp/",
++ "<rootDir>/app/javascript/themes/"
+ ],
+ "setupFiles": [
+ "raf/polyfill"
+ ],
+ "setupFilesAfterEnv": [
+ "<rootDir>/app/javascript/mastodon/test_setup.js"
+ ],
+ "collectCoverageFrom": [
+ "app/javascript/mastodon/**/*.js",
+ "!app/javascript/mastodon/features/emoji/emoji_compressed.js",
+ "!app/javascript/mastodon/locales/locale-data/*.js",
+ "!app/javascript/mastodon/service_worker/entry.js",
+ "!app/javascript/mastodon/test_setup.js"
+ ],
+ "coverageDirectory": "<rootDir>/coverage",
+ "moduleDirectories": [
+ "<rootDir>/node_modules",
+ "<rootDir>/app/javascript"
+ ]
+ },
"private": true,
"dependencies": {
- "@babel/core": "^7.2.2",
- "@babel/plugin-proposal-class-properties": "^7.2.3",
- "@babel/plugin-proposal-decorators": "^7.2.3",
- "@babel/plugin-proposal-object-rest-spread": "^7.2.0",
+ "@babel/core": "^7.3.4",
+ "@babel/plugin-proposal-class-properties": "^7.3.4",
+ "@babel/plugin-proposal-decorators": "^7.3.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.3.4",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-react-inline-elements": "^7.2.0",
"@babel/plugin-transform-react-jsx-self": "^7.2.0",
"@babel/plugin-transform-react-jsx-source": "^7.2.0",
- "@babel/plugin-transform-runtime": "^7.2.0",
- "@babel/preset-env": "^7.2.3",
+ "@babel/plugin-transform-runtime": "^7.3.4",
+ "@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
- "@babel/runtime": "^7.2.0",
- "@gfx/zopfli": "^1.0.10",
+ "@babel/runtime": "^7.3.4",
"array-includes": "^3.0.3",
- "autoprefixer": "^9.4.3",
+ "atrament": "^0.2.3",
+ "autoprefixer": "^9.4.10",
"axios": "^0.18.0",
- "babel-core": "^7.0.0-bridge.0",
- "babel-loader": "^8.0.4",
+ "babel-loader": "^8.0.5",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-preval": "^3.0.1",
"babel-plugin-react-intl": "^3.0.1",
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
- autoprefixer@^9.4.3:
- version "9.4.3"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.3.tgz#c97384a8fd80477b78049163a91bbc725d9c41d9"
- integrity sha512-/XSnzDepRkAU//xLcXA/lUWxpsBuw0WiriAHOqnxkuCtzLhaz+fL4it4gp20BQ8n5SyLzK/FOc7A0+u/rti2FQ==
+atrament@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/atrament/-/atrament-0.2.3.tgz#6ccbc0daa6d3f25e5aeaeb31befeb78e86980348"
+
+ autoprefixer@^9.4.10:
+ version "9.4.10"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.10.tgz#e1be61fc728bacac8f4252ed242711ec0dcc6a7b"
+ integrity sha512-XR8XZ09tUrrSzgSlys4+hy5r2/z4Jp7Ag3pHm31U4g/CTccYPOVe19AkaJ4ey/vRd1sfj+5TtuD6I0PXtutjvQ==
dependencies:
- browserslist "^4.3.6"
- caniuse-lite "^1.0.30000921"
+ browserslist "^4.4.2"
+ caniuse-lite "^1.0.30000940"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
- postcss "^7.0.6"
+ postcss "^7.0.14"
postcss-value-parser "^3.3.1"
aws-sign2@~0.7.0:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
- fastparse@^1.1.1:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
- integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
-
+favico.js@^0.3.10:
+ version "0.3.10"
+ resolved "https://registry.yarnpkg.com/favico.js/-/favico.js-0.3.10.tgz#80586e27a117f24a8d51c18a99bdc714d4339301"
+
faye-websocket@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"