@ -0,0 +1,30 @@ | |||
# 闭社新增配置参数说明 | |||
## 匿名相关 | |||
闭社新增了匿名发言的功能,开启后,以特定标记结尾(注意不要有多余的回车或空格),发言者就会变成特定的帐号。 | |||
`ANON_TAG` 匿名标记,以这个结尾的嘟文就会成为匿名嘟文(本质只是修改了发言者) | |||
`ANON_NAME_LIST` 匿名代号列表,一个文件名,文件内容的格式为每行一个名字。每个用户会随机取一个代号加在匿名嘟文的最前面。对应关系每日更新。 | |||
`ANON_ACC` 匿名帐号的id。匿名嘟文的发言者会变成这个帐号。 | |||
## 闭社树相关 | |||
闭社设计了一套闭社树功能。 | |||
`TREE_ADDRESS` 作为闭社树根节点的嘟文的路径(示例:`/statuses/102995298871197915`) | |||
`TREE_ACC`建树帐号的id。跟节点发布自这个帐号的嘟文,在现实上会特殊处理。 | |||
## 邮件提示加强 | |||
考虑到一定会限制邮箱,闭社加强了邮件提示。 | |||
`EMAIL_DEFAULT_DOMAIN` 默认的邮箱后缀,会显示在注册界面并自动补全 | |||
`EMAIL_REGEX` 邮箱正则,在前端输入时检查邮箱格式是否正确,这主要是为了及时发现输错邮箱的情况,提升用户体验和减少对邮箱服务器的浪费。 | |||
* 注意: 邮箱正则只是一个纯前端的辅助校验,与后端的实际邮箱规则无关。有些时候需要用不公开的邮箱规则创建一些机器人帐号,用于匿名功能的匿名帐号/闭社树功能的建树机器人/管理员帐号/其他bot,邮箱正则会给注册带来不便,可以直接f12打开调试界面直接删除input元素中的正则限制,也可以前期先不使用邮箱正则功能,还可以直接在服务器上通过命令行创建帐号。 | |||
@ -1 +1 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.4144 232.00976"><path d="M211.80734 139.0875c-3.18125 16.36625-28.4925 34.2775-57.5625 37.74875-15.15875 1.80875-30.08375 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.39125 27.9425 21.11625.7225 39.91875-5.20625 39.91875-5.20625l.8675 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234 213.82 1.40609 165.31125.20859 116.09125c-.365-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67234 3.45375 78.20359.2425 107.86484 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.975 14.7525 32.975 65.0825 0 0 .41375 37.13375-4.59875 62.915" fill="#3088d4"/><path d="M177.50984 80.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025 0-17.4175 7.5075-17.4175 22.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375 0-15.74 6.32875-15.74 18.7975v59.15H38.90484V80.077c0-12.455 3.17125-22.3525 9.54125-29.675 6.56875-7.3225 15.17125-11.07625 25.85-11.07625 12.355 0 21.71125 4.74875 27.8975 14.2475l6.01375 10.08125 6.015-10.08125c6.185-9.49875 15.54125-14.2475 27.8975-14.2475 10.6775 0 19.28 3.75375 25.85 11.07625 6.36875 7.3225 9.54 17.22 9.54 29.675" fill="#fff"/></svg> | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57.484 71.644" fill="#2ea7e0"><path d="M0 21.607h7.203v50.416H0z"/><path d="M0 64.82h57.62v7.202H0z"/><path d="M50.417 14.405h7.202V64.82h-7.202z"/><path d="M43.215 0h7.203v14.405h-7.203z"/><path d="M14.405 0h36.013v7.202H14.405zm21.607 42.045c0 1.17-5.74.42-7.202 1.17-4.313 2.217-5.017 7.2-5.017 7.2-2.186 0-2.186 0-2.186-8.37V29.98a1.17 1.17 0 0 1 1.17-1.17h12.065a1.17 1.17 0 0 1 1.17 1.17v12.066z"/><path d="M7.203 0h7.202v14.405H7.203z"/></svg> |
@ -1 +1 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.41507 232.00976"><path d="M211.80683 139.0875c-3.1825 16.36625-28.4925 34.2775-57.5625 37.74875-15.16 1.80875-30.0825 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.3925 27.9425 21.115.7225 39.91625-5.20625 39.91625-5.20625l.86875 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23183 213.82 1.40558 165.31125.20808 116.09125c-.36375-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67058 3.45375 78.20308.2425 107.86433 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.97625 14.7525 32.97625 65.0825 0 0 .4125 37.13375-4.6 62.915" fill="#3088d4"/><path d="M65.68743 96.45938c0 9.01375-7.3075 16.32125-16.3225 16.32125-9.01375 0-16.32-7.3075-16.32-16.32125 0-9.01375 7.30625-16.3225 16.32-16.3225 9.015 0 16.3225 7.30875 16.3225 16.3225M124.52893 96.45938c0 9.01375-7.30875 16.32125-16.3225 16.32125-9.01375 0-16.32125-7.3075-16.32125-16.32125 0-9.01375 7.3075-16.3225 16.32125-16.3225 9.01375 0 16.3225 7.30875 16.3225 16.3225M183.36933 96.45938c0 9.01375-7.3075 16.32125-16.32125 16.32125-9.01375 0-16.32125-7.3075-16.32125-16.32125 0-9.01375 7.3075-16.3225 16.32125-16.3225 9.01375 0 16.32125 7.30875 16.32125 16.3225" fill="#fff"/></svg> | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57.484 71.644" fill="#2ea7e0"><path d="M0 21.607h7.203v50.416H0z"/><path d="M0 64.82h57.62v7.202H0z"/><path d="M50.417 14.405h7.202V64.82h-7.202z"/><path d="M43.215 0h7.203v14.405h-7.203z"/><path d="M14.405 0h36.013v7.202H14.405zm21.607 42.045c0 1.17-5.74.42-7.202 1.17-4.313 2.217-5.017 7.2-5.017 7.2-2.186 0-2.186 0-2.186-8.37V29.98a1.17 1.17 0 0 1 1.17-1.17h12.065a1.17 1.17 0 0 1 1.17 1.17v12.066z"/><path d="M7.203 0h7.202v14.405H7.203z"/></svg> |
@ -1 +1 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg"><symbol id="mastodon-svg-logo" viewBox="0 0 216.4144 232.00976"><path d="M107.86523 0C78.203984.2425 49.672422 3.4535937 33.044922 11.089844c0 0-32.97656262 14.752031-32.97656262 65.082031 0 11.525-.224375 25.306175.140625 39.919925 1.19750002 49.22 9.02375002 97.72843 54.53124962 109.77343 20.9825 5.55375 38.99711 6.71547 53.505856 5.91797 26.31125-1.45875 41.08203-9.38867 41.08203-9.38867l-.86914-19.08984s-18.80171 5.92758-39.91796 5.20508c-20.921254-.7175-43.006879-2.25516-46.390629-27.94141-.3125-2.25625-.46875-4.66938-.46875-7.20313 0 0 20.536953 5.0204 46.564449 6.21289 15.915.73001 30.8393-.93343 45.99805-2.74218 29.07-3.47125 54.38125-21.3818 57.5625-37.74805 5.0125-25.78125 4.59961-62.916015 4.59961-62.916015 0-50.33-32.97461-65.082031-32.97461-65.082031C166.80539 3.4535938 138.255.2425 108.59375 0h-.72852zM74.296875 39.326172c12.355 0 21.710234 4.749297 27.896485 14.248047l6.01367 10.080078 6.01563-10.080078c6.185-9.49875 15.54023-14.248047 27.89648-14.248047 10.6775 0 19.28156 3.753672 25.85156 11.076172 6.36875 7.3225 9.53907 17.218828 9.53907 29.673828v60.941408h-24.14454V81.869141c0-12.46875-5.24453-18.798829-15.73828-18.798829-11.6025 0-17.41797 7.508516-17.41797 22.353516v32.375002H96.207031V85.423828c0-14.845-5.815468-22.353515-17.417969-22.353516-10.49375 0-15.740234 6.330079-15.740234 18.798829v59.148439H38.904297V80.076172c0-12.455 3.171016-22.351328 9.541015-29.673828 6.568751-7.3225 15.172813-11.076172 25.851563-11.076172z" /></symbol></svg> | |||
<svg xmlns="http://www.w3.org/2000/svg"><symbol id="mastodon-svg-logo" viewBox="0 0 57.484 71.644"><path d="M0 21.607h7.203v50.416H0z"/><path d="M0 64.82h57.62v7.202H0z"/><path d="M50.417 14.405h7.202V64.82h-7.202z"/><path d="M43.215 0h7.203v14.405h-7.203z"/><path d="M14.405 0h36.013v7.202H14.405zm21.607 42.045c0 1.17-5.74.42-7.202 1.17-4.313 2.217-5.017 7.2-5.017 7.2-2.186 0-2.186 0-2.186-8.37V29.98a1.17 1.17 0 0 1 1.17-1.17h12.065a1.17 1.17 0 0 1 1.17 1.17v12.066z"/><path d="M7.203 0h7.202v14.405H7.203z"/></symbol></svg> |
@ -1 +1 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.4144 232.00976"><path d="M107.86523 0C78.203984.2425 49.672422 3.4535937 33.044922 11.089844c0 0-32.97656262 14.752031-32.97656262 65.082031 0 11.525-.224375 25.306175.140625 39.919925 1.19750002 49.22 9.02375002 97.72843 54.53124962 109.77343 20.9825 5.55375 38.99711 6.71547 53.505856 5.91797 26.31125-1.45875 41.08203-9.38867 41.08203-9.38867l-.86914-19.08984s-18.80171 5.92758-39.91796 5.20508c-20.921254-.7175-43.006879-2.25516-46.390629-27.94141-.3125-2.25625-.46875-4.66938-.46875-7.20313 0 0 20.536953 5.0204 46.564449 6.21289 15.915.73001 30.8393-.93343 45.99805-2.74218 29.07-3.47125 54.38125-21.3818 57.5625-37.74805 5.0125-25.78125 4.59961-62.916015 4.59961-62.916015 0-50.33-32.97461-65.082031-32.97461-65.082031C166.80539 3.4535938 138.255.2425 108.59375 0h-.72852zM74.296875 39.326172c12.355 0 21.710234 4.749297 27.896485 14.248047l6.01367 10.080078 6.01563-10.080078c6.185-9.49875 15.54023-14.248047 27.89648-14.248047 10.6775 0 19.28156 3.753672 25.85156 11.076172 6.36875 7.3225 9.53907 17.218828 9.53907 29.673828v60.941408h-24.14454V81.869141c0-12.46875-5.24453-18.798829-15.73828-18.798829-11.6025 0-17.41797 7.508516-17.41797 22.353516v32.375002H96.207031V85.423828c0-14.845-5.815468-22.353515-17.417969-22.353516-10.49375 0-15.740234 6.330079-15.740234 18.798829v59.148439H38.904297V80.076172c0-12.455 3.171016-22.351328 9.541015-29.673828 6.568751-7.3225 15.172813-11.076172 25.851563-11.076172z" fill="#000"/></svg> | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57.484 71.644" fill="#000"><path d="M0 21.607h7.203v50.416H0z"/><path d="M0 64.82h57.62v7.202H0z"/><path d="M50.417 14.405h7.202V64.82h-7.202z"/><path d="M43.215 0h7.203v14.405h-7.203z"/><path d="M14.405 0h36.013v7.202H14.405zm21.607 42.045c0 1.17-5.74.42-7.202 1.17-4.313 2.217-5.017 7.2-5.017 7.2-2.186 0-2.186 0-2.186-8.37V29.98a1.17 1.17 0 0 1 1.17-1.17h12.065a1.17 1.17 0 0 1 1.17 1.17v12.066z"/><path d="M7.203 0h7.202v14.405H7.203z"/></svg> |
@ -0,0 +1,473 @@ | |||
import React from 'react'; | |||
import Immutable from 'immutable'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import PropTypes from 'prop-types'; | |||
import Avatar from './avatar'; | |||
import AvatarOverlay from './avatar_overlay'; | |||
import AvatarComposite from './avatar_composite'; | |||
import RelativeTimestamp from './relative_timestamp'; | |||
import DisplayName from './display_name'; | |||
import StatusContent from './status_content'; | |||
import StatusActionBar from './status_action_bar'; | |||
import AttachmentList from './attachment_list'; | |||
import Card from '../features/status/components/card'; | |||
import { injectIntl, FormattedMessage } from 'react-intl'; | |||
import ImmutablePureComponent from 'react-immutable-pure-component'; | |||
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; | |||
import { HotKeys } from 'react-hotkeys'; | |||
import classNames from 'classnames'; | |||
import Icon from 'mastodon/components/icon'; | |||
import { displayMedia } from '../initial_state'; | |||
//import DetailedStatus from '../features/status/components/detailed_status'; | |||
// We use the component (and not the container) since we do not want | |||
// to use the progress bar to show download progress | |||
import Bundle from '../features/ui/components/bundle'; | |||
export const textForScreenReader = (intl, status, rebloggedByText = false) => { | |||
const displayName = status.getIn(['account', 'display_name']); | |||
const values = [ | |||
displayName.length === 0 ? status.getIn(['account', 'acct']).split('@')[0] : displayName, | |||
status.get('spoiler_text') && status.get('hidden') ? status.get('spoiler_text') : status.get('search_index').slice(status.get('spoiler_text').length), | |||
intl.formatDate(status.get('created_at'), { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }), | |||
status.getIn(['account', 'acct']), | |||
]; | |||
if (rebloggedByText) { | |||
values.push(rebloggedByText); | |||
} | |||
return values.join(', '); | |||
}; | |||
export const defaultMediaVisibility = (status) => { | |||
if (!status) { | |||
return undefined; | |||
} | |||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') { | |||
status = status.get('reblog'); | |||
} | |||
return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all'); | |||
}; | |||
export default @injectIntl | |||
class Status extends ImmutablePureComponent { | |||
static contextTypes = { | |||
router: PropTypes.object, | |||
}; | |||
static propTypes = { | |||
status: ImmutablePropTypes.map, | |||
account: ImmutablePropTypes.map, | |||
otherAccounts: ImmutablePropTypes.list, | |||
onClick: PropTypes.func, | |||
onReply: PropTypes.func, | |||
onFavourite: PropTypes.func, | |||
onReblog: PropTypes.func, | |||
onDelete: PropTypes.func, | |||
onDirect: PropTypes.func, | |||
onMention: PropTypes.func, | |||
onPin: PropTypes.func, | |||
onOpenMedia: PropTypes.func, | |||
onOpenVideo: PropTypes.func, | |||
onBlock: PropTypes.func, | |||
onEmbed: PropTypes.func, | |||
onHeightChange: PropTypes.func, | |||
onToggleHidden: PropTypes.func, | |||
muted: PropTypes.bool, | |||
hidden: PropTypes.bool, | |||
unread: PropTypes.bool, | |||
onMoveUp: PropTypes.func, | |||
onMoveDown: PropTypes.func, | |||
showThread: PropTypes.bool, | |||
getScrollPosition: PropTypes.func, | |||
updateScrollBottom: PropTypes.func, | |||
cacheMediaWidth: PropTypes.func, | |||
cachedMediaWidth: PropTypes.number, | |||
sonsIds: ImmutablePropTypes.list, | |||
}; | |||
// Avoid checking props that are functions (and whose equality will always | |||
// evaluate to false. See react-immutable-pure-component for usage. | |||
updateOnProps = [ | |||
'status', | |||
'account', | |||
'muted', | |||
'hidden', | |||
]; | |||
state = { | |||
showMedia: defaultMediaVisibility(this.props.status), | |||
statusId: undefined, | |||
}; | |||
// Track height changes we know about to compensate scrolling | |||
componentDidMount () { | |||
this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card'); | |||
} | |||
getSnapshotBeforeUpdate () { | |||
if (this.props.getScrollPosition) { | |||
return this.props.getScrollPosition(); | |||
} else { | |||
return null; | |||
} | |||
} | |||
static getDerivedStateFromProps(nextProps, prevState) { | |||
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) { | |||
return { | |||
showMedia: defaultMediaVisibility(nextProps.status), | |||
statusId: nextProps.status.get('id'), | |||
}; | |||
} else { | |||
return null; | |||
} | |||
} | |||
// Compensate height changes | |||
componentDidUpdate (prevProps, prevState, snapshot) { | |||
const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card'); | |||
if (doShowCard && !this.didShowCard) { | |||
this.didShowCard = true; | |||
if (snapshot !== null && this.props.updateScrollBottom) { | |||
if (this.node && this.node.offsetTop < snapshot.top) { | |||
this.props.updateScrollBottom(snapshot.height - snapshot.top); | |||
} | |||
} | |||
} | |||
} | |||
componentWillUnmount() { | |||
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); | |||
}); | |||
} | |||
} | |||
} | |||
handleToggleMediaVisibility = () => { | |||
this.setState({ showMedia: !this.state.showMedia }); | |||
} | |||
handleClick = () => { | |||
if (this.props.onClick) { | |||
this.props.onClick(); | |||
return; | |||
} | |||
if (!this.context.router) { | |||
return; | |||
} | |||
const { status } = this.props; | |||
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`); | |||
} | |||
handleExpandClick = (e) => { | |||
if (this.props.onClick) { | |||
this.props.onClick(); | |||
return; | |||
} | |||
if (e.button === 0) { | |||
if (!this.context.router) { | |||
return; | |||
} | |||
const { status } = this.props; | |||
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`); | |||
} | |||
} | |||
handleAccountClick = (e) => { | |||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) { | |||
const id = e.currentTarget.getAttribute('data-id'); | |||
e.preventDefault(); | |||
this.context.router.history.push(`/accounts/${id}`); | |||
} | |||
} | |||
handleExpandedToggle = () => { | |||
this.props.onToggleHidden(this._properStatus()); | |||
}; | |||
renderLoadingMediaGallery () { | |||
return <div className='media-gallery' style={{ height: '110px' }} />; | |||
} | |||
renderLoadingVideoPlayer () { | |||
return <div className='video-player' style={{ height: '110px' }} />; | |||
} | |||
renderLoadingAudioPlayer () { | |||
return <div className='audio-player' style={{ height: '110px' }} />; | |||
} | |||
handleOpenVideo = (media, startTime) => { | |||
this.props.onOpenVideo(media, startTime); | |||
} | |||
handleHotkeyReply = e => { | |||
e.preventDefault(); | |||
this.props.onReply(this._properStatus(), this.context.router.history); | |||
} | |||
handleHotkeyFavourite = () => { | |||
this.props.onFavourite(this._properStatus()); | |||
} | |||
handleHotkeyBoost = e => { | |||
this.props.onReblog(this._properStatus(), e); | |||
} | |||
handleHotkeyMention = e => { | |||
e.preventDefault(); | |||
this.props.onMention(this._properStatus().get('account'), this.context.router.history); | |||
} | |||
handleHotkeyOpen = () => { | |||
this.context.router.history.push(`/statuses/${this._properStatus().get('id')}`); | |||
} | |||
handleHotkeyOpenProfile = () => { | |||
this.context.router.history.push(`/accounts/${this._properStatus().getIn(['account', 'id'])}`); | |||
} | |||
handleHotkeyMoveUp = e => { | |||
this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured')); | |||
} | |||
handleHotkeyMoveDown = e => { | |||
this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured')); | |||
} | |||
handleHotkeyToggleHidden = () => { | |||
this.props.onToggleHidden(this._properStatus()); | |||
} | |||
handleHotkeyToggleSensitive = () => { | |||
this.handleToggleMediaVisibility(); | |||
} | |||
_properStatus () { | |||
const { status } = this.props; | |||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') { | |||
return status.get('reblog'); | |||
} else { | |||
return status; | |||
} | |||
} | |||
handleRef = c => { | |||
this.node = c; | |||
} | |||
render () { | |||
let media = null; | |||
let statusAvatar, prepend, rebloggedByText; | |||
const { intl, hidden, featured, otherAccounts, unread, showThread, sonsIds } = this.props; | |||
let { status, account, ...other } = this.props; | |||
if (status === null) { | |||
return null; | |||
} | |||
const handlers = this.props.muted ? {} : { | |||
reply: this.handleHotkeyReply, | |||
favourite: this.handleHotkeyFavourite, | |||
boost: this.handleHotkeyBoost, | |||
mention: this.handleHotkeyMention, | |||
open: this.handleHotkeyOpen, | |||
openProfile: this.handleHotkeyOpenProfile, | |||
moveUp: this.handleHotkeyMoveUp, | |||
moveDown: this.handleHotkeyMoveDown, | |||
toggleHidden: this.handleHotkeyToggleHidden, | |||
toggleSensitive: this.handleHotkeyToggleSensitive, | |||
}; | |||
if (hidden) { | |||
return ( | |||
<HotKeys handlers={handlers}> | |||
<div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex='0'> | |||
{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])} | |||
{status.get('content')} | |||
</div> | |||
</HotKeys> | |||
); | |||
} | |||
if (status.get('filtered') || status.getIn(['reblog', 'filtered'])) { | |||
const minHandlers = this.props.muted ? {} : { | |||
moveUp: this.handleHotkeyMoveUp, | |||
moveDown: this.handleHotkeyMoveDown, | |||
}; | |||
return ( | |||
<HotKeys handlers={minHandlers}> | |||
<div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}> | |||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' /> | |||
</div> | |||
</HotKeys> | |||
); | |||
} | |||
if (featured) { | |||
prepend = ( | |||
<div className='status__prepend'> | |||
<div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' className='status__prepend-icon' fixedWidth /></div> | |||
<FormattedMessage id='status.pinned' defaultMessage='Pinned toot' /> | |||
</div> | |||
); | |||
} else if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') { | |||
const display_name_html = { __html: status.getIn(['account', 'display_name_html']) }; | |||
prepend = ( | |||
<div className='status__prepend'> | |||
<div className='status__prepend-icon-wrapper'><Icon id='retweet' className='status__prepend-icon' fixedWidth /></div> | |||
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} /> | |||
</div> | |||
); | |||
rebloggedByText = intl.formatMessage({ id: 'status.reblogged_by', defaultMessage: '{name} boosted' }, { name: status.getIn(['account', 'acct']) }); | |||
account = status.get('account'); | |||
status = status.get('reblog'); | |||
} | |||
if (status.get('media_attachments').size > 0) { | |||
if (this.props.muted) { | |||
media = ( | |||
<AttachmentList | |||
compact | |||
media={status.get('media_attachments')} | |||
/> | |||
); | |||
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { | |||
const attachment = status.getIn(['media_attachments', 0]); | |||
media = ( | |||
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} > | |||
{Component => ( | |||
<Component | |||
src={attachment.get('url')} | |||
alt={attachment.get('description')} | |||
duration={attachment.getIn(['meta', 'original', 'duration'], 0)} | |||
peaks={[0]} | |||
height={70} | |||
/> | |||
)} | |||
</Bundle> | |||
); | |||
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { | |||
const attachment = status.getIn(['media_attachments', 0]); | |||
media = ( | |||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > | |||
{Component => ( | |||
<Component | |||
preview={attachment.get('preview_url')} | |||
blurhash={attachment.get('blurhash')} | |||
src={attachment.get('url')} | |||
alt={attachment.get('description')} | |||
width={this.props.cachedMediaWidth} | |||
height={110} | |||
inline | |||
sensitive={status.get('sensitive')} | |||
onOpenVideo={this.handleOpenVideo} | |||
cacheWidth={this.props.cacheMediaWidth} | |||
visible={this.state.showMedia} | |||
onToggleVisibility={this.handleToggleMediaVisibility} | |||
/> | |||
)} | |||
</Bundle> | |||
); | |||
} else { | |||
media = ( | |||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}> | |||
{Component => ( | |||
<Component | |||
media={status.get('media_attachments')} | |||
sensitive={status.get('sensitive')} | |||
height={110} | |||
onOpenMedia={this.props.onOpenMedia} | |||
cacheWidth={this.props.cacheMediaWidth} | |||
defaultWidth={this.props.cachedMediaWidth} | |||
visible={this.state.showMedia} | |||
onToggleVisibility={this.handleToggleMediaVisibility} | |||
/> | |||
)} | |||
</Bundle> | |||
); | |||
} | |||
} else if (status.get('spoiler_text').length === 0 && status.get('card')) { | |||
media = ( | |||
<Card | |||
onOpenMedia={this.props.onOpenMedia} | |||
card={status.get('card')} | |||
compact | |||
cacheWidth={this.props.cacheMediaWidth} | |||
defaultWidth={this.props.cachedMediaWidth} | |||
/> | |||
); | |||
} | |||
if (otherAccounts && otherAccounts.size > 0) { | |||
statusAvatar = <AvatarComposite accounts={otherAccounts} size={48} />; | |||
} else if (account === undefined || account === null) { | |||
statusAvatar = <Avatar account={status.get('account')} size={48} />; | |||
} else { | |||
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />; | |||
} | |||
return ( | |||
<HotKeys handlers={handlers}> | |||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}> | |||
{prepend} | |||
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}> | |||
<div className='status__expand' onClick={this.handleExpandClick} role='presentation' /> | |||
<div className='status__info'> | |||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a> | |||
<a onClick={this.handleAccountClick} target='_blank' data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} title={status.getIn(['account', 'acct'])} className='status__display-name'> | |||
<div className='status__avatar'> | |||
{statusAvatar} | |||
</div> | |||
<DisplayName account={status.get('account')} others={otherAccounts} /> | |||
</a> | |||
</div> | |||
<StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable /> | |||
{media} | |||
{showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) && ( | |||
<button className='status__content__read-more-button' onClick={this.handleClick}> | |||
<FormattedMessage id='status.show_thread' defaultMessage='Show thread' /> | |||
</button> | |||
)} | |||
<StatusActionBar status={status} account={account} {...other} /> | |||
</div> | |||
</div> | |||
</HotKeys> | |||
); | |||
} | |||
} |
@ -0,0 +1,168 @@ | |||
import { connect } from 'react-redux'; | |||
import Status from '../components/status2'; | |||
import { makeGetStatus } from '../selectors'; | |||
import { | |||
replyCompose, | |||
mentionCompose, | |||
directCompose, | |||
} from '../actions/compose'; | |||
import { | |||
reblog, | |||
favourite, | |||
unreblog, | |||
unfavourite, | |||
pin, | |||
unpin, | |||
} from '../actions/interactions'; | |||
import { | |||
muteStatus, | |||
unmuteStatus, | |||
deleteStatus, | |||
hideStatus, | |||
revealStatus, | |||
} from '../actions/statuses'; | |||
import { initMuteModal } from '../actions/mutes'; | |||
import { initBlockModal } from '../actions/blocks'; | |||
import { initReport } from '../actions/reports'; | |||
import { openModal } from '../actions/modal'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
import { boostModal, deleteModal } from '../initial_state'; | |||
import { showAlertForError } from '../actions/alerts'; | |||
const messages = defineMessages({ | |||
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, | |||
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' }, | |||
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' }, | |||
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' }, | |||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, | |||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, | |||
}); | |||
const makeMapStateToProps = () => { | |||
const getStatus = makeGetStatus(); | |||
const mapStateToProps = (state, props) => ({ | |||
status: getStatus(state, props), | |||
sonsIds: state.getIn(['contexts', 'replies', props.id]), | |||
}); | |||
return mapStateToProps; | |||
}; | |||
const mapDispatchToProps = (dispatch, { intl }) => ({ | |||
onReply (status, router) { | |||
dispatch((_, getState) => { | |||
let state = getState(); | |||
if (state.getIn(['compose', 'text']).trim().length !== 0) { | |||
dispatch(openModal('CONFIRM', { | |||
message: intl.formatMessage(messages.replyMessage), | |||
confirm: intl.formatMessage(messages.replyConfirm), | |||
onConfirm: () => dispatch(replyCompose(status, router)), | |||
})); | |||
} else { | |||
dispatch(replyCompose(status, router)); | |||
} | |||
}); | |||
}, | |||
onModalReblog (status) { | |||
if (status.get('reblogged')) { | |||
dispatch(unreblog(status)); | |||
} else { | |||
dispatch(reblog(status)); | |||
} | |||
}, | |||
onReblog (status, e) { | |||
if ((e && e.shiftKey) || !boostModal) { | |||
this.onModalReblog(status); | |||
} else { | |||
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog })); | |||
} | |||
}, | |||
onFavourite (status) { | |||
if (status.get('favourited')) { | |||
dispatch(unfavourite(status)); | |||
} else { | |||
dispatch(favourite(status)); | |||
} | |||
}, | |||
onPin (status) { | |||
if (status.get('pinned')) { | |||
dispatch(unpin(status)); | |||
} else { | |||
dispatch(pin(status)); | |||
} | |||
}, | |||
onEmbed (status) { | |||
dispatch(openModal('EMBED', { | |||
url: status.get('url'), | |||
onError: error => dispatch(showAlertForError(error)), | |||
})); | |||
}, | |||
onDelete (status, history, withRedraft = false) { | |||
if (!deleteModal) { | |||
dispatch(deleteStatus(status.get('id'), history, withRedraft)); | |||
} else { | |||
dispatch(openModal('CONFIRM', { | |||
message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage), | |||
confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm), | |||
onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)), | |||
})); | |||
} | |||
}, | |||
onDirect (account, router) { | |||
dispatch(directCompose(account, router)); | |||
}, | |||
onMention (account, router) { | |||
dispatch(mentionCompose(account, router)); | |||
}, | |||
onOpenMedia (media, index) { | |||
dispatch(openModal('MEDIA', { media, index })); | |||
}, | |||
onOpenVideo (media, time) { | |||
dispatch(openModal('VIDEO', { media, time })); | |||
}, | |||
onBlock (status) { | |||
const account = status.get('account'); | |||
dispatch(initBlockModal(account)); | |||
}, | |||
onReport (status) { | |||
dispatch(initReport(status.get('account'), status)); | |||
}, | |||
onMute (account) { | |||
dispatch(initMuteModal(account)); | |||
}, | |||
onMuteConversation (status) { | |||
if (status.get('muted')) { | |||
dispatch(unmuteStatus(status.get('id'))); | |||
} else { | |||
dispatch(muteStatus(status.get('id'))); | |||
} | |||
}, | |||
onToggleHidden (status) { | |||
if (status.get('hidden')) { | |||
dispatch(revealStatus(status.get('id'))); | |||
} else { | |||
dispatch(hideStatus(status.get('id'))); | |||
} | |||
}, | |||
}); | |||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status)); |
@ -0,0 +1,115 @@ | |||
.column { | |||
flex: 1 0 auto; | |||
} | |||
.pinned-info { | |||
position: relative; | |||
opacity: 0.85; | |||
font-size: 15px; | |||
padding: 10px 20px; | |||
a { | |||
color: #3fadfd; | |||
} | |||
} | |||
.pinned-info__icon { | |||
.fa { | |||
position: absolute; | |||
bottom: 10px; | |||
right: 10px; | |||
cursor: pointer; | |||
} | |||
} | |||
div { | |||
&.status__info { | |||
& > a { | |||
&.status__display-name { | |||
display: inline-block; | |||
} | |||
} | |||
} | |||
} | |||
.gifv { | |||
& > video { | |||
width: 100%; | |||
max-height: 100%; | |||
} | |||
} | |||
.columns-area--mobile { | |||
.getting-started__trends { | |||
display: block; | |||
.trends__item { | |||
display: flex; | |||
} | |||
} | |||
} | |||
.status__quote__wrapper { | |||
margin-top:16px; | |||
border-left: 5px solid #dbdbdb80; | |||
background: #dbdbdb40; | |||
.status { | |||
padding-left:40px; | |||
.status__action-bar { | |||
display: none; | |||
} | |||
.status__avatar { | |||
transform: scale(0.5); | |||
transform-origin: 0% 0%; | |||
} | |||
} | |||
} | |||
.status__tree__quote__wrapper { | |||
padding: 10px 5px; | |||
background: #dbdbdb40; | |||
cursor: pointer; | |||
} | |||
@keyframes like { | |||
0% { | |||
transform: scale(1); | |||
} | |||
25% { | |||
transform: scale(1.75); | |||
} | |||
100% { | |||
transform: scale(1); | |||
} | |||
} | |||
@keyframes unlike { | |||
0% { | |||
transform: rotateY(0deg); | |||
} | |||
50% { | |||
transform: rotateY(240deg); | |||
} | |||
80% { | |||
transform: rotateY(140deg); | |||
} | |||
100% { | |||
transform: rotateY(180deg); | |||
} | |||
} | |||
.no-reduce-motion .icon-button.star-icon { | |||
&.activate { | |||
& > .fa-heart { | |||
animation: like 1s linear; | |||
} | |||
} | |||
} | |||
.no-reduce-motion .icon-button.star-icon { | |||
&.deactivate { | |||
& > .fa-heart { | |||
animation: unlike 1s linear; | |||
} | |||
} | |||
} |
@ -0,0 +1,39 @@ | |||
.comments-timeline { | |||
max-height: 160px; | |||
min-width: 60%; | |||
max-width: 100%; | |||
overflow: hidden; | |||
-webkit-mask-image: linear-gradient(#1a1a1a,transparent); | |||
mask-image: linear-gradient(#1a1a1a,transparent); | |||
transform: scale(0.85); | |||
transform-origin: 100% 0%; | |||
margin-bottom: -32px; | |||
margin-right:8px; | |||
position: relative; | |||
z-index:9; | |||
&:hover { | |||
max-height: 60vh; | |||
overflow-y: auto; | |||
-webkit-mask-image: none; | |||
mask-image: none; | |||
z-index:99; | |||
background: $tc-background; | |||
box-shadow: $primary-text-color 3.2px 3.2px 8px; | |||
} | |||
&:active { | |||
max-height: 60vh; | |||
overflow-y: auto; | |||
-webkit-mask-image: none; | |||
mask-image: none; | |||
z-index:99; | |||
background: $tc-background; | |||
box-shadow: $primary-text-color 3.2px 3.2px 8px; | |||
} | |||
& .comments-timeline-2 { | |||
margin-left:42px; | |||
} | |||
} | |||
.comments-timeline__wrapper { | |||
height: 135px; | |||
} |
@ -0,0 +1,65 @@ | |||
div.tree-ance { | |||
.account__avatar { | |||
display: none; | |||
} | |||
.display-name { | |||
display: none; | |||
} | |||
} | |||
.tree { | |||
a.status__display-name { | |||
>span { | |||
>bdi { | |||
>strong { | |||
animation: none; | |||
} | |||
} | |||
} | |||
} | |||
a { | |||
>div { | |||
>div.account__avatar { | |||
animation: none; | |||
} | |||
} | |||
} | |||
} | |||
.tree-ance { | |||
background: linear-gradient(-15deg, #282C3710,90%, #197171, 95%, #0DA454); | |||
>div.status { | |||
padding-left: 18px; | |||
>div.deep__number { | |||
text-align: left; | |||
} | |||
} | |||
} | |||
.tree-desc { | |||
background: linear-gradient(15deg, #282C3710,90%, #197171, 95%, #0da454); | |||
>div.status { | |||
>div.deep__number { | |||
text-align: right; | |||
} | |||
} | |||
} | |||
svg.tree-svg { | |||
.node { | |||
circle { | |||
fill: #F3F3FF; | |||
stroke: #2593B8; | |||
stroke-width: 1.5px; | |||
} | |||
text { | |||
font-size: 11px; | |||
background-color: #444; | |||
fill: #F4F4F4; | |||
text-shadow: 0 1px 4px black; | |||
} | |||
cursor: pointer; | |||
} | |||
path.link { | |||
fill: none; | |||
stroke: #2593B8; | |||
stroke-width: 1.5px; | |||
} | |||
} | |||
@ -0,0 +1,3 @@ | |||
@import 'thu/variables'; | |||
@import 'application'; | |||
@import 'thu/diff'; |
@ -0,0 +1,221 @@ | |||
/* Fil */ | |||
/* Status */ | |||
/* Drawer */ | |||
body { | |||
background: rgba(73, 58, 99, 1) url(https://www.tsinghua.edu.cn/images/footer.jpg) no-repeat fixed; | |||
background-size: cover; | |||
background-attachment: fixed; | |||
background-position: center; | |||
height: 100vh !important; | |||
} | |||
body.theme-thu { | |||
background: rgba(73, 58, 99, 1) url(https://www.tsinghua.edu.cn/images/footer.jpg) no-repeat fixed; | |||
background-size: cover; | |||
background-attachment: fixed; | |||
background-position: center; | |||
height: 100vh !important; | |||
} | |||
.ui { | |||
background: rgba(0, 0, 0, .4); | |||
} | |||
.column { | |||
>.scrollable { | |||
background: rgba(128, 112, 132, 0); | |||
border-radius: 0 0 0.25rem 0.25rem; | |||
color: rgba(240, 240, 240, 1); | |||
} | |||
} | |||
.column-back-button { | |||
background: rgba(240, 240, 240, 1); | |||
box-shadow: inset 0 5px 5px rgba(0, 0, 0, 0.05); | |||
border-bottom: 1px solid transparent; | |||
height: auto; | |||
} | |||
.column-header { | |||
background: rgba(73, 58, 99, 0.4); | |||
border-bottom: 1px solid #aaa; | |||
border-radius: 0.25rem 0.25rem 0 0; | |||
} | |||
.column-icon { | |||
background: transparent !important; | |||
color: rgba(255, 255, 255, .5); | |||
} | |||
.collapsable-collapsed { | |||
background: transparent !important; | |||
color: rgba(255, 255, 255, .5); | |||
} | |||
.column-header__button { | |||
background: transparent !important; | |||
color: rgba(255, 255, 255, .5); | |||
} | |||
.column-header__back-button { | |||
background: transparent !important; | |||
color: rgba(255, 255, 255, .5); | |||
} | |||
.column-link { | |||
background: rgba(40, 40, 40, 0); | |||
color: rgba(200, 200, 200, 1); | |||
box-shadow: inset 0 5px 5px rgba(0, 0, 0, 0.05); | |||
&:hover { | |||
background: rgba(102, 8, 116, 0.5); | |||
color: rgba(255, 255, 255, 1); | |||
} | |||
} | |||
.drawer__header { | |||
a { | |||
&:hover { | |||
background: rgba(66, 40, 72, 1); | |||
} | |||
} | |||
background: transparent; | |||
} | |||
.getting-started { | |||
p { | |||
color: rgba(102, 102, 102, 1); | |||
} | |||
background: rgb(52, 40, 62, 0.4); | |||
} | |||
.static-content { | |||
p { | |||
margin-bottom: 0.5rem; | |||
} | |||
} | |||
.column-subheading { | |||
background: rgba(255, 255, 255, 0.2); | |||
color: white; | |||
} | |||
.column-header__collapsible { | |||
>div { | |||
background: rgba(40, 60, 85, 0.6); | |||
border-bottom: 1px solid; | |||
} | |||
} | |||
.account__moved-note__message { | |||
color: rgba(255, 255, 255, 1); | |||
} | |||
.account__moved-note { | |||
.detailed-status__display-name { | |||
span { | |||
color: rgba(255, 255, 255, 1); | |||
} | |||
strong { | |||
color: rgba(255, 255, 255, 0.5) !important; | |||
} | |||
} | |||
} | |||
.reply-indicator__content { | |||
color: #DDD; | |||
a { | |||
color: rgba(37, 136, 208, 1); | |||
} | |||
.status__content__spoiler-link { | |||
background: rgba(49, 53, 67, 1); | |||
line-height: 1.2rem; | |||
} | |||
} | |||
.status__content { | |||
color: #DDD; | |||
a { | |||
color: rgba(37, 136, 208, 1); | |||
} | |||
.status__content__spoiler-link { | |||
background: rgba(49, 53, 67, 1); | |||
line-height: 1.2rem; | |||
} | |||
} | |||
.status__wrapper { | |||
border-top: 1px solid #ccc; | |||
} | |||
.focusable { | |||
&:focus { | |||
background: rgba(200, 222, 243, 0.5); | |||
} | |||
} | |||
.account__header { | |||
.icon-button { | |||
color: rgba(255, 255, 255, 1); | |||
color: rgba(255, 255, 255, 0.8); | |||
&:hover { | |||
color: rgba(255, 255, 255, 1); | |||
} | |||
} | |||
background: rgba(57, 48, 59, 0); | |||
>div { | |||
background: rgba(57, 48, 59, 0); | |||
} | |||
} | |||
.icon-button { | |||
color: rgba(255, 255, 255, 0.6); | |||
} | |||
.status__display-name { | |||
strong { | |||
color: rgba(240, 240, 240, 1); | |||
} | |||
} | |||
.account__display-name { | |||
strong { | |||
color: rgba(240, 240, 240, 1); | |||
} | |||
} | |||
.status__content__spoiler-link { | |||
span { | |||
color: rgba(255, 255, 255, 1); | |||
} | |||
} | |||
.account__action-bar { | |||
.icon-button { | |||
color: rgba(255, 255, 255, 0.8); | |||
&:hover { | |||
color: rgba(255, 255, 255, 1); | |||
} | |||
} | |||
background: rgba(255, 255, 255, 1); | |||
border-top: 1px solid rgba(255, 255, 255, 0.25); | |||
border-bottom: 1px solid rgba(255, 255, 255, 0.25); | |||
} | |||
.account__action-bar__tab { | |||
border-left: 1px solid rgba(255, 255, 255, 0.25); | |||
} | |||
.notification__message { | |||
a { | |||
&:hover { | |||
color: rgba(37, 136, 208, 1); | |||
} | |||
} | |||
} | |||
.detailed-status { | |||
background: rgba(200, 222, 243, .2); | |||
color: rgba(51, 51, 51, 1); | |||
} | |||
.detailed-status__display-name { | |||
color: rgba(255, 255, 255, 0.5); | |||
strong { | |||
color: rgba(255, 255, 255, 0.5); | |||
} | |||
} | |||
.detailed-status__meta { | |||
color: rgba(255, 255, 255, 0.5); | |||
} | |||
.detailed-status__action-bar { | |||
background: rgba(0, 0, 0, 0.05); | |||
border-top: 1px solid #ccc; | |||
border-bottom: 1px solid #ccc; | |||
box-shadow: inset 0 5px 5px rgba(0, 0, 0, 0.05); | |||
} | |||
.drawer__inner { | |||
background: rgb(52, 40, 62, 0.7); | |||
border-radius: 0.25rem; | |||
height: auto; | |||
max-height: 100%; | |||
overflow-y: auto; | |||
} | |||
.getting-started__wrapper { | |||
background: rgb(52, 40, 62, 0.4); | |||
} | |||
.pinned-info { | |||
background: rgba(73, 58, 99, 0.7); | |||
} | |||
.tabs-bar__wrapper { | |||
background: rgba(23,25,31); | |||
} |
@ -0,0 +1,8 @@ | |||
// Dependent colors | |||
$classic-base-color: rgba(40,44,55,0.8); | |||
// Differences | |||
$ui-base-color: $classic-base-color !default; | |||
$tc-background: rgba(50,41,64,0.9) !default; | |||
@ -0,0 +1,11 @@ | |||
- content_for :page_title do | |||
= @jump_url | |||
.grid | |||
.column-0 | |||
.box-widget | |||
.rich-formatting | |||
%h2= '将前往:' | |||
%h4= link_to @jump_url, @jump_url | |||
.column-1 | |||
= render 'application/sidebar' |
@ -0,0 +1,55 @@ | |||
- content_for :page_title do | |||
= "我的#{@year_text}" | |||
.grid | |||
.column-0 | |||
.box-widget | |||
.rich-formatting | |||
- if @uid | |||
= account_link_to(@account) | |||
%h2= "#{@year_text}在闭社:" | |||
%p | |||
我总共发布了 | |||
%strong | |||
#{@total} | |||
嘟文 | |||
- if @total > 0 | |||
%p | |||
我发得最多的一天是 | |||
%strong | |||
#{@most_times[0][:date]} | |||
,一下子发了 | |||
%strong | |||
#{@most_times[0][:num]} | |||
条 | |||
- if @most_fav&.favourites_count or 0 > 0 | |||
%p | |||
其中最高赞是“ | |||
=link_to @most_fav.text[0..8]+'...', @most_fav.uri | |||
”,收获了 | |||
%strong | |||
#{@most_fav.favourites_count} | |||
赞 | |||
- if @like_me_most.size > 0 | |||
%p | |||
给我点赞最多的是他们: | |||
%ul | |||
- @like_me_most.each do |a| | |||
%li= account_link_to(a[:account], a[:num], full: a == @like_me_most.first) | |||
- if @i_like_most.size > 0 | |||
%p | |||
收到我的赞最多的是他们: | |||
%ul | |||
- @i_like_most.each do |a| | |||
%li= account_link_to(a[:account], a[:num], full: a == @i_like_most.first) | |||
- if @communi_most.size > 0 | |||
%p | |||
和我相互交流最频繁的是: | |||
%ul | |||
- @communi_most.each do |a| | |||
%li= account_link_to(a[:account], a[:num], full: a == @communi_most.first) | |||
%br | |||
%br | |||
%p= '感谢陪伴,新的一年,祝平安喜乐' | |||
.column-1 | |||
= render 'application/sidebar' |
@ -0,0 +1,13 @@ | |||
# frozen_string_literal: true | |||
Rails.application.configure do | |||
config.x.email_default_domain = ENV.fetch('EMAIL_DEFAULT_DOMAIN') { '' } | |||
config.x.email_regex = ENV.fetch('EMAIL_REGEX') { '.+' } | |||
config.x.tree_address = ENV.fetch('TREE_ADDRESS') {''} | |||
config.x.tree_acc = ENV.fetch('TREE_ACC') {'0'} | |||
config.x.anon.tag = ENV.fetch('ANON_TAG') {'[mask]'} | |||
config.x.anon.acc = ENV.fetch('ANON_ACC') {nil} | |||
config.x.anon.namelist = ENV['ANON_NAME_LIST'] ? File.readlines(ENV['ANON_NAME_LIST']).collect(&:strip) : ['Alice', 'Bob', 'Carol', 'Dave'] | |||
config.x.anon.salt = (1..42).map { ('a'..'z').to_a.sample }.join | |||
end | |||
@ -1,3 +1,4 @@ | |||
default: styles/application.scss | |||
contrast: styles/contrast.scss | |||
mastodon-light: styles/mastodon-light.scss | |||
thu: styles/thu.scss |
@ -0,0 +1,14 @@ | |||
if(navigator.userAgent.search('MicroMessenger') !== -1) | |||
location.href = `https://closed.social/tools/safe_jump/?go=${encodeURIComponent(location.href)}&t=${encodeURIComponent(document.title)}`; | |||
var em = document.getElementById("registration_user_email"); | |||
if(!em) | |||
em = document.getElementById("user_email"); | |||
var ap = em.nextSibling; | |||
em.addEventListener("blur", function( event ) { | |||
if(ap.style.display != 'none' && em.value) { | |||
em.value+=ap.innerText; | |||
ap.style.display = 'none'; | |||
//alert('注意:清华邮箱收取外部邮件会有至多十分钟的延迟,完成注册后请稍后再查收邮件。请务必确保邮箱正确,闭社已经遇到了大量无效邮箱(例如漏掉了数字)') | |||
} | |||
}); | |||
@ -1 +1 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 94.023018 100.80365" height="28.44903mm" width="26.535385mm"><path d="M72.57077 49.00625c-3.9125 0-7.085-3.1825-7.085-7.095 0-3.91125 3.1725-7.09375 7.085-7.09375 3.92125 0 7.09375 3.1825 7.09375 7.09375 0 3.9125-3.1725 7.095-7.09375 7.095m-25.55875 0c-3.9225 0-7.095-3.1825-7.095-7.095 0-3.91125 3.1725-7.09375 7.095-7.09375 3.91125 0 7.09375 3.1825 7.09375 7.09375 0 3.9125-3.1825 7.095-7.09375 7.095m-25.57 0c-3.91125 0-7.08375-3.1825-7.08375-7.095 0-3.91125 3.1725-7.09375 7.08375-7.09375 3.92125 0 7.09375 3.1825 7.09375 7.09375 0 3.9125-3.1725 7.095-7.09375 7.095m72.5775-15.905c0-21.86625-14.32375-28.27375-14.32375-28.27375-7.23-3.31875-19.63-4.7125-32.5175-4.8275h-.3125c-12.88875.115-25.28875 1.50875-32.5075 4.8275 0 0-14.32375 6.4075-14.32375 28.27375 0 5.00375-.105 10.995.05125 17.34C.60577 71.83 4.00702 92.905 23.78327 98.1375c9.1125 2.4125 16.945 2.9125 23.24875 2.56875 11.4225-.63375 17.84-4.07625 17.84-4.07625l-.37375-8.3025s-8.16625 2.58-17.34125 2.2675c-9.09125-.3125-18.6825-.9775-20.16-12.13875-.135-.97875-.1975-2.02875-.1975-3.13125 0 0 8.915 2.185 20.2325 2.69375 6.9075.3225 13.39875-.39375 19.98375-1.185 12.6275-1.50875 23.62375-9.29 25.0075-16.405 2.17375-11.1925 1.99625-27.3275 1.99625-27.3275" fill="#000"/></svg> | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="15 25 2 30" height="28.44903mm" width="26.535385mm"><path d="0h36.013v7.202H14.405zm21.607 42.045c0 1.17-5.74.42-7.202 1.17-4.313 2.217-5.017 7.2-5.017 7.2-2.186 0-2.186 0-2.186-8.37V29.98a1.17 1.17 0 0 1 1.17-1.17h12.065a1.17 1.17 0 0 1 1.17 1.17v12.066z"/></svg> |