From b5038da7dddcf8e75288ee5635153a730ae47528 Mon Sep 17 00:00:00 2001 From: ihateblueb Date: Wed, 28 May 2025 17:46:05 -0400 Subject: [PATCH 1/3] bubble timeline feature --- .../flavours/glitch/actions/streaming.js | 8 +- .../flavours/glitch/actions/timelines.js | 2 + .../components/column_settings.jsx | 41 +++++ .../containers/column_settings_container.js | 29 ++++ .../glitch/features/bubble_timeline/index.jsx | 160 ++++++++++++++++++ .../glitch/features/firehose/index.jsx | 23 ++- .../glitch/features/getting_started/index.jsx | 6 + .../flavours/glitch/features/ui/index.jsx | 1 + .../features/ui/util/async-components.js | 4 + .../glitch/reducers/local_settings.js | 6 +- .../flavours/glitch/styles/modern/style.scss | 5 +- .../material-icons/400-24px/bubble-chart.svg | 1 + 12 files changed, 277 insertions(+), 9 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/bubble_timeline/components/column_settings.jsx create mode 100644 app/javascript/flavours/glitch/features/bubble_timeline/containers/column_settings_container.js create mode 100644 app/javascript/flavours/glitch/features/bubble_timeline/index.jsx create mode 100644 app/javascript/material-icons/400-24px/bubble-chart.svg diff --git a/app/javascript/flavours/glitch/actions/streaming.js b/app/javascript/flavours/glitch/actions/streaming.js index 7b006c1be7..237b85d91e 100644 --- a/app/javascript/flavours/glitch/actions/streaming.js +++ b/app/javascript/flavours/glitch/actions/streaming.js @@ -22,7 +22,7 @@ import { fillHomeTimelineGaps, fillPublicTimelineGaps, fillCommunityTimelineGaps, - fillListTimelineGaps, + fillListTimelineGaps, fillBubbleTimelineGaps } from './timelines'; /** @@ -157,6 +157,12 @@ export const connectUserStream = () => export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`, {}, { fillGaps: () => (fillCommunityTimelineGaps({ onlyMedia })) }); +/** + * @returns {function(): void} + */ +export const connectBubbleStream = () => + connectTimelineStream(`bubble`, `public:bubble`, {}, { fillGaps: () => (fillBubbleTimelineGaps()) }); + /** * @param {Object} options * @param {boolean} [options.onlyMedia] diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js index eb5050f152..d379e17fb0 100644 --- a/app/javascript/flavours/glitch/actions/timelines.js +++ b/app/javascript/flavours/glitch/actions/timelines.js @@ -165,6 +165,7 @@ export function fillTimelineGaps(timelineId, path, params = {}, done = noOp) { export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done); export const expandPublicTimeline = ({ maxId, onlyMedia, onlyRemote, allowLocalOnly } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : (allowLocalOnly ? ':allow_local_only' : '')}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, allow_local_only: !!allowLocalOnly, max_id: maxId, only_media: !!onlyMedia }, done); export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done); +export const expandBubbleTimeline = ({ maxId } = {}, done = noOp) => expandTimeline(`bubble`, '/api/v1/timelines/bubble', { local: true, max_id: maxId }, done); export const expandDirectTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done); export const expandAccountTimeline = (accountId, { maxId, withReplies, tagged } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}${tagged ? `:${tagged}` : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, exclude_reblogs: withReplies, tagged, max_id: maxId }); export const expandAccountFeaturedTimeline = (accountId, { tagged } = {}) => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true, tagged }); @@ -184,6 +185,7 @@ export const expandHashtagTimeline = (hashtag, { maxId, tags, local } = export const fillHomeTimelineGaps = (done = noOp) => fillTimelineGaps('home', '/api/v1/timelines/home', {}, done); export const fillPublicTimelineGaps = ({ onlyMedia, onlyRemote, allowLocalOnly } = {}, done = noOp) => fillTimelineGaps(`public${onlyRemote ? ':remote' : (allowLocalOnly ? ':allow_local_only' : '')}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, only_media: !!onlyMedia, allow_local_only: !!allowLocalOnly }, done); export const fillCommunityTimelineGaps = ({ onlyMedia } = {}, done = noOp) => fillTimelineGaps(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, only_media: !!onlyMedia }, done); +export const fillBubbleTimelineGaps = ({} = {}, done = noOp) => fillTimelineGaps(`bubble`, '/api/v1/timelines/bubble', { local: true }, done); export const fillListTimelineGaps = (id, done = noOp) => fillTimelineGaps(`list:${id}`, `/api/v1/timelines/list/${id}`, {}, done); export function expandTimelineRequest(timeline, isLoadingMore) { diff --git a/app/javascript/flavours/glitch/features/bubble_timeline/components/column_settings.jsx b/app/javascript/flavours/glitch/features/bubble_timeline/components/column_settings.jsx new file mode 100644 index 0000000000..e1aab76612 --- /dev/null +++ b/app/javascript/flavours/glitch/features/bubble_timeline/components/column_settings.jsx @@ -0,0 +1,41 @@ +import PropTypes from 'prop-types'; +import { PureComponent } from 'react'; + +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; + +import ImmutablePropTypes from 'react-immutable-proptypes'; + +import SettingText from 'flavours/glitch/components/setting_text'; +import SettingToggle from 'flavours/glitch/features/notifications/components/setting_toggle'; + +const messages = defineMessages({ + filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' }, + settings: { id: 'home.settings', defaultMessage: 'Column settings' }, +}); + +class ColumnSettings extends PureComponent { + + static propTypes = { + settings: ImmutablePropTypes.map.isRequired, + onChange: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + columnId: PropTypes.string, + }; + + render () { + const { settings, onChange, intl } = this.props; + + return ( +
+ + +
+ +
+
+ ); + } + +} + +export default injectIntl(ColumnSettings); diff --git a/app/javascript/flavours/glitch/features/bubble_timeline/containers/column_settings_container.js b/app/javascript/flavours/glitch/features/bubble_timeline/containers/column_settings_container.js new file mode 100644 index 0000000000..95e589df8d --- /dev/null +++ b/app/javascript/flavours/glitch/features/bubble_timeline/containers/column_settings_container.js @@ -0,0 +1,29 @@ +import { connect } from 'react-redux'; + +import { changeColumnParams } from '../../../actions/columns'; +import { changeSetting } from '../../../actions/settings'; +import ColumnSettings from '../components/column_settings'; + +const mapStateToProps = (state, { columnId }) => { + const uuid = columnId; + const columns = state.getIn(['settings', 'columns']); + const index = columns.findIndex(c => c.get('uuid') === uuid); + + return { + settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'bubble']), + }; +}; + +const mapDispatchToProps = (dispatch, { columnId }) => { + return { + onChange (key, checked) { + if (columnId) { + dispatch(changeColumnParams(columnId, key, checked)); + } else { + dispatch(changeSetting(['bubble', ...key], checked)); + } + }, + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings); diff --git a/app/javascript/flavours/glitch/features/bubble_timeline/index.jsx b/app/javascript/flavours/glitch/features/bubble_timeline/index.jsx new file mode 100644 index 0000000000..ca6ec6ae43 --- /dev/null +++ b/app/javascript/flavours/glitch/features/bubble_timeline/index.jsx @@ -0,0 +1,160 @@ +import PropTypes from 'prop-types'; +import { PureComponent } from 'react'; + +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; + +import { Helmet } from 'react-helmet'; + +import { connect } from 'react-redux'; + +import BubbleChartIcon from '@/material-icons/400-24px/bubble-chart.svg?react'; +import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; +import { domain } from 'flavours/glitch/initial_state'; + +import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; +import { connectBubbleStream } from '../../actions/streaming'; +import { expandBubbleTimeline } from '../../actions/timelines'; +import Column from '../../components/column'; +import ColumnHeader from '../../components/column_header'; +import StatusListContainer from '../ui/containers/status_list_container'; + +import ColumnSettingsContainer from './containers/column_settings_container'; + +const messages = defineMessages({ + title: { id: 'column.bubble', defaultMessage: 'Bubble timeline' }, +}); + +const mapStateToProps = (state, { columnId }) => { + const uuid = columnId; + const columns = state.getIn(['settings', 'columns']); + const index = columns.findIndex(c => c.get('uuid') === uuid); + const regex = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'regex', 'body']) : state.getIn(['settings', 'bubble', 'regex', 'body']); + const timelineState = state.getIn(['timelines', `bubble`]); + + return { + hasUnread: !!timelineState && timelineState.get('unread') > 0, + regex, + }; +}; + +class BubbleTimeline extends PureComponent { + static defaultProps = {}; + + static propTypes = { + identity: identityContextPropShape, + dispatch: PropTypes.func.isRequired, + columnId: PropTypes.string, + intl: PropTypes.object.isRequired, + hasUnread: PropTypes.bool, + multiColumn: PropTypes.bool, + regex: PropTypes.string, + }; + + handlePin = () => { + const { columnId, dispatch } = this.props; + + if (columnId) { + dispatch(removeColumn(columnId)); + } else { + dispatch(addColumn('BUBBLE')); + } + }; + + handleMove = (dir) => { + const { columnId, dispatch } = this.props; + dispatch(moveColumn(columnId, dir)); + }; + + handleHeaderClick = () => { + this.column.scrollTop(); + }; + + componentDidMount () { + const { dispatch } = this.props; + const { signedIn } = this.props.identity; + + dispatch(expandBubbleTimeline()); + + if (signedIn) { + this.disconnect = dispatch(connectBubbleStream()); + } + } + + componentDidUpdate (prevProps) { + const { signedIn } = this.props.identity; + + if (prevProps !== this.props) { + const { dispatch } = this.props; + + if (this.disconnect) { + this.disconnect(); + } + + dispatch(expandBubbleTimeline()); + + if (signedIn) { + this.disconnect = dispatch(connectBubbleStream()); + } + } + } + + componentWillUnmount () { + if (this.disconnect) { + this.disconnect(); + this.disconnect = null; + } + } + + setRef = c => { + this.column = c; + }; + + handleLoadMore = maxId => { + const { dispatch } = this.props; + + dispatch(expandBubbleTimeline({ maxId })); + }; + + render () { + const { intl, hasUnread, columnId, multiColumn } = this.props; + const pinned = !!columnId; + + return ( + + + + + + } + trackScroll={!pinned} + scrollKey={`bubble_timeline-${columnId}`} + timelineId={`bubble`} + onLoadMore={this.handleLoadMore} + emptyMessage={} + bindToDocument={!multiColumn} + regex={this.props.regex} + /> + + + {intl.formatMessage(messages.title)} + + + + ); + } + +} + +export default withIdentity(connect(mapStateToProps)(injectIntl(BubbleTimeline))); diff --git a/app/javascript/flavours/glitch/features/firehose/index.jsx b/app/javascript/flavours/glitch/features/firehose/index.jsx index 2a6d790d52..1aa0455390 100644 --- a/app/javascript/flavours/glitch/features/firehose/index.jsx +++ b/app/javascript/flavours/glitch/features/firehose/index.jsx @@ -10,8 +10,8 @@ import { useIdentity } from '@/flavours/glitch/identity_context'; import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import { addColumn } from 'flavours/glitch/actions/columns'; import { changeSetting } from 'flavours/glitch/actions/settings'; -import { connectPublicStream, connectCommunityStream } from 'flavours/glitch/actions/streaming'; -import { expandPublicTimeline, expandCommunityTimeline } from 'flavours/glitch/actions/timelines'; +import { connectPublicStream, connectCommunityStream, connectBubbleStream } from 'flavours/glitch/actions/streaming'; +import { expandPublicTimeline, expandCommunityTimeline, expandBubbleTimeline } from 'flavours/glitch/actions/timelines'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; import SettingText from 'flavours/glitch/components/setting_text'; import { domain } from 'flavours/glitch/initial_state'; @@ -90,6 +90,9 @@ const Firehose = ({ feedType, multiColumn }) => { case 'community': dispatch(addColumn('COMMUNITY', { other: { onlyMedia }, regex: { body: regex } })); break; + case 'bubble': + dispatch(addColumn('BUBBLE', { other: { onlyMedia }, regex: { body: regex } })); + break; case 'public': dispatch(addColumn('PUBLIC', { other: { onlyMedia, allowLocalOnly }, regex: { body: regex } })); break; @@ -107,6 +110,9 @@ const Firehose = ({ feedType, multiColumn }) => { case 'community': dispatch(expandCommunityTimeline({ maxId, onlyMedia })); break; + case 'bubble': + dispatch(expandBubbleTimeline({ maxId, onlyMedia })); + break; case 'public': dispatch(expandPublicTimeline({ maxId, onlyMedia, allowLocalOnly })); break; @@ -130,6 +136,12 @@ const Firehose = ({ feedType, multiColumn }) => { disconnect = dispatch(connectCommunityStream({ onlyMedia })); } break; + case 'bubble': + dispatch(expandBubbleTimeline()); + if (signedIn) { + disconnect = dispatch(connectBubbleStream()); + } + break; case 'public': dispatch(expandPublicTimeline({ onlyMedia, allowLocalOnly })); if (signedIn) { @@ -147,6 +159,7 @@ const Firehose = ({ feedType, multiColumn }) => { return () => disconnect?.(); }, [dispatch, signedIn, feedType, onlyMedia, allowLocalOnly]); + // todo bubble const prependBanner = feedType === 'community' ? ( { ); - const emptyMessage = feedType === 'community' ? ( + const emptyMessage = feedType === 'community' || feedType === 'bubble' ? ( { + + + + diff --git a/app/javascript/flavours/glitch/features/getting_started/index.jsx b/app/javascript/flavours/glitch/features/getting_started/index.jsx index 2d13d3d584..994df4548e 100644 --- a/app/javascript/flavours/glitch/features/getting_started/index.jsx +++ b/app/javascript/flavours/glitch/features/getting_started/index.jsx @@ -11,6 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react'; +import BubbleChartIcon from '@/material-icons/400-24px/bubble-chart.svg?react'; import ExploreIcon from '@/material-icons/400-24px/explore.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react'; @@ -47,6 +48,7 @@ const messages = defineMessages({ navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation' }, settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' }, community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' }, + bubble_timeline: { id: 'navigation_bar.bubble_timeline', defaultMessage: 'Bubble timeline' }, explore: { id: 'navigation_bar.explore', defaultMessage: 'Explore' }, direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' }, bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, @@ -149,6 +151,10 @@ class GettingStarted extends ImmutablePureComponent { navItems.push(); } + if (!columns.find(item => item.get('id') === 'BUBBLE')) { + navItems.push(); + } + if (!columns.find(item => item.get('id') === 'PUBLIC')) { navItems.push(); } diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index b27b19c533..4c4e5c4918 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -210,6 +210,7 @@ class SwitchingColumnsArea extends PureComponent { + diff --git a/app/javascript/flavours/glitch/features/ui/util/async-components.js b/app/javascript/flavours/glitch/features/ui/util/async-components.js index e334e1a3b6..01a0620db7 100644 --- a/app/javascript/flavours/glitch/features/ui/util/async-components.js +++ b/app/javascript/flavours/glitch/features/ui/util/async-components.js @@ -30,6 +30,10 @@ export function CommunityTimeline () { return import(/* webpackChunkName: "flavours/glitch/async/community_timeline" */'../../community_timeline'); } +export function BubbleTimeline () { + return import(/* webpackChunkName: "flavours/glitch/async/community_timeline" */'../../bubble_timeline'); +} + export function Firehose () { return import(/* webpackChunkName: "flavours/glitch/async/firehose" */'../../firehose'); } diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js index e6a6f50df5..1e33694ce3 100644 --- a/app/javascript/flavours/glitch/reducers/local_settings.js +++ b/app/javascript/flavours/glitch/reducers/local_settings.js @@ -9,12 +9,12 @@ const initialState = ImmutableMap({ stretch : true, side_arm : 'none', side_arm_reply_mode : 'keep', - show_reply_count : false, - always_show_spoilers_field: false, + show_reply_count : true, + always_show_spoilers_field: true, confirm_missing_media_description: false, confirm_boost_missing_media_description: false, confirm_before_clearing_draft: true, - prepend_cw_re: false, + prepend_cw_re: true, preselect_on_reply: false, inline_preview_cards: true, hicolor_privacy_icons: false, diff --git a/app/javascript/flavours/glitch/styles/modern/style.scss b/app/javascript/flavours/glitch/styles/modern/style.scss index 272bba2a53..54762c2bd6 100644 --- a/app/javascript/flavours/glitch/styles/modern/style.scss +++ b/app/javascript/flavours/glitch/styles/modern/style.scss @@ -97,10 +97,11 @@ a:focus-visible, background: none; } .account__avatar, +.account__avatar img, #profile_page_avatar, .account__avatar-composite, .account-card__title__avatar img { - border-radius: 30%; + border-radius: 25%; flex: none; } :not(body):not(.scrollable)::-webkit-scrollbar { @@ -3258,4 +3259,4 @@ a:focus-visible, } .emoji-picker-dropdown__modifiers { top: 16px; -} \ No newline at end of file +} diff --git a/app/javascript/material-icons/400-24px/bubble-chart.svg b/app/javascript/material-icons/400-24px/bubble-chart.svg new file mode 100644 index 0000000000..c8b53a18ce --- /dev/null +++ b/app/javascript/material-icons/400-24px/bubble-chart.svg @@ -0,0 +1 @@ + \ No newline at end of file From 5f393044414aa673c74f79bd3e4c096a3b291fa6 Mon Sep 17 00:00:00 2001 From: ihateblueb Date: Wed, 28 May 2025 18:09:14 -0400 Subject: [PATCH 2/3] remlit mascot --- README.md | 5 +++++ app/javascript/flavours/glitch/reducers/local_settings.js | 2 +- app/javascript/flavours/glitch/styles/modern/style.scss | 4 ++++ public/verify-state.js | 4 ++-- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cbd2c296ea..5a61fc8af2 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ - add bite user and bite status buttons on status and status detailed components - don't alert for 501 api errors - show alerts after biting a user or a note +- add bubble timeline to "Getting started" and "Live feeds" +- fix avatar corner rounding to look as intended +- add remlit mascot + +for my tweaked version, you can choose to use an express-hosted version. instead of the below steps, you can just reverse proxy or tunnel localhost:3132 when running frontend-server/server.mjs. # Chuckya (standalone frontend) diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js index 1e33694ce3..d0e7145ab0 100644 --- a/app/javascript/flavours/glitch/reducers/local_settings.js +++ b/app/javascript/flavours/glitch/reducers/local_settings.js @@ -10,7 +10,7 @@ const initialState = ImmutableMap({ side_arm : 'none', side_arm_reply_mode : 'keep', show_reply_count : true, - always_show_spoilers_field: true, + always_show_spoilers_field: false, confirm_missing_media_description: false, confirm_boost_missing_media_description: false, confirm_before_clearing_draft: true, diff --git a/app/javascript/flavours/glitch/styles/modern/style.scss b/app/javascript/flavours/glitch/styles/modern/style.scss index 54762c2bd6..dd2349433d 100644 --- a/app/javascript/flavours/glitch/styles/modern/style.scss +++ b/app/javascript/flavours/glitch/styles/modern/style.scss @@ -2587,6 +2587,10 @@ a:focus-visible, .layout-multiple-columns.layout-multiple-columns .drawer__inner__mastodon { background-color: transparent !important; } +.drawer__inner__mastodon > img { + padding: 10px; + box-sizing: border-box; +} .layout-multiple-columns.layout-multiple-columns .darker { background-color: var(--surface-background-color); border-radius: var(--radius) var(--radius) 0 0; diff --git a/public/verify-state.js b/public/verify-state.js index 0b3765bc2c..48d2d74a9e 100644 --- a/public/verify-state.js +++ b/public/verify-state.js @@ -76,7 +76,7 @@ async function loadState() { "domain": domain, "enable_reaction": true, "locale": "en", - "mascot": "/images/mascot.svg", + "mascot": "/images/remlit.webp", "me": credentials.id, "reduce_motion": false, "show_quote_button": true, @@ -84,7 +84,7 @@ async function loadState() { "streaming_api_base_url": `wss://${domain}`, "title": `${instance.title}`, "unfollow_modal": true, - "source_url": 'https://iceshrimp.dev/ShittyKopper/Chuckya-fe-standalone', + "source_url": 'https://iceshrimp.dev/blueb/Chuckya-fe-standalone', "version": instance.version, "activity_api_enabled": false, "default_content_type": "text/x.misskeymarkdown", From 1377a141d70242c75b5d77b1f5bed509cfbca446 Mon Sep 17 00:00:00 2001 From: ihateblueb Date: Wed, 28 May 2025 18:09:26 -0400 Subject: [PATCH 3/3] add remlit.webp --- public/images/remlit.webp | Bin 0 -> 23586 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 public/images/remlit.webp diff --git a/public/images/remlit.webp b/public/images/remlit.webp new file mode 100755 index 0000000000000000000000000000000000000000..2f1a59a7314bf386ea0a441266fd28e49fe4896f GIT binary patch literal 23586 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