Skip to content
This repository has been archived by the owner on Sep 26, 2022. It is now read-only.

Add AJAX functions for following/unfollowing users #9

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ __pycache__
.vscode/
venv/
.flaskenv
app.db
app.db
.idea/
.env/
8 changes: 6 additions & 2 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from flask_migrate import Migrate
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from config import Config
from config import Config, BASE_DIR

# extensions
db = SQLAlchemy()
Expand All @@ -17,7 +17,11 @@


def create_app(config_class=Config):
app = Flask(__name__)
app = Flask(
__name__,
static_url_path='',
static_folder=f'{BASE_DIR}/static'
)
app.config.from_object(Config)

db.init_app(app)
Expand Down
16 changes: 13 additions & 3 deletions app/auth/views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
from flask import render_template, request, flash,\
redirect, url_for
from flask import (
render_template,
request,
flash,
redirect,
url_for,
)
from flask_login import current_user, login_user, logout_user
from werkzeug.urls import url_parse

from app import db
from app.auth import bp
from app.auth.email import send_password_reset_email
from app.models import User
from app.auth.forms import LoginForm, RegistrationForm, PasswordResetRequestForm, ResetPasswordForm
from app.auth.forms import (
LoginForm,
RegistrationForm,
PasswordResetRequestForm,
ResetPasswordForm
)

@bp.route('/login', methods=["GET", "POST"])
def login():
Expand Down
78 changes: 58 additions & 20 deletions app/core/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
from datetime import datetime
from flask import render_template, flash, redirect, \
url_for, request, current_app

from flask import (
make_response,
current_app,
flash,
redirect,
render_template,
request,
url_for
)
from flask_login import current_user, login_required
from app import db
from app.core import bp
Expand Down Expand Up @@ -83,30 +91,60 @@ def edit_profile():
return render_template('edit_profile.html', form=form, title="Edit Profile")


def ajax_flash(msg_type, message, message_list):
message_list.append({
'type': msg_type,
'message': message
})
return message_list


@bp.route('/follow/<username>')
def follow(username):
flash_messages = []

data = {
'redirect': ''
}

user = User.query.filter_by(username=username).first()

if not user:
flash("User not found")
return redirect(url_for("core.index"))
if user == current_user:
flash("You can't follow yourself")
return redirect(url_for('core.user', username=username))
current_user.follow(user)
db.session.commit()
flash(f"You are following {username}!")
return redirect(url_for("core.user", username=username))
flash_messages = ajax_flash('info', 'User not found', flash_messages)
elif user == current_user:
flash_messages = ajax_flash('info', 'You can\'t follow yourself!', flash_messages)
else:
current_user.follow(user)
db.session.commit()
flash_messages = ajax_flash('success', f'You are following {username}', flash_messages)

data['flash_messages'] = flash_messages

r = make_response(data)
r.mimetype = 'application/json'
return r


@bp.route('/unfollow/<username>')
def unfollow(username):
flash_messages = [];

data = {
'redirect': ''
}
user = User.query.filter_by(username=username).first()
if not user:
flash("User not found")
return redirect(url_for("core.index"))
if user == current_user:
flash("You can unfollow yourself")
return redirect(url_for("core.user", username=username))
current_user.unfollow(user)
db.session.commit()
flash(f"You unfollowed {username}")
return redirect(url_for('core.user', username=username))
flash_messages = ajax_flash('info', 'User not found', flash_messages)
data['redirect'] = url_for('core.index')
elif user == current_user:
flash_messages = ajax_flash('info', 'You can\'t unfollow yourself', flash_messages)
else:
current_user.unfollow(user)
db.session.commit()
flash_messages = ajax_flash('success', f'You unfollowed {username}', flash_messages)

data['flash_messages'] = flash_messages

r = make_response(data)
r.mimetype = 'application/json'
return r
65 changes: 15 additions & 50 deletions app/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@
</div>
</nav>
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-info">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<div id="flash">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-info">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
{% block content %}{% endblock content %}
</div>
</body>
Expand All @@ -52,49 +54,12 @@
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>

{{ moment.include_moment() }}
<script>
$(function () {
var timer = null;
var xhr = null;
$(".user_popup").hover(
function (event) {
// mouse in handler
var element = $(event.currentTarget);
timer = setTimeout(function () {
timer = null;
xhr = $.ajax(
`/user/${element.first().text().trim()}/popup`
)
.done(function (data) {
xhr = null;
element.popover({
trigger: 'manual',
html: true,
animation: false,
container: element,
content: data,
}).popover('show');
flask_moment_render_all();
})
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>

},
function (event) {
// mouseout handler
var element = $(event.currentTarget)
if (timer) {
clearTimeout(timer);
timer = null;
} else if (xhr) {
xhr.abort();
xhr = null;
} else {
element.popover('dispose');
}
}
)
})
</script>
<script src="{{ url_for('static', filename='js/hover.js') }}"></script>
<script src="{{ url_for('static', filename='js/follow_unfollow.js') }}"></script>

{% block extra_script %}{% endblock %}
{{ moment.include_moment() }}

</html>
5 changes: 5 additions & 0 deletions app/templates/snippets/follow_button.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="follow-link">
<div data-follow="true" data-user="{{user.username}}" class="btn btn-info pt-1 pb-1">
Follow
</div>
</div>
5 changes: 5 additions & 0 deletions app/templates/snippets/unfollow_button.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="follow-link">
<div data-follow="false" data-user="{{user.username}}" class="btn btn-info pt-1 pb-1">
Unfollow
</div>
</div>
14 changes: 11 additions & 3 deletions app/templates/user.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ <h1>User: {{ user.username }}</h1>
{% if user.last_seen %}<p>Last seen on: {{ moment(user.last_seen).format('LLL') }}</p>{% endif %}
<p>{{ user.followers.count()}} followers, {{ user.followed.count()}} following.</p>
{% if user == current_user %}
<p><a href="{{ url_for('core.edit_profile')}}">Edit Profile</a></p>
<p>
<a href="{{ url_for('core.edit_profile')}}">
Edit Profile
</a>
</p>
{% elif current_user.is_following(user) %}
<p><a href="{{ url_for('core.unfollow', username=user.username)}}">Unfollow</a></p>
{% with user=user %}
{% include 'snippets/unfollow_button.html' %}
{% endwith %}
{% else %}
<p><a href="{{ url_for('core.follow', username=user.username)}}">Follow</a></p>
{% with user=user %}
{% include 'snippets/follow_button.html' %}
{% endwith %}
{% endif %}
</td>
</tr>
Expand Down
8 changes: 6 additions & 2 deletions app/templates/user_popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
<p>{{ user.followers.count()}} followers, {{ user.followed.count()}} following.</p>
{% if user != current_user %}
{% if current_user.is_following(user) %}
<p><a href="{{ url_for('core.unfollow', username=user.username)}}">Unfollow</a></p>
{% with user=user %}
{% include 'snippets/unfollow_button.html' %}
{% endwith %}
{% else %}
<p><a href="{{ url_for('core.follow', username=user.username)}}">Follow</a></p>
{% with user=user %}
{% include 'snippets/follow_button.html' %}
{% endwith %}
{% endif %}
{% endif %}
</div>
Expand Down
67 changes: 67 additions & 0 deletions static/js/follow_unfollow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
function flashMessages(data){
let html = '';

for (let i = 0; i < data.length; i++) {
html += `
<div class="alert alert-${data[i]['type']}">
<a href="#" class="close" data-dismiss="alert">&times;</a>
${data[i].message}
</div>`;
}
return html;
}

function ajaxGetWithFlash(target_user, follow) {
const endpoint = follow ? `/follow/${target_user}` : `/unfollow/${target_user}`;
const buttonTxt = follow ? "Unfollow" : "Follow";
const Axios = window.axios;

const linkHTML =
`<button data-follow="${!follow}" data-user="${target_user}" class="btn btn-info pt-1 pb-1">
${buttonTxt}
</button>`;

Axios.get(endpoint).then((response) => {
const messages = response.data['flash_messages'];
const flashBox = $('#flash');
const followLinks = $('.follow-link');
const popover = $('.popover');

followLinks.each((index, _this) => {
_this.innerHTML = linkHTML;
});
if (flashBox) {
flashBox.html(flashMessages(messages))
}

if (popover) {
popover.remove();
}
}).catch((error) => {
console.error(`Error: ${error}, please contact the administrator`);
});
}

function unFollowUser(target_user) {
ajaxGetWithFlash(target_user, false)
}

function followUser(target_user) {
ajaxGetWithFlash(target_user, true)
}

$(function() {
$('body').on('click', '.follow-link .btn', (e) => {
e.preventDefault();

const link = $('.follow-link .btn');
const follow = link.data('follow');
const target_user = link.data('user');

if (follow) {
followUser(target_user);
} else {
unFollowUser(target_user);
}
})
});
Loading