Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 83 additions & 6 deletions unfold_studio/profiles/templates/profiles/user_self_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@

{% block content %}
<h1>{{user}}</h1>

<!-- Add Follow User Section -->
<div>
<h2>Follow a User</h2>
<div class="user-search-container">
<input type="text" id="username-search" placeholder="Enter username to follow" autocomplete="off">
<div id="search-results" class="search-results"></div>
</div>
</div>

{% if unsubmitted_prompts > 0 %}
<h2>To Do</h2>
<ul>
Expand All @@ -11,6 +21,7 @@ <h2>To Do</h2>
{% endfor %}
</ul>
{% endif %}

<div class="feed">
<h2>Feed</h2>
<ul>
Expand All @@ -22,18 +33,25 @@ <h2>Feed</h2>
<p><a href="{% url 'show_feed' user.username %}">More...</a></p>
{% endif %}
</div>

{% if user.profile.following.exists %}
<div>
<h2>Users you follow</h2>
<ul>
<h2>Creators You Follow</h2>
<div class="creators-grid">
{% for p in user.profile.following.all %}
<li>
<a href="{% url 'show_user' p.user.username %}">{{p.user.username}}</a>
</li>
<div class="creator-card">
<div class="creator-avatar" style="background-color: #9333ea;"></div>
<div class="creator-info">
<h3>{{ p.user.username }}</h3>
<a href="{% url 'show_user' p.user.username %}" class="view-stories-btn">View Stories</a>
</div>
</div>
{% endfor %}
</ul>
</div>
</div>
{% endif %}

<!-- Rest of existing content -->
<div>
<h2>Stories</h2>
<ul>
Expand All @@ -48,6 +66,7 @@ <h2>Stories</h2>
{% endfor %}
</ul>
</div>

<div>
<h2>Books</h2>
<ul>
Expand All @@ -59,4 +78,62 @@ <h2>Books</h2>
<li>[<a href="{% url 'create_book'%}">Start a new book</a>]</li>
</ul>
</div>

<script>
const searchInput = document.getElementById('username-search');
const searchResults = document.getElementById('search-results');
let searchTimeout;

searchInput.addEventListener('input', function() {
const query = this.value.trim();

clearTimeout(searchTimeout);

if (query.length < 2) {
searchResults.innerHTML = '';
searchResults.style.display = 'none';
return;
}

searchTimeout = setTimeout(() => {
fetch(`/api/search-users/?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(users => {
searchResults.innerHTML = '';

if (users.length === 0) {
searchResults.innerHTML = '<div class="search-result-item">No users found</div>';
} else {
users.forEach(user => {
const resultItem = document.createElement('div');
resultItem.className = 'search-result-item';
resultItem.innerHTML = `
<span class="username">${user.username}</span>
<button onclick="visitProfile('${user.username}')" class="visit-btn">Visit Profile</button>
`;
searchResults.appendChild(resultItem);
});
}

searchResults.style.display = 'block';
})
.catch(error => {
console.error('Search error:', error);
searchResults.innerHTML = '<div class="search-result-item">Search error</div>';
searchResults.style.display = 'block';
});
}, 300);
});

function visitProfile(username) {
window.location.href = `/users/${username}/`;
}

// Hide results when clicking outside
document.addEventListener('click', function(e) {
if (!e.target.closest('.user-search-container')) {
searchResults.style.display = 'none';
}
});
</script>
{% endblock %}
15 changes: 13 additions & 2 deletions unfold_studio/profiles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,30 @@
from django.conf import settings as s
from django.db.models import Q, OuterRef, Subquery
from django.core.paginator import Paginator, PageNotAnInteger
from django.http import HttpResponse, Http404
from django.http import HttpResponse, Http404, JsonResponse
from django.contrib.sites.shortcuts import get_current_site
import structlog

from literacy_events.models import Notification, LiteracyEvent

log = structlog.get_logger("unfold_studio")


def un(request):
"Helper to return username"
return request.user.username if request.user.is_authenticated else "<anonymous>"

def search_users_api(request):
query = request.GET.get('q', '').strip()
if len(query) < 2:
return JsonResponse([], safe=False)

users = User.objects.filter(
Q(username__icontains=query) & Q(is_active=True)
).exclude(id=request.user.id if request.user.is_authenticated else None)[:10]

user_data = [{'username': user.username} for user in users]
return JsonResponse(user_data, safe=False)

class UserDetailView(DetailView):
model = User
slug_field = 'username'
Expand Down
107 changes: 107 additions & 0 deletions unfold_studio/static/base/base_style.css
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,110 @@ form button[type="submit"]:hover {
width: auto;
margin-top: 0;
}

.creators-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}

.creator-card {
display: flex;
align-items: center;
gap: 15px;
padding: 15px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.creator-avatar {
width: 50px;
height: 50px;
border-radius: 8px;
flex-shrink: 0;
}

.creator-info h3 {
margin: 0 0 8px 0;
font-size: 18px;
font-weight: 600;
}

.view-stories-btn {
background: #8b5cf6;
color: white;
padding: 6px 12px;
border-radius: 4px;
text-decoration: none;
font-size: 14px;
border: 1px solid #8b5cf6;
}

.view-stories-btn:hover {
background: #7c3aed;
color: white;
}

.view-stories-btn:visited {
color: white;
}

.user-search-container {
position: relative;
width: 300px;
}

.user-search-container input {
width: 100%;
padding: 10px;
border: 2px solid #bdc3c7;
border-radius: 4px;
font-size: 14px;
}

.search-results {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #bdc3c7;
border-top: none;
border-radius: 0 0 4px 4px;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
display: none;
}

.search-result-item {
padding: 10px;
border-bottom: 1px solid #ecf0f1;
display: flex;
justify-content: space-between;
align-items: center;
}

.search-result-item:hover {
background-color: #f8f9fa;
}

.search-result-item .username {
font-weight: 500;
}

.search-result-item .visit-btn {
background: #3498db;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 12px;
cursor: pointer;
}

.search-result-item .visit-btn:hover {
background: #2980b9;
}
3 changes: 2 additions & 1 deletion unfold_studio/unfold_studio/urls/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
path('users/<slug:slug>/feed/', profile_views.FeedView.as_view(), name="show_feed"),
path('users/<slug:slug>/follow/', profile_views.FollowUserView.as_view(), name="follow_user"),
path('users/<slug:slug>/unfollow/', profile_views.UnfollowUserView.as_view(), name="unfollow_user"),
path('api/search-users/', profile_views.search_users_api, name='search_users_api'),

path('groups/', literacy_group_views.ListGroupsView.as_view(), name="list_groups"),
path('groups/new', literacy_group_views.CreateGroupView.as_view(), name="create_group"),
Expand Down Expand Up @@ -85,4 +86,4 @@
urlpatterns += [
path('__debug__/', include(debug_toolbar.urls)),
path('silk/', include('silk.urls', namespace='silk')),
]
]
Loading