Compare commits

...
Sign in to create a new pull request.

41 commits

Author SHA1 Message Date
22ca0a7c4a eslint everything
Some checks failed
ci/woodpecker/tag/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline failed
2022-06-18 15:18:08 +01:00
f4e64e5d4d remove restrictions on node
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-06-18 15:14:43 +01:00
ac4bcb04d7 add CI
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline failed
2022-06-18 15:13:23 +01:00
Haelwenn
68105a39bf Merge branch 'features/notification-settings' into 'rebase/glitch-soc'
Add support for notification_settings

See merge request pleroma/mastofe!30
2020-06-30 21:58:14 +00:00
Haelwenn (lanodan) Monnier
4b041c1551
initial_state: sync notifications.shows with pleroma notification_settings
lodash suggestion from Alex Gleason
2020-06-30 23:49:33 +02:00
Haelwenn (lanodan) Monnier
cfefd0aecd
Sync notification_settings 2020-06-30 01:41:16 +02:00
Haelwenn (lanodan) Monnier
4788384b1f
link_footer.js: remove mapDispatchToProps call 2020-05-20 08:01:01 +02:00
Haelwenn (lanodan) Monnier
ce085cf1d7
upload_button.js: Accept all MIME types 2020-05-20 08:01:01 +02:00
Haelwenn (lanodan) Monnier
1e59ed8223
local_settings.js: Allow to pick a content-type by default 2020-05-20 08:01:01 +02:00
Haelwenn (lanodan) Monnier
f2866f1e99
Add BBCode compositing option 2020-05-20 08:01:01 +02:00
Haelwenn (lanodan) Monnier
473e84916f
Put a max-size on inlined images 2020-05-20 08:01:00 +02:00
Haelwenn (lanodan) Monnier
312e3674c3
mastodon/locales/{en,fr}.json: Replace toot and it’s translations 2020-05-20 08:00:59 +02:00
Haelwenn (lanodan) Monnier
fae133ca7f
Remove bundling of unused fonts (roboto, montserrat) 2020-05-20 08:00:04 +02:00
Haelwenn (lanodan) Monnier
463987ffe3
Remove unsupported Local-only
Note: It’s still being considered to be added, but in a different way so removing it for now
2020-05-20 08:00:03 +02:00
Haelwenn (lanodan) Monnier
47125c9016
Remove unsupported Delete&Redraft 2020-05-20 08:00:03 +02:00
Haelwenn (lanodan) Monnier
3554d8fc2f
flavours/glitch/reducers/compose.js: Fix toLowerCase TypeError
[Error] TypeError: e.toLowerCase is not a function. (In 'e.toLowerCase()', 'e.toLowerCase' is undefined)

Some added context: https://queer.hacktivis.me/notice/9g4OfhpIWQLNoZ3mLI
2020-05-20 08:00:03 +02:00
Haelwenn (lanodan) Monnier
4700e27d12
flavours/glitch/components/error_boundary: Add switching to pleroma-fe 2020-05-20 08:00:02 +02:00
Haelwenn (lanodan) Monnier
c9d4a963ca
flavours/glitch/util/backend_links.js: Initial Configuration 2020-05-20 08:00:02 +02:00
Haelwenn (lanodan) Monnier
f1cd875a4e
config/webpack/production.js: Remove caching in OfflinePlugin (introduced in 0692991b5) 2020-05-20 08:00:02 +02:00
Haelwenn (lanodan) Monnier
f6ec8afcda
Revert: Resize images before upload in web UI to reduce bandwidth
Closes: https://git.pleroma.social/pleroma/mastofe/issues/22

(Can also fix an issue with canvas and the Tor Browser)
2020-05-20 08:00:02 +02:00
Morgan Bazalgette
2fa8015c39
Pleroma theme for mastofe
- resize columns dynamically, with min width of 330↵
- Change colours of mastodon frontend↵
- make avatars circled↵
- add slight hint of rounding on main ui components↵
2020-05-20 08:00:02 +02:00
Haelwenn (lanodan) Monnier
1651f733b0
Remove branded images and their usage 2020-05-20 08:00:01 +02:00
Haelwenn (lanodan) Monnier
316694bea6
Remove countableText to count all text 2020-05-20 08:00:01 +02:00
Haelwenn (lanodan) Monnier
4e170d46ad
[mastofe] Adds deleting others notice if you have the rights(~moderator)
Closes: https://git.pleroma.social/pleroma/mastofe/issues/20
2020-05-20 08:00:01 +02:00
Haelwenn (lanodan) Monnier
2942716fb9
Remove extra /settings/profile; Change /settings/{profile,preferences} to /user-settings 2020-05-20 08:00:01 +02:00
Haelwenn (lanodan) Monnier
1af76fae0e
app/javascript/mastodon/components/hashtag.js: Do not crash when hashtag.history is undefined 2020-05-20 08:00:00 +02:00
Morgan Bazalgette
6b3614ae4c
Getting-Started column customisation (panel, notice) 2020-05-20 08:00:00 +02:00
Haelwenn (lanodan) Monnier
6bf8551755
app/javascript/mastodon/components/media_gallery.js: Add support for audio attachements 2020-05-20 08:00:00 +02:00
Morgan Bazalgette
3918d358ee
handle empty message (ping) in ws 2020-05-20 07:59:59 +02:00
Morgan Bazalgette
faba7113c8
always make /web/login go through
Original commit: 011eda089abad18c89324ca4bcb6930bb682a0b9
2020-05-20 07:59:59 +02:00
Morgan Bazalgette
6599840fa5
package.json: Add description field
APPARENTLY NPM ADDS A npm_package_description WHICH IS THE description
FIELD IN package.json WHICH HOWEVER IF NOT SET BECOMES THE SECOND LINE
OF THE README LIKE WHAT THE FUCKKKKKKKKKKKKK
2020-05-20 07:59:59 +02:00
Morgan Bazalgette
f0d1a559ae
fix the homepage being loaded issue 2020-05-20 07:59:59 +02:00
Morgan Bazalgette
882d44afdf
Default when there is no applicationServerKey meta on the page 2020-05-20 07:59:59 +02:00
Morgan Bazalgette
a915aca1db
remove bundle analyzer, remove chunk hashes from css, create build.sh 2020-05-20 07:59:57 +02:00
Morgan Bazalgette
65787ddcac
Remove unnecessary ruby from build scripts 2020-05-20 07:58:09 +02:00
Lain Iwakura
0aafacfb39
Handle character limit in initial state in frontend. 2020-05-20 07:58:09 +02:00
Haelwenn (lanodan) Monnier
ca704f09dd
package.json: Add (npm run) dev command 2020-05-20 07:58:09 +02:00
Haelwenn (lanodan) Monnier
9377b5ca7b
{mastodon,flavours/glitch/util}/api.js: Remove csrf-token query 2020-05-20 07:58:09 +02:00
Morgan Bazalgette
3893e49a1f
Add GitLab CI config 2020-05-20 07:58:08 +02:00
Morgan Bazalgette
38ca2a8425
README.md: full rewrite for pleroma 2020-05-20 07:58:08 +02:00
Haelwenn (lanodan) Monnier
1a151397e5
Changelog-mastofe.txt: Init distribution 2020-05-20 07:58:06 +02:00
180 changed files with 837 additions and 65759 deletions

23
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,23 @@
image: alpine:latest
before_script:
- apk add yarn
- yarn global add node-gyp
- yarn install
cache:
paths:
- node_modules
test:
script:
- yarn run test:jest
build:
script:
- yarn run build
artifacts:
paths:
- public/packs
- public/assets
expire_in: 1 week

49
.woodpecker.yml Normal file
View file

@ -0,0 +1,49 @@
pipeline:
lint:
when:
event:
- push
- pull_request
image: node:16
commands:
- yarn
- yarn test:lint
test:
when:
event:
- push
- pull_request
image: node:16
commands:
- apt update
- apt install firefox-esr -y --no-install-recommends
- yarn
- yarn test
build:
when:
event:
- tag
image: node:16
commands:
- yarn
- TARGET=distribution ./build.sh
release:
when:
event:
- tag
image: node:16
secrets:
- SCW_ACCESS_KEY
- SCW_SECRET_KEY
- SCW_DEFAULT_ORGANIZATION_ID
commands:
- apt-get update && apt-get install -y rclone wget zip
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
- mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
- chmod +x scaleway-cli
- ./scaleway-cli object config install type=rclone
- zip mastofe.zip -r distribution
- rclone copyto mastofe.zip scaleway:akkoma-updates/frontend/masto-fe.zip

4
Changelog-mastofe.txt Normal file
View file

@ -0,0 +1,4 @@
# MastoFE Changelog
Note: This file is to be cleared after each rebase on upstream
Based on b7e178d2e4102bdaa1ea41dfd8ed50093cf3f60a 2020-05-14T23:30:05Z

View file

