diff --git a/app/controllers/api/v1/gab_trends_controller.rb b/app/controllers/api/v1/gab_trends_controller.rb index 065684ff..5a2cb7c1 100644 --- a/app/controllers/api/v1/gab_trends_controller.rb +++ b/app/controllers/api/v1/gab_trends_controller.rb @@ -6,10 +6,20 @@ class Api::V1::GabTrendsController < Api::BaseController skip_before_action :set_cache_headers def index - uri = URI('https://trends.gab.com/trend-feed/json') - uri.query = URI.encode_www_form() + body = Redis.current.get("gabtrends") + + if body.nil? + uri = URI("https://trends.gab.com/trend-feed/json") + uri.query = URI.encode_www_form({}) - res = Net::HTTP.get_response(uri) - render json: res.body if res.is_a?(Net::HTTPSuccess) + res = Net::HTTP.get_response(uri) + if res.is_a?(Net::HTTPSuccess) + body = res.body + Redis.current.set("gabtrends", res.body) + Redis.current.expire("gabtrends", 1.hour.seconds) + end + end + + render json: body end end diff --git a/app/controllers/settings/expenses_controller.rb b/app/controllers/settings/expenses_controller.rb index 0322194b..f33095d3 100644 --- a/app/controllers/settings/expenses_controller.rb +++ b/app/controllers/settings/expenses_controller.rb @@ -1,10 +1,10 @@ class Settings::ExpensesController < Admin::BaseController def index - @ammount = Redis.current.get("monthly_funding_ammount") || 0 + @amount = Redis.current.get("monthly_funding_amount") || 0 end def create - Redis.current.set("monthly_funding_ammount", params[:ammount]) + Redis.current.set("monthly_funding_amount", params[:amount]) redirect_to settings_expenses_path end diff --git a/app/javascript/gabsocial/actions/gab_trends.js b/app/javascript/gabsocial/actions/gab_trends.js index e69de29b..8e9942e5 100644 --- a/app/javascript/gabsocial/actions/gab_trends.js +++ b/app/javascript/gabsocial/actions/gab_trends.js @@ -0,0 +1,40 @@ +import api from '../api'; +import { me } from '../initial_state' + +export const GAB_TRENDS_RESULTS_FETCH_REQUEST = 'GAB_TRENDS_RESULTS_FETCH_REQUEST' +export const GAB_TRENDS_RESULTS_FETCH_SUCCESS = 'GAB_TRENDS_RESULTS_FETCH_SUCCESS' +export const GAB_TRENDS_RESULTS_FETCH_FAIL = 'GAB_TRENDS_RESULTS_FETCH_FAIL' + +export const fetchGabTrends = () => { + return function (dispatch, getState) { + if (!me) return + + dispatch(fetchGabTrendsRequest()) + + api(getState).get('/api/v1/gab_trends').then(response => { + dispatch(fetchGabTrendsSuccess(response.data.items)) + }).catch(function (error) { + dispatch(fetchGabTrendsFail(error)) + }) + } +} + +function fetchGabTrendsRequest() { + return { + type: GAB_TRENDS_RESULTS_FETCH_REQUEST, + } +} + +function fetchGabTrendsSuccess(items) { + return { + type: GAB_TRENDS_RESULTS_FETCH_SUCCESS, + items, + } +} + +function fetchGabTrendsFail(error) { + return { + type: GAB_TRENDS_RESULTS_FETCH_FAIL, + error, + } +} \ No newline at end of file diff --git a/app/javascript/gabsocial/actions/tenor.js b/app/javascript/gabsocial/actions/tenor.js index 49563c66..1bb2ad76 100644 --- a/app/javascript/gabsocial/actions/tenor.js +++ b/app/javascript/gabsocial/actions/tenor.js @@ -13,6 +13,8 @@ export const GIF_CATEGORIES_FETCH_REQUEST = 'GIF_CATEGORIES_FETCH_REQUEST' export const GIF_CATEGORIES_FETCH_SUCCESS = 'GIF_CATEGORIES_FETCH_SUCCESS' export const GIF_CATEGORIES_FETCH_FAIL = 'GIF_CATEGORIES_FETCH_FAIL' +// : todo : + export const fetchGifCategories = () => { return function (dispatch, getState) { if (!me) return @@ -20,7 +22,6 @@ export const fetchGifCategories = () => { dispatch(fetchGifCategoriesRequest()) api(getState).get('/api/v1/gifs').then(response => { - console.log("fetchGifCategoriesSuccess:", response) dispatch(fetchGifCategoriesSuccess(response.data.tags)) }).catch(function (error) { dispatch(fetchGifCategoriesFail(error)) @@ -36,7 +37,7 @@ export const fetchGifResults = () => { const searchText = getState().getIn(['tenor', 'searchText'], ''); - axios.get(`https://api.tenor.com/v1/search?q=${searchText}&media_filter=minimal&limit=30&key=QHFJ0C5EWGBH`) + axios.get(`https://api.tenor.com/v1/search?q=${searchText}&media_filter=minimal&limit=30&key=${tenorkey}`) .then((response) => { console.log("response:", response) dispatch(fetchGifResultsSuccess(response.data.results)) diff --git a/app/javascript/gabsocial/components/composer.js b/app/javascript/gabsocial/components/composer.js index d349c092..f0e6d60e 100644 --- a/app/javascript/gabsocial/components/composer.js +++ b/app/javascript/gabsocial/components/composer.js @@ -181,6 +181,8 @@ class Composer extends PureComponent { onChange = (editorState) => { this.setState({ editorState }) + const text = editorState.getCurrentContent().getPlainText('\u0001') + this.props.onChange(text) } onToggleInlineStyle = (style) => { diff --git a/app/javascript/gabsocial/components/group_collection_item.js b/app/javascript/gabsocial/components/group_collection_item.js index f3899c91..f8cc5db3 100644 --- a/app/javascript/gabsocial/components/group_collection_item.js +++ b/app/javascript/gabsocial/components/group_collection_item.js @@ -50,7 +50,7 @@ class GroupCollectionItem extends ImmutablePureComponent { ) : intl.formatMessage(messages.no_recent_activity) - const imageHeight = '200px' + const imageHeight = '180px' const isMember = relationships.get('member') @@ -109,7 +109,7 @@ class GroupCollectionItem extends ImmutablePureComponent { {group.get('title')} -
+
{shortNumberFormat(group.get('member_count'))}   @@ -119,21 +119,11 @@ class GroupCollectionItem extends ImmutablePureComponent { {subtitle} + +
-
- -
- ) diff --git a/app/javascript/gabsocial/components/image.js b/app/javascript/gabsocial/components/image.js index 42d5ffd1..79a416b2 100644 --- a/app/javascript/gabsocial/components/image.js +++ b/app/javascript/gabsocial/components/image.js @@ -1,8 +1,9 @@ import classNames from 'classnames/bind' -import { autoPlayGif } from '../initial_state' // : testing : -const placeholderSource = 'https://via.placeholder.com/150' +// : todo : +const placeholderSource = 'https://source.unsplash.com/random' +const imageUnavailable = 'https://source.unsplash.com/random' const cx = classNames.bind(_s) @@ -14,6 +15,7 @@ export default class Image extends PureComponent { width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), fit: PropTypes.oneOf(['contain', 'cover', 'tile', 'none']), + nullable: PropTypes.bool, } static defaultProps = { @@ -21,20 +23,37 @@ export default class Image extends PureComponent { fit: 'cover', } - render() { - const { src, fit, className, ...otherProps } = this.props + state = { + error: false, + } - const source = src || placeholderSource + handleOnError = () => { + this.setState({ error: true }) + } + + render() { + const { src, fit, className, nullable, ...otherProps } = this.props + const { error } = this.state + + let source = src || placeholderSource const classes = cx(className, { default: 1, objectFitCover: fit === 'cover' }) + //If error and not our own image + if (error && nullable) { + return null + } else if (error) { + source = imageUnavailable + } + return ( ) } diff --git a/app/javascript/gabsocial/components/panel/lists_panel.js b/app/javascript/gabsocial/components/panel/lists_panel.js new file mode 100644 index 00000000..6d629a36 --- /dev/null +++ b/app/javascript/gabsocial/components/panel/lists_panel.js @@ -0,0 +1,77 @@ +import ImmutablePropTypes from 'react-immutable-proptypes' +import ImmutablePureComponent from 'react-immutable-pure-component' +import { defineMessages, injectIntl } from 'react-intl' +import { createSelector } from 'reselect' +import { fetchLists } from '../../actions/lists' +import PanelLayout from './panel_layout' +import List from '../list' + +const getOrderedLists = createSelector([state => state.get('lists')], lists => { + if (!lists) return lists + + return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))) +}) + +const messages = defineMessages({ + title: { id: 'lists.subheading', defaultMessage: 'Your Lists' }, + show_all: { id: 'groups.sidebar-panel.show_all', defaultMessage: 'Show all' }, + all: { id: 'groups.sidebar-panel.all', defaultMessage: 'All' }, +}) + +const mapStateToProps = (state) => ({ + lists: getOrderedLists(state), +}) + +const mapDispatchToProps = (dispatch) => ({ + onFetchLists() { + return dispatch(fetchLists()) + }, +}) + +export default +@connect(mapStateToProps, mapDispatchToProps) +@injectIntl +class ListsPanel extends ImmutablePureComponent { + static propTypes = { + onFetchLists: PropTypes.func.isRequired, + lists: ImmutablePropTypes.list, + intl: PropTypes.object.isRequired, + } + + componentWillMount() { + this.props.onFetchLists() + } + + render() { + const { intl, lists } = this.props + const count = lists.count() + + if (count === 0) return null + + const maxCount = 6 + + const listItems = lists.slice(0, maxCount).map(list => ({ + to: `/lists/${list.get('id')}`, + title: list.get('title'), + })) + + return ( + maxCount ? intl.formatMessage(messages.show_all) : undefined} + footerButtonTo={count > maxCount ? '/lists' : undefined} + noPadding + > +
+ +
+
+ ) + } +} \ No newline at end of file diff --git a/app/javascript/gabsocial/components/panel/trends_panel.js b/app/javascript/gabsocial/components/panel/trends_panel.js index 04a52300..ac29c477 100644 --- a/app/javascript/gabsocial/components/panel/trends_panel.js +++ b/app/javascript/gabsocial/components/panel/trends_panel.js @@ -1,63 +1,68 @@ import { injectIntl, defineMessages } from 'react-intl' -// import { fetchTrends } from '../../actions/trends' import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePropTypes from 'react-immutable-proptypes' -import TrendingItem from '../trends_panel_item' +import { fetchGabTrends } from '../../actions/gab_trends' import PanelLayout from './panel_layout' +import ColumnIndicator from '../column_indicator' +import TrendingItem from '../trends_item' const messages = defineMessages({ title: { id:'trends.title', defaultMessage: 'Trending right now' }, - show_all: { id: 'groups.sidebar-panel.show_all', defaultMessage: 'Show all' }, }) -// const mapStateToProps = state => ({ -// trends: state.getIn(['trends', 'items']), -// }) +const mapStateToProps = state => ({ + gabtrends: state.getIn(['gab_trends', 'items']), +}) -// const mapDispatchToProps = dispatch => { -// return { -// fetchTrends: () => dispatch(fetchTrends()), -// } -// } +const mapDispatchToProps = dispatch => { + return { + onFetchGabTrends: () => dispatch(fetchGabTrends()), + } +} export default -// @connect(mapStateToProps, mapDispatchToProps) +@connect(mapStateToProps, mapDispatchToProps) @injectIntl class TrendsPanel extends ImmutablePureComponent { static propTypes = { - trends: ImmutablePropTypes.list.isRequired, - // fetchTrends: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, + gabtrends: ImmutablePropTypes.list.isRequired, + onFetchGabTrends: PropTypes.func.isRequired, } - componentDidMount () { - // this.props.fetchTrends() + componentWillMount () { + this.props.onFetchGabTrends() } render() { - const { intl, trends } = this.props - - // !!! TESTING !!! - // if (trends.isEmpty()) { - // return null - // } + const { intl, gabtrends } = this.props return (
- { /* trends && trends.map(hashtag => ( - - )) */ } - - - - + { + gabtrends.isEmpty() && + + } + { + gabtrends && gabtrends.slice(0, 8).map((trend, i) => ( + + )) + }
) diff --git a/app/javascript/gabsocial/components/status_content/status_content.js b/app/javascript/gabsocial/components/status_content/status_content.js index f48d7917..9a53e40d 100644 --- a/app/javascript/gabsocial/components/status_content/status_content.js +++ b/app/javascript/gabsocial/components/status_content/status_content.js @@ -195,7 +195,7 @@ class StatusContent extends ImmutablePureComponent { to={`/${item.get('acct')}`} href={`/${item.get('acct')}`} key={item.get('id')} - className={['mention', _s.mr5, _s.mb5].join(' ')} + className={['mention', _s.mr5, _s.pb5].join(' ')} > @{item.get('username')} diff --git a/app/javascript/gabsocial/components/trends_item.js b/app/javascript/gabsocial/components/trends_item.js new file mode 100644 index 00000000..8c79c61c --- /dev/null +++ b/app/javascript/gabsocial/components/trends_item.js @@ -0,0 +1,114 @@ +import classNames from 'classnames/bind' +import Button from './button' +import DotTextSeperator from './dot_text_seperator' +import Image from './image' +import RelativeTimestamp from './relative_timestamp' +import Text from './text' + +const cx = classNames.bind(_s) + +export default class TrendingItem extends PureComponent { + + static propTypes = { + index: PropTypes.number, + url: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + description: PropTypes.string, + imageUrl: PropTypes.string, + author: PropTypes.string, + publishDate: PropTypes.string, + isLast: PropTypes.bool, + } + + state = { + hovering: false, + } + + handleOnMouseEnter = () => { + this.setState({ hovering: true }) + } + + handleOnMouseLeave = () => { + this.setState({ hovering: false }) + } + + render() { + const { + index, + url, + title, + description, + imageUrl, + author, + publishDate, + isLast + } = this.props + const { hovering } = this.state + + const containerClasses = cx({ + default: 1, + noUnderline: 1, + px15: 1, + pt10: 1, + pb5: 1, + borderColorSecondary: !isLast, + borderBottom1PX: !isLast, + backgroundSubtle_onHover: 1, + }) + + const subtitleClasses = cx({ + ml5: 1, + underline: hovering, + }) + + const correctedAuthor = author.replace('www.', '') + const correctedDescription = description.length > 120 ? `${description.substring(0, 120)}...` : description + + return ( + + ) + } + +} diff --git a/app/javascript/gabsocial/components/trends_panel_item.js b/app/javascript/gabsocial/components/trends_panel_item.js deleted file mode 100644 index 8ba5c3f4..00000000 --- a/app/javascript/gabsocial/components/trends_panel_item.js +++ /dev/null @@ -1,73 +0,0 @@ -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 classNames from 'classnames/bind' -import { shortNumberFormat } from '../utils/numbers' -import Text from './text' -import Button from './button' -import Image from './image' -import TrendingItemCard from './trends_panel_item_card' -import DotTextSeperator from './dot_text_seperator' - -const cx = classNames.bind(_s) - -export default class TrendingItem extends ImmutablePureComponent { - - static propTypes = { - index: PropTypes.number, - trend: ImmutablePropTypes.map.isRequired, - } - - state = { - hovering: false, - } - - handleOnMouseEnter = () => { - this.setState({ hovering: true }) - } - - handleOnMouseLeave = () => { - this.setState({ hovering: false }) - } - - render() { - const { trend, index } = this.props - const { hovering } = this.state - - const subtitleClasses = cx({ - default: 1, - text: 1, - displayFlex: 1, - fontSize13PX: 1, - fontWeightNormal: 1, - colorSecondary: 1, - underline: hovering, - }) - - // return null; - - // : todo : - - return ( - this.handleOnMouseEnter()} - onMouseLeave={() => this.handleOnMouseLeave()} - > -
- {index} - - Politics -
-
- Trump Campaign - 46.7K Gabs - -
-
- ) - } - -} diff --git a/app/javascript/gabsocial/components/trends_panel_item_card.js b/app/javascript/gabsocial/components/trends_panel_item_card.js deleted file mode 100644 index d9d44ff6..00000000 --- a/app/javascript/gabsocial/components/trends_panel_item_card.js +++ /dev/null @@ -1,66 +0,0 @@ -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 classNames from 'classnames/bind' -import { shortNumberFormat } from '../utils/numbers' -import Text from './text' -import Button from './button' -import Image from './image' - -const cx = classNames.bind(_s) - -export default class TrendingItemCard extends ImmutablePureComponent { - - static propTypes = { - trend: ImmutablePropTypes.map.isRequired, - } - - state = { - hovering: false, - } - - handleOnMouseEnter = () => { - this.setState({ hovering: true }) - } - - handleOnMouseLeave = () => { - this.setState({ hovering: false }) - } - - render() { - const { trend } = this.props - const { hovering } = this.state - - const subtitleClasses = cx({ - default: 1, - text: 1, - displayFlex: 1, - fontSize13PX: 1, - fontWeightNormal: 1, - colorSecondary: 1, - underline: hovering, - }) - - - // URL with title, description - - // URL with video - - // URL with title, description, image - return ( -
-
- - NYPost - - - The best flower subscription services: BloomsyBox, Bouqs... - -
- -
- ) - } - -} diff --git a/app/javascript/gabsocial/features/compose/components/search_results/search_results.js b/app/javascript/gabsocial/features/compose/components/search_results/search_results.js index 51034fc8..4ba64e7d 100644 --- a/app/javascript/gabsocial/features/compose/components/search_results/search_results.js +++ b/app/javascript/gabsocial/features/compose/components/search_results/search_results.js @@ -1,7 +1,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePureComponent from 'react-immutable-pure-component' import { FormattedMessage } from 'react-intl' -import TrendingItem from '../../../../components/trends_panel_item' +import TrendingItem from '../../../../components/trends_item' import Icon from '../../../../components/icon' import { WhoToFollowPanel } from '../../../../components/panel' // import TrendsPanel from '../../ui/components/trends_panel' diff --git a/app/javascript/gabsocial/pages/home_page.js b/app/javascript/gabsocial/pages/home_page.js index 1d2046f4..7241ca59 100644 --- a/app/javascript/gabsocial/pages/home_page.js +++ b/app/javascript/gabsocial/pages/home_page.js @@ -1,6 +1,7 @@ import { Fragment } from 'react' import { openModal } from '../actions/modal' -import GroupSidebarPanel from '../components/panel/groups_panel' +import GroupsPanel from '../components/panel/groups_panel' +import ListsPanel from '../components/panel/lists_panel' import LinkFooter from '../components/link_footer' import WhoToFollowPanel from '../components/panel/who_to_follow_panel' import ProgressPanel from '../components/panel/progress_panel' @@ -46,9 +47,10 @@ class HomePage extends PureComponent { + - + )} diff --git a/app/javascript/gabsocial/reducers/gab_trends.js b/app/javascript/gabsocial/reducers/gab_trends.js index e69de29b..8d050bc9 100644 --- a/app/javascript/gabsocial/reducers/gab_trends.js +++ b/app/javascript/gabsocial/reducers/gab_trends.js @@ -0,0 +1,36 @@ +import { + GAB_TRENDS_RESULTS_FETCH_REQUEST, + GAB_TRENDS_RESULTS_FETCH_SUCCESS, + GAB_TRENDS_RESULTS_FETCH_FAIL +} from '../actions/gab_trends' +import { + Map as ImmutableMap, + List as ImmutableList, + fromJS +} from 'immutable' + +const initialState = ImmutableMap({ + items: ImmutableList(), + loading: false, + error: false, +}) + +export default function (state = initialState, action) { + switch (action.type) { + case GAB_TRENDS_RESULTS_FETCH_REQUEST: + return state.set('loading', true) + case GAB_TRENDS_RESULTS_FETCH_SUCCESS: + return state.withMutations(map => { + map.set('items', fromJS(action.items)); + map.set('error', false); + map.set('loading', false); + }); + case GAB_TRENDS_RESULTS_FETCH_FAIL: + return state.withMutations(map => { + map.set('error', !!action.error); + map.set('loading', false); + }); + default: + return state + } +} \ No newline at end of file diff --git a/app/javascript/gabsocial/reducers/index.js b/app/javascript/gabsocial/reducers/index.js index f25b7bba..cb146af4 100644 --- a/app/javascript/gabsocial/reducers/index.js +++ b/app/javascript/gabsocial/reducers/index.js @@ -8,6 +8,7 @@ import conversations from './conversations' import custom_emojis from './custom_emojis' import domain_lists from './domain_lists' import filters from './filters' +import gab_trends from './gab_trends' import groups from './groups' import group_editor from './group_editor' import group_lists from './group_lists' @@ -48,6 +49,7 @@ const reducers = { custom_emojis, domain_lists, filters, + gab_trends, groups, group_editor, group_lists, diff --git a/app/javascript/styles/global.css b/app/javascript/styles/global.css index 51be029a..70e59e7f 100644 --- a/app/javascript/styles/global.css +++ b/app/javascript/styles/global.css @@ -472,6 +472,10 @@ body { max-height: 80vh; } +.heightMax56PX { + max-height: 56px; +} + .heightMin50VH { min-height: 50vh; } diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 1dd1cd59..531cb7ea 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -37,7 +37,7 @@ class InitialStateSerializer < ActiveModel::Serializer store[:group_in_home_feed] = object.current_account.user.setting_group_in_home_feed store[:is_staff] = object.current_account.user.staff? store[:unread_count] = unread_count object.current_account - store[:monthly_expenses_complete] = Redis.current.get("monthly_funding_ammount") || 0 + store[:monthly_expenses_complete] = Redis.current.get("monthly_funding_amount") || 0 store[:favourites_count] = object.current_account.favourites.count.to_s store[:tenorkey] = "QHFJ0C5EWGBH" end diff --git a/app/views/settings/expenses/index.html.haml b/app/views/settings/expenses/index.html.haml index cfa518f5..e1e4a0c7 100644 --- a/app/views/settings/expenses/index.html.haml +++ b/app/views/settings/expenses/index.html.haml @@ -2,5 +2,5 @@ = t('monthly_funding.title') = form_tag settings_expenses_url, :method => :post do - = text_field_tag "ammount", "", placeholder: "0-100", :value => @ammount + = text_field_tag "amount", "", placeholder: "0-100", :value => @amount = submit_tag "Submit" diff --git a/config/routes.rb b/config/routes.rb index 172e5e35..222b92fd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -326,6 +326,7 @@ Rails.application.routes.draw do resources :group, only: :show end + resources :gab_trends, only: [:index] resources :streaming, only: [:index] resources :custom_emojis, only: [:index] resources :gifs, only: [:index]