13 Commits

Author SHA1 Message Date
Zoë Bijl d1a4aafabf [build] update mini-css-extract-plugin dependencies 2025-10-08 15:49:15 +02:00
Zoë Bijl a011c5addf [build] update caniuse-lite browserlist (#81)
Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/81
Co-authored-by: Zoë Bijl <code@moiety.me>
Co-committed-by: Zoë Bijl <code@moiety.me>
2025-10-08 14:45:40 +02:00
Zoë Bijl d7d013609c [bugfix] correctly format editorconfig (#78)
I had incorrectly assumed you could split the array over multiple lines. Also removed non-existant (for now) files.

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/78
Co-authored-by: Zoë Bijl <code@moiety.me>
Co-committed-by: Zoë Bijl <code@moiety.me>
2025-10-05 12:00:31 +02:00
Matthew Jorgensen e36d764aaa [feature] Preserve 'mastodon-settings' from localStorage on logout (#66)
This preserves the settings for the FE, things like theme, etc.

---

Retains the `mastodon-settings` object from localStorage on logout. I think this is safe to do, it doesn't appear there's any instance-specific or otherwise sensitive info in this object:

```json
{
  "side_arm_reply_mode": "keep",
  "media": {
    "use_blurhash": true,
    "letterbox": false,
    "fullwidth": true,
    "reveal_behind_cw": false,
    "pop_in_player": true,
    "pop_in_position": "right"
  },
  "stretch": true,
  "inline_preview_cards": true,
  "hicolor_privacy_icons": false,
  "notifications": {
    "favicon_badge": false,
    "tab_badge": true
  },
  "confirm_missing_media_description": false,
  "theme": "mastodon",
  "layout": "mobile",
  "preselect_on_reply": false,
  "status_icons": {
    "language": true,
    "reply": true,
    "local_only": true,
    "media": true,
    "visibility": true
  },
  "prepend_cw_re": false,
  "confirm_boost_missing_media_description": false,
  "tag_misleading_links": true,
  "side_arm": "none",
  "collapsed": {
    "enabled": true,
    "auto": {
      "all": false,
      "notifications": false,
      "lengthy": true,
      "reblogs": false,
      "replies": false,
      "media": false,
      "height": 400
    },
    "backgrounds": {
      "user_backgrounds": false,
      "preview_images": false
    },
    "show_action_bar": true
  },
  "show_reply_count": false,
  "confirm_before_clearing_draft": true,
  "rewrite_mentions": "no",
  "content_warnings": {
    "auto_unfold": false,
    "filter": null,
    "media_outside": true,
    "shared_state": true
  },
  "always_show_spoilers_field": true,
  "show_content_type_choice": true
}

```

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/66
Co-authored-by: Matthew Jorgensen <me@prplecake.com>
Co-committed-by: Matthew Jorgensen <me@prplecake.com>
2025-10-01 14:03:18 +02:00
Zoë Bijl c6eb0b1927 [feature] Redesigned login page (#76)
Reworked the login page

- Simplified some of the HTML and CSS
- Removed some `aria-label`s
- Added alt text for the logo
- Added a label for the input field
- Added subtle borders to otherwise flat elements

![redesigned login page with the mentioned changes](/attachments/53dc5c16-deac-4a68-912e-8a4486a93b53)

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/76
Co-authored-by: Zoë Bijl <code@moiety.me>
Co-committed-by: Zoë Bijl <code@moiety.me>
2025-10-01 14:02:18 +02:00
Zoë Bijl 4edb2f2b2c [docs] add documentation for yarn dev (#75)
Added a few lines about running `yarn dev`. It’s easy enough to miss which port it runs on or whether it runs a server at all. Having this explicit URL should help.

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/75
Co-authored-by: Zoë Bijl <code@moiety.me>
Co-committed-by: Zoë Bijl <code@moiety.me>
2025-09-30 11:35:34 +02:00
Sqx. Flann 110c8fb8cc [bugfix] account for data structure change in instance API fallback (#63)
Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/63
Co-authored-by: Sqx. Flann <fl4nn+codeberg@opensuse.org>
Co-committed-by: Sqx. Flann <fl4nn+codeberg@opensuse.org>
2025-07-14 16:42:05 +02:00
Sqx. Flann 6d0b964e2b [feature] Query media description limit (#62)
fixes #61

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/62
Co-authored-by: Sqx. Flann <fl4nn+codeberg@opensuse.org>
Co-committed-by: Sqx. Flann <fl4nn+codeberg@opensuse.org>
2025-06-23 10:51:25 +02:00
prplecake 60792ec753 [feature] Add logout button to sidebar (#48)
This one should probably be scrutinized a little more than my previous two.

For example, I don't know why I needed to do this: https://codeberg.org/prplecake/masto-fe-standalone/src/commit/1a1f48ceaa544650a8b8f50f8fa4e8c7cdc44c12/app/javascript/flavours/glitch/features/ui/index.jsx#L152

~~But, it appears to work.~~ It's live at https://masto-fe.compostintraining.club if you want to test it out.

~~Edit: it breaks stuff. Images don't load anymore...~~ Maybe that was just a network glitch...

Fixes #21.

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/48
Co-authored-by: prplecake <me@prplecake.com>
Co-committed-by: prplecake <me@prplecake.com>
2025-06-07 15:12:37 +02:00
tobi e5869dc945 [docs] Mention correct port (80 not 3000) (#58)
Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/58
Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-06-07 15:00:01 +02:00
prplecake f61625f4bd [bugfix] Don't show deprecated setting modal when auto unfold CWs is toggled on (#47)
Missed in #46, sorry!

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/47
Co-authored-by: prplecake <me@prplecake.com>
Co-committed-by: prplecake <me@prplecake.com>
2025-05-14 20:59:44 +00:00
prplecake 02106153ec [feature] Re-enable auto-unfold CW setting (#46)
Fixes #45

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/46
Co-authored-by: prplecake <me@prplecake.com>
Co-committed-by: prplecake <me@prplecake.com>
2025-05-14 17:23:29 +00:00
daj 8eba3f59f8 [bugfix] Update public/auth.js (#38)
trim trailing spaces from user input for Instance URL

Fix for Issue #37

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/38
Co-authored-by: daj <daj@findmyinbox.co.uk>
Co-committed-by: daj <daj@findmyinbox.co.uk>
2025-04-27 20:56:29 +00:00
24 changed files with 210 additions and 149 deletions
+7
View File
@@ -0,0 +1,7 @@
root = true
[*.scss]
indent_size = 2
[login.scss]
indent_size = 4
+9 -2
View File
@@ -8,7 +8,14 @@ To work on the code decently, you should have Node and Yarn installed. To avoid
## Testing Locally
You can fairly easily test builds of Masto-FE locally by using Docker and the GoToSocial testrig.
If you want to run Masto-FE in dev mode:
- install the project: `yarn install`
- start the dev server: `yarn dev`
You should now be able to connect on http://localhost:3035. Changes will automatically compile.
You can also fairly easily test builds of Masto-FE locally by using Docker and the GoToSocial testrig.
### Build GoToSocial + launch the GtS testrig
@@ -37,7 +44,7 @@ Leave the testrig running.
### Build Masto-FE + run it locally
Now in a *separate* terminal window, get back to the Masto-FE directory, and do a Docker build (this might take a bit of time):
Now in a _separate_ terminal window, get back to the Masto-FE directory, and do a Docker build (this might take a bit of time):
```bash
docker build -t superseriousbusiness/masto-fe-standalone:latest .
+1 -1
View File
@@ -40,7 +40,7 @@ Serve all the stuff in `public` behind an nginx or whatever you want! See the in
### Docker (definitely the easiest way)
The Docker container is based on Nginx, and serves over port 3000. Just deploy it and listen on that port, preferably with a reverse proxy at some point (Traefik? Caddy? Another Nginx perhaps?) handling https.
The Docker container is based on Nginx, and serves over port 80. Just deploy it and listen on that port, preferably with a reverse proxy at some point (Traefik? Caddy? Another Nginx perhaps?) handling https.
## Testing locally, linting, etc
@@ -1,4 +1,4 @@
import { expandSpoilers, disableSwiping } from 'flavours/glitch/initial_state';
import { disableSwiping } from 'flavours/glitch/initial_state';
import { openModal } from './modal';
@@ -7,18 +7,9 @@ export const LOCAL_SETTING_DELETE = 'LOCAL_SETTING_DELETE';
export function checkDeprecatedLocalSettings() {
return (dispatch, getState) => {
const local_auto_unfold = getState().getIn(['local_settings', 'content_warnings', 'auto_unfold']);
const local_swipe_to_change_columns = getState().getIn(['local_settings', 'swipe_to_change_columns']);
let changed_settings = [];
if (local_auto_unfold !== null && local_auto_unfold !== undefined) {
if (local_auto_unfold === expandSpoilers) {
dispatch(deleteLocalSetting(['content_warnings', 'auto_unfold']));
} else {
changed_settings.push('user_setting_expand_spoilers');
}
}
if (local_swipe_to_change_columns !== null && local_swipe_to_change_columns !== undefined) {
if (local_swipe_to_change_columns === !disableSwiping) {
dispatch(deleteLocalSetting(['swipe_to_change_columns']));
@@ -21,11 +21,18 @@ export const fetchServer = () => (dispatch, getState) => {
dispatch(fetchServerRequest());
api(getState)
.get('/api/v2/instance').then(({ data }) => {
if (data.contact.account) dispatch(importFetchedAccount(data.contact.account));
dispatch(fetchServerSuccess(data));
try {
api(getState)
.get('/api/v2/instance').then({ data })
if (data.contact.account) dispatch(importFetchedAccount(data.contact.account));
dispatch(fetchServerSuccess(data));
} catch (e) {
api(getState)
.get('/api/v1/instance').then(({ data }) => {
if (data.contact_account) dispatch(importFetchedAccount(data.contact_account));
dispatch(fetchServerSuccess(data));
}).catch(err => dispatch(fetchServerFail(err)));
}
};
const fetchServerRequest = () => ({
@@ -8,9 +8,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
// Our imports
import { expandSpoilers } from 'flavours/glitch/initial_state';
import DeprecatedLocalSettingsPageItem from './deprecated_item';
import LocalSettingsPageItem from './item';
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -308,35 +305,21 @@ class LocalSettingsPage extends PureComponent {
</LocalSettingsPageItem>
<section>
<h2><FormattedMessage id='settings.content_warnings_unfold_opts' defaultMessage='Auto-unfolding options' /></h2>
<DeprecatedLocalSettingsPageItem
<LocalSettingsPageItem
settings={settings}
item={['content_warnings', 'auto_unfold']}
onChange={onChange}
id='mastodon-settings--content_warnings-auto_unfold'
value={expandSpoilers}
>
<FormattedMessage id='settings.enable_content_warnings_auto_unfold' defaultMessage='Automatically unfold content-warnings' />
<span className='hint'>
<FormattedMessage
id='settings.deprecated_setting'
defaultMessage="This setting is now controlled from Mastodon's {settings_page_link}"
values={{
settings_page_link: (
<span>
<FormattedMessage
id='settings.shared_settings_link'
defaultMessage='user preferences'
/>
</span>
),
}}
/>
</span>
</DeprecatedLocalSettingsPageItem>
</LocalSettingsPageItem>
<LocalSettingsPageItem
settings={settings}
item={['content_warnings', 'filter']}
id='mastodon-settings--content_warnings-auto_unfold'
onChange={onChange}
dependsOn={[['content_warnings', 'auto_unfold']]}
placeholder={intl.formatMessage(messages.regexp)}
disabled={!expandSpoilers}
>
<FormattedMessage id='settings.content_warnings_filter' defaultMessage='Content warnings to not automatically unfold:' />
</LocalSettingsPageItem>
@@ -55,6 +55,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
singleColumn: PropTypes.bool,
children: PropTypes.node,
openSettings: PropTypes.func,
onLogout: PropTypes.func,
};
// Corresponds to (max-width: $no-gap-breakpoint + 285px - 1px) in SCSS
@@ -139,7 +140,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
};
render () {
const { columns, children, singleColumn, openSettings } = this.props;
const { columns, children, singleColumn, openSettings, onLogout } = this.props;
const { renderComposePanel } = this.state;
if (singleColumn) {
@@ -158,7 +159,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
<div className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational'>
<div className='columns-area__panels__pane__inner'>
<NavigationPanel onOpenSettings={openSettings} />
<NavigationPanel onOpenSettings={openSettings} onLogout={onLogout} />
</div>
</div>
</div>
@@ -11,7 +11,6 @@ import illustration from 'flavours/glitch/images/logo_warn_glitch.svg';
const messages = defineMessages({
discardChanges: { id: 'confirmations.deprecated_settings.confirm', defaultMessage: 'Use Mastodon preferences' },
user_setting_expand_spoilers: { id: 'settings.enable_content_warnings_auto_unfold', defaultMessage: 'Automatically unfold content-warnings' },
user_setting_disable_swiping: { id: 'settings.swipe_to_change_columns', defaultMessage: 'Allow swiping to change columns (Mobile only)' },
});
@@ -26,6 +26,7 @@ import { Tesseract as fetchTesseract } from 'flavours/glitch/features/ui/util/as
import Video, { getPointerPosition } from 'flavours/glitch/features/video';
import { me } from 'flavours/glitch/initial_state';
import { assetHost } from 'flavours/glitch/utils/config';
import { maxMediaDescChars } from 'flavours/glitch/initial_state';
import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from '../../../actions/compose';
@@ -367,10 +368,10 @@ class FocalPointModal extends ImmutablePureComponent {
<div className='setting-text__toolbar'>
<button disabled={detecting || media.get('type') !== 'image' || is_changing_upload} className='link-button' onClick={this.handleTextDetection}><FormattedMessage id='upload_modal.detect_text' defaultMessage='Detect text from picture' /></button>
<CharacterCounter max={1500} text={detecting ? '' : description} />
<CharacterCounter max={maxMediaDescChars} text={detecting ? '' : description} />
</div>
<Button disabled={!dirty || detecting || isUploadingThumbnail || length(description) > 1500 || is_changing_upload} text={intl.formatMessage(is_changing_upload ? messages.applying : messages.apply)} onClick={this.handleSubmit} />
<Button disabled={!dirty || detecting || isUploadingThumbnail || length(description) > maxMediaDescChars || is_changing_upload} text={intl.formatMessage(is_changing_upload ? messages.applying : messages.apply)} onClick={this.handleSubmit} />
</div>
<div className='focal-point-modal__content'>
@@ -30,6 +30,7 @@ const messages = defineMessages({
advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' },
openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' },
app_settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Log out' },
});
class NavigationPanel extends Component {
@@ -42,6 +43,7 @@ class NavigationPanel extends Component {
static propTypes = {
intl: PropTypes.object.isRequired,
onOpenSettings: PropTypes.func,
onLogout: PropTypes.func,
};
isFirehoseActive = (match, location) => {
@@ -49,7 +51,7 @@ class NavigationPanel extends Component {
};
render() {
const { intl, onOpenSettings } = this.props;
const { intl, onOpenSettings, onLogout } = this.props;
const { signedIn, disabledAccountId } = this.context.identity;
return (
@@ -104,6 +106,7 @@ class NavigationPanel extends Component {
<hr />
<ColumnLink transparent onClick={onOpenSettings} icon='cogs' text={intl.formatMessage(messages.app_settings)} />
<ColumnLink transparent onClick={onLogout} icon='sign-out' text={intl.formatMessage(messages.logout)} />
</>
)}
@@ -112,6 +115,7 @@ class NavigationPanel extends Component {
<ColumnLink transparent to='/about' icon='ellipsis-h' text={intl.formatMessage(messages.about)} />
</div>
<NavigationPortal />
</div>
);
@@ -1,14 +1,22 @@
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { openModal } from 'flavours/glitch/actions/modal';
import { logOut } from 'flavours/glitch/utils/log_out';
import ColumnsArea from '../components/columns_area';
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 mapStateToProps = state => ({
columns: state.getIn(['settings', 'columns']),
});
const mapDispatchToProps = dispatch => ({
const mapDispatchToProps = (dispatch, { intl }) => ({
openSettings (e) {
e.preventDefault();
e.stopPropagation();
@@ -17,6 +25,17 @@ const mapDispatchToProps = dispatch => ({
modalProps: {},
}));
},
onLogout () {
dispatch(openModal({
modalType: 'CONFIRM',
modalProps: {
message: intl.formatMessage(messages.logoutMessage),
confirm: intl.formatMessage(messages.logoutConfirm),
closeWhenConfirm: false,
onConfirm: () => logOut(),
},
}));
},
});
export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })(ColumnsArea);
export default injectIntl(connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })(ColumnsArea));
@@ -149,7 +149,9 @@ class SwitchingColumnsArea extends PureComponent {
componentDidUpdate (prevProps) {
if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
this.node.handleChildrenContentChange();
if (this.node && this.node.handleChildrenContentChange === 'function') {
this.node.handleChildrenContentChange();
}
}
if (prevProps.singleColumn !== this.props.singleColumn) {
@@ -104,6 +104,7 @@ export const hasMultiColumnPath = initialPath === '/'
* @property {object} local_settings
* @property {number} max_toot_chars
* @property {number} max_media_attachments
* @property {number} max_media_desc_chars
* @property {number} poll_limits
*/
@@ -167,6 +168,7 @@ export const sso_redirect = getMeta('sso_redirect');
// Glitch-soc-specific settings
export const maxChars = (initialState && initialState.max_toot_chars) || 500;
export const maxMediaAttachments = (initialState && initialState.max_media_attachments) || 4;
export const maxMediaDescChars = (initialState && initialState.max_media_desc_chars) || 1500;
export const favouriteModal = getMeta('favourite_modal');
export const pollLimits = (initialState && initialState.poll_limits);
export const defaultContentType = getMeta('default_content_type');
@@ -23,6 +23,7 @@ const initialState = ImmutableMap({
tag_misleading_links: true,
rewrite_mentions: 'no',
content_warnings : ImmutableMap({
auto_unfold : false,
filter : null,
media_outside: true,
shared_state : true,
@@ -5,88 +5,76 @@
@import 'basics';
body {
height: 100vh;
a {
color: #89caff;
}
font-family: ui-rounded, 'mastodon-font-sans-serif', sans-serif;
font-size: 1rem;
line-height: 1.5;
}
.login-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
max-width: 30rem;
--login-row-gap: 10px;
display: grid;
row-gap: var(--login-row-gap);
margin: 50px auto;
padding: 1rem;
margin: auto;
max-width: 30rem;
box-sizing: border-box;
}
header {
display: flex;
justify-content: center;
margin-bottom: 1rem;
}
.mascot {
height: 10rem;
img {
height: 150px;
}
}
main {
display: flex;
position: relative;
flex-direction: column;
row-gap: 10px;
row-gap: var(--login-row-gap);
}
.login {
display: flex;
position: relative;
column-gap: 10px;
h1 {
font-size: 28px;
line-height: 33px;
font-weight: 700;
margin-bottom: 15px;
}
.instance {
background: #282c37;
border: 0;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box;
color: #9baec8;
display: block;
label {
font-family: inherit;
font-size: 16px;
line-height: 18px;
margin: 0;
outline: 0;
padding: 15px;
padding-inline-end: 30px;
width: 100%;
color: $primary-text-color;
display: block;
word-wrap: break-word;
font-weight: 500;
grid-column: 1 / -1;
}
.button {
display: flex;
column-gap: .5rem;
align-items: center;
justify-content: center;
background-color: #66befe;
border: 0 none;
form {
display: grid;
gap: 10px;
grid-template-columns: 1fr min-content;
}
:focus-visible,
button:focus-visible {
outline: 2px solid #66befe;
outline-offset: 3px;
}
button {
padding: 7px 10px;
border: 1px solid lighten(#66befe, 7%);
border-radius: 4px;
box-sizing: border-box;
color: #2a2b2f;
cursor: pointer;
font-size: 16px;
letter-spacing: 0;
line-height: 22px;
font-family: inherit;
font-size: inherit;
font-weight: 500;
padding: 7px 10px;
position: relative;
text-align: center;
text-decoration: none;
white-space: nowrap;
flex-basis: auto;
background-color: #66befe;
&:hover {
background-color: #89caff;
@@ -98,21 +86,35 @@ main {
}
}
.content {
background-color: #444b5d;
input[type='text'] {
display: block;
marhing: 0;
padding: 15px;
font-size: 1rem;
line-height: 1.5rem;
border: 1px solid lighten(#282c37, 7%);
border-radius: 4px;
display: flex;
flex-direction: column;
row-gap: .75rem;
box-sizing: border-box;
box-shadow: none;
color: #9baec8;
font-family: inherit;
font-size: inherit;
line-height: 18px;
background: #282c37;
}
.content {
padding: 20px 15px 20px;
border-radius: 4px;
border: 1px solid lighten(#39404f, 7%);
color: #fff;
background-color: #39404f;
}
.link-footer {
padding: 10px;
padding-inline: 10px;
color: #97a8b4;
font-size: 0.875rem;
p, a {
color: #97A8B4;
a {
color: inherit;
}
}
}
@@ -1,9 +1,9 @@
import { expandSpoilers } from 'flavours/glitch/initial_state';
function _autoUnfoldCW(spoiler_text, skip_unfold_regex) {
if (!expandSpoilers)
function _autoUnfoldCW(spoiler_text, settings) {
if (!settings.getIn(['content_warnings', 'auto_unfold']))
return false;
const skip_unfold_regex = settings.getIn(['content_warnings', 'filter']);
if (!skip_unfold_regex)
return true;
@@ -20,12 +20,12 @@ function _autoUnfoldCW(spoiler_text, skip_unfold_regex) {
}
export function autoHideCW(settings, spoiler_text) {
return !_autoUnfoldCW(spoiler_text, settings.getIn(['content_warnings', 'filter']));
return !_autoUnfoldCW(spoiler_text, settings);
}
export function autoUnfoldCW(settings, status) {
if (!status)
return false;
return _autoUnfoldCW(status.get('spoiler_text'), settings.getIn(['content_warnings', 'filter']));
return _autoUnfoldCW(status.get('spoiler_text'), settings);
}
+11 -4
View File
@@ -17,11 +17,18 @@ export const fetchServer = () => (dispatch, getState) => {
dispatch(fetchServerRequest());
api(getState)
.get('/api/v2/instance').then(({ data }) => {
if (data.contact.account) dispatch(importFetchedAccount(data.contact.account));
dispatch(fetchServerSuccess(data));
try {
api(getState)
.get('/api/v2/instance').then({ data });
if (data.contact.account) dispatch(importFetchedAccount(data.contact.account));
dispatch(fetchServerSuccess(data));
} catch (e) {
api(getState)
.get('/api/v1/instance').then(({ data }) => {
if (data.contact_account) dispatch(importFetchedAccount(data.contact_account));
dispatch(fetchServerSuccess(data));
}).catch(err => dispatch(fetchServerFail(err)));
}
};
const fetchServerRequest = () => ({
@@ -25,6 +25,7 @@ import UploadProgress from 'mastodon/features/compose/components/upload_progress
import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
import { me } from 'mastodon/initial_state';
import { assetHost } from 'mastodon/utils/config';
import { maxMediaDescChars } from 'mastodon/initial_state';
import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from '../../../actions/compose';
import Video, { getPointerPosition } from '../../video';
@@ -374,12 +375,12 @@ class FocalPointModal extends ImmutablePureComponent {
>
<FormattedMessage id='upload_modal.detect_text' defaultMessage='Detect text from picture' />
</button>
<CharacterCounter max={1500} text={detecting ? '' : description} />
<CharacterCounter max={maxMediaDescChars} text={detecting ? '' : description} />
</div>
<Button
type='submit'
disabled={!dirty || detecting || isUploadingThumbnail || length(description) > 1500 || is_changing_upload}
disabled={!dirty || detecting || isUploadingThumbnail || length(description) > maxMediaDescChars || is_changing_upload}
text={intl.formatMessage(is_changing_upload ? messages.applying : messages.apply)}
/>
</form>
+2
View File
@@ -90,6 +90,7 @@
* @property {boolean=} critical_updates_pending
* @property {InitialStateMeta} meta
* @property {number} max_toot_chars
* @property {number} max_media_desc_chars
*/
const element = document.getElementById('initial-state');
@@ -149,5 +150,6 @@ export const sso_redirect = getMeta('sso_redirect');
// Glitch-soc-specific settings
export const maxChars = (initialState && initialState.max_toot_chars) || 500;
export const maxMediaDescChars = (initialState && initialState.max_media_desc_chars) || 1500;
export default initialState;
+7 -1
View File
@@ -1,5 +1,11 @@
document.addEventListener("DOMContentLoaded", async function() {
await ready();
const form = document.querySelector('#login')
form?.addEventListener("submit", (event) => {
event.preventDefault();
auth();
});
});
async function ready() {
@@ -20,7 +26,7 @@ async function ready() {
async function auth() {
setMessage('Please wait');
const instance = document.getElementById('instance').value;
const instance = document.getElementById('instance').value.trim();
const matches = instance.match(/((?:http|https):\/\/)?(.*)/);
const protocol = matches[1];
+13 -17
View File
@@ -5,35 +5,31 @@
<meta charset="UTF-8">
<title>Login | Masto-FE (🦥 flavour)</title>
<meta content="width=device-width, initial-scale=1" name="viewport">
<link rel="stylesheet" media="all" href="/packs/css/core/common.css" />
<link rel="stylesheet" media="all" href="/packs/css/flavours/glitch/login.css" />
<script src="/auth.js"></script>
</head>
<body>
<body class="app-body">
<div class="login-container">
<header>
<img class="mascot" alt src="images/mascot.svg" />
<img alt="a friendly smiling sloth" src="images/mascot.svg" />
</header>
<main>
<div class="login">
<input class="instance" id="instance" placeholder="Instance URL" aria-label="Instance URL" value="" onkeypress="if (event.keyCode == 13) auth()">
<button type="submit" class="button" id="btn" onclick="auth()">
<h1>Log into your instance</h1>
<form method="post" id="login">
<label for="instance">Instance URL</label>
<input type="text" id="instance" value="" class="input instance">
<button type="submit" class="button" id="btn">
<span id="message">Authorize</span>
</button>
</div>
<div class="content">
</form>
<aside class="content">
<p>
This is a standalone version of the Mastodon front-end that offers compatibility with GoToSocial instances. It is based
on <a href="https://iceshrimp.dev/iceshrimp/masto-fe-standalone" rel="nofollow">Iceshrimp's Masto-FE Standalone</a>,
which is itself a fork of <a href="https://github.com/glitch-soc/mastodon" rel="nofollow">Mastodon Glitch Edition</a>,
which in turn forks <a href="https://github.com/mastodon/mastodon/" rel="nofollow">Mastodon</a>. Phew!
<strong>Note:</strong>
this application is completely client-side, meaning everything happens in the browser on your machine.
It does not store information anywhere else than your browser's local storage.
</p>
<p>
The application is completely client-side, meaning everything happens in the browser on your machine. It does not store
information anywhere else than your browser's local storage.
</p>
</div>
</aside>
</main>
<footer class="link-footer">
<p>
+4
View File
@@ -4,7 +4,11 @@
<meta charset="UTF-8">
<title>Logout | Masto-FE (🦥 flavour)</title>
<script>
const preserveSettings = localStorage.getItem("mastodon-settings");
localStorage.clear();
if (preserveSettings !== null) {
localStorage.setItem("mastodon-settings", preserveSettings);
}
window.location.href = "/login.html";
</script>
</head>
+10 -1
View File
@@ -23,7 +23,15 @@ async function loadState() {
}
const apiUrl = `${protocol}${domain}/api`;
const instance = await fetch(`${apiUrl}/v1/instance`).then(async p => await p.json());
let instance
try {
instance = await fetch(`${apiUrl}/v2/instance`).then(async p => await p.json());
if (!instance.configuration) {
throw new Error('Instance API v2 unavaialble');
}
} catch (e) {
instance = await fetch(`${apiUrl}/v1/instance`).then(async p => await p.json());
}
const options = {headers: {Authorization: `Bearer ${access_token}`}};
const credentials = await fetch(`${apiUrl}/v1/accounts/verify_credentials`, options).then(async p => await p.json());
const state = {
@@ -93,6 +101,7 @@ async function loadState() {
},
"max_toot_chars": instance.configuration.statuses.max_characters,
"max_media_attachments": instance.configuration.statuses.max_media_attachments,
"max_media_desc_chars": instance.configuration.media_attachments.description_limit,
"poll_limits": {
"max_expiration": instance.configuration.polls.max_expiration,
"max_option_chars": instance.configuration.polls.max_characters_per_option,
+15 -5
View File
@@ -2266,7 +2266,7 @@
"@types/tough-cookie" "*"
parse5 "^7.0.0"
"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
"@types/json-schema@*", "@types/json-schema@^7.0.5":
version "7.0.12"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
@@ -2276,6 +2276,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85"
integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==
"@types/json-schema@^7.0.8":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
"@types/json-stable-stringify@^1.0.32":
version "1.0.34"
resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz#c0fb25e4d957e0ee2e497c1f553d7f8bb668fd75"
@@ -3941,9 +3946,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001502, caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001538:
version "1.0.30001688"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz"
integrity sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==
version "1.0.30001749"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001749.tgz"
integrity sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==
chalk@5.2.0:
version "5.2.0"
@@ -9928,7 +9933,12 @@ punycode@1.4.1, punycode@^1.2.4, punycode@^1.4.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==
punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0:
punycode@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
punycode@^2.1.1, punycode@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==