@ -1,12 +1,72 @@
# Mastodon Glitch Edition #
# Mastodon Frontend, Glitch-soc + Pleroma Edition
> Now with automated deploys!
Here is a distribution of the glitch-soc frontend for pleroma. Everything from the upstream repository is kept and rebased on for easy updates, this does screws up on Merge Requests so theyll be treated as a patchset if done here.
[![Build Status](https://img.shields.io/circleci/project/github/glitch-soc/mastodon.svg)][circleci]
# Fork vs. Distribution
This repository is not a fork of glitch-soc, there is only a few patches on it that are kept and designed to be easily removable if it needs to (for example when upstream does one of our features on their side too).
This also means that there is no merge, only one big rebase to get the patches up-to-date. See the Development section for more information.
[circleci]: https://circleci.com/gh/glitch-soc/mastodon
# Deployement
## Fetching CI artifacts
Pleroma (backend) has a shell script in `installation/download-mastofe-build.sh` which can be used to get a build from the CI artifacts, by default it will install the one from `rebase/glitch-soc` into `instance/static` (so you can revert to the bundled frontend by removing it and it is free from git conflicts), this behaviour can be changed by modifying the first lines of the script.
So here's the deal: we all work on this code, and then it runs on dev.glitch.social and anyone who uses that does so absolutely at their own risk. can you dig it?
## Updating the bundle
This is what you want to do to update the mastofe bundled with pleroma.
- You can view documentation for this project at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/).
- And contributing guidelines are available [here](CONTRIBUTING.md) and [here](https://glitch-soc.github.io/docs/contributing/).
- Run ``build.sh`` at the root of this repo, you can set the ``TARGET`` environment variable if pleroma isnt at ``../pleroma`` (default value of ``TARGET``)
- Go to pleroma repo:
- ``git add priv/static/sw.js priv/static/packs``
- ``git commit -m "update mastofe"``
# Development
## Branches
- `pleroma` branch which get re-referenced to `rebase/glitch-soc` once it is stable
- `master`: Same branch as upstream repository, should be updated before doing an updating rebase
- `rebase/glitch-soc`: branch which rebases from upstream, used for testing
For developement/Merge Requests I would suggest to use `master` when you are introducing new modifications that cannot be in the upstream, and when you are changing current modifications prefer `rebase/glitch-soc`.
Never use `pleroma` as a base for Merge Requests, it is not meant to be modified directly.
## Changelog
There is a `Changelog-mastofe.txt` file used to do the release notes for the message on tagging a new release. It is cleaned when updating from upstream (`git filter-branch` should be a nice friend).
Changes on the behaviour of the frontend should be noted in this file together with their modification (commit at best, MR at worst).
## Tools
- Node.js
- yarn (preferred) or npm
- HTTP proxy (such as nginx)
## nginx setup
I'll assume that you have already fired up pleroma using the installation guide. To work on the frontend while still having the backend up, use this nginx config.
```
server {
listen 80;
server_name pleroma.testing;
location /packs {
add_header 'Access-Control-Allow-Origin' '*';
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_pass http://localhost:3035;
}
location / {
add_header 'Access-Control-Allow-Origin' '*';
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_pass http://localhost:4000;
}
}
```
Change the `server_name` if you like. I personally like to create a new entry in /etc/hosts and add `127.0.0.1 pleroma.testing`, but you do what suits you.
## Running
- Getting the node dependencies is done with `yarn install -D` (or `npm install` if you dont have yarn)
- Launching the frontend is done with `npm run dev`. It should be reachable once it finnishes compiling.

View file

@ -1,7 +1,9 @@
// This file will be loaded on settings pages, regardless of theme.
import escapeTextContentForBrowser from 'escape-html';
const { delegate } = require('@rails/ujs');
import emojify from '../mastodon/features/emoji/emoji';
delegate(document, '#account_display_name', 'input', ({ target }) => {
@ -62,7 +64,7 @@ delegate(document, '.input-copy button', 'click', ({ target }) => {
input.blur();
target.parentNode.classList.add('copied');
setTimeout(() => {
setTimeout(() => {
target.parentNode.classList.remove('copied');
}, 700);
}

View file

@ -760,7 +760,7 @@ export function fetchPinnedAccounts() {
return (dispatch, getState) => {
dispatch(fetchPinnedAccountsRequest());
api(getState).get(`/api/v1/endorsements`, { params: { limit: 0 } }).then(response => {
api(getState).get('/api/v1/endorsements', { params: { limit: 0 } }).then(response => {
dispatch(importFetchedAccounts(response.data));
dispatch(fetchPinnedAccountsSuccess(response.data));
}).catch(err => dispatch(fetchPinnedAccountsFail(err)));
@ -822,7 +822,7 @@ export function changePinnedAccountsSuggestions(value) {
return {
type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE,
value,
}
};
};
export function resetPinnedAccountsEditor() {

View file

@ -5,7 +5,6 @@ import { search as emojiSearch } from 'flavours/glitch/util/emoji/emoji_mart_sea
import { useEmoji } from './emojis';
import { tagHistory } from 'flavours/glitch/util/settings';
import { recoverHashtags } from 'flavours/glitch/util/hashtag';
import resizeImage from 'flavours/glitch/util/resize_image';
import { importFetchedAccounts } from './importer';
import { updateTimeline } from './timelines';
import { showAlertForError } from './alerts';
@ -83,12 +82,6 @@ export function changeCompose(text) {
};
};
export function cycleElefriendCompose() {
return {
type: COMPOSE_CYCLE_ELEFRIEND,
};
};
export function replyCompose(status, routerHistory) {
return (dispatch, getState) => {
const prependCWRe = getState().getIn(['local_settings', 'prepend_cw_re']);
@ -250,41 +243,37 @@ export function uploadCompose(files) {
dispatch(uploadComposeRequest());
for (const [i, f] of Array.from(files).entries()) {
for (const [i, file] of Array.from(files).entries()) {
if (media.size + i > 3) break;
resizeImage(f).then(file => {
const data = new FormData();
data.append('file', file);
// Account for disparity in size of original image and resized data
total += file.size - f.size;
const data = new FormData();
data.append('file', file);
return api(getState).post('/api/v2/media', data, {
onUploadProgress: function({ loaded }){
progress[i] = loaded;
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
},
}).then(({ status, data }) => {
// If server-side processing of the media attachment has not completed yet,
// poll the server until it is, before showing the media attachment as uploaded
return api(getState).post('/api/v2/media', data, {
onUploadProgress: function({ loaded }){
progress[i] = loaded;
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
},
}).then(({ status, data }) => {
// If server-side processing of the media attachment has not completed yet,
// poll the server until it is, before showing the media attachment as uploaded
if (status === 200) {
dispatch(uploadComposeSuccess(data, f));
} else if (status === 202) {
const poll = () => {
api(getState).get(`/api/v1/media/${data.id}`).then(response => {
if (response.status === 200) {
dispatch(uploadComposeSuccess(response.data, f));
} else if (response.status === 206) {
setTimeout(() => poll(), 1000);
}
}).catch(error => dispatch(uploadComposeFail(error)));
};
if (status === 200) {
dispatch(uploadComposeSuccess(data, file));
} else if (status === 202) {
const poll = () => {
api(getState).get(`/api/v1/media/${data.id}`).then(response => {
if (response.status === 200) {
dispatch(uploadComposeSuccess(response.data, file));
} else if (response.status === 206) {
setTimeout(() => poll(), 1000);
}
}).catch(error => dispatch(uploadComposeFail(error)));
};
poll();
}
});
}).catch(error => dispatch(uploadComposeFail(error)));
poll();
}
});
};
};
};

View file

@ -36,15 +36,15 @@ export const submitMarkers = () => (dispatch, getState) => {
};
export const fetchMarkers = () => (dispatch, getState) => {
const params = { timeline: ['notifications'] };
const params = { timeline: ['notifications'] };
dispatch(fetchMarkersRequest());
dispatch(fetchMarkersRequest());
api(getState).get('/api/v1/markers', { params }).then(response => {
dispatch(fetchMarkersSuccess(response.data));
}).catch(error => {
dispatch(fetchMarkersFail(error));
});
api(getState).get('/api/v1/markers', { params }).then(response => {
dispatch(fetchMarkersSuccess(response.data));
}).catch(error => {
dispatch(fetchMarkersFail(error));
});
};
export function fetchMarkersRequest() {

View file

@ -117,7 +117,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
};
};
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
export const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
const excludeTypesFromFilter = filter => {

View file

@ -18,7 +18,10 @@ const urlBase64ToUint8Array = (base64String) => {
return outputArray;
};
const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content');
const getApplicationServerKey = () => {
const k = document.querySelector('[name="applicationServerKey"]');
return k === null ? '' : k.getAttribute('content');
};
const getRegistration = () => navigator.serviceWorker.ready;

View file

@ -1,6 +1,7 @@
import api from 'flavours/glitch/util/api';
import { debounce } from 'lodash';
import { showAlertForError } from './alerts';
import { excludeTypesFromSettings } from './notifications';
export const SETTING_CHANGE = 'SETTING_CHANGE';
export const SETTING_SAVE = 'SETTING_SAVE';
@ -24,6 +25,11 @@ const debouncedSave = debounce((dispatch, getState) => {
const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS();
api(getState).put(
'/api/pleroma/notification_settings',
{ exclude_types: excludeTypesFromSettings(getState()) },
).catch(error => dispatch(showAlertForError(error)));
api(getState).put('/api/web/settings', { data })
.then(() => dispatch({ type: SETTING_SAVE }))
.catch(error => dispatch(showAlertForError(error)));

View file

@ -47,7 +47,7 @@ export function updateTimeline(timeline, status, accept) {
timeline,
status,
usePendingItems: preferPendingItems,
filtered
filtered,
});
};
};

View file

@ -87,7 +87,7 @@ export default class DisplayName extends React.PureComponent {
)).reduce((prev, cur) => [prev, ', ', cur]);
if (others.size - 2 > 0) {
displayName.push(` +${others.size - 2}`);
displayName.push(` +${others.size - 2}`);
}
suffix = (

View file

@ -74,7 +74,7 @@ export default class ErrorBoundary extends React.PureComponent {
<FormattedMessage
id='web_app_crash.report_issue'
defaultMessage='Report a bug in the {issuetracker}'
values={{ issuetracker: <a href='https://github.com/glitch-soc/mastodon/issues' rel='noopener noreferrer' target='_blank'><FormattedMessage id='web_app_crash.issue_tracker' defaultMessage='issue tracker' /></a> }}
values={{ issuetracker: <a href='https://git.pleroma.social/pleroma/mastofe/issues' rel='noopener noreferrer' target='_blank'><FormattedMessage id='web_app_crash.issue_tracker' defaultMessage='issue tracker' /></a> }}
/>
{ debugInfo !== '' && (
<details>
@ -104,6 +104,13 @@ export default class ErrorBoundary extends React.PureComponent {
/>
</li>
)}
<li>
<FormattedMessage
id='web_app_crash.load_pleromafe'
defaultMessage='Load {pleromafe} instead of mastofe'
values={{ pleromafe: <a href='/main/all'><FormattedMessage id='web_app_crash.pleromafe' defaultMessage='Pleroma Frontend' /></a> }}
/>
</li>
</ul>
</div>
</div>

View file

@ -20,7 +20,7 @@ const Hashtag = ({ hashtag }) => (
</div>
<div className='trends__item__sparkline'>
<Sparklines width={50} height={28} data={hashtag.get('history').reverse().map(day => day.get('uses')).toArray()}>
<Sparklines width={50} height={28} data={hashtag.get('history') && hashtag.get('history').reverse().map(day => day.get('uses')).toArray()}>
<SparklinesCurve style={{ fill: 'none' }} />
</Sparklines>
</div>

View file

@ -121,8 +121,9 @@ export default class IntersectionObserverArticle extends React.Component {
aria-setsize={listLength}
data-id={id}
tabIndex='0'
style={style}>
{children && React.cloneElement(children, { hidden: !isIntersecting && (isHidden || !!cachedHeight) })}
style={style}
>
{children && React.cloneElement(children, { hidden: !isIntersecting && (isHidden || !!cachedHeight) })}
</article>
);
}

View file

@ -226,6 +226,21 @@ class Item extends React.PureComponent {
/>
</a>
);
} else if (attachment.get('type') === 'audio') {
thumbnail = (
<div
className='media-gallery__audio'
aria-label={attachment.get('description')}
role='application'
>
<span><p>sound<br />only</p></span>
<span><p>{attachment.get('description')}</p></span>
<audio
src={attachment.get('url')}
controls
/>
</div>
);
} else if (attachment.get('type') === 'gifv') {
const autoPlay = !isIOS() && this.getAutoPlay();
@ -352,7 +367,7 @@ class MediaGallery extends React.PureComponent {
} else if (width) {
style.height = width / (16/9);
} else {
return (<div className={computedClass} ref={this.handleRef}></div>);
return (<div className={computedClass} ref={this.handleRef} />);
}
if (this.isStandaloneEligible()) {

View file

@ -1,15 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import illustration from 'flavours/glitch/images/elephant_ui_disappointed.svg';
import classNames from 'classnames';
const MissingIndicator = ({ fullPage }) => (
<div className={classNames('regeneration-indicator', { 'regeneration-indicator--without-header': fullPage })}>
<div className='regeneration-indicator__figure'>
<img src={illustration} alt='' />
</div>
<div className='regeneration-indicator__label'>
<FormattedMessage id='missing_indicator.label' tagName='strong' defaultMessage='Not found' />
<FormattedMessage id='missing_indicator.sublabel' defaultMessage='This resource could not be found' />

View file

@ -4,6 +4,7 @@ import 'wicg-inert';
import { createBrowserHistory } from 'history';
export default class ModalRoot extends React.PureComponent {
static contextTypes = {
router: PropTypes.object,
};
@ -104,7 +105,7 @@ export default class ModalRoot extends React.PureComponent {
handleModalOpen () {
const history = this.history;
const state = {...history.location.state, mastodonModalOpen: true};
const state = { ...history.location.state, mastodonModalOpen: true };
history.push(history.location.pathname, state);
this.unlistenHistory = history.listen(() => {
this.props.onClose();

View file

@ -24,7 +24,7 @@ export default class Permalink extends React.PureComponent {
if (this.context.router) {
e.preventDefault();
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(this.props.to, state);
}

View file

@ -1,13 +1,8 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import illustration from 'flavours/glitch/images/elephant_ui_working.svg';
const MissingIndicator = () => (
<div className='regeneration-indicator'>
<div className='regeneration-indicator__figure'>
<img src={illustration} alt='' />
</div>
<div className='regeneration-indicator__label'>
<FormattedMessage id='regeneration_indicator.label' tagName='strong' defaultMessage='Loading&hellip;' />
<FormattedMessage id='regeneration_indicator.sublabel' defaultMessage='Your home feed is being prepared!' />

View file

@ -4,6 +4,7 @@ import { FormattedMessage } from 'react-intl';
export default
class Spoilers extends React.PureComponent {
static propTypes = {
spoilerText: PropTypes.string,
children: PropTypes.node,
@ -21,17 +22,17 @@ class Spoilers extends React.PureComponent {
const { spoilerText, children } = this.props;
const { hidden } = this.state;
const toggleText = hidden ?
<FormattedMessage
id='status.show_more'
defaultMessage='Show more'
key='0'
/> :
<FormattedMessage
id='status.show_less'
defaultMessage='Show less'
key='0'
/>;
const toggleText = hidden ?
(<FormattedMessage
id='status.show_more'
defaultMessage='Show more'
key='0'
/>) :
(<FormattedMessage
id='status.show_less'
defaultMessage='Show less'
key='0'
/>);
return ([
<p className='spoiler__text'>
@ -43,8 +44,9 @@ class Spoilers extends React.PureComponent {
</p>,
<div className={`status__content__spoiler ${!hidden ? 'status__content__spoiler--visible' : ''}`}>
{children}
</div>
</div>,
]);
}
}

View file

@ -53,7 +53,7 @@ export const defaultMediaVisibility = (status, settings) => {
}
return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all');
}
};
export default @injectIntl
class Status extends ImmutablePureComponent {
@ -291,7 +291,9 @@ class Status extends ImmutablePureComponent {
if (this.node && this.props.getScrollPosition) {
const position = this.props.getScrollPosition();
if (position !== null && this.node.offsetTop < position.top) {
requestAnimationFrame(() => { this.props.updateScrollBottom(position.height - position.top); });
requestAnimationFrame(() => {
this.props.updateScrollBottom(position.height - position.top);
});
}
}
}
@ -344,7 +346,7 @@ class Status extends ImmutablePureComponent {
status.getIn(['reblog', 'id'], status.get('id'))
}`;
}
let state = {...router.history.location.state};
let state = { ...router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
router.history.push(destination, state);
}
@ -360,7 +362,7 @@ class Status extends ImmutablePureComponent {
if (this.context.router && e.button === 0) {
const id = e.currentTarget.getAttribute('data-id');
e.preventDefault();
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(`/accounts/${id}`, state);
}
@ -415,13 +417,13 @@ class Status extends ImmutablePureComponent {
}
handleHotkeyOpen = () => {
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(`/statuses/${this.props.status.get('id')}`, state);
}
handleHotkeyOpenProfile = () => {
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`, state);
}

View file

@ -5,7 +5,7 @@ import IconButton from './icon_button';
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me, isStaff } from 'flavours/glitch/util/initial_state';
import { me, isStaff, deleteOthersNotice } from 'flavours/glitch/util/initial_state';
import RelativeTimestamp from './relative_timestamp';
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/util/backend_links';
@ -122,7 +122,7 @@ class StatusActionBar extends ImmutablePureComponent {
_openInteractionDialog = type => {
window.open(`/interact/${this.props.status.get('id')}?type=${type}`, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
}
}
handleDeleteClick = () => {
this.props.onDelete(this.props.status, this.context.router.history);
@ -153,7 +153,7 @@ class StatusActionBar extends ImmutablePureComponent {
}
handleOpen = () => {
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
if (state.mastodonModalOpen) {
this.context.router.history.replace(`/statuses/${this.props.status.get('id')}`, { mastodonBackSteps: (state.mastodonBackSteps || 0) + 1 });
} else {
@ -231,7 +231,6 @@ class StatusActionBar extends ImmutablePureComponent {
}
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
} else {
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
@ -255,6 +254,9 @@ class StatusActionBar extends ImmutablePureComponent {
});
}
}
if ( deleteOthersNotice ) {
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
}
}
if (status.get('in_reply_to_id', null) === null) {

View file

@ -13,7 +13,7 @@ const textMatchesTarget = (text, origin, host) => {
return (text === origin || text === host
|| text.startsWith(origin + '/') || text.startsWith(host + '/')
|| 'www.' + text === host || ('www.' + text).startsWith(host + '/'));
}
};
const isLinkMisleading = (link) => {
let linkTextParts = [];

View file

@ -48,16 +48,16 @@ class StatusIcons extends React.PureComponent {
const { intl, mediaIcon } = this.props;
switch (mediaIcon) {
case 'link':
return intl.formatMessage(messages.previewCard);
case 'picture-o':
return intl.formatMessage(messages.pictures);
case 'tasks':
return intl.formatMessage(messages.poll);
case 'video-camera':
return intl.formatMessage(messages.video);
case 'music':
return intl.formatMessage(messages.audio);
case 'link':
return intl.formatMessage(messages.previewCard);
case 'picture-o':
return intl.formatMessage(messages.pictures);
case 'tasks':
return intl.formatMessage(messages.poll);
case 'video-camera':
return intl.formatMessage(messages.video);
case 'music':
return intl.formatMessage(messages.audio);
}
}

View file

@ -19,8 +19,10 @@ const mapDispatchToProps = (dispatch, { status, items }) => ({
(item, i) => item ? {
...item,
name: `${item.text}-${i}`,
onClick: item.action ? ((e) => { return onItemClick(i, e) }) : null,
} : null
onClick: item.action ? ((e) => {
return onItemClick(i, e);
}) : null,
} : null,
),
}) : openDropdownMenu(id, dropdownPlacement, keyboard));
},

View file

@ -201,7 +201,7 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
<Spoilers spoilerText={intl.formatMessage(messages.author)}>
<AccountContainer id={status.getIn(['account', 'id'])} />
</Spoilers>
<Spoilers spoilerText={intl.formatMessage(messages.matchingFilters, {count: matchingFilters.size})}>
<Spoilers spoilerText={intl.formatMessage(messages.matchingFilters, { count: matchingFilters.size })}>
<ul>
{matchingFilters.map(filter => (
<li>
@ -221,7 +221,7 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
))}
</ul>
</Spoilers>
</div>
</div>,
],
confirm: intl.formatMessage(messages.unfilterConfirm),
onConfirm: onConfirm,

View file

@ -128,8 +128,7 @@ class Header extends ImmutablePureComponent {
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
info.push(<span className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>);
}
else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
} else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
info.push(<span className='relationship-tag'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>);
}
@ -297,7 +296,7 @@ class Header extends ImmutablePureComponent {
{fields.map((pair, i) => (
<dl key={i}>
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
<dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')}>
{pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
</dd>
@ -308,9 +307,9 @@ class Header extends ImmutablePureComponent {
{account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content' dangerouslySetInnerHTML={content} />}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View file

@ -21,7 +21,7 @@ export default class MovedNote extends ImmutablePureComponent {
handleAccountClick = e => {
if (e.button === 0) {
e.preventDefault();
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(`/accounts/${this.props.to.get('id')}`, state);
}

View file

@ -12,7 +12,7 @@ import {
} from 'flavours/glitch/actions/accounts';
import {
mentionCompose,
directCompose
directCompose,
} from 'flavours/glitch/actions/compose';
import { initMuteModal } from 'flavours/glitch/actions/mutes';
import { initBlockModal } from 'flavours/glitch/actions/blocks';

View file

@ -12,7 +12,7 @@ const mapStateToProps = (state, { columnId }) => {
settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'community']),
};
};
const mapDispatchToProps = (dispatch, { columnId }) => {
return {
onChange (key, checked) {

View file

@ -11,7 +11,6 @@ import UploadFormContainer from '../containers/upload_form_container';
import WarningContainer from '../containers/warning_container';
import { isMobile } from 'flavours/glitch/util/is_mobile';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { countableText } from 'flavours/glitch/util/counter';
import OptionsContainer from '../containers/options_container';
import Publisher from './publisher';
import TextareaIcons from './textarea_icons';
@ -21,9 +20,9 @@ import CharacterCounter from './character_counter';
const messages = defineMessages({
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
missingDescriptionMessage: { id: 'confirmations.missing_media_description.message',
defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.' },
defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.' },
missingDescriptionConfirm: { id: 'confirmations.missing_media_description.confirm',
defaultMessage: 'Send anyway' },
defaultMessage: 'Send anyway' },
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
});
@ -292,7 +291,7 @@ class ComposeForm extends ImmutablePureComponent {
let disabledButton = isSubmitting || isUploading || isChangingUpload || (!text.trim().length && !anyMedia);
const countText = `${spoilerText}${countableText(text)}${advancedOptions && advancedOptions.get('do_not_federate') ? ' 👁️' : ''}`;
const countText = `${spoilerText}${text}`;
return (
<div className='composer'>

View file

@ -141,7 +141,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
onChange(name);
component.setState({ needsModalUpdate: true });
},
})
}),
),
};
}
@ -166,7 +166,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
const { items } = this.props;
const { needsModalUpdate } = this.state;
if (needsModalUpdate && items.find(
(item, i) => item.on !== prevProps.items[i].on
(item, i) => item.on !== prevProps.items[i].on,
)) {
this.handleUpdate();
this.setState({ needsModalUpdate: false });

View file

@ -167,7 +167,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
if (on !== null && typeof on !== 'undefined') {
prefix = <Toggle checked={on} onChange={this.handleClick.bind(this, name)} />;
} else if (icon) {
prefix = <Icon className='icon' fixedWidth id={icon} />
prefix = <Icon className='icon' fixedWidth id={icon} />;
}
return (

View file

@ -47,6 +47,7 @@ const messages = defineMessages({
export default @injectIntl
class Header extends ImmutablePureComponent {
static propTypes = {
columns: ImmutablePropTypes.list,
unreadNotifications: PropTypes.number,
@ -71,8 +72,8 @@ class Header extends ImmutablePureComponent {
// Only renders the component if the column isn't being shown.
const renderForColumn = conditionalRender.bind(null,
columnId => !columns || !columns.some(
column => column.get('id') === columnId
)
column => column.get('id') === columnId,
),
);
// The result.
@ -125,10 +126,11 @@ class Header extends ImmutablePureComponent {
<a
aria-label={intl.formatMessage(messages.logout)}
onClick={this.handleLogoutClick}
href={ signOutLink }
href={signOutLink}
title={intl.formatMessage(messages.logout)}
><Icon id='sign-out' /></a>
</nav>
);
};
}

View file

@ -28,11 +28,12 @@ export default class NavigationBar extends ImmutablePureComponent {
{ profileLink !== undefined && (
<a
className='edit'
href={ profileLink }
href={profileLink}
><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
)}
</div>
</div>
);
};
}

View file

@ -61,6 +61,10 @@ const messages = defineMessages({
defaultMessage: 'Markdown',
id: 'compose.content-type.markdown',
},
bbcode: {
defaultMessage: 'BBCode',
id: 'compose.content-type.bbcode',
},
plain: {
defaultMessage: 'Plain text',
id: 'compose.content-type.plain',
@ -246,13 +250,17 @@ class ComposerOptions extends ImmutablePureComponent {
name: 'text/markdown',
text: <FormattedMessage {...messages.markdown} />,
},
bbcode: {
icon: 'bold',
name: 'text/bbcode',
text: <FormattedMessage {...messages.bbcode} />,
},
};
// The result.
return (
<div className='composer--options'>
<input
accept={acceptContentTypes}
disabled={disabled || !allowMedia}
key={resetFileKey}
onChange={this.handleChangeFiles}
@ -320,6 +328,7 @@ class ComposerOptions extends ImmutablePureComponent {
contentTypeItems.plain,
contentTypeItems.html,
contentTypeItems.markdown,
contentTypeItems.bbcode,
]}
onChange={onChangeContentType}
onModalClose={onModalClose}
@ -342,12 +351,6 @@ class ComposerOptions extends ImmutablePureComponent {
disabled={disabled}
icon='ellipsis-h'
items={advancedOptions ? [
{
meta: <FormattedMessage {...messages.local_only_long} />,
name: 'do_not_federate',
on: advancedOptions.get('do_not_federate'),
text: <FormattedMessage {...messages.local_only_short} />,
},
{
meta: <FormattedMessage {...messages.threaded_mode_long} />,
name: 'threaded_mode',

View file

@ -115,4 +115,5 @@ class Publisher extends ImmutablePureComponent {
</div>
);
};
}

View file

@ -97,7 +97,7 @@ class SearchResults extends ImmutablePureComponent {
<section>
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
{results.get('statuses').map(statusId => <StatusContainer id={statusId} key={statusId}/>)}
{results.get('statuses').map(statusId => <StatusContainer id={statusId} key={statusId} />)}
{results.get('statuses').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
</section>
@ -131,4 +131,5 @@ class SearchResults extends ImmutablePureComponent {
</div>
);
};
}

View file

@ -51,9 +51,10 @@ class TextareaIcons extends ImmutablePureComponent {
id={icon}
/>
</span>
) : null
) : null,
) : null}
</div>
);
}
}

View file

@ -40,7 +40,7 @@ export default class Upload extends ImmutablePureComponent {
return (
<div className='composer--upload_form--item' tabIndex='0' role='button'>
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12, }) }}>
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
{({ scale }) => (
<div style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
<div className={classNames('composer--upload_form--actions', { active: true })}>

View file

@ -7,6 +7,7 @@ import SensitiveButtonContainer from '../containers/sensitive_button_container';
import { FormattedMessage } from 'react-intl';
export default class UploadForm extends ImmutablePureComponent {
static propTypes = {
mediaIds: ImmutablePropTypes.list.isRequired,
};

View file

@ -30,7 +30,8 @@ export default class UploadProgress extends React.PureComponent {
<div className='backdrop'>
<Motion defaultStyle={{ width: 0 }} style={{ width: spring(progress) }}>
{({ width }) =>
(<div className='tracker' style={{ width: `${width}%` }}
(<div
className='tracker' style={{ width: `${width}%` }}
/>)
}
</Motion>

View file

@ -22,11 +22,11 @@ import { privacyPreference } from 'flavours/glitch/util/privacy_preference';
const messages = defineMessages({
missingDescriptionMessage: { id: 'confirmations.missing_media_description.message',
defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.' },
defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.' },
missingDescriptionConfirm: { id: 'confirmations.missing_media_description.confirm',
defaultMessage: 'Send anyway' },
defaultMessage: 'Send anyway' },
missingDescriptionEdit: { id: 'confirmations.missing_media_description.edit',
defaultMessage: 'Edit media' },
defaultMessage: 'Edit media' },
});
// State mapping.
@ -38,12 +38,12 @@ function mapStateToProps (state) {
const sideArmRestrictedPrivacy = replyPrivacy ? privacyPreference(replyPrivacy, sideArmBasePrivacy) : null;
let sideArmPrivacy = null;
switch (state.getIn(['local_settings', 'side_arm_reply_mode'])) {
case 'copy':
sideArmPrivacy = replyPrivacy;
break;
case 'restrict':
sideArmPrivacy = sideArmRestrictedPrivacy;
break;
case 'copy':
sideArmPrivacy = replyPrivacy;
break;
case 'restrict':
sideArmPrivacy = sideArmRestrictedPrivacy;
break;
}
sideArmPrivacy = sideArmPrivacy || sideArmBasePrivacy;
return {

View file

@ -12,7 +12,6 @@ import Motion from 'flavours/glitch/util/optional_motion';
import spring from 'react-motion/lib/spring';
import SearchResultsContainer from './containers/search_results_container';
import { me, mascot } from 'flavours/glitch/util/initial_state';
import { cycleElefriendCompose } from 'flavours/glitch/actions/compose';
import HeaderContainer from './containers/header_container';
const messages = defineMessages({
@ -20,15 +19,10 @@ const messages = defineMessages({
});
const mapStateToProps = (state, ownProps) => ({
elefriend: state.getIn(['compose', 'elefriend']),
showSearch: ownProps.multiColumn ? state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']) : ownProps.isSearchPage,
});
const mapDispatchToProps = (dispatch, { intl }) => ({
onClickElefriend () {
dispatch(cycleElefriendCompose());
},
onMount () {
dispatch(mountCompose());
},
@ -37,16 +31,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(unmountCompose());
},
});
export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class Compose extends React.PureComponent {
static propTypes = {
multiColumn: PropTypes.bool,
showSearch: PropTypes.bool,
isSearchPage: PropTypes.bool,
elefriend: PropTypes.number,
onClickElefriend: PropTypes.func,
onMount: PropTypes.func,
onUnmount: PropTypes.func,
intl: PropTypes.object.isRequired,
@ -70,14 +62,12 @@ class Compose extends React.PureComponent {
render () {
const {
elefriend,
intl,
multiColumn,
onClickElefriend,
isSearchPage,
showSearch,
} = this.props;
const computedClass = classNames('drawer', `mbstobon-${elefriend}`);
const computedClass = classNames('drawer', 'mbstobon');
return (
<div className={computedClass} role='region' aria-label={intl.formatMessage(messages.compose)}>
@ -92,7 +82,7 @@ class Compose extends React.PureComponent {
<ComposeFormContainer />
<div className='drawer__inner__mastodon'>
{mascot ? <img alt='' draggable='false' src={mascot} /> : <button className='mastodon' onClick={onClickElefriend} />}
{mascot ? <img alt='' draggable='false' src={mascot} /> : <button className='mastodon' />}
</div>
</div>}

View file

@ -59,7 +59,7 @@ class Conversation extends ImmutablePureComponent {
}
destination = `/statuses/${lastStatus.get('id')}`;
}
let state = {...router.history.location.state};
let state = { ...router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
router.history.push(destination, state);
e.preventDefault();

View file

@ -7,7 +7,6 @@ import IconButton from 'flavours/glitch/components/icon_button';
import Icon from 'flavours/glitch/components/icon';
import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl';
import { autoPlayGif, reduceMotion } from 'flavours/glitch/util/initial_state';
import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg';
import { mascot } from 'flavours/glitch/util/initial_state';
import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light';
import classNames from 'classnames';
@ -423,7 +422,7 @@ class Announcements extends ImmutablePureComponent {
return (
<div className='announcements'>
<img className='announcements__mastodon' alt='' draggable='false' src={mascot || elephantUIPlane} />
mascot && ( <img className='announcements__mastodon' alt='' draggable='false' src={mascot} /> )
<div className='announcements__container'>
<ReactSwipeableViews animateHeight={!reduceMotion} adjustHeight={reduceMotion} index={index} onChangeIndex={this.handleChangeIndex}>

View file

@ -16,6 +16,7 @@ import { fetchLists } from 'flavours/glitch/actions/lists';
import { preferencesLink } from 'flavours/glitch/util/backend_links';
import NavigationBar from '../compose/components/navigation_bar';
import LinkFooter from 'flavours/glitch/features/ui/components/link_footer';
import { fetchPanel, fetchPleromaConfig } from 'mastodon/actions/pleroma';
import TrendsContainer from './containers/trends_container';
const messages = defineMessages({
@ -55,6 +56,8 @@ const makeMapStateToProps = () => {
columns: state.getIn(['settings', 'columns']),
unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
unreadNotifications: state.getIn(['notifications', 'unread']),
customPanelEnabled: state.getIn(['custom_panel', 'enabled']),
customPanel: state.getIn(['custom_panel', 'panel']),
});
return mapStateToProps;
@ -64,6 +67,8 @@ const mapDispatchToProps = dispatch => ({
fetchFollowRequests: () => dispatch(fetchFollowRequests()),
fetchLists: () => dispatch(fetchLists()),
openSettings: () => dispatch(openModal('SETTINGS', {})),
fetchPanel: () => dispatch(fetchPanel()),
fetchPleromaConfig: () => dispatch(fetchPleromaConfig()),
});
const badgeDisplay = (number, limit) => {
@ -78,9 +83,9 @@ const badgeDisplay = (number, limit) => {
const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
export default @connect(makeMapStateToProps, mapDispatchToProps)
export default @connect(makeMapStateToProps, mapDispatchToProps)
@injectIntl
class GettingStarted extends ImmutablePureComponent {
class GettingStarted extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object.isRequired,
@ -97,6 +102,10 @@ const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
lists: ImmutablePropTypes.list,
fetchLists: PropTypes.func.isRequired,
openSettings: PropTypes.func.isRequired,
fetchPanel: PropTypes.func.isRequired,
fetchPleromaConfig: PropTypes.func.isRequired,
customPanelEnabled: PropTypes.bool,
customPanel: PropTypes.string.isRequired,
};
componentWillMount () {
@ -104,7 +113,7 @@ const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
}
componentDidMount () {
const { fetchFollowRequests, multiColumn } = this.props;
const { fetchFollowRequests, multiColumn, fetchPleromaConfig, fetchPanel } = this.props;
if (!multiColumn && window.innerWidth >= NAVIGATION_PANEL_BREAKPOINT) {
this.context.router.history.replace('/timelines/home');
@ -112,10 +121,13 @@ const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
}
fetchFollowRequests();
fetchPleromaConfig();
fetchPanel();
}
render () {
const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings } = this.props;
const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings, customPanelEnabled, customPanel } = this.props;
const navItems = [];
let listItems = [];
@ -160,11 +172,13 @@ const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
<div key='9'>
<ColumnLink key='10' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' />
{lists.map(list =>
<ColumnLink key={(11 + Number(list.get('id'))).toString()} to={`/timelines/list/${list.get('id')}`} icon='list-ul' text={list.get('title')} />
<ColumnLink key={(11 + Number(list.get('id'))).toString()} to={`/timelines/list/${list.get('id')}`} icon='list-ul' text={list.get('title')} />,
)}
</div>,
]);
const instance_panel = (customPanelEnabled ? <div className='getting-started getting-started__panel' dangerouslySetInnerHTML={{ __html: customPanel }} /> : null);
return (
<Column bindToDocument={!multiColumn} name='getting-started' icon='asterisk' heading={intl.formatMessage(messages.heading)} label={intl.formatMessage(messages.menu)} hideHeadingOnMobile>
<div className='scrollable optionally-scrollable'>
@ -179,6 +193,8 @@ const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
<ColumnLink icon='cogs' text={intl.formatMessage(messages.settings)} onClick={openSettings} />
</div>
{instance_panel}
<LinkFooter />
</div>

View file

@ -80,7 +80,7 @@ class LocalSettingsNavigation extends React.PureComponent {
/>
<LocalSettingsNavigationItem
active={index === 6}
href={ preferencesLink }
href={preferencesLink}
index={6}
icon='cog'
title={intl.formatMessage(messages.preferences)}

View file

@ -59,7 +59,7 @@ class LocalSettingsPage extends React.PureComponent {
onChange={onChange}
>
<FormattedMessage id='settings.hicolor_privacy_icons' defaultMessage='High color privacy icons' />
<span className='hint'><FormattedMessage id='settings.hicolor_privacy_icons.hint' defaultMessage="Display privacy icons in bright and easily distinguishable colors" /></span>
<span className='hint'><FormattedMessage id='settings.hicolor_privacy_icons.hint' defaultMessage='Display privacy icons in bright and easily distinguishable colors' /></span>
</LocalSettingsPageItem>
<LocalSettingsPageItem
settings={settings}
@ -76,7 +76,7 @@ class LocalSettingsPage extends React.PureComponent {
onChange={onChange}
>
<FormattedMessage id='settings.tag_misleading_links' defaultMessage='Tag misleading links' />
<span className='hint'><FormattedMessage id='settings.tag_misleading_links.hint' defaultMessage="Add a visual indication with the link target host to every link not mentioning it explicitly" /></span>
<span className='hint'><FormattedMessage id='settings.tag_misleading_links.hint' defaultMessage='Add a visual indication with the link target host to every link not mentioning it explicitly' /></span>
</LocalSettingsPageItem>
<LocalSettingsPageItem
settings={settings}
@ -99,7 +99,7 @@ class LocalSettingsPage extends React.PureComponent {
id='mastodon-settings--notifications-tab_badge'
onChange={onChange}
>
<FormattedMessage id='settings.notifications.tab_badge' defaultMessage="Unread notifications badge" />
<FormattedMessage id='settings.notifications.tab_badge' defaultMessage='Unread notifications badge' />
<span className='hint'><FormattedMessage id='settings.notifications.tab_badge.hint' defaultMessage="Display a badge for unread notifications in the column icons when the notifications column isn't open" /></span>
</LocalSettingsPageItem>
<LocalSettingsPageItem
@ -109,7 +109,7 @@ class LocalSettingsPage extends React.PureComponent {
onChange={onChange}
>
<FormattedMessage id='settings.notifications.favicon_badge' defaultMessage='Unread notifications favicon badge' />
<span className='hint'><FormattedMessage id='settings.notifications.favicon_badge.hint' defaultMessage="Add a badge for unread notifications to the favicon" /></span>
<span className='hint'><FormattedMessage id='settings.notifications.favicon_badge.hint' defaultMessage='Add a badge for unread notifications to the favicon' /></span>
</LocalSettingsPageItem>
</section>
<section>
@ -272,7 +272,7 @@ class LocalSettingsPage extends React.PureComponent {
{ value: 'drop', message: intl.formatMessage(messages.filters_drop) },
{ value: 'upstream', message: intl.formatMessage(messages.filters_upstream) },
{ value: 'hide', message: intl.formatMessage(messages.filters_hide) },
{ value: 'content_warning', message: intl.formatMessage(messages.filters_cw) }
{ value: 'content_warning', message: intl.formatMessage(messages.filters_cw) },
]}
>
<FormattedMessage id='settings.filtering_behavior' defaultMessage='Filtering behavior' />

View file

@ -53,13 +53,14 @@ export default class LocalSettingsPageItem extends React.PureComponent {
let optionId = `${id}--${opt.value}`;
return (
<label htmlFor={optionId}>
<input type='radio'
<input
type='radio'
name={id}
id={optionId}
value={opt.value}
onBlur={handleChange}
onChange={handleChange}
checked={ currentValue === opt.value }
checked={currentValue === opt.value}
disabled={!enabled}
/>
{opt.message}

View file

@ -4,7 +4,7 @@ import { injectIntl } from 'react-intl';
import {
fetchPinnedAccountsSuggestions,
clearPinnedAccountsSuggestions,
changePinnedAccountsSuggestions
changePinnedAccountsSuggestions,
} from '../../../actions/accounts';
import Search from 'flavours/glitch/features/list_editor/components/search';

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import ColumnSettings from '../components/column_settings';
import { changeSetting } from 'flavours/glitch/actions/settings';
import { changeColumnParams } from 'flavours/glitch/actions/columns';
const mapStateToProps = (state, { columnId }) => {
const uuid = columnId;
const columns = state.getIn(['settings', 'columns']);

View file

@ -46,7 +46,7 @@ class PublicTimeline extends React.PureComponent {
dispatch(local ? expandCommunityTimeline() : expandPublicTimeline());
}
handleLoadMore = () => {
const { dispatch, statusIds, local } = this.props;
const maxId = statusIds.last();

View file

@ -163,7 +163,6 @@ class ActionBar extends React.PureComponent {
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
menu.push(null);
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
} else {
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });

View file

@ -47,7 +47,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
handleAccountClick = (e) => {
if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey) && this.context.router) {
e.preventDefault();
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`, state);
}
@ -58,7 +58,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
parseClick = (e, destination) => {
if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey) && this.context.router) {
e.preventDefault();
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(destination, state);
}

View file

@ -402,7 +402,7 @@ class Status extends ImmutablePureComponent {
}
handleHotkeyOpenProfile = () => {
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`, state);
}

View file

@ -44,7 +44,7 @@ class BoostModal extends ImmutablePureComponent {
if (e.button === 0) {
e.preventDefault();
this.props.onClose();
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`, state);
}
@ -90,9 +90,9 @@ class BoostModal extends ImmutablePureComponent {
<div className='boost-modal__action-bar'>
<div>
{ missingMediaDescription ?
<FormattedMessage id='boost_modal.missing_description' defaultMessage='This toot contains some media without description' />
<FormattedMessage id='boost_modal.missing_description' defaultMessage='This toot contains some media without description' />
:
<FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></span> }} />
<FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></span> }} />
}
</div>
<Button text={intl.formatMessage(buttonText)} onClick={this.handleReblog} ref={this.setRef} />

View file

@ -27,7 +27,7 @@ const ColumnLink = ({ icon, text, to, onClick, href, method, badge }) => {
e.preventDefault();
e.stopPropagation();
return onClick(e);
}
};
return (
<a href='#' onClick={onClick && handleOnClick} className='column-link' tabIndex='0'>
<Icon id={icon} fixedWidth className='column-link__icon' />

View file

@ -575,7 +575,7 @@ class DoodleModal extends ImmutablePureComponent {
<div>
<select aria-label='Canvas size' onInput={this.changeSize} defaultValue={this.size}>
{ Object.values(mapValues(DOODLE_SIZES, (val, k) =>
<option key={k} value={k}>{val[2]}</option>
<option key={k} value={k}>{val[2]}</option>,
)) }
</select>
</div>
@ -602,7 +602,7 @@ class DoodleModal extends ImmutablePureComponent {
'foreground': this.fg === c[0],
'background': this.bg === c[0],
})}
/>
/>,
)
}
</div>

View file

@ -41,7 +41,7 @@ class FavouriteModal extends ImmutablePureComponent {
if (e.button === 0) {
e.preventDefault();
this.props.onClose();
let state = {...this.context.router.history.location.state};
let state = { ...this.context.router.history.location.state };
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`, state);
}

View file

@ -1,67 +1,28 @@
import { connect } from 'react-redux';
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { FormattedMessage, injectIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { invitesEnabled, version, repository, source_url } from 'flavours/glitch/util/initial_state';
import { signOutLink, securityLink } from 'flavours/glitch/util/backend_links';
import { logOut } from 'flavours/glitch/util/log_out';
import { openModal } from 'flavours/glitch/actions/modal';
const messages = defineMessages({
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' },
});
const mapDispatchToProps = (dispatch, { intl }) => ({
onLogout () {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.logoutMessage),
confirm: intl.formatMessage(messages.logoutConfirm),
onConfirm: () => logOut(),
}));
},
});
export default @injectIntl
@connect(null, mapDispatchToProps)
class LinkFooter extends React.PureComponent {
static propTypes = {
onLogout: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleLogoutClick = e => {
e.preventDefault();
e.stopPropagation();
this.props.onLogout();
return false;
}
render () {
return (
<div className='getting-started__footer'>
<ul>
{invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
{!!securityLink && <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>}
<li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
<li><a href={signOutLink} onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
</ul>
<p>
<FormattedMessage
id='getting_started.open_source_notice'
defaultMessage='Glitchsoc is open source software, a friendly fork of {Mastodon}. You can contribute or report issues on GitHub at {github}.'
id='getting_started.mastofe_notice'
defaultMessage='Mastofe is a libre distribution of the frontend from {glitchsoc}, a friendly fork of {mastodon}. You can contribute or report issues at {mastofe}.'
values={{
github: <span><a href='https://github.com/glitch-soc/mastodon' rel='noopener noreferrer' target='_blank'>glitch-soc/mastodon</a> (v{version})</span>,
Mastodon: <a href='https://github.com/tootsuite/mastodon' rel='noopener noreferrer' target='_blank'>Mastodon</a> }}
mastofe: <a href='https://git.pleroma.social/pleroma/mastofe' rel='noopener noreferrer' target='_blank'>Mastofe</a>,
glitchsoc: <a href='https://github.com/glitch-soc/mastodon' rel='noopener noreferrer' target='_blank'>glitch-soc</a>,
mastodon: <a href='https://github.com/tootsuite/mastodon' rel='noopener noreferrer' target='_blank'>Mastodon</a>,
pleroma: <a href='https://pleroma.social' rel='noopener noreferrer' target='_blank'>Pleroma</a>,
}}
/>
</p>
</div>

View file

@ -49,7 +49,7 @@ const PageTwo = ({ intl, myAccount }) => (
privacy='public'
text='Awoo! #introductions'
spoilerText=''
suggestions={ [] }
suggestions={[]}
/>
</div>
</div>

View file

@ -386,7 +386,7 @@ class UI extends React.Component {
navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage);
}
this.favicon = new Favico({ animation:"none" });
this.favicon = new Favico({ animation:'none' });
this.props.dispatch(fetchMarkers());
this.props.dispatch(expandHomeTimeline());
@ -596,7 +596,7 @@ class UI extends React.Component {
<PermaLink href={moved.get('url')} to={`/accounts/${moved.get('id')}`}>
@{moved.get('acct')}
</PermaLink>
)}}
) }}
/>
</div>)}
<SwitchingColumnsArea location={location} layout={layout} navbarUnder={navbarUnder} onLayoutChange={this.handleLayoutChange}>

View file

@ -429,7 +429,7 @@ class Video extends React.PureComponent {
playerStyle.height = height;
} else if (inline) {
return (<div className={computedClass} ref={this.setPlayerRef} tabindex={0}></div>);
return (<div className={computedClass} ref={this.setPlayerRef} tabindex={0} />);
}
let warning;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View file

@ -84,7 +84,7 @@ const messages = {
'navigation_bar.direct': 'ダイレクトメッセージ',
'navigation_bar.bookmarks': 'ブックマーク',
'column.bookmarks': 'ブックマーク'
'column.bookmarks': 'ブックマーク',
};
export default Object.assign({}, inherited, messages);

View file

@ -67,13 +67,13 @@ const messages = {
'advanced_options.threaded_mode.short': 'Tryb wątków',
'advanced_options.threaded_mode.long': 'Przechodzi do tworzenia odpowiedzi po publikacji wpisu',
'advanced_options.threaded_mode.tooltip': 'Włączono tryb wątków',
'column.bookmarks': 'Zakładki',
'compose_form.sensitive': 'Oznacz zawartość multimedialną jako wrażliwą',
'compose_form.spoiler': 'Ukryj tekst za ostrzeżeniem',
'favourite_modal.combo': 'Możesz nacisnąć {combo}, aby pominąć to następnym razem',
'tabs_bar.compose': 'Napisz',
};
export default Object.assign({}, inherited, messages);

View file

@ -128,16 +128,16 @@ function apiStatusToTextMentions (state, status) {
}
return set.union(status.mentions.filter(
mention => mention.id !== me
mention => mention.id !== me,
).map(
mention => `@${mention.acct} `
mention => `@${mention.acct} `,
)).join('');
}
function apiStatusToTextHashtags (state, status) {
const text = unescapeHTML(status.content);
return ImmutableOrderedSet([]).union(recoverHashtags(status.tags, text).map(
(name) => `#${name} `
(name) => `#${name} `,
)).join('');
}
@ -152,7 +152,7 @@ function clearAll(state) {
map.set('in_reply_to', null);
map.update(
'advanced_options',
map => map.mergeWith(overwrite, state.get('default_advanced_options'))
map => map.mergeWith(overwrite, state.get('default_advanced_options')),
);
map.set('privacy', state.get('default_privacy'));
map.set('sensitive', false);
@ -178,7 +178,7 @@ function continueThread (state, status) {
map.set('in_reply_to', status.id);
map.update(
'advanced_options',
map => map.merge(new ImmutableMap({ do_not_federate: /👁\ufe0f?\u200b?(?:<\/p>)?$/.test(status.content) }))
map => map.merge(new ImmutableMap({ do_not_federate: /👁\ufe0f?\u200b?(?:<\/p>)?$/.test(status.content) })),
);
map.set('privacy', status.visibility);
map.set('sensitive', false);
@ -295,7 +295,7 @@ const expiresInFromExpiresAt = expires_at => {
const mergeLocalHashtagResults = (suggestions, prefix, tagHistory) => {
prefix = prefix.toLowerCase();
if (suggestions.length < 4) {
const localTags = tagHistory.filter(tag => tag.toLowerCase().startsWith(prefix) && !suggestions.some(suggestion => suggestion.type === 'hashtag' && suggestion.name.toLowerCase() === tag.toLowerCase()));
const localTags = tagHistory.filter(tag => tag && tag.toLowerCase().startsWith(prefix) && !suggestions.some(suggestion => suggestion.type === 'hashtag' && suggestion.name.toLowerCase() === tag.toLowerCase()));
return suggestions.concat(localTags.slice(0, 4 - suggestions.length).toJS().map(tag => ({ type: 'hashtag', name: tag })));
} else {
return suggestions;
@ -377,7 +377,7 @@ export default function compose(state = initialState, action) {
map.set('privacy', privacyPreference(action.status.get('visibility'), state.get('default_privacy')));
map.update(
'advanced_options',
map => map.merge(new ImmutableMap({ do_not_federate: /👁\ufe0f?\u200b?(?:<\/p>)?$/.test(action.status.get('content')) }))
map => map.merge(new ImmutableMap({ do_not_federate: /👁\ufe0f?\u200b?(?:<\/p>)?$/.test(action.status.get('content')) })),
);
map.set('focusDate', new Date());
map.set('caretPosition', null);
@ -409,7 +409,7 @@ export default function compose(state = initialState, action) {
map.set('poll', null);
map.update(
'advanced_options',
map => map.mergeWith(overwrite, state.get('default_advanced_options'))
map => map.mergeWith(overwrite, state.get('default_advanced_options')),
);
map.set('idempotencyKey', uuid());
});

View file

@ -67,7 +67,7 @@ const deleteFromContexts = (immutableState, ids) => immutableState.withMutations
const filterContexts = (state, relationship, statuses) => {
const ownedStatusIds = statuses.filter(status => status.get('account') === relationship.id)
.map(status => status.get('id'));
.map(status => status.get('id'));
return deleteFromContexts(state, ownedStatusIds);
};

View file

@ -34,6 +34,7 @@ import suggestions from './suggestions';
import pinnedAccountsEditor from './pinned_accounts_editor';
import polls from './polls';
import identity_proofs from './identity_proofs';
import custom_panel from './pleroma';
import trends from './trends';
import announcements from './announcements';
@ -74,6 +75,7 @@ const reducers = {
suggestions,
pinnedAccountsEditor,
polls,
custom_panel,
trends,
};

View file

@ -54,10 +54,10 @@ export default function listEditorReducer(state = initialState, action) {
});
case LIST_CREATE_REQUEST:
case LIST_UPDATE_REQUEST:
return state.withMutations(map => {
map.set('isSubmitting', true);
map.set('isChanged', false);
});
return state.withMutations(map => {
map.set('isSubmitting', true);
map.set('isChanged', false);
});
case LIST_CREATE_FAIL:
case LIST_UPDATE_FAIL:
return state.set('isSubmitting', false);

View file

@ -21,7 +21,7 @@ const initialState = ImmutableMap({
preselect_on_reply: true,
inline_preview_cards: true,
hicolor_privacy_icons: false,
show_content_type_choice: false,
show_content_type_choice: true,
filtering_behavior: 'hide',
tag_misleading_links: true,
rewrite_mentions: 'no',

View file

@ -127,7 +127,7 @@ const clearUnread = (state) => {
state = state.set('unread', state.get('pendingItems').size);
const lastNotification = state.get('items').find(item => item !== null);
return state.set('lastReadId', lastNotification ? lastNotification.get('id') : '0');
}
};
const updateTop = (state, top) => {
state = state.set('top', top);
@ -210,7 +210,7 @@ const recountUnread = (state, last_read_id) => {
mutable.set('unread', mutable.get('pendingItems').count(item => item !== null) + mutable.get('items').count(item => item && compareId(item.get('id'), last_read_id) > 0));
}
});
}
};
export default function notifications(state = initialState, action) {
let st;

View file

@ -0,0 +1,18 @@
import { Map as ImmutableMap } from 'immutable';
import { PANEL_FETCH_SUCCESS, PLEROMA_CONFIG_FETCH_SUCCESS } from 'mastodon/actions/pleroma';
const initialPanel = ImmutableMap({
enabled: false,
panel: '',
});
export default function custom_panel(state = initialPanel, action) {
switch (action.type) {
case PANEL_FETCH_SUCCESS:
return state.set('panel', action.panel); break;
case PLEROMA_CONFIG_FETCH_SUCCESS:
return state.set('enabled', (action.config || {}).showInstanceSpecificPanel);
}
return state;
};

View file

@ -245,21 +245,3 @@
height: 100%;
background: rgba($base-overlay-background, 0.5);
}
@for $i from 0 through 3 {
.mbstobon-#{$i} .drawer__inner__mastodon {
@if $i == 3 {
background: url('~flavours/glitch/images/wave-drawer.png') no-repeat bottom / 100% auto, lighten($ui-base-color, 13%);
} @else {
background: url('~flavours/glitch/images/wave-drawer-glitched.png') no-repeat bottom / 100% auto, lighten($ui-base-color, 13%);
}
& > .mastodon {
background: url("~flavours/glitch/images/mbstobon-ui-#{$i}.png") no-repeat left bottom / contain;
@if $i != 3 {
filter: contrast(50%) brightness(50%);
}
}
}
}

View file

@ -894,10 +894,15 @@
color: $dark-text-color;
}
&__panel {
height: min-content;
}
&__panel,
&__footer {
flex: 0 0 auto;
padding: 10px;
padding-top: 20px;
flex: 0 1 auto;
ul {
margin-bottom: 10px;
@ -910,7 +915,6 @@
p {
color: $dark-text-color;
font-size: 13px;
margin-bottom: 20px;
a {
color: $dark-text-color;
@ -1095,10 +1099,6 @@
.missing-indicator {
padding-top: 20px + 48px;
.regeneration-indicator__figure {
background-image: url('~flavours/glitch/images/elephant_ui_disappointed.svg');
}
}
.scrollable > div > :first-child .notification__dismiss-overlay > .wrappy {

View file

@ -69,6 +69,28 @@
}
}
.media-gallery__audio {
height: 100%;
display: flex;
flex-direction: column;
span {
text-align: center;
color: $darker-text-color;
display: flex;
height: 100%;
align-items: center;
p {
width: 100%;
}
}
audio {
width: 100%;
}
}
.media-gallery {
box-sizing: border-box;
margin-top: 8px;

View file

@ -255,7 +255,6 @@
}
.onboarding-modal__page__wrapper-0 {
background: url('~images/elephant_ui_greeting.svg') no-repeat left bottom / auto 250px;
height: 100%;
padding: 0;
}

View file

@ -66,6 +66,12 @@
margin: -3px 0 0;
}
img {
max-width: 100%;
max-height: 400px;
object-fit: contain;
}
p, pre, blockquote {
margin-bottom: 20px;
white-space: pre-wrap;
@ -149,7 +155,7 @@
}
a {
color: $secondary-text-color;
color: $pleroma-links;
text-decoration: none;
&:hover {

View file

@ -1,8 +1,5 @@
@import 'mixins';
@import 'variables';
@import 'styles/fonts/roboto';
@import 'styles/fonts/roboto-mono';
@import 'styles/fonts/montserrat';
@import 'reset';
@import 'basics';

View file

@ -15,7 +15,6 @@
> * {
flex: 1;
max-height: 235px;
background: url('~images/elephant_ui_plane.svg') no-repeat left bottom / contain;
}
}

View file

@ -8,11 +8,17 @@ $gold-star: #ca8f04; // Dark Goldenrod
$red-bookmark: $warning-red;
// Pleroma-Dark colors
$pleroma-bg: #121a24;
$pleroma-fg: #182230;
$pleroma-text: #b9b9ba;
$pleroma-links: #d8a070;
// Values from the classic Mastodon UI
$classic-base-color: #282c37; // Midnight Express
$classic-primary-color: #9baec8; // Echo Blue
$classic-secondary-color: #d9e1e8; // Pattens Blue
$classic-highlight-color: #2b90d9; // Summer Sky
$classic-base-color: $pleroma-bg;
$classic-primary-color: #9baec8;
$classic-secondary-color: #d9e1e8;
$classic-highlight-color: #d8a070;
// Variables for defaults in UI
$base-shadow-color: $black !default;
@ -51,9 +57,9 @@ $media-modal-media-max-height: 80%;
$no-gap-breakpoint: 415px;
$font-sans-serif: 'mastodon-font-sans-serif' !default;
$font-display: 'mastodon-font-display' !default;
$font-monospace: 'mastodon-font-monospace' !default;
$font-sans-serif: sans-serif !default;
$font-display: sans-serif !default;
$font-monospace: monospace !default;
// Avatar border size (8% default, 100% for rounded avatars)
$ui-avatar-border-size: 8%;

View file

@ -27,10 +27,6 @@ pack:
# language tags and whose default exports are a messages object.
locales: locales
# (OPTIONAL) A file to use as the preview screenshot for the flavour,
# or an array thereof. These are the full path from `app/javascript/`.
screenshot: flavours/glitch/images/glitch-preview.jpg
# (OPTIONAL) The directory which contains the pack files.
# Defaults to the theme directory (`app/javascript/themes/[theme]`),
# which should be sufficient for like 99% of use-cases lol.

View file

@ -1,5 +1,4 @@
import axios from 'axios';
import ready from './ready';
import LinkHeader from 'http-link-header';
export const getLinks = response => {
@ -12,21 +11,10 @@ export const getLinks = response => {
return LinkHeader.parse(value);
};
let csrfHeader = {};
function setCSRFHeader() {
const csrfToken = document.querySelector('meta[name=csrf-token]');
if (csrfToken) {
csrfHeader['X-CSRF-Token'] = csrfToken.content;
}
}
ready(setCSRFHeader);
export default getState => axios.create({
headers: Object.assign(csrfHeader, getState ? {
headers: getState ? {
'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`,
} : {}),
} : {},
transformResponse: [function (data) {
try {

View file

@ -1,9 +1,9 @@
export const preferencesLink = '/settings/preferences';
export const profileLink = '/settings/profile';
export const preferencesLink = '/settings';
export const profileLink = '/user-settings';
export const signOutLink = '/auth/sign_out';
export const termsLink = '/terms';
export const accountAdminLink = (id) => `/admin/accounts/${id}`;
export const statusAdminLink = (account_id, status_id) => `/admin/accounts/${account_id}/statuses/${status_id}`;
export const filterEditLink = (id) => `/filters/${id}/edit`;
export const relationshipsLink = '/relationships';
export const securityLink = '/auth/edit';
export const accountAdminLink = (id) => `/pleroma/admin/#/users/${id}/`;
export const statusAdminLink = (account_id, status_id) => `/pleroma/admin/#/users/${account_id}/`;
export const filterEditLink = undefined; // (id) => `/filters/${id}/edit`;
export const relationshipsLink = undefined; // = '/relationships';
export const securityLink = undefined ; // = '/auth/edit';

View file

@ -1,9 +0,0 @@
import { urlRegex } from './url_regex';
const urlPlaceholder = 'xxxxxxxxxxxxxxxxxxxxxxx';
export function countableText(inputText) {
return inputText
.replace(urlRegex, urlPlaceholder)
.replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '$1@$3');
};

View file

@ -1,8 +1,8 @@
export function recoverHashtags (recognizedTags, text) {
return recognizedTags.map(tag => {
const re = new RegExp(`(?:^|[^\/\)\w])#(${tag.name})`, 'i');
const matched_hashtag = text.match(re);
return matched_hashtag ? matched_hashtag[1] : null;
}
const re = new RegExp(`(?:^|[^\/\)\w])#(${tag.name})`, 'i');
const matched_hashtag = text.match(re);
return matched_hashtag ? matched_hashtag[1] : null;
},
).filter(x => x !== null);
}

View file

@ -1,3 +1,16 @@
import { get, set } from 'lodash';
const maybeSetNotificationsSettings = result => {
const me = get(result, ['meta', 'me']);
if(!me) return;
const showTypes = get(result, ['settings', 'notifications', 'shows'], {});
const excludeTypes = get(result, ['accounts', me, 'pleroma', 'notification_settings', 'exclude_types'], []);
excludeTypes.forEach(x => showTypes[x] = false);
set(result, ['settings', 'notifications', 'shows'], showTypes);
};
const element = document.getElementById('initial-state');
const initialState = element && function () {
const result = JSON.parse(element.textContent);
@ -6,10 +19,12 @@ const initialState = element && function () {
} catch (e) {
result.local_settings = {};
}
maybeSetNotificationsSettings(result);
return result;
}();
const getMeta = (prop) => initialState && initialState.meta && initialState.meta[prop];
const getRight = (prop) => initialState && initialState.rights && initialState.rights[prop];
export const reduceMotion = getMeta('reduce_motion');
export const autoPlayGif = getMeta('auto_play_gif');
@ -21,7 +36,7 @@ export const favouriteModal = getMeta('favourite_modal');
export const deleteModal = getMeta('delete_modal');
export const me = getMeta('me');
export const searchEnabled = getMeta('search_enabled');
export const maxChars = (initialState && initialState.max_toot_chars) || 500;
export const maxChars = getMeta('max_toot_chars') || 500;
export const pollLimits = (initialState && initialState.poll_limits);
export const invitesEnabled = getMeta('invites_enabled');
export const version = getMeta('version');
@ -35,4 +50,6 @@ export const usePendingItems = getMeta('use_pending_items');
export const useSystemEmojiFont = getMeta('system_emoji_font');
export const showTrends = getMeta('trends');
export const deleteOthersNotice = getRight('delete_others_notice');
export default initialState;

View file

@ -7,7 +7,7 @@ export function assignHandlers (target, handlers) {
// We just bind each handler to the `target`.
const handle = target.handlers = {};
Object.keys(handlers).forEach(
key => handle[key] = handlers[key].bind(target)
key => handle[key] = handlers[key].bind(target),
);
}

View file

@ -85,7 +85,10 @@ export default function getStream(streamingAPIBaseURL, accessToken, stream, { co
const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`, accessToken);
ws.onopen = connected;
ws.onmessage = e => received(JSON.parse(e.data));
ws.onmessage = e => {
if (e.data !== '')
received(JSON.parse(e.data));
};
ws.onclose = disconnected;
ws.onreconnect = reconnected;

View file

@ -25,10 +25,6 @@ pack:
# the flavour, relative to this directory.
locales: ../../mastodon/locales
# (OPTIONAL) A file to use as the preview screenshot for the flavour,
# or an array thereof. These are the full path from `app/javascript/`.
screenshot: images/screenshot.jpg
# (OPTIONAL) The directory which contains the pack files.
# Defaults to this directory (`app/javascript/flavour/[flavour]`),
# but in the case of the vanilla Mastodon flavour the pack files are

Some files were not shown because too many files have changed in this diff Show more