0.3.5
- Show timezone next to post datetimes - Take read status in consideration when sorting posts
This commit is contained in:
parent
ee9b36d8ae
commit
116b6d1308
19 changed files with 166 additions and 57 deletions
|
|
@ -1,9 +1,11 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from newsreader.accounts.models import User
|
from newsreader.accounts.models import User
|
||||||
|
from newsreader.core.forms import CheckboxInput
|
||||||
|
|
||||||
|
|
||||||
class UserSettingsForm(forms.ModelForm):
|
class UserSettingsForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ("first_name", "last_name")
|
fields = ("first_name", "last_name", "auto_mark_read")
|
||||||
|
widgets = {"auto_mark_read": CheckboxInput}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 3.0.7 on 2020-10-27 21:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [("accounts", "0012_remove_user_task")]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="user",
|
||||||
|
name="auto_mark_read",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
help_text="Wether posts should be marked as read after x amount of seconds of reading",
|
||||||
|
verbose_name="Auto read marking",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
@ -45,6 +45,15 @@ class User(AbstractUser):
|
||||||
twitter_oauth_token = models.CharField(max_length=255, blank=True, null=True)
|
twitter_oauth_token = models.CharField(max_length=255, blank=True, null=True)
|
||||||
twitter_oauth_token_secret = models.CharField(max_length=255, blank=True, null=True)
|
twitter_oauth_token_secret = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
# settings
|
||||||
|
auto_mark_read = models.BooleanField(
|
||||||
|
_("Auto read marking"),
|
||||||
|
default=True,
|
||||||
|
help_text=_(
|
||||||
|
"Wether posts should be marked as read after x amount of seconds of reading"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
username = None
|
username = None
|
||||||
|
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
from .dev import * # isort:skip
|
from .base import * # isort:skip
|
||||||
|
|
||||||
|
|
||||||
SECRET_KEY = "=q(ztyo)b6noom#a164g&s9vcj1aawa^g#ing_ir99=_zl4g&$"
|
SECRET_KEY = "=q(ztyo)b6noom#a164g&s9vcj1aawa^g#ing_ir99=_zl4g&$"
|
||||||
|
|
||||||
|
INSTALLED_APPS += ["debug_toolbar", "django_extensions"]
|
||||||
|
|
||||||
|
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
|
||||||
|
|
||||||
|
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
"default": {
|
||||||
"ENGINE": "django.db.backends.postgresql",
|
"ENGINE": "django.db.backends.postgresql",
|
||||||
|
|
@ -24,6 +30,16 @@ CACHES = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Third party settings
|
||||||
|
# Axes
|
||||||
|
AXES_FAILURE_LIMIT = 50
|
||||||
|
AXES_COOLOFF_TIME = None
|
||||||
|
|
||||||
# Celery
|
# Celery
|
||||||
# https://docs.celeryproject.org/en/latest/userguide/configuration.html
|
# https://docs.celeryproject.org/en/latest/userguide/configuration.html
|
||||||
CELERY_BROKER_URL = "amqp://guest:guest@rabbitmq:5672//"
|
CELERY_BROKER_URL = "amqp://guest:guest@rabbitmq:5672//"
|
||||||
|
|
||||||
|
try:
|
||||||
|
from .local import * # noqa
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,9 @@ class App extends React.Component {
|
||||||
feedUrl={this.props.feedUrl}
|
feedUrl={this.props.feedUrl}
|
||||||
subredditUrl={this.props.subredditUrl}
|
subredditUrl={this.props.subredditUrl}
|
||||||
timelineUrl={this.props.timelineUrl}
|
timelineUrl={this.props.timelineUrl}
|
||||||
|
timezone={this.props.timezone}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{this.props.error && (
|
|
||||||
<Messages messages={[{ type: 'error', text: this.props.error.message }]} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isEqual(this.props.post, {}) && (
|
{!isEqual(this.props.post, {}) && (
|
||||||
<PostModal
|
<PostModal
|
||||||
post={this.props.post}
|
post={this.props.post}
|
||||||
|
|
@ -38,8 +35,14 @@ class App extends React.Component {
|
||||||
subredditUrl={this.props.subredditUrl}
|
subredditUrl={this.props.subredditUrl}
|
||||||
timelineUrl={this.props.timelineUrl}
|
timelineUrl={this.props.timelineUrl}
|
||||||
categoriesUrl={this.props.categoriesUrl}
|
categoriesUrl={this.props.categoriesUrl}
|
||||||
|
timezone={this.props.timezone}
|
||||||
|
autoMarking={this.props.autoMarking}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{this.props.error && (
|
||||||
|
<Messages messages={[{ type: 'error', text: this.props.error.message }]} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export const RECEIVE_POST = 'RECEIVE_POST';
|
||||||
export const REQUEST_POSTS = 'REQUEST_POSTS';
|
export const REQUEST_POSTS = 'REQUEST_POSTS';
|
||||||
|
|
||||||
export const MARK_POST_READ = 'MARK_POST_READ';
|
export const MARK_POST_READ = 'MARK_POST_READ';
|
||||||
|
export const MARKING_POST = 'MARKING_POST';
|
||||||
|
|
||||||
export const requestPosts = () => ({ type: REQUEST_POSTS });
|
export const requestPosts = () => ({ type: REQUEST_POSTS });
|
||||||
|
|
||||||
|
|
@ -30,10 +31,14 @@ export const postRead = (post, section) => ({
|
||||||
section,
|
section,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const markingPostRead = () => ({ type: MARKING_POST });
|
||||||
|
|
||||||
export const markPostRead = (post, token) => {
|
export const markPostRead = (post, token) => {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const { selected } = getState();
|
const { selected } = getState();
|
||||||
|
|
||||||
|
dispatch(markingPostRead());
|
||||||
|
|
||||||
const url = `/api/posts/${post.id}/`;
|
const url = `/api/posts/${post.id}/`;
|
||||||
const options = {
|
const options = {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class PostModal extends React.Component {
|
||||||
const markPostRead = this.props.markPostRead;
|
const markPostRead = this.props.markPostRead;
|
||||||
const token = Cookies.get('csrftoken');
|
const token = Cookies.get('csrftoken');
|
||||||
|
|
||||||
if (!post.read) {
|
if (this.props.autoMarking && !post.read) {
|
||||||
this.readTimer = setTimeout(markPostRead, 3000, post, token);
|
this.readTimer = setTimeout(markPostRead, 3000, post, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,31 +48,47 @@ class PostModal extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const post = this.props.post;
|
const post = this.props.post;
|
||||||
|
const token = Cookies.get('csrftoken');
|
||||||
const publicationDate = formatDatetime(post.publicationDate);
|
const publicationDate = formatDatetime(post.publicationDate);
|
||||||
const titleClassName = post.read ? 'post__title post__title--read' : 'post__title';
|
const titleClassName = post.read ? 'post__title post__title--read' : 'post__title';
|
||||||
let ruleUrl = '';
|
const readButtonDisabled = post.read || this.props.isMarkingPost;
|
||||||
|
|
||||||
if (this.props.rule.type === SUBREDDIT) {
|
let ruleUrl = '';
|
||||||
|
switch (this.props.rule.type) {
|
||||||
|
case SUBREDDIT:
|
||||||
ruleUrl = `${this.props.subredditUrl}/${this.props.rule.id}/`;
|
ruleUrl = `${this.props.subredditUrl}/${this.props.rule.id}/`;
|
||||||
} else if (this.props.rule.type === TWITTER_TIMELINE) {
|
break;
|
||||||
|
case TWITTER_TIMELINE:
|
||||||
ruleUrl = `${this.props.timelineUrl}/${this.props.rule.id}/`;
|
ruleUrl = `${this.props.timelineUrl}/${this.props.rule.id}/`;
|
||||||
} else {
|
break;
|
||||||
|
default:
|
||||||
ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`;
|
ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal post-modal">
|
<div className="modal post-modal">
|
||||||
<div className="post">
|
<div className="post">
|
||||||
<span
|
<div className="post__actions">
|
||||||
|
<button
|
||||||
|
className={`button read-button ${readButtonDisabled && 'button--disabled'}`}
|
||||||
|
onClick={() => !readButtonDisabled && this.props.markPostRead(post, token)}
|
||||||
|
>
|
||||||
|
Mark as read
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
className="button post__close-button"
|
className="button post__close-button"
|
||||||
onClick={() => this.props.unSelectPost()}
|
onClick={() => this.props.unSelectPost()}
|
||||||
>
|
>
|
||||||
Close <i className="gg-close"></i>
|
Close <i className="gg-close"></i>
|
||||||
</span>
|
</button>
|
||||||
|
</div>
|
||||||
<div className="post__header">
|
<div className="post__header">
|
||||||
<h2 className={titleClassName}>{`${post.title} `}</h2>
|
<h2 className={titleClassName}>{`${post.title} `}</h2>
|
||||||
<div className="post__meta-info">
|
<div className="post__meta-info">
|
||||||
<span className="post__date">{publicationDate}</span>
|
<span className="post__date">
|
||||||
|
{publicationDate} {this.props.timezone}
|
||||||
|
</span>
|
||||||
{post.author && <span className="post__author">{post.author}</span>}
|
{post.author && <span className="post__author">{post.author}</span>}
|
||||||
{this.props.category && (
|
{this.props.category && (
|
||||||
<span className="badge post__category" title={this.props.category.name}>
|
<span className="badge post__category" title={this.props.category.name}>
|
||||||
|
|
@ -114,4 +130,6 @@ const mapDispatchToProps = dispatch => ({
|
||||||
markPostRead: (post, token) => dispatch(markPostRead(post, token)),
|
markPostRead: (post, token) => dispatch(markPostRead(post, token)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(PostModal);
|
const mapStateToProps = state => ({ isMarkingPost: state.posts.isMarking });
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(PostModal);
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class PostItem extends React.Component {
|
||||||
|
|
||||||
<div className="posts-info">
|
<div className="posts-info">
|
||||||
<span className="posts-info__date" title={publicationDate}>
|
<span className="posts-info__date" title={publicationDate}>
|
||||||
{publicationDate} {post.author && `By ${post.author}`}
|
{publicationDate} {this.props.timezone} {post.author && `By ${post.author}`}
|
||||||
</span>
|
</span>
|
||||||
{this.props.selected.type == CATEGORY_TYPE && (
|
{this.props.selected.type == CATEGORY_TYPE && (
|
||||||
<span className="badge">
|
<span className="badge">
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ class PostList extends React.Component {
|
||||||
feedUrl={this.props.feedUrl}
|
feedUrl={this.props.feedUrl}
|
||||||
subredditUrl={this.props.subredditUrl}
|
subredditUrl={this.props.subredditUrl}
|
||||||
timelineUrl={this.props.timelineUrl}
|
timelineUrl={this.props.timelineUrl}
|
||||||
|
timezone={this.props.timezone}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,19 @@ const isEmpty = (object = {}) => {
|
||||||
return Object.keys(object).length === 0;
|
return Object.keys(object).length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sortOrdering = (firstPost, secondPost) => {
|
||||||
|
const dateOrdering =
|
||||||
|
new Date(firstPost.publicationDate) < new Date(secondPost.publicationDate);
|
||||||
|
|
||||||
|
if (firstPost.read && !secondPost.read) {
|
||||||
|
return 1;
|
||||||
|
} else if (secondPost.read && !firstPost.read) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateOrdering;
|
||||||
|
};
|
||||||
|
|
||||||
export const filterPostsByRule = (rule = {}, posts = []) => {
|
export const filterPostsByRule = (rule = {}, posts = []) => {
|
||||||
const filteredPosts = posts.filter(post => {
|
const filteredPosts = posts.filter(post => {
|
||||||
return post.rule === rule.id;
|
return post.rule === rule.id;
|
||||||
|
|
@ -11,9 +24,7 @@ export const filterPostsByRule = (rule = {}, posts = []) => {
|
||||||
|
|
||||||
const filteredData = filteredPosts.map(post => ({ ...post, rule: { ...rule } }));
|
const filteredData = filteredPosts.map(post => ({ ...post, rule: { ...rule } }));
|
||||||
|
|
||||||
return filteredData.sort((firstPost, secondPost) => {
|
return filteredData.sort(sortOrdering);
|
||||||
return new Date(secondPost.publicationDate) - new Date(firstPost.publicationDate);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const filterPostsByCategory = (category = {}, rules = [], posts = []) => {
|
export const filterPostsByCategory = (category = {}, rules = [], posts = []) => {
|
||||||
|
|
@ -29,9 +40,7 @@ export const filterPostsByCategory = (category = {}, rules = [], posts = []) =>
|
||||||
return filteredPosts.map(post => ({ ...post, rule: { ...rule } }));
|
return filteredPosts.map(post => ({ ...post, rule: { ...rule } }));
|
||||||
});
|
});
|
||||||
|
|
||||||
const sortedPosts = [...filteredData.flat()].sort((firstPost, secondPost) => {
|
const sortedPosts = [...filteredData.flat()].sort(sortOrdering);
|
||||||
return new Date(secondPost.publicationDate) - new Date(firstPost.publicationDate);
|
|
||||||
});
|
|
||||||
|
|
||||||
return sortedPosts;
|
return sortedPosts;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,18 @@ const page = document.getElementById('homepage--page');
|
||||||
if (page) {
|
if (page) {
|
||||||
const store = configureStore();
|
const store = configureStore();
|
||||||
|
|
||||||
let feedUrl = document.getElementById('feedUrl').textContent;
|
const settings = JSON.parse(document.getElementById('homepageSettings').textContent);
|
||||||
let subredditUrl = document.getElementById('subredditUrl').textContent;
|
const { feedUrl, subredditUrl, timelineUrl, categoriesUrl } = settings;
|
||||||
let timelineUrl = document.getElementById('timelineUrl').textContent;
|
|
||||||
let categoriesUrl = document.getElementById('categoriesUrl').textContent;
|
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<App
|
<App
|
||||||
feedUrl={feedUrl.substring(1, feedUrl.length - 4)}
|
feedUrl={feedUrl.substring(1, feedUrl.length - 3)}
|
||||||
subredditUrl={subredditUrl.substring(1, subredditUrl.length - 4)}
|
subredditUrl={subredditUrl.substring(1, subredditUrl.length - 3)}
|
||||||
timelineUrl={timelineUrl.substring(1, timelineUrl.length - 4)}
|
timelineUrl={timelineUrl.substring(1, timelineUrl.length - 3)}
|
||||||
categoriesUrl={categoriesUrl.substring(1, categoriesUrl.length - 4)}
|
categoriesUrl={categoriesUrl.substring(1, categoriesUrl.length - 3)}
|
||||||
|
timezone={settings.timezone}
|
||||||
|
autoMarking={settings.autoMarking}
|
||||||
/>
|
/>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
page
|
page
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SELECT_POST,
|
SELECT_POST,
|
||||||
|
MARKING_POST,
|
||||||
|
MARK_POST_READ,
|
||||||
RECEIVE_POST,
|
RECEIVE_POST,
|
||||||
RECEIVE_POSTS,
|
RECEIVE_POSTS,
|
||||||
REQUEST_POSTS,
|
REQUEST_POSTS,
|
||||||
|
|
@ -13,7 +15,7 @@ import { SELECT_CATEGORY } from '../actions/categories.js';
|
||||||
import { SELECT_RULE } from '../actions/rules.js';
|
import { SELECT_RULE } from '../actions/rules.js';
|
||||||
import { MARK_SECTION_READ } from '../actions/selected.js';
|
import { MARK_SECTION_READ } from '../actions/selected.js';
|
||||||
|
|
||||||
const defaultState = { items: {}, isFetching: false };
|
const defaultState = { items: {}, isFetching: false, isMarking: false };
|
||||||
|
|
||||||
export const posts = (state = { ...defaultState }, action) => {
|
export const posts = (state = { ...defaultState }, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
@ -62,6 +64,10 @@ export const posts = (state = { ...defaultState }, action) => {
|
||||||
...updatedPosts,
|
...updatedPosts,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
case MARKING_POST:
|
||||||
|
return { ...state, isMarking: true };
|
||||||
|
case MARK_POST_READ:
|
||||||
|
return { ...state, isMarking: false };
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,18 @@ describe('post actions', () => {
|
||||||
fetchMock.restore();
|
fetchMock.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create an action request posts', () => {
|
it('should create an action to request posts', () => {
|
||||||
const expectedAction = { type: actions.REQUEST_POSTS };
|
const expectedAction = { type: actions.REQUEST_POSTS };
|
||||||
|
|
||||||
expect(actions.requestPosts()).toEqual(expectedAction);
|
expect(actions.requestPosts()).toEqual(expectedAction);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create an action to mark a post read', () => {
|
||||||
|
const expectedAction = { type: actions.MARKING_POST };
|
||||||
|
|
||||||
|
expect(actions.markingPostRead()).toEqual(expectedAction);
|
||||||
|
});
|
||||||
|
|
||||||
it('should create an action receive a post', () => {
|
it('should create an action receive a post', () => {
|
||||||
const post = {
|
const post = {
|
||||||
id: 2067,
|
id: 2067,
|
||||||
|
|
@ -147,6 +153,7 @@ describe('post actions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
|
{ type: actions.MARKING_POST },
|
||||||
{
|
{
|
||||||
type: actions.RECEIVE_POST,
|
type: actions.RECEIVE_POST,
|
||||||
post: { ...post, read: true },
|
post: { ...post, read: true },
|
||||||
|
|
@ -362,6 +369,7 @@ describe('post actions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
|
{ type: actions.MARKING_POST },
|
||||||
{ type: actions.RECEIVE_POST, post: {} },
|
{ type: actions.RECEIVE_POST, post: {} },
|
||||||
{ type: errorActions.RECEIVE_API_ERROR, error: TypeError(errorMessage) },
|
{ type: errorActions.RECEIVE_API_ERROR, error: TypeError(errorMessage) },
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ describe('post actions', () => {
|
||||||
it('should return state after requesting posts', () => {
|
it('should return state after requesting posts', () => {
|
||||||
const action = { type: actions.REQUEST_POSTS };
|
const action = { type: actions.REQUEST_POSTS };
|
||||||
|
|
||||||
const expectedState = { ...defaultState, isFetching: true };
|
const expectedState = { ...defaultState, isFetching: true, isMarking: false };
|
||||||
|
|
||||||
expect(reducer(undefined, action)).toEqual(expectedState);
|
expect(reducer(undefined, action)).toEqual(expectedState);
|
||||||
});
|
});
|
||||||
|
|
@ -40,6 +40,7 @@ describe('post actions', () => {
|
||||||
const expectedState = {
|
const expectedState = {
|
||||||
...defaultState,
|
...defaultState,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
|
isMarking: false,
|
||||||
items: { [post.id]: post },
|
items: { [post.id]: post },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -85,6 +86,7 @@ describe('post actions', () => {
|
||||||
const expectedState = {
|
const expectedState = {
|
||||||
...defaultState,
|
...defaultState,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
|
isMarking: false,
|
||||||
items: expectedPosts,
|
items: expectedPosts,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ feed_url|json_script:"feedUrl" }}
|
{{ homepageSettings|json_script:"homepageSettings" }}
|
||||||
{{ subreddit_url|json_script:"subredditUrl" }}
|
|
||||||
{{ twitter_timeline_url|json_script:"timelineUrl" }}
|
|
||||||
{{ categories_url|json_script:"categoriesUrl" }}
|
|
||||||
|
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
{% endblock scripts %}
|
{% endblock scripts %}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.conf import settings
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
from django.views.generic.edit import CreateView, UpdateView
|
from django.views.generic.edit import CreateView, UpdateView
|
||||||
|
|
@ -16,14 +17,18 @@ class NewsView(TemplateView):
|
||||||
|
|
||||||
return {
|
return {
|
||||||
**context,
|
**context,
|
||||||
"feed_url": reverse_lazy("news:collection:feed-update", args=(0,)),
|
"homepageSettings": {
|
||||||
"subreddit_url": reverse_lazy(
|
"feedUrl": reverse_lazy("news:collection:feed-update", args=(0,)),
|
||||||
|
"subredditUrl": reverse_lazy(
|
||||||
"news:collection:subreddit-update", args=(0,)
|
"news:collection:subreddit-update", args=(0,)
|
||||||
),
|
),
|
||||||
"twitter_timeline_url": reverse_lazy(
|
"timelineUrl": reverse_lazy(
|
||||||
"news:collection:twitter-timeline-update", args=(0,)
|
"news:collection:twitter-timeline-update", args=(0,)
|
||||||
),
|
),
|
||||||
"categories_url": reverse_lazy("news:core:category-update", args=(0,)),
|
"categoriesUrl": reverse_lazy("news:core:category-update", args=(0,)),
|
||||||
|
"timezone": settings.TIME_ZONE,
|
||||||
|
"autoMarking": self.request.user.auto_mark_read,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,15 @@
|
||||||
|
|
||||||
cursor: initial;
|
cursor: initial;
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
padding: 20px 50px 0;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -81,9 +90,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__close-button {
|
&__close-button {
|
||||||
position: relative;
|
|
||||||
margin: 1% 2% 0 0;
|
|
||||||
align-self: flex-end;
|
|
||||||
background-color: $blue;
|
background-color: $blue;
|
||||||
color: $white;
|
color: $white;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,8 @@
|
||||||
padding: 2px 10px 5px 10px;
|
padding: 2px 10px 5px 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.read-button {
|
||||||
|
margin: 20px 0 0 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
.read-button {
|
.read-button {
|
||||||
@extend .button;
|
@extend .button;
|
||||||
|
|
||||||
margin: 20px 0 0 0;
|
|
||||||
|
|
||||||
color: $white;
|
color: $white;
|
||||||
background-color: $green;
|
background-color: $green;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue