Progress on little important things
removing .mov for now until we can figure out solution with videojs, added model to track username changes, got chat creation flow down, progress on bookmark collections, albums, filtering blocks/mutes from group, explore, collection timelines
This commit is contained in:
parent
2bbb5be505
commit
6fbea0a59e
|
@ -20,7 +20,7 @@ class Api::V1::ChatConversationController < Api::BaseController
|
|||
|
||||
def create
|
||||
chat_conversation_account = find_or_create_conversation
|
||||
render json: chat_conversation_account, each_serializer: REST::ChatConversationAccountSerializer
|
||||
render json: chat_conversation_account, serializer: REST::ChatConversationAccountSerializer
|
||||
end
|
||||
|
||||
def mark_chat_conversation_read
|
||||
|
|
|
@ -45,7 +45,11 @@ class Api::V1::Timelines::ExploreController < EmptyController
|
|||
end
|
||||
|
||||
def explore_statuses
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id])
|
||||
if current_account
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id]).reject { |status| FeedManager.instance.filter?(:home, status, current_account.id) }
|
||||
else
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id])
|
||||
end
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -70,7 +70,11 @@ class Api::V1::Timelines::GroupCollectionController < EmptyController
|
|||
return []
|
||||
end
|
||||
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id], @groupIds)
|
||||
if current_account
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id], @groupIds).reject { |status| FeedManager.instance.filter?(:home, status, current_account.id) }
|
||||
else
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id], @groupIds)
|
||||
end
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -51,7 +51,12 @@ class Api::V1::Timelines::GroupController < Api::BaseController
|
|||
end
|
||||
|
||||
def group_statuses
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id], @group)
|
||||
if current_account
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id], @group).reject { |status| FeedManager.instance.filter?(:home, status, current_account.id) }
|
||||
else
|
||||
SortingQueryBuilder.new.call(@sort_type, params[:max_id], @group)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -36,6 +36,13 @@ class EmptyController < ActionController::Base
|
|||
|
||||
protected
|
||||
|
||||
def set_pagination_headers(next_path = nil, prev_path = nil)
|
||||
links = []
|
||||
links << [next_path, [%w(rel next)]] if next_path
|
||||
links << [prev_path, [%w(rel prev)]] if prev_path
|
||||
response.headers['Link'] = LinkHeader.new(links) unless links.empty?
|
||||
end
|
||||
|
||||
def current_user
|
||||
nil
|
||||
end
|
||||
|
|
|
@ -24,11 +24,12 @@ class Settings::ProfilesController < Settings::BaseController
|
|||
flash[:alert] = 'Unable to change username for your account. You are not GabPRO'
|
||||
redirect_to settings_profile_path
|
||||
else
|
||||
# : todo :
|
||||
# only allowed to change username once per day
|
||||
if params[:account][:username] && @account.username != params[:account][:username]
|
||||
Redis.current.set("username_change:#{account.id}", true)
|
||||
Redis.current.expire("username_change:#{account.id}", 24.huors.seconds)
|
||||
if @account.username != params[:account][:username]
|
||||
AccountUsernameChange.create!(
|
||||
account: @account,
|
||||
from_username: @account.username,
|
||||
to_username: params[:account][:username]
|
||||
)
|
||||
end
|
||||
|
||||
if UpdateAccountService.new.call(@account, account_params)
|
||||
|
|
|
@ -11,6 +11,8 @@ export const ALBUMS_EXPAND_REQUEST = 'ALBUMS_EXPAND_REQUEST'
|
|||
export const ALBUMS_EXPAND_SUCCESS = 'ALBUMS_EXPAND_SUCCESS'
|
||||
export const ALBUMS_EXPAND_FAIL = 'ALBUMS_EXPAND_FAIL'
|
||||
|
||||
//
|
||||
|
||||
export const ALBUM_CREATE_REQUEST = 'ALBUM_CREATE_REQUEST'
|
||||
export const ALBUM_CREATE_SUCCESS = 'ALBUM_CREATE_SUCCESS'
|
||||
export const ALBUM_CREATE_FAIL = 'ALBUM_CREATE_FAIL'
|
||||
|
@ -23,7 +25,252 @@ export const ALBUM_EDIT_REQUEST = 'ALBUM_EDIT_REQUEST'
|
|||
export const ALBUM_EDIT_SUCCESS = 'ALBUM_EDIT_SUCCESS'
|
||||
export const ALBUM_EDIT_FAIL = 'ALBUM_EDIT_FAIL'
|
||||
|
||||
//
|
||||
|
||||
export const ALBUM_UPDATE_MEDIA_REQUEST = 'ALBUM_UPDATE_MEDIA_REQUEST'
|
||||
export const ALBUM_UPDATE_MEDIA_SUCCESS = 'ALBUM_UPDATE_MEDIA_SUCCESS'
|
||||
export const ALBUM_UPDATE_MEDIA_FAIL = 'ALBUM_UPDATE_MEDIA_FAIL'
|
||||
|
||||
export const SET_ALBUM_COVER_REQUEST = 'SET_ALBUM_COVER_REQUEST'
|
||||
export const SET_ALBUM_COVER_SUCCESS = 'SET_ALBUM_COVER_SUCCESS'
|
||||
export const SET_ALBUM_COVER_FAIL = 'SET_ALBUM_COVER_FAIL'
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const fetchAlbums = (accountId) => (dispatch, getState) => {
|
||||
if (!accountId) return
|
||||
|
||||
if (getState().getIn(['album_lists', accountId, 'isLoading'])) {
|
||||
return
|
||||
}
|
||||
|
||||
dispatch(fetchAlbumsRequest(accountId))
|
||||
|
||||
api(getState).get(`/api/v1/albums/find_by_account/${accountId}`).then((response) => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next')
|
||||
dispatch(fetchAlbumsSuccess(response.data, accountId, next ? next.uri : null))
|
||||
}).catch((error) => {
|
||||
dispatch(fetchAlbumsFail(accountId, error))
|
||||
})
|
||||
}
|
||||
|
||||
const fetchAlbumsRequest = (accountId) => ({
|
||||
type: ALBUMS_FETCH_REQUEST,
|
||||
accountId,
|
||||
})
|
||||
|
||||
const fetchAlbumsSuccess = (albums, accountId, next) => ({
|
||||
type: ALBUMS_FETCH_SUCCESS,
|
||||
albums,
|
||||
accountId,
|
||||
next,
|
||||
})
|
||||
|
||||
const fetchAlbumsFail = (accountId, error) => ({
|
||||
type: ALBUMS_FETCH_FAIL,
|
||||
showToast: true,
|
||||
accountId,
|
||||
error,
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const expandAlbums = (accountId) => (dispatch, getState) => {
|
||||
if (!me) return
|
||||
|
||||
const url = getState().getIn(['album_lists', accountId, 'next'], null)
|
||||
|
||||
if (url === null || getState().getIn(['album_lists', accountId, 'isLoading'])) {
|
||||
return
|
||||
}
|
||||
|
||||
dispatch(expandAlbumsRequest(accountId))
|
||||
|
||||
api(getState).get(url).then((response) => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next')
|
||||
dispatch(expandAlbumsSuccess(response.data, accountId, next ? next.uri : null))
|
||||
}).catch((error) => {
|
||||
dispatch(expandAlbumsFail(accountId, error))
|
||||
})
|
||||
}
|
||||
|
||||
const expandAlbumsRequest = (accountId) => ({
|
||||
type: ALBUMS_EXPAND_REQUEST,
|
||||
accountId,
|
||||
})
|
||||
|
||||
const expandAlbumsSuccess = (statuses, accountId, next) => ({
|
||||
type: ALBUMS_EXPAND_SUCCESS,
|
||||
accountId,
|
||||
statuses,
|
||||
next,
|
||||
})
|
||||
|
||||
const expandAlbumsFail = (accountId, error) => ({
|
||||
type: ALBUMS_EXPAND_FAIL,
|
||||
showToast: true,
|
||||
accountId,
|
||||
error,
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const createAlbum = (title, description, visibility) => (dispatch, getState) => {
|
||||
if (!me || !title) return
|
||||
|
||||
dispatch(createAlbumRequest())
|
||||
|
||||
api(getState).post('/api/v1/albums', {
|
||||
title,
|
||||
description,
|
||||
visibility,
|
||||
}).then((response) => {
|
||||
dispatch(createAlbumSuccess(response.data))
|
||||
}).catch((error) => {
|
||||
dispatch(createAlbumFail(error))
|
||||
})
|
||||
}
|
||||
|
||||
const createAlbumRequest = () => ({
|
||||
type: ALBUM_CREATE_REQUEST,
|
||||
})
|
||||
|
||||
const createAlbumSuccess = (bookmarkCollection) => ({
|
||||
type: ALBUM_CREATE_SUCCESS,
|
||||
bookmarkCollection,
|
||||
})
|
||||
|
||||
const createAlbumFail = (error) => ({
|
||||
type: ALBUM_CREATE_FAIL,
|
||||
showToast: true,
|
||||
error,
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const editAlbum = (albumId, title, description, visibility) => (dispatch, getState) => {
|
||||
if (!me || !albumId || !title) return
|
||||
|
||||
dispatch(editAlbumRequest(albumId))
|
||||
|
||||
api(getState).put(`/api/v1/albums/${albumId}`, {
|
||||
title,
|
||||
description,
|
||||
visibility,
|
||||
}).then((response) => {
|
||||
dispatch(editAlbumSuccess(response.data))
|
||||
}).catch((error) => {
|
||||
dispatch(editAlbumFail(error))
|
||||
})
|
||||
}
|
||||
|
||||
const editAlbumRequest = (albumId) => ({
|
||||
type: ALBUM_EDIT_REQUEST,
|
||||
albumId,
|
||||
})
|
||||
|
||||
const editAlbumSuccess = (album) => ({
|
||||
type: ALBUM_EDIT_SUCCESS,
|
||||
album,
|
||||
})
|
||||
|
||||
const editAlbumFail = (error) => ({
|
||||
type: ALBUM_EDIT_FAIL,
|
||||
showToast: true,
|
||||
error,
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const removeAlbum = (albumId) => (dispatch, getState) => {
|
||||
if (!me || !albumId) return
|
||||
|
||||
dispatch(removeAlbumRequest(albumId))
|
||||
|
||||
api(getState).delete(`/api/v1/albums/${albumId}`).then((response) => {
|
||||
dispatch(removeAlbumSuccess(response.data))
|
||||
}).catch((error) => {
|
||||
dispatch(removeAlbumFail(error))
|
||||
})
|
||||
}
|
||||
|
||||
const removeAlbumRequest = (albumId) => ({
|
||||
type: ALBUM_REMOVE_REQUEST,
|
||||
albumId,
|
||||
})
|
||||
|
||||
const removeAlbumSuccess = () => ({
|
||||
type: ALBUM_REMOVE_SUCCESS,
|
||||
})
|
||||
|
||||
const removeAlbumFail = (error) => ({
|
||||
type: ALBUM_REMOVE_FAIL,
|
||||
showToast: true,
|
||||
error,
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const updateMediaAttachmentAlbum = (albumId, mediaAttachmentId) => (dispatch, getState) => {
|
||||
if (!me || !albumId || !mediaAttachmentId) return
|
||||
|
||||
dispatch(updateMediaAttachmentAlbumRequest())
|
||||
|
||||
api(getState).post(`/api/v1/albums/${albumId}/update_status`, { statusId }).then((response) => {
|
||||
dispatch(updateMediaAttachmentAlbumSuccess(response.data))
|
||||
}).catch((error) => {
|
||||
dispatch(updateMediaAlbumFail(error))
|
||||
})
|
||||
}
|
||||
|
||||
const updateMediaAttachmentAlbumRequest = () => ({
|
||||
type: ALBUM_UPDATE_MEDIA_REQUEST,
|
||||
})
|
||||
|
||||
const updateMediaAttachmentAlbumSuccess = (album) => ({
|
||||
type: ALBUM_UPDATE_MEDIA_SUCCESS,
|
||||
album,
|
||||
})
|
||||
|
||||
const updateMediaAlbumFail = (error) => ({
|
||||
type: ALBUM_UPDATE_MEDIA_FAIL,
|
||||
showToast: true,
|
||||
error,
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const setAlbumCover = (albumId, mediaAttachmentId) => (dispatch, getState) => {
|
||||
if (!me || !albumId || !mediaAttachmentId) return
|
||||
|
||||
dispatch(setAlbumCoverRequest())
|
||||
|
||||
api(getState).post(`/api/v1/albums/${albumId}/set_cover`, { mediaAttachmentId }).then((response) => {
|
||||
dispatch(setAlbumCoverSuccess(response.data))
|
||||
}).catch((error) => {
|
||||
dispatch(setAlbumCoverFail(error))
|
||||
})
|
||||
}
|
||||
|
||||
const setAlbumCoverRequest = () => ({
|
||||
type: SET_ALBUM_COVER_REQUEST,
|
||||
})
|
||||
|
||||
const setAlbumCoverSuccess = (album) => ({
|
||||
type: SET_ALBUM_COVER_SUCCESS,
|
||||
album,
|
||||
})
|
||||
|
||||
const setAlbumCoverFail = (error) => ({
|
||||
type: SET_ALBUM_COVER_FAIL,
|
||||
showToast: true,
|
||||
error,
|
||||
})
|
||||
|
|
|
@ -194,16 +194,16 @@ export const removeBookmarkCollection = (bookmarkCollectionId) => (dispatch, get
|
|||
}
|
||||
|
||||
const removeBookmarkCollectionRequest = (bookmarkCollectionId) => ({
|
||||
type: BOOKMARK_COLLECTIONS_CREATE_REQUEST,
|
||||
type: BOOKMARK_COLLECTIONS_REMOVE_REQUEST,
|
||||
bookmarkCollectionId,
|
||||
})
|
||||
|
||||
const removeBookmarkCollectionSuccess = () => ({
|
||||
type: BOOKMARK_COLLECTIONS_CREATE_SUCCESS,
|
||||
type: BOOKMARK_COLLECTIONS_REMOVE_SUCCESS,
|
||||
})
|
||||
|
||||
const removeBookmarkCollectionFail = (error) => ({
|
||||
type: BOOKMARK_COLLECTIONS_CREATE_FAIL,
|
||||
type: BOOKMARK_COLLECTIONS_REMOVE_FAIL,
|
||||
showToast: true,
|
||||
error,
|
||||
})
|
||||
|
|
|
@ -41,7 +41,7 @@ export const blockChatMessenger = (accountId) => (dispatch, getState) => {
|
|||
dispatch(blockChatMessengerRequest(accountId))
|
||||
|
||||
api(getState).post(`/api/v1/chat_conversation_accounts/_/block_messenger`, { account_id: accountId }).then((response) => {
|
||||
dispatch(blockChatMessengerSuccess(response.data))
|
||||
dispatch(blockChatMessengerSuccess(response))
|
||||
}).catch((error) => {
|
||||
dispatch(blockChatMessengerFail(accountId, error))
|
||||
})
|
||||
|
@ -74,7 +74,7 @@ export const unblockChatMessenger = (accountId) => (dispatch, getState) => {
|
|||
dispatch(unblockChatMessengerRequest(accountId))
|
||||
|
||||
api(getState).post(`/api/v1/chat_conversation_accounts/_/unblock_messenger`, { account_id: accountId }).then((response) => {
|
||||
dispatch(unblockChatMessengerSuccess(response.data))
|
||||
dispatch(unblockChatMessengerSuccess(response))
|
||||
}).catch((error) => {
|
||||
dispatch(unblockChatMessengerFail(accountId, error))
|
||||
})
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import api, { getLinks } from '../api'
|
||||
import debounce from 'lodash.debounce'
|
||||
import { importFetchedAccounts } from './importer'
|
||||
import { closeModal } from './modal'
|
||||
import { setChatConversationSelected } from './chats'
|
||||
import { me } from '../initial_state'
|
||||
|
||||
//
|
||||
|
@ -309,15 +311,17 @@ export const expandChatConversationMutedFail = (error) => ({
|
|||
* @description Create a chat conversation with given accountId. May fail because of blocks.
|
||||
* @param {String} accountId
|
||||
*/
|
||||
export const createChatConversation = (accountId) => (dispatch, getState) => {
|
||||
export const createChatConversation = (accountId, routerHistory) => (dispatch, getState) => {
|
||||
if (!me || !accountId) return
|
||||
|
||||
dispatch(createChatConversationRequest())
|
||||
|
||||
api(getState).post('/api/v1/chat_conversation', { account_id: accountId }).then((response) => {
|
||||
dispatch(createChatConversationSuccess(response.data))
|
||||
dispatch(closeModal())
|
||||
dispatch(setChatConversationSelected(response.data.chat_conversation_id))
|
||||
if (routerHistory) routerHistory.push(`/messages/${response.data.chat_conversation_id}`)
|
||||
}).catch((error) => {
|
||||
console.log("error:", error)
|
||||
dispatch(createChatConversationFail(error))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ const createGroup = (options, routerHistory) => (dispatch, getState) => {
|
|||
}
|
||||
}).then(({ data }) => {
|
||||
dispatch(createGroupSuccess(data))
|
||||
console.log("pushing routerHistory:", routerHistory)
|
||||
routerHistory.push(`/groups/${data.id}`)
|
||||
}).catch((err) => dispatch(createGroupFail(err)))
|
||||
}
|
||||
|
@ -98,13 +99,13 @@ const createGroupRequest = (id) => ({
|
|||
id,
|
||||
})
|
||||
|
||||
const createSuccess = (group) => ({
|
||||
const createGroupSuccess = (group) => ({
|
||||
type: GROUP_CREATE_SUCCESS,
|
||||
showToast: true,
|
||||
group,
|
||||
})
|
||||
|
||||
const createFail = (error) => ({
|
||||
const createGroupFail = (error) => ({
|
||||
type: GROUP_CREATE_FAIL,
|
||||
showToast: true,
|
||||
error,
|
||||
|
@ -138,7 +139,7 @@ const updateGroup = (groupId, options, routerHistory) => (dispatch, getState) =>
|
|||
|
||||
api(getState).put(`/api/v1/groups/${groupId}`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
'Content-Type': 'multipart/form-data',
|
||||
}
|
||||
}).then(({ data }) => {
|
||||
dispatch(updateGroupSuccess(data))
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Sparklines, SparklinesCurve } from 'react-sparklines'
|
||||
import { FormattedMessage } from 'react-intl'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import Button from './button'
|
||||
import Block from './block'
|
||||
import Text from './text'
|
||||
|
||||
class HashtagItem extends ImmutablePureComponent {
|
||||
|
||||
render() {
|
||||
const { hashtag, isCompact } = this.props
|
||||
|
||||
if (!hashtag) return
|
||||
|
||||
const count = hashtag.get('history').map((block) => {
|
||||
return parseInt(block.get('uses'))
|
||||
}).reduce((a, c) => a + c)
|
||||
|
||||
return (
|
||||
<Block>
|
||||
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.noUnderline, _s.px15, _s.py5].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.aiCenter].join(' ')}>
|
||||
<div>
|
||||
<Text color='brand' size='medium' weight='bold' className={[_s.py2, _s.lineHeight15].join(' ')}>
|
||||
#{hashtag.get('name')}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
!isCompact &&
|
||||
<Text color='secondary' size='small' className={_s.py2}>
|
||||
<FormattedMessage id='number_of_gabs' defaultMessage='{count} Gabs' values={{
|
||||
count,
|
||||
}} />
|
||||
</Text>
|
||||
}
|
||||
</div>
|
||||
|
||||
<Sparklines
|
||||
width={50}
|
||||
height={28}
|
||||
data={hashtag.get('history').reverse().map((day) => day.get('uses')).toArray()}
|
||||
>
|
||||
<SparklinesCurve style={{ fill: 'none' }} />
|
||||
</Sparklines>
|
||||
</div>
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HashtagItem.propTypes = {
|
||||
hashtag: ImmutablePropTypes.map.isRequired,
|
||||
isCompact: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default HashtagItem
|
|
@ -49,6 +49,8 @@ class MediaItem extends ImmutablePureComponent {
|
|||
if (!attachment) return
|
||||
|
||||
const hash = attachment.get('blurhash')
|
||||
if (!hash) return
|
||||
|
||||
const pixels = decode(hash, 160, 160)
|
||||
|
||||
if (pixels && this.canvas) {
|
||||
|
@ -103,7 +105,7 @@ class MediaItem extends ImmutablePureComponent {
|
|||
const statusUrl = `/${account.getIn(['acct'])}/posts/${status.get('id')}`
|
||||
|
||||
const isSmallRatio = aspectRatio < 1
|
||||
const isSquare = aspectRatio === 1
|
||||
const isSquare = aspectRatio === 1 || isSmall
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
px5: 1,
|
||||
|
|
|
@ -33,12 +33,9 @@ class ChatNavigationBar extends React.PureComponent {
|
|||
const otherAccounts = chatConversation ? chatConversation.get('other_accounts') : null
|
||||
const nameHTML = !!otherAccounts ? otherAccounts.get(0).get('display_name_html') : ''
|
||||
|
||||
// : todo :
|
||||
// fix padding on mobile device
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.z4, _s.h53PX, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.h53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||
<div className={[_s.d, _s.z4, _s.minH53PX, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.minH53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||
|
||||
<div className={[_s.d, _s.flexRow, _s.saveAreaInsetPT, _s.saveAreaInsetPL, _s.saveAreaInsetPR, _s.w100PC].join(' ')}>
|
||||
|
||||
|
|
|
@ -34,9 +34,9 @@ class ComposeNavigationBar extends React.PureComponent {
|
|||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.z4, _s.h53PX, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.h53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||
|
||||
<div className={[_s.d, _s.z4, _s.minH53PX, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.minH53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||
|
||||
<div className={innerClasses}>
|
||||
|
||||
<BackButton
|
||||
|
|
|
@ -57,7 +57,7 @@ class MediaGalleryPanel extends ImmutablePureComponent {
|
|||
headerButtonTitle={!!account ? intl.formatMessage(messages.show_all) : undefined}
|
||||
headerButtonTo={!!account ? `/${account.get('acct')}/albums` : undefined}
|
||||
>
|
||||
<div className={[_s.d, _s.flexRow, _s.flexWrap, _s.px10, _s.py10].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.flexWrap, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||
{
|
||||
!!account && attachments.size > 0 &&
|
||||
attachments.slice(0, 16).map((attachment, i) => (
|
||||
|
|
|
@ -108,6 +108,7 @@ class Poll extends ImmutablePureComponent {
|
|||
aiCenter: !showResults,
|
||||
})
|
||||
|
||||
// : todo : fix widths and truncate for large poll options
|
||||
return (
|
||||
<li className={listItemClasses} key={option.get('title')}>
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
addShortcut,
|
||||
removeShortcut,
|
||||
} from '../actions/shortcuts'
|
||||
import { createChatConversation } from '../actions/chat_conversations'
|
||||
import { openModal } from '../actions/modal'
|
||||
import { openPopover } from '../actions/popover'
|
||||
import { me } from '../initial_state'
|
||||
|
@ -32,6 +33,10 @@ import ProfileHeaderXSPlaceholder from './placeholder/profile_header_xs_placehol
|
|||
|
||||
class ProfileHeader extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object
|
||||
}
|
||||
|
||||
state = {
|
||||
stickied: false,
|
||||
}
|
||||
|
@ -71,6 +76,15 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
handleOnCreateChatConversation = () => {
|
||||
const { account } = this.props
|
||||
const accountId = !!account ? account.get('id') : null
|
||||
|
||||
if (!accountId) return
|
||||
|
||||
this.props.onCreateChatConversation(accountId, this.context.router.history)
|
||||
}
|
||||
|
||||
setOpenMoreNodeRef = (n) => {
|
||||
this.openMoreNode = n
|
||||
}
|
||||
|
@ -226,10 +240,8 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
// : TODO :
|
||||
className={[_s.jcCenter, _s.aiCenter, _s.mr10, _s.px10].join(' ')}
|
||||
onClick={this.handleOpenMore}
|
||||
buttonRef={this.setOpenMoreNodeRef}
|
||||
onClick={this.handleOnCreateChatConversation}
|
||||
/>
|
||||
</div>
|
||||
<div className={[_s.d, _s.flexRow, _s.h40PX].join(' ')}>
|
||||
|
@ -373,10 +385,8 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
// : TODO :
|
||||
className={[_s.jcCenter, _s.aiCenter, _s.mr10, _s.px10].join(' ')}
|
||||
onClick={this.handleOpenMore}
|
||||
buttonRef={this.setOpenMoreNodeRef}
|
||||
onClick={this.handleOnCreateChatConversation}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
@ -435,6 +445,9 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
onRemoveShortcut(accountId) {
|
||||
dispatch(removeShortcut(null, 'account', accountId))
|
||||
},
|
||||
onCreateChatConversation(accountId, routerHistory) {
|
||||
dispatch(createChatConversation(accountId, routerHistory))
|
||||
},
|
||||
});
|
||||
|
||||
ProfileHeader.propTypes = {
|
||||
|
|
|
@ -34,6 +34,7 @@ class StatusCheckBox extends ImmutablePureComponent {
|
|||
src={video.get('url')}
|
||||
alt={video.get('description')}
|
||||
aspectRatio={video.getIn(['meta', 'small', 'aspect'])}
|
||||
fileContentType={video.get('file_content_type')}
|
||||
width={239}
|
||||
height={110}
|
||||
inline
|
||||
|
|
|
@ -67,6 +67,7 @@ class StatusMedia extends ImmutablePureComponent {
|
|||
src={video.get('url')}
|
||||
alt={video.get('description')}
|
||||
aspectRatio={video.getIn(['meta', 'small', 'aspect'])}
|
||||
fileContentType={video.get('file_content_type')}
|
||||
sensitive={status.get('sensitive')}
|
||||
height={110}
|
||||
width={width}
|
||||
|
|
|
@ -31,7 +31,10 @@ class Video extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
videoJsOptions.sources = [{ src: this.props.src }]
|
||||
videoJsOptions.sources = [{
|
||||
src: this.props.src,
|
||||
type: this.props.fileContentType,
|
||||
}]
|
||||
this.videoPlayer = videojs(this.video, videoJsOptions)
|
||||
}
|
||||
|
||||
|
@ -193,6 +196,7 @@ Video.propTypes = {
|
|||
blurhash: PropTypes.string,
|
||||
aspectRatio: PropTypes.number,
|
||||
meta: ImmutablePropTypes.map,
|
||||
fileContentType: PropTypes.string,
|
||||
}
|
||||
|
||||
export default injectIntl(Video)
|
|
@ -14,6 +14,10 @@ import Text from '../components/text'
|
|||
|
||||
class ChatConversationCreate extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object
|
||||
}
|
||||
|
||||
state = {
|
||||
query: '',
|
||||
}
|
||||
|
@ -24,7 +28,7 @@ class ChatConversationCreate extends React.PureComponent {
|
|||
}
|
||||
|
||||
handleOnCreateChatConversation = (accountId) => {
|
||||
this.props.onCreateChatConversation(accountId)
|
||||
this.props.onCreateChatConversation(accountId, this.context.router.history)
|
||||
this.props.onClearChatConversationAccountSuggestions()
|
||||
|
||||
if (this.props.isModal && !!this.props.onCloseModal) {
|
||||
|
@ -81,8 +85,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
onChange(value) {
|
||||
dispatch(fetchChatConversationAccountSuggestions(value))
|
||||
},
|
||||
onCreateChatConversation(accountId) {
|
||||
dispatch(createChatConversation(accountId))
|
||||
onCreateChatConversation(accountId, routerHistory) {
|
||||
dispatch(createChatConversation(accountId, routerHistory))
|
||||
},
|
||||
onClearChatConversationAccountSuggestions() {
|
||||
dispatch(clearChatConversationAccountSuggestions())
|
||||
|
|
|
@ -215,7 +215,7 @@ class Deck extends React.PureComponent {
|
|||
<DeckColumn title={title} icon='pro' noButtons>
|
||||
<div className={[_s.d, _s.px15, _s.py15].join(' ')}>
|
||||
<Text>
|
||||
GabDeck is a unique way to customize your Gab experience. Upgrade to GabPRO to unlock the GabDeck.
|
||||
GabDeck is a unique way to customize your Gab experience. Upgrade to GabPRO to unlock the GabDeck.
|
||||
</Text>
|
||||
<div className={[_s.mt15, _s.d, _s.flexRow].join(' ')}>
|
||||
<Button href={URL_GAB_PRO}>
|
||||
|
|
|
@ -6,7 +6,6 @@ import isEqual from 'lodash.isequal'
|
|||
import { expandHashtagTimeline, clearTimeline } from '../actions/timelines'
|
||||
import { fetchHashtag } from '../actions/hashtags'
|
||||
import StatusList from '../components/status_list'
|
||||
import HashtagItem from '../components/hashtag_item'
|
||||
|
||||
class HashtagTimeline extends React.PureComponent {
|
||||
|
||||
|
@ -70,7 +69,7 @@ class HashtagTimeline extends React.PureComponent {
|
|||
const { id, tags } = this.props.params
|
||||
|
||||
dispatch(expandHashtagTimeline(id, { tags }))
|
||||
dispatch(fetchHashtag(tagName))
|
||||
// dispatch(fetchHashtag(tagName))
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
|
@ -94,15 +93,12 @@ class HashtagTimeline extends React.PureComponent {
|
|||
console.log("tagName:", tag)
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{ tag && <HashtagItem hashtag={tag} /> }
|
||||
<StatusList
|
||||
scrollKey='hashtag_timeline'
|
||||
timelineId={`hashtag:${tagName}`}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
|
||||
/>
|
||||
</React.Fragment>
|
||||
<StatusList
|
||||
scrollKey='hashtag_timeline'
|
||||
timelineId={`hashtag:${tagName}`}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -110,7 +106,7 @@ class HashtagTimeline extends React.PureComponent {
|
|||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
tagName: props.params.id,
|
||||
tag: state.getIn(['hashtags', `${props.params.id}`]),
|
||||
// tag: state.getIn(['hashtags', `${props.params.id}`]),
|
||||
hasUnread: state.getIn(['timelines', `hashtag:${props.params.id}`, 'unread']) > 0,
|
||||
})
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
|||
|
||||
handleOnSendChatMessage = () => {
|
||||
this.props.onSendChatMessage(this.state.value, this.props.chatConversationId)
|
||||
document.querySelector('#gabsocial').focus()
|
||||
this.onBlur()
|
||||
// document.querySelector('#gabsocial').focus()
|
||||
// this.onBlur()
|
||||
this.setState({ value: '' })
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,9 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
scrollToBottom = () => {
|
||||
this.messagesEnd.scrollIntoView({ behavior: 'smooth' });
|
||||
if (this.messagesEnd) {
|
||||
this.messagesEnd.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
_selectChild(index, align_top) {
|
||||
|
|
|
@ -30,7 +30,7 @@ class MessagesSettings extends ImmutablePureComponent {
|
|||
<div className={[_s.d, _s.px15, _s.py15, _s.overflowHidden].join(' ')}>
|
||||
<Form>
|
||||
<SettingSwitch
|
||||
label='Restrict messages from people you dont follow'
|
||||
label="Hide chats from users you don't follow"
|
||||
settings={chatSettings}
|
||||
settingPath='restrict_non_followers'
|
||||
onChange={this.handleOnChange}
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
CHAT_CONVERSATIONS_MUTED_EXPAND_REQUEST,
|
||||
CHAT_CONVERSATIONS_MUTED_EXPAND_SUCCESS,
|
||||
CHAT_CONVERSATIONS_MUTED_EXPAND_FAIL,
|
||||
CHAT_CONVERSATIONS_CREATE_SUCCESS,
|
||||
} from '../actions/chat_conversations'
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
|
@ -103,6 +104,9 @@ export default function chat_conversation_lists(state = initialState, action) {
|
|||
case CHAT_CONVERSATIONS_MUTED_EXPAND_SUCCESS:
|
||||
return appendToList(state, 'muted', action.chatConversations, action.next)
|
||||
|
||||
case CHAT_CONVERSATIONS_CREATE_SUCCESS:
|
||||
return appendToList(state, 'approved', [action.chatConversation], action.next)
|
||||
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ import {
|
|||
CHAT_MESSAGES_DELETE_REQUEST,
|
||||
CHAT_MESSAGES_PURGE_REQUEST,
|
||||
} from '../actions/chat_messages'
|
||||
import {
|
||||
import {
|
||||
CHAT_CONVERSATIONS_CREATE_SUCCESS,
|
||||
CHAT_CONVERSATIONS_APPROVED_FETCH_SUCCESS,
|
||||
CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS,
|
||||
CHAT_CONVERSATIONS_REQUESTED_FETCH_SUCCESS,
|
||||
|
@ -43,6 +44,7 @@ export default function chat_conversations(state = initialState, action) {
|
|||
switch(action.type) {
|
||||
case CHAT_CONVERSATION_REQUEST_APPROVE_SUCCESS:
|
||||
case SET_CHAT_CONVERSATION_EXPIRATION_SUCCESS:
|
||||
case CHAT_CONVERSATIONS_CREATE_SUCCESS:
|
||||
return importChatConversation(state, action.chatConversation)
|
||||
case CHAT_CONVERSATIONS_APPROVED_FETCH_SUCCESS:
|
||||
case CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: account_username_changes
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# account_id :bigint(8) not null
|
||||
# from_username :text default(""), not null
|
||||
# to_username :text default(""), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class AccountUsernameChange < ApplicationRecord
|
||||
|
||||
belongs_to :account
|
||||
|
||||
end
|
|
@ -31,7 +31,8 @@ class Group < ApplicationRecord
|
|||
include GroupInteractions
|
||||
include GroupCoverImage
|
||||
|
||||
PER_ACCOUNT_LIMIT = 50
|
||||
PER_ACCOUNT_LIMIT_PRO = 100
|
||||
PER_ACCOUNT_LIMIT_NORMAL = 10
|
||||
|
||||
belongs_to :account, optional: true
|
||||
|
||||
|
@ -53,7 +54,9 @@ class Group < ApplicationRecord
|
|||
validates :description, presence: true
|
||||
|
||||
validates_each :account_id, on: :create do |record, _attr, value|
|
||||
record.errors.add(:base, I18n.t('groups.errors.limit')) if Group.where(account_id: value).count >= PER_ACCOUNT_LIMIT
|
||||
account = Account.find(value)
|
||||
limit = account.is_pro ? PER_ACCOUNT_LIMIT_PRO : PER_ACCOUNT_LIMIT_NORMAL
|
||||
record.errors.add(:base, "You have reached the limit for group creation.") if Group.where(account_id: value).count >= limit
|
||||
end
|
||||
|
||||
before_save :set_slug
|
||||
|
|
|
@ -28,11 +28,11 @@ class MediaAttachment < ApplicationRecord
|
|||
enum type: [:image, :gifv, :video, :unknown]
|
||||
|
||||
IMAGE_FILE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp'].freeze
|
||||
VIDEO_FILE_EXTENSIONS = ['.webm', '.mp4', '.m4v', '.mov'].freeze
|
||||
VIDEO_FILE_EXTENSIONS = ['.webm', '.mp4', '.m4v'].freeze
|
||||
|
||||
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
|
||||
VIDEO_MIME_TYPES = ['video/webm', 'video/mp4', 'video/quicktime'].freeze
|
||||
VIDEO_CONVERTIBLE_MIME_TYPES = ['video/webm', 'video/quicktime'].freeze
|
||||
VIDEO_MIME_TYPES = ['video/webm', 'video/mp4'].freeze
|
||||
VIDEO_CONVERTIBLE_MIME_TYPES = ['video/webm'].freeze
|
||||
|
||||
BLURHASH_OPTIONS = {
|
||||
x_comp: 4,
|
||||
|
@ -170,6 +170,7 @@ class MediaAttachment < ApplicationRecord
|
|||
elsif IMAGE_MIME_TYPES.include? f.instance.file_content_type
|
||||
IMAGE_STYLES
|
||||
elsif VIDEO_CONVERTIBLE_MIME_TYPES.include?(f.instance.file_content_type)
|
||||
puts "tilly convert"
|
||||
{
|
||||
small: VIDEO_STYLES[:small],
|
||||
original: VIDEO_FORMAT,
|
||||
|
|
|
@ -5,7 +5,7 @@ class REST::MediaAttachmentSerializer < ActiveModel::Serializer
|
|||
|
||||
attributes :id, :type, :url, :preview_url,
|
||||
:remote_url, :text_url, :meta,
|
||||
:description, :blurhash
|
||||
:description, :blurhash, :file_content_type
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
class CreateAccountUsernameChanges < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :account_username_changes do |t|
|
||||
t.belongs_to :account, foreign_key: { on_delete: :cascade }, null: false
|
||||
t.text :from_username, null: false, default: ''
|
||||
t.text :to_username, null: false, default: ''
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
12
db/schema.rb
12
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2020_12_18_012018) do
|
||||
ActiveRecord::Schema.define(version: 2020_12_22_040559) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
|
@ -59,6 +59,15 @@ ActiveRecord::Schema.define(version: 2020_12_18_012018) do
|
|||
t.index ["tag_id"], name: "index_account_tag_stats_on_tag_id", unique: true
|
||||
end
|
||||
|
||||
create_table "account_username_changes", force: :cascade do |t|
|
||||
t.bigint "account_id", null: false
|
||||
t.text "from_username", default: "", null: false
|
||||
t.text "to_username", default: "", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["account_id"], name: "index_account_username_changes_on_account_id"
|
||||
end
|
||||
|
||||
create_table "account_verification_requests", force: :cascade do |t|
|
||||
t.bigint "account_id"
|
||||
t.string "image_file_name"
|
||||
|
@ -873,6 +882,7 @@ ActiveRecord::Schema.define(version: 2020_12_18_012018) do
|
|||
add_foreign_key "account_moderation_notes", "accounts", column: "target_account_id"
|
||||
add_foreign_key "account_stats", "accounts", on_delete: :cascade
|
||||
add_foreign_key "account_tag_stats", "tags", on_delete: :cascade
|
||||
add_foreign_key "account_username_changes", "accounts", on_delete: :cascade
|
||||
add_foreign_key "account_warnings", "accounts", column: "target_account_id", on_delete: :cascade
|
||||
add_foreign_key "account_warnings", "accounts", on_delete: :nullify
|
||||
add_foreign_key "accounts", "accounts", column: "moved_to_account_id", on_delete: :nullify
|
||||
|
|
|
@ -114,7 +114,6 @@
|
|||
"react-router-dom": "^4.1.1",
|
||||
"react-router-scroll-4": "^1.0.0-beta.2",
|
||||
"react-sortable-hoc": "^1.11.0",
|
||||
"react-sparklines": "^1.7.0",
|
||||
"react-stickynode": "^3.0.4",
|
||||
"react-swipeable-views": "^0.13.9",
|
||||
"react-textarea-autosize": "^7.1.0",
|
||||
|
|
|
@ -6618,13 +6618,6 @@ react-sortable-hoc@^1.11.0:
|
|||
invariant "^2.2.4"
|
||||
prop-types "^15.5.7"
|
||||
|
||||
react-sparklines@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-sparklines/-/react-sparklines-1.7.0.tgz#9b1d97e8c8610095eeb2ad658d2e1fcf91f91a60"
|
||||
integrity sha512-bJFt9K4c5Z0k44G8KtxIhbG+iyxrKjBZhdW6afP+R7EnIq+iKjbWbEFISrf3WKNFsda+C46XAfnX0StS5fbDcg==
|
||||
dependencies:
|
||||
prop-types "^15.5.10"
|
||||
|
||||
react-stickynode@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-stickynode/-/react-stickynode-3.0.4.tgz#1e9c096cec3613cc8294807eba319ced074c8b21"
|
||||
|
|
Loading…
Reference in New Issue