User:Docmoates/Social: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| Line 28: | Line 28: | ||
border-radius: 50%; | border-radius: 50%; | ||
object-fit: cover; | object-fit: cover; | ||
background: # | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
color: white; | |||
font-weight: 600; | |||
font-size: 18px; | |||
flex-shrink: 0; | |||
cursor: pointer; | |||
position: relative; | |||
overflow: hidden; | |||
} | } | ||
.sf-avatar-small { width: 36px; height: 36px; } | .sf-avatar img { | ||
width: 100%; | |||
height: 100%; | |||
object-fit: cover; | |||
border-radius: 50%; | |||
} | |||
.sf-avatar-small { width: 36px; height: 36px; font-size: 14px; } | |||
.sf-avatar-edit { | |||
position: absolute; | |||
bottom: 0; | |||
left: 0; | |||
right: 0; | |||
background: rgba(0,0,0,0.6); | |||
color: white; | |||
font-size: 10px; | |||
padding: 2px; | |||
text-align: center; | |||
opacity: 0; | |||
transition: opacity 0.2s; | |||
} | |||
.sf-avatar:hover .sf-avatar-edit { opacity: 1; } | |||
.sf-composer-input { | .sf-composer-input { | ||
flex: 1; | flex: 1; | ||
| Line 116: | Line 146: | ||
.sf-empty-text { font-size: 18px; font-weight: 500; } | .sf-empty-text { font-size: 18px; font-weight: 500; } | ||
.sf-empty-sub { font-size: 14px; margin-top: 8px; color: #8a8d91; } | .sf-empty-sub { font-size: 14px; margin-top: 8px; color: #8a8d91; } | ||
.sf-stories { display: flex; gap: 12px; padding: 16px; background: white; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 20px; overflow-x: auto; } | .sf-stories { display: flex; gap: 12px; padding: 16px; background: white; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 20px; overflow-x: auto; } | ||
.sf-story { display: flex; flex-direction: column; align-items: center; gap: 8px; cursor: pointer; } | .sf-story { display: flex; flex-direction: column; align-items: center; gap: 8px; cursor: pointer; } | ||
| Line 124: | Line 153: | ||
.sf-create-story { width: 68px; height: 68px; border-radius: 50%; background: #e4e6eb; display: flex; align-items: center; justify-content: center; font-size: 28px; color: #1877f2; cursor: pointer; border: none; } | .sf-create-story { width: 68px; height: 68px; border-radius: 50%; background: #e4e6eb; display: flex; align-items: center; justify-content: center; font-size: 28px; color: #1877f2; cursor: pointer; border: none; } | ||
.sf-create-story:hover { background: #d8dadf; } | .sf-create-story:hover { background: #d8dadf; } | ||
.sf-modal { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 1000; align-items: center; justify-content: center; } | |||
.sf-modal.active { display: flex; } | |||
.sf-modal-content { background: white; border-radius: 12px; padding: 24px; max-width: 400px; width: 90%; } | |||
.sf-modal-title { font-size: 20px; font-weight: 600; margin-bottom: 16px; } | |||
.sf-modal-input { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 8px; font-size: 14px; margin-bottom: 12px; } | |||
.sf-modal-buttons { display: flex; gap: 12px; justify-content: flex-end; } | |||
.sf-modal-btn { padding: 10px 20px; border-radius: 8px; border: none; font-weight: 600; cursor: pointer; } | |||
.sf-modal-btn-cancel { background: #e4e6eb; color: #050505; } | |||
.sf-modal-btn-save { background: #1877f2; color: white; } | |||
</style> | </style> | ||
| Line 135: | Line 173: | ||
<div class="sf-composer"> | <div class="sf-composer"> | ||
<div class="sf-composer-main"> | <div class="sf-composer-main"> | ||
< | <div class="sf-avatar" id="composer-avatar" onclick="openProfileModal()"> | ||
<span id="avatar-initials"></span> | |||
<div class="sf-avatar-edit">Edit</div> | |||
</div> | |||
<textarea class="sf-composer-input" id="post-content" placeholder="What is on your mind?"></textarea> | <textarea class="sf-composer-input" id="post-content" placeholder="What is on your mind?"></textarea> | ||
</div> | </div> | ||
<div class="sf-composer-actions"> | <div class="sf-composer-actions"> | ||
<div class="sf-action-buttons"> | <div class="sf-action-buttons"> | ||
<button class="sf-action-btn" title=" | <button class="sf-action-btn" title="Add Image" onclick="promptImage()">🖼️</button> | ||
<button class="sf-action-btn" title="Emoji">😊</button> | <button class="sf-action-btn" title="Emoji">😊</button> | ||
<button class="sf-action-btn" title="GIF">🎬</button> | <button class="sf-action-btn" title="GIF">🎬</button> | ||
| Line 146: | Line 187: | ||
</div> | </div> | ||
<button class="sf-post-btn" id="post-btn">Post</button> | <button class="sf-post-btn" id="post-btn">Post</button> | ||
</div> | |||
</div> | |||
<div id="image-preview" style="display:none; padding: 0 20px 16px; background: white; margin-top: -20px; border-radius: 0 0 12px 12px;"> | |||
<div style="position: relative; display: inline-block;"> | |||
<img id="preview-img" style="max-width: 100%; max-height: 300px; border-radius: 8px;"> | |||
<button onclick="removeImage()" style="position: absolute; top: 8px; right: 8px; background: rgba(0,0,0,0.6); color: white; border: none; border-radius: 50%; width: 28px; height: 28px; cursor: pointer;">×</button> | |||
</div> | </div> | ||
</div> | </div> | ||
<div id="timeline-container"> | <div id="timeline-container"> | ||
<div class="sf- | <div class="sf-empty"> | ||
<div class="sf-empty-icon">📝</div> | |||
<div class="sf-empty-text">No posts yet</div> | |||
<div class="sf-empty-sub">Be the first to share something!</div> | |||
</div> | |||
</div> | |||
<div class="sf-modal" id="profile-modal"> | |||
<div class="sf-modal-content"> | |||
<div class="sf-modal-title">Set Profile Photo</div> | |||
<input type="text" class="sf-modal-input" id="profile-url-input" placeholder="Enter image URL..."> | |||
<p style="font-size: 12px; color: #65676b; margin: 0 0 12px;">Tip: Upload an image to your wiki, then paste the URL here</p> | |||
<div class="sf-modal-buttons"> | |||
<button class="sf-modal-btn sf-modal-btn-cancel" onclick="closeProfileModal()">Cancel</button> | |||
<button class="sf-modal-btn sf-modal-btn-save" onclick="saveProfilePhoto()">Save</button> | |||
</div> | |||
</div> | |||
</div> | </div> | ||
<script> | <script> | ||
(function() { | (function() { | ||
var currentUser = mw.config.get("wgUserName"); | var currentUser = mw.config.get("wgUserName") || "Guest"; | ||
var profilePhoto = localStorage.getItem("sf_profile_photo_" + currentUser) || ""; | |||
var pendingImageUrl = ""; | |||
function getInitials(name) { | |||
return name.split(/[\s_]+/).map(function(w) { return w[0]; }).join("").substring(0,2).toUpperCase(); | |||
} | } | ||
function setAvatar(container, photo, name) { | |||
if (photo) { | |||
container.innerHTML = "<img src=\"" + photo + "\" onerror=\"this.parentNode.innerHTML= + getInitials(name) + \"><div class=\"sf-avatar-edit\">Edit</div>"; | |||
} else { | |||
container.innerHTML = getInitials(name) + "<div class=\"sf-avatar-edit\">Edit</div>"; | |||
} | |||
} | |||
// Initialize | |||
var composerAvatar = document.getElementById("composer-avatar"); | |||
setAvatar(composerAvatar, profilePhoto, currentUser); | |||
document.getElementById("post-content").placeholder = "What is on your mind, " + currentUser + "?"; | |||
document.getElementById("post-btn").addEventListener("click", createPost); | document.getElementById("post-btn").addEventListener("click", createPost); | ||
loadPosts(); | loadPosts(); | ||
window.openProfileModal = function() { | |||
document.getElementById("profile-modal").classList.add("active"); | |||
document.getElementById("profile-url-input").value = profilePhoto; | |||
}; | |||
window.closeProfileModal = function() { | |||
document.getElementById("profile-modal").classList.remove("active"); | |||
}; | |||
window.saveProfilePhoto = function() { | |||
var url = document.getElementById("profile-url-input").value.trim(); | |||
profilePhoto = url; | |||
localStorage.setItem("sf_profile_photo_" + currentUser, url); | |||
setAvatar(composerAvatar, profilePhoto, currentUser); | |||
closeProfileModal(); | |||
}; | |||
window.promptImage = function() { | |||
var url = prompt("Enter image URL:"); | |||
if (url) { | |||
pendingImageUrl = url; | |||
document.getElementById("preview-img").src = url; | |||
document.getElementById("image-preview").style.display = "block"; | |||
} | |||
}; | |||
window.removeImage = function() { | |||
pendingImageUrl = ""; | |||
document.getElementById("image-preview").style.display = "none"; | |||
}; | |||
function loadPosts() { | function loadPosts() { | ||
| Line 173: | Line 282: | ||
}).done(function(data) { | }).done(function(data) { | ||
renderPosts(data.socialfeed.posts || []); | renderPosts(data.socialfeed.posts || []); | ||
}).fail(function() { | }).fail(function(code, data) { | ||
document.getElementById("timeline-container").innerHTML = "<div class=\"sf-empty\"><div class=\"sf-empty-icon\"> | console.error("API Error:", code, data); | ||
document.getElementById("timeline-container").innerHTML = "<div class=\"sf-empty\"><div class=\"sf-empty-icon\">📝</div><div class=\"sf-empty-text\">No posts yet</div><div class=\"sf-empty-sub\">Be the first to share something!</div></div>"; | |||
}); | }); | ||
} | } | ||
| Line 207: | Line 299: | ||
var totalReactions = 0; | var totalReactions = 0; | ||
for (var k in post.reaction_counts) totalReactions += post.reaction_counts[k]; | for (var k in post.reaction_counts) totalReactions += post.reaction_counts[k]; | ||
var userPhoto = localStorage.getItem("sf_profile_photo_" + post.username) || ""; | |||
var avatarContent = userPhoto ? "<img src=\"" + userPhoto + "\">" : getInitials(post.username); | |||
html += "<div class=\"sf-post\" data-id=\"" + post.id + "\">"; | html += "<div class=\"sf-post\" data-id=\"" + post.id + "\">"; | ||
html += "<div class=\"sf-post-header\">< | html += "<div class=\"sf-post-header\"><div class=\"sf-avatar sf-avatar-small\">" + avatarContent + "</div><div class=\"sf-post-user-info\"><div class=\"sf-post-username\">" + escapeHtml(post.username) + "</div><div class=\"sf-post-meta\">" + timeAgo + " · 🌐</div></div>"; | ||
if (post.username === currentUser) html += "<button class=\"sf-post-menu\" onclick=\"deletePost(" + post.id + ")\">🗑️</button>"; | if (post.username === currentUser) html += "<button class=\"sf-post-menu\" onclick=\"deletePost(" + post.id + ")\">🗑️</button>"; | ||
html += "</div>"; | html += "</div>"; | ||
| Line 216: | Line 311: | ||
if (totalReactions > 0) { | if (totalReactions > 0) { | ||
html += "<div class=\"sf-reaction-icons\">"; | html += "<div class=\"sf-reaction-icons\">"; | ||
if (post.reaction_counts.like) html += "<span class=\"sf-reaction-icon like\">👍</span>"; | if (post.reaction_counts && post.reaction_counts.like) html += "<span class=\"sf-reaction-icon like\">👍</span>"; | ||
if (post.reaction_counts.love) html += "<span class=\"sf-reaction-icon love\">❤️</span>"; | if (post.reaction_counts && post.reaction_counts.love) html += "<span class=\"sf-reaction-icon love\">❤️</span>"; | ||
html += "</div><span>" + totalReactions + "</span>"; | html += "</div><span>" + totalReactions + "</span>"; | ||
} | } | ||
| Line 232: | Line 327: | ||
window.createPost = function() { | window.createPost = function() { | ||
var content = document.getElementById("post-content").value.trim(); | var content = document.getElementById("post-content").value.trim(); | ||
if (!content) return; | if (!content && !pendingImageUrl) return; | ||
document.getElementById("post-btn").disabled = true; | document.getElementById("post-btn").disabled = true; | ||
var params = { | |||
action: "socialfeed", | action: "socialfeed", | ||
sfaction: "createpost", | sfaction: "createpost", | ||
content: content | content: content || "(photo)" | ||
}).done(function() { | }; | ||
if (pendingImageUrl) params.image_url = pendingImageUrl; | |||
new mw.Api().postWithToken("csrf", params).done(function() { | |||
document.getElementById("post-content").value = ""; | document.getElementById("post-content").value = ""; | ||
pendingImageUrl = ""; | |||
document.getElementById("image-preview").style.display = "none"; | |||
document.getElementById("post-btn").disabled = false; | document.getElementById("post-btn").disabled = false; | ||
loadPosts(); | loadPosts(); | ||
}).fail(function(c, d) { | }).fail(function(c, d) { | ||
alert("Error: " + (d.error ? d.error.info : c)); | alert("Error: " + (d && d.error ? d.error.info : c)); | ||
document.getElementById("post-btn").disabled = false; | document.getElementById("post-btn").disabled = false; | ||
}); | }); | ||
| Line 269: | Line 370: | ||
var html = ""; | var html = ""; | ||
comments.forEach(function(cm) { | comments.forEach(function(cm) { | ||
html += "<div class=\"sf-comment\">< | var cPhoto = localStorage.getItem("sf_profile_photo_" + cm.username) || ""; | ||
var cAvatar = cPhoto ? "<img src=\"" + cPhoto + "\">" : getInitials(cm.username); | |||
html += "<div class=\"sf-comment\"><div class=\"sf-avatar sf-avatar-small\">" + cAvatar + "</div><div class=\"sf-comment-bubble\"><div class=\"sf-comment-author\">" + escapeHtml(cm.username) + "</div><div class=\"sf-comment-text\">" + escapeHtml(cm.content) + "</div></div></div>"; | |||
}); | }); | ||
html += "<div class=\"sf-add-comment\">< | var myPhoto = profilePhoto ? "<img src=\"" + profilePhoto + "\">" : getInitials(currentUser); | ||
html += "<div class=\"sf-add-comment\"><div class=\"sf-avatar sf-avatar-small\">" + myPhoto + "</div><input class=\"sf-comment-input\" placeholder=\"Write a comment...\" onkeypress=\"if(event.key===String.fromCharCode(13))addComment(" + id + ",this)\"></div>"; | |||
c.innerHTML = html; | c.innerHTML = html; | ||
}); | }); | ||
Revision as of 21:35, 2 February 2026
Tip: Upload an image to your wiki, then paste the URL here