MediaWiki:Gadget-SocialFeed.js: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| (One intermediate revision by the same user not shown) | |||
| Line 1: | Line 1: | ||
/* SocialFeed | /* SocialFeed v11 - Fixed buttons and full functionality */ | ||
if (mw.config.get("wgPageName").indexOf("User:Docmoates/Social") !== -1) { | if (mw.config.get("wgPageName").indexOf("User:Docmoates/Social") !== -1) { | ||
mw.loader.using(["mediawiki.api", "mediawiki.util"]).then(function() { | mw.loader.using(["mediawiki.api", "mediawiki.util"]).then(function() { | ||
var $=jQuery,api=new mw.Api(),apiUrl=mw.util.wikiScript("api"),user=mw.config.get("wgUserName")||"Guest"; | var $=jQuery,api=new mw.Api(),apiUrl=mw.util.wikiScript("api"),user=mw.config.get("wgUserName")||"Guest"; | ||
var ud={photo:localStorage.getItem("sf_photo_"+user)||"",cover:localStorage.getItem("sf_cover_"+user)||"",bio:localStorage.getItem("sf_bio_"+user)||"",location:localStorage.getItem("sf_location_"+user)||""}; | var ud={photo:localStorage.getItem("sf_photo_"+user)||"",cover:localStorage.getItem("sf_cover_"+user)||"",bio:localStorage.getItem("sf_bio_"+user)||"",location:localStorage.getItem("sf_location_"+user)||""}; | ||
function gi(n){return(n||"?").charAt(0).toUpperCase();} | function gi(n){return(n||"?").charAt(0).toUpperCase();} | ||
function esc(t){var d=document.createElement("div");d.textContent=t;return d.innerHTML;} | function esc(t){var d=document.createElement("div");d.textContent=t;return d.innerHTML;} | ||
function ago(ds){var s=Math.floor((Date.now()-new Date(ds))/1000);if(s<60)return"Just now";var m=Math.floor(s/60);if(m<60)return m+"m";var h=Math.floor(m/60);if(h<24)return h+"h";return Math.floor(h/24)+"d";} | function ago(ds){var s=Math.floor((Date.now()-new Date(ds))/1000);if(s<60)return"Just now";var m=Math.floor(s/60);if(m<60)return m+"m";var h=Math.floor(m/60);if(h<24)return h+"h";return Math.floor(h/24)+"d";} | ||
function av(p,n,s){return p?'<img src="'+esc(p)+'">':'<div class=" | function av(p,n,s){return p?'<img src="'+esc(p)+'">':'<div class="sf-av-ph" style="font-size:'+s+'px">'+gi(n)+'</div>';} | ||
function om(id){$("#"+id).addClass("open");} | function om(id){$("#"+id).addClass("open");} | ||
function cm(id){$("#"+id).removeClass("open");} | function cm(id){$("#"+id).removeClass("open");} | ||
function upload(file,pre | function upload(file,pre){ | ||
return api.getToken("csrf").then(function(tk){ | |||
var fd=new FormData(); | |||
fd.append("action","upload"); | |||
fd.append("filename",pre+"_"+user+"_"+Date.now()+"."+file.name.split(".").pop()); | |||
fd.append("file",file); | |||
fd.append("token",tk); | |||
fd.append("format","json"); | |||
fd.append("ignorewarnings","1"); | |||
return $.ajax({url:apiUrl,type:"POST",data:fd,processData:false,contentType:false}); | |||
}).then(function(r){ | |||
if(r.upload&&r.upload.imageinfo)return r.upload.imageinfo.url; | |||
throw new Error("Upload failed"); | |||
}); | |||
} | |||
setTimeout(function(){$('button,a').filter(function(){return $(this).text().indexOf('Design')!==-1;}).hide();},200); | setTimeout(function(){$('button,a').filter(function(){return $(this).text().indexOf('Design')!==-1;}).hide();},200); | ||
var css='[class*="wikibox"]{display:none!important}'+ | var css=''; | ||
'#social-app{font-family:Segoe UI,Helvetica,Arial,sans-serif;background:#f0f2f5;min-height:100vh;margin:-20px;padding:0}'+ | css+='[class*="wikibox"]{display:none!important}'; | ||
'#social-app *{box-sizing:border-box | css+='#social-app{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:#f0f2f5;min-height:100vh;margin:-20px;padding:0}'; | ||
'. | css+='#social-app *{box-sizing:border-box}'; | ||
css+='.sf-cover-wrap{max-width:940px;margin:0 auto;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,0.1)}'; | |||
'. | css+='.sf-cover{height:350px;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);border-radius:0 0 8px 8px;position:relative;overflow:hidden}'; | ||
'. | css+='.sf-cover img{width:100%;height:100%;object-fit:cover}'; | ||
'. | css+='.sf-cover-btn{position:absolute;bottom:16px;right:16px;background:rgba(0,0,0,0.6);color:#fff;border:none;padding:8px 12px;border-radius:6px;font-weight:600;font-size:14px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}'; | ||
'. | css+='.sf-cover-btn:hover{background:rgba(0,0,0,0.8)}'; | ||
'. | css+='.sf-cover-btn svg{width:16px;height:16px;fill:currentColor}'; | ||
'. | css+='.sf-header{display:flex;align-items:flex-end;padding:0 32px 16px;margin-top:-32px;position:relative}'; | ||
'. | css+='.sf-pic-wrap{position:relative;margin-right:24px}'; | ||
'. | css+='.sf-pic{width:168px;height:168px;border-radius:50%;border:4px solid #fff;overflow:hidden;background:#fff;box-shadow:0 2px 8px rgba(0,0,0,0.15);cursor:pointer}'; | ||
'. | css+='.sf-pic img{width:100%;height:100%;object-fit:cover}'; | ||
'. | css+='.sf-av-ph{width:100%;height:100%;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700}'; | ||
'. | css+='.sf-cam{position:absolute;bottom:12px;right:12px;width:36px;height:36px;border-radius:50%;background:#e4e6eb;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px rgba(0,0,0,0.1)}'; | ||
'. | css+='.sf-cam:hover{background:#d8dadf}'; | ||
'. | css+='.sf-cam svg{width:20px;height:20px;fill:#050505}'; | ||
'. | css+='.sf-info{flex:1;padding-bottom:8px}'; | ||
'. | css+='.sf-name{font-size:32px;font-weight:700;color:#050505;margin:0 0 4px 0}'; | ||
'. | css+='.sf-stats{font-size:15px;color:#65676b}'; | ||
css+='.sf-actions{display:flex;gap:8px;padding-bottom:8px;margin-left:auto}'; | |||
'. | css+='.sf-btn{padding:8px 16px;border-radius:6px;font-weight:600;font-size:15px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;border:none}'; | ||
'. | css+='.sf-btn-primary{background:#1877f2;color:#fff}'; | ||
'. | css+='.sf-btn-primary:hover{background:#166fe5}'; | ||
css+='.sf-btn-secondary{background:#e4e6eb;color:#050505}'; | |||
'. | css+='.sf-btn-secondary:hover{background:#d8dadf}'; | ||
css+='.sf-nav{border-top:1px solid #ddd}'; | |||
css+='.sf-tabs{display:flex;max-width:940px;margin:0 auto;padding:4px 32px 0}'; | |||
'. | css+='.sf-tab{padding:16px 16px;font-size:15px;font-weight:600;color:#65676b;cursor:pointer;border-bottom:3px solid transparent;margin-right:4px;text-decoration:none}'; | ||
'. | css+='.sf-tab:hover{background:#f0f2f5;border-radius:8px 8px 0 0}'; | ||
'. | css+='.sf-tab.active{color:#1877f2;border-bottom-color:#1877f2}'; | ||
'. | css+='.sf-main{max-width:908px;margin:16px auto;display:flex;gap:16px;padding:0 16px}'; | ||
'. | css+='.sf-sidebar{width:360px;flex-shrink:0}'; | ||
'. | css+='.sf-feed{flex:1;min-width:0}'; | ||
css+='.sf-card{background:#fff;border-radius:8px;box-shadow:0 1px 2px rgba(0,0,0,0.1);margin-bottom:16px;overflow:hidden}'; | |||
'. | css+='.sf-card-title{padding:16px;font-size:20px;font-weight:700;color:#050505}'; | ||
css+='.sf-intro-item{display:flex;align-items:center;gap:12px;padding:12px 16px;font-size:15px;color:#050505}'; | |||
'. | css+='.sf-intro-item svg{width:20px;height:20px;fill:#65676b;flex-shrink:0}'; | ||
'. | css+='.sf-intro-btn{display:block;width:calc(100% - 32px);margin:8px 16px 16px;padding:8px 12px;background:#e4e6eb;border:none;border-radius:6px;font-weight:600;font-size:15px;color:#050505;cursor:pointer;text-align:center}'; | ||
css+='.sf-intro-btn:hover{background:#d8dadf}'; | |||
'. | css+='.sf-composer{padding:12px 16px}'; | ||
'. | css+='.sf-composer-row{display:flex;gap:8px;align-items:center}'; | ||
css+='.sf-composer-av{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}'; | |||
'. | css+='.sf-composer-av img{width:100%;height:100%;object-fit:cover}'; | ||
'. | css+='.sf-composer-input{flex:1;background:#f0f2f5;border:none;border-radius:20px;padding:8px 16px;font-size:17px;color:#65676b;cursor:pointer;text-align:left}'; | ||
'. | css+='.sf-composer-input:hover{background:#e4e6eb}'; | ||
'. | css+='.sf-composer-btns{display:flex;border-top:1px solid #e4e6eb;margin-top:12px;padding-top:8px}'; | ||
'. | css+='.sf-composer-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:8px;padding:8px;border:none;background:none;border-radius:8px;font-size:14px;font-weight:600;color:#65676b;cursor:pointer}'; | ||
'. | css+='.sf-composer-btn:hover{background:#f0f2f5}'; | ||
'. | css+='.sf-composer-btn svg{width:24px;height:24px}'; | ||
'. | css+='.sf-post{border-top:8px solid #f0f2f5}'; | ||
'. | css+='.sf-post:first-child{border-top:none}'; | ||
'. | css+='.sf-post-head{display:flex;gap:8px;padding:12px 16px}'; | ||
css+='.sf-post-av{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}'; | |||
'. | css+='.sf-post-av img{width:100%;height:100%;object-fit:cover}'; | ||
css+='.sf-post-meta{flex:1}'; | |||
css+='.sf-post-name{font-weight:600;font-size:15px;color:#050505}'; | |||
'. | css+='.sf-post-time{font-size:13px;color:#65676b}'; | ||
css+='.sf-post-del{width:32px;height:32px;border-radius:50%;background:none;border:none;cursor:pointer;font-size:20px;color:#65676b;display:flex;align-items:center;justify-content:center}'; | |||
'. | css+='.sf-post-del:hover{background:#f0f2f5}'; | ||
'. | css+='.sf-post-text{padding:0 16px 12px;font-size:15px;line-height:1.4;color:#050505;white-space:pre-wrap}'; | ||
'. | css+='.sf-post-img{width:100%}'; | ||
'. | css+='.sf-post-img img{width:100%;display:block}'; | ||
'. | css+='.sf-post-stats{display:flex;justify-content:space-between;padding:10px 16px;font-size:15px;color:#65676b}'; | ||
'. | css+='.sf-post-stats span{cursor:pointer}'; | ||
css+='.sf-post-stats span:hover{text-decoration:underline}'; | |||
css+='.sf-post-bar{display:flex;border-top:1px solid #e4e6eb;margin:0 16px;padding:4px 0}'; | |||
'. | css+='.sf-post-act{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:8px;border:none;background:none;border-radius:4px;font-size:15px;font-weight:600;color:#65676b;cursor:pointer}'; | ||
'. | css+='.sf-post-act:hover{background:#f0f2f5}'; | ||
'. | css+='.sf-post-act.liked{color:#1877f2}'; | ||
'. | css+='.sf-post-act svg{width:20px;height:20px;fill:currentColor}'; | ||
'. | css+='.sf-comments{padding:8px 16px;display:none}'; | ||
'. | css+='.sf-comments.show{display:block}'; | ||
'. | css+='.sf-cmt{display:flex;gap:8px;margin-bottom:8px}'; | ||
'. | css+='.sf-cmt-av{width:32px;height:32px;border-radius:50%;overflow:hidden;flex-shrink:0}'; | ||
'. | css+='.sf-cmt-av img{width:100%;height:100%;object-fit:cover}'; | ||
'. | css+='.sf-cmt-bubble{background:#f0f2f5;padding:8px 12px;border-radius:18px;max-width:calc(100% - 48px)}'; | ||
'. | css+='.sf-cmt-name{font-weight:600;font-size:13px;color:#050505}'; | ||
'. | css+='.sf-cmt-text{font-size:15px;color:#050505}'; | ||
'. | css+='.sf-cmt-form{display:flex;gap:8px;padding:8px 16px;border-top:1px solid #e4e6eb}'; | ||
css+='.sf-cmt-input{flex:1;background:#f0f2f5;border:none;border-radius:20px;padding:8px 12px;font-size:15px;outline:none}'; | |||
'. | css+='.sf-empty{padding:48px 24px;text-align:center}'; | ||
'. | css+='.sf-empty svg{width:64px;height:64px;fill:#bcc0c4;margin-bottom:16px}'; | ||
css+='.sf-empty-title{font-size:20px;font-weight:700;color:#050505;margin-bottom:8px}'; | |||
'. | css+='.sf-empty-text{font-size:15px;color:#65676b}'; | ||
css+='.sf-modal{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(255,255,255,0.9);z-index:9999;align-items:center;justify-content:center}'; | |||
'. | css+='.sf-modal.open{display:flex}'; | ||
css+='.sf-modal-box{background:#fff;border-radius:8px;box-shadow:0 2px 20px rgba(0,0,0,0.2);width:500px;max-width:95vw;max-height:90vh;overflow:auto}'; | |||
'. | css+='.sf-modal-head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #e4e6eb}'; | ||
'. | css+='.sf-modal-title{font-size:20px;font-weight:700;margin:0}'; | ||
'. | css+='.sf-modal-x{width:36px;height:36px;border-radius:50%;background:#e4e6eb;border:none;font-size:24px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1}'; | ||
'. | css+='.sf-modal-x:hover{background:#d8dadf}'; | ||
css+='.sf-modal-body{padding:16px}'; | |||
'. | css+='.sf-textarea{width:100%;min-height:100px;border:none;font-size:18px;resize:none;outline:none;font-family:inherit}'; | ||
'. | css+='.sf-modal-foot{padding:16px;border-top:1px solid #e4e6eb}'; | ||
'. | css+='.sf-modal-submit{width:100%;padding:10px;background:#1877f2;color:#fff;border:none;border-radius:6px;font-size:15px;font-weight:600;cursor:pointer}'; | ||
'. | css+='.sf-modal-submit:disabled{background:#e4e6eb;color:#bcc0c4;cursor:not-allowed}'; | ||
'. | css+='.sf-modal-submit:not(:disabled):hover{background:#166fe5}'; | ||
'. | css+='.sf-upload{border:2px dashed #ddd;border-radius:8px;padding:24px;text-align:center;cursor:pointer;margin-top:12px;background:#f7f8fa}'; | ||
'. | css+='.sf-upload:hover{background:#ebedf0;border-color:#1877f2}'; | ||
'. | css+='.sf-upload input{display:none}'; | ||
'. | css+='.sf-upload svg{width:40px;height:40px;fill:#65676b;margin-bottom:8px}'; | ||
css+='.sf-upload-text{font-weight:600;color:#1877f2}'; | |||
'. | css+='.sf-upload-hint{font-size:12px;color:#65676b;margin-top:4px}'; | ||
css+='.sf-preview{width:100%;border-radius:8px;margin-top:12px;display:none}'; | |||
css+='.sf-preview.show{display:block}'; | |||
css+='.sf-field{margin-bottom:16px}'; | |||
css+='.sf-field label{display:block;font-weight:600;margin-bottom:8px;color:#050505}'; | |||
css+='.sf-field input,.sf-field textarea{width:100%;padding:10px 12px;border:1px solid #ddd;border-radius:6px;font-size:15px;font-family:inherit}'; | |||
css+='.sf-field input:focus,.sf-field textarea:focus{outline:none;border-color:#1877f2}'; | |||
css+='.sf-toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:#323232;color:#fff;padding:12px 24px;border-radius:8px;font-size:14px;z-index:10000;display:none}'; | |||
css+='.sf-toast.show{display:block}'; | |||
css+='@media(max-width:900px){.sf-sidebar{display:none}.sf-main{padding:0 8px}.sf-header{flex-direction:column;align-items:center;text-align:center;padding:0 16px 16px}.sf-pic{width:128px;height:128px}.sf-actions{margin:16px 0 0}}'; | |||
$("<style>").text(css).appendTo("head"); | $("<style>").text(css).appendTo("head"); | ||
var | var icons={ | ||
camera:'<svg viewBox="0 0 24 24"><path d="M12 15.2a3.2 3.2 0 100-6.4 3.2 3.2 0 000 6.4z"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></svg>', | |||
h+='<div class=" | home:'<svg viewBox="0 0 24 24"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>', | ||
h+='<div class=" | pin:'<svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>', | ||
h+='<div class=" | video:'<svg viewBox="0 0 24 24" fill="#f3425f"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>', | ||
h+='<div class=" | photo:'<svg viewBox="0 0 24 24" fill="#45bd62"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>', | ||
smile:'<svg viewBox="0 0 24 24" fill="#f7b928"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></svg>', | |||
h+='<div class=" | like:'<svg viewBox="0 0 24 24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>', | ||
comment:'<svg viewBox="0 0 24 24"><path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18z"/></svg>', | |||
share:'<svg viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/></svg>', | |||
images:'<svg viewBox="0 0 24 24"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></svg>' | |||
}; | |||
function toast(msg){ | |||
var $t=$("#sf-toast"); | |||
if(!$t.length)$t=$('<div id="sf-toast" class="sf-toast"></div>').appendTo("body"); | |||
$t.text(msg).addClass("show"); | |||
setTimeout(function(){$t.removeClass("show");},3000); | |||
} | |||
var h='<div class="sf-cover-wrap">'; | |||
h+='<div class="sf-cover" id="cover-area">'+(ud.cover?'<img src="'+esc(ud.cover)+'">':'')+'<button class="sf-cover-btn" id="edit-cover-btn">'+icons.camera+' Edit cover photo</button></div>'; | |||
h+='<div class="sf-header">'; | |||
h+='<div class="sf-pic-wrap"><div class="sf-pic" id="profile-pic">'+av(ud.photo,user,64)+'</div><button class="sf-cam" id="edit-pic-btn">'+icons.camera+'</button></div>'; | |||
h+='<div class="sf-info"><h1 class="sf-name">'+esc(user.replace(/_/g," "))+'</h1><div class="sf-stats"><span id="post-count">0</span> posts · <span id="follower-count">0</span> followers</div></div>'; | |||
h+='<div class="sf-actions"><button class="sf-btn sf-btn-primary" id="edit-profile-btn">Edit profile</button></div>'; | |||
h+='</div>'; | h+='</div>'; | ||
h+='<div class=" | h+='<div class="sf-nav"><div class="sf-tabs">'; | ||
h+='<a class="sf-tab active" href="#" data-tab="posts">Posts</a>'; | |||
h+='<a class="sf-tab" href="#" data-tab="about">About</a>'; | |||
h+='<a class="sf-tab" href="#" data-tab="photos">Photos</a>'; | |||
h+='</div></div>'; | h+='</div></div>'; | ||
h+='</div>'; | |||
h+='<div class=" | h+='<div class="sf-main">'; | ||
h+='<div class=" | h+='<div class="sf-sidebar">'; | ||
h+='<div class=" | h+='<div class="sf-card"><div class="sf-card-title">Intro</div>'; | ||
h+='<div id="intro- | h+='<div id="intro-bio"></div>'; | ||
h+='<button class="sf-intro-btn" id="add-bio-btn">'+(ud.bio?'Edit bio':'Add bio')+'</button>'; | |||
h+='<div class="sf-intro-item">'+icons.home+'<span>Lives in <strong id="location-display">'+(ud.location||'Add hometown')+'</strong></span></div>'; | |||
h+='<button class=" | h+='<div class="sf-intro-item">'+icons.pin+'<span>From <strong>Earth</strong></span></div>'; | ||
h+='<div class=" | h+='<button class="sf-intro-btn" id="edit-details-btn">Edit details</button>'; | ||
h+='<div class=" | |||
h+='<button class=" | |||
h+='</div></div>'; | h+='</div></div>'; | ||
h+='<div class=" | h+='<div class="sf-feed">'; | ||
h+='<div class=" | h+='<div class="sf-card">'; | ||
h+='<div class=" | h+='<div class="sf-composer">'; | ||
h+='<button class=" | h+='<div class="sf-composer-row"><div class="sf-composer-av">'+av(ud.photo,user,16)+'</div>'; | ||
h+='<div class=" | h+='<button class="sf-composer-input" id="open-composer">What\'s on your mind, '+esc(user.split(/[\s_]/)[0])+'?</button></div>'; | ||
h+='<button class=" | h+='<div class="sf-composer-btns">'; | ||
h+='<button class=" | h+='<button class="sf-composer-btn" id="btn-live">'+icons.video+' Live video</button>'; | ||
h+='<button class=" | h+='<button class="sf-composer-btn" id="btn-photo">'+icons.photo+' Photo/video</button>'; | ||
h+='</div></div>'; | h+='<button class="sf-composer-btn" id="btn-feeling">'+icons.smile+' Feeling/activity</button>'; | ||
h+='</div></div></div>'; | |||
h+='<div id="feed"></div>'; | h+='<div id="feed"></div>'; | ||
h+='</div></div>'; | h+='</div></div>'; | ||
h+='<div class=" | // Create Post Modal | ||
h+='<div class=" | h+='<div class="sf-modal" id="post-modal"><div class="sf-modal-box">'; | ||
h+='<div class=" | h+='<div class="sf-modal-head"><h2 class="sf-modal-title">Create post</h2><button class="sf-modal-x" data-close="post-modal">×</button></div>'; | ||
h+='<div style="display:flex;gap:12px;margin-bottom:16px"><div class=" | h+='<div class="sf-modal-body">'; | ||
h+='<textarea class=" | h+='<div style="display:flex;gap:12px;margin-bottom:16px"><div class="sf-composer-av">'+av(ud.photo,user,16)+'</div><div><strong>'+esc(user.replace(/_/g," "))+'</strong><div style="font-size:12px;color:#65676b">Public</div></div></div>'; | ||
h+='<img class=" | h+='<textarea class="sf-textarea" id="post-text" placeholder="What\'s on your mind, '+esc(user.split(/[\s_]/)[0])+'?"></textarea>'; | ||
h+='<div class=" | h+='<img class="sf-preview" id="post-preview">'; | ||
h+='<div class="sf-upload" id="post-upload"><input type="file" accept="image/*">'+icons.images+'<div class="sf-upload-text">Add photos/videos</div><div class="sf-upload-hint">or drag and drop</div></div>'; | |||
h+='</div>'; | h+='</div>'; | ||
h+='<div class=" | h+='<div class="sf-modal-foot"><button class="sf-modal-submit" id="submit-post" disabled>Post</button></div>'; | ||
h+='</div></div>'; | h+='</div></div>'; | ||
h+='<div class=" | // Edit Profile Modal | ||
h+='<div class=" | h+='<div class="sf-modal" id="profile-modal"><div class="sf-modal-box">'; | ||
h+='<div class=" | h+='<div class="sf-modal-head"><h2 class="sf-modal-title">Edit profile</h2><button class="sf-modal-x" data-close="profile-modal">×</button></div>'; | ||
h+='<div | h+='<div class="sf-modal-body">'; | ||
h+='<div class="sf-field"><label>Profile photo</label><div class="sf-upload" id="pic-upload"><input type="file" accept="image/*">'+icons.camera+'<div class="sf-upload-text">Upload new photo</div></div><img class="sf-preview" id="pic-preview"'+(ud.photo?' src="'+esc(ud.photo)+'" style="display:block"':'')+'></div>'; | |||
h+='<div | h+='<div class="sf-field"><label>Cover photo</label><div class="sf-upload" id="cover-upload"><input type="file" accept="image/*">'+icons.images+'<div class="sf-upload-text">Upload new cover</div></div><img class="sf-preview" id="cover-preview"'+(ud.cover?' src="'+esc(ud.cover)+'" style="display:block"':'')+'></div>'; | ||
h+='<div | h+='<div class="sf-field"><label>Bio</label><textarea id="bio-input" rows="3" placeholder="Tell people about yourself...">'+esc(ud.bio)+'</textarea></div>'; | ||
h+='<div class="sf-field"><label>Current city</label><input type="text" id="location-input" value="'+esc(ud.location)+'" placeholder="Where do you live?"></div>'; | |||
h+='</div>'; | h+='</div>'; | ||
h+='<div class=" | h+='<div class="sf-modal-foot"><button class="sf-modal-submit" id="save-profile">Save changes</button></div>'; | ||
h+='</div></div>'; | h+='</div></div>'; | ||
$("#social-app").html(h); | $("#social-app").html(h); | ||
// Update bio display | |||
if(ud.bio)$("#intro-bio").html('<div style="text-align:center;padding:12px 16px;color:#050505">'+esc(ud.bio)+'</div>'); | |||
var $app=$("#social-app"); | var $app=$("#social-app"); | ||
// Modal close handlers | |||
$app.on("click","[data-close]",function(){cm($(this).data("close"));}); | $app.on("click","[data-close]",function(){cm($(this).data("close"));}); | ||
$app.on("click",". | $app.on("click",".sf-modal",function(e){if($(e.target).hasClass("sf-modal"))cm(this.id);}); | ||
// Tab switching | |||
$app.on("click",".sf-tab",function(e){ | |||
e.preventDefault(); | |||
$(".sf-tab").removeClass("active"); | |||
$(this).addClass("active"); | |||
}); | |||
// Open create post modal | |||
$("#open-composer,#btn-photo").on("click",function(){om("post-modal");$("#post-text").focus();}); | |||
$("#btn-live,#btn-feeling").on("click",function(){toast("This feature is coming soon!");}); | |||
// Post file upload | |||
var postFile=null; | var postFile=null; | ||
$("# | $("#post-upload").on("click",function(){$(this).find("input").click();}); | ||
$("#post-upload input").on("change",function(){ | |||
$("# | if(this.files&&this.files[0]){ | ||
$("#post-text").on("input", | postFile=this.files[0]; | ||
function | var reader=new FileReader(); | ||
reader.onload=function(e){$("#post-preview").attr("src",e.target.result).addClass("show");updatePostBtn();}; | |||
reader.readAsDataURL(this.files[0]); | |||
} | |||
}); | |||
$("#post-text").on("input",updatePostBtn); | |||
function updatePostBtn(){$("#submit-post").prop("disabled",!$("#post-text").val().trim()&&!postFile);} | |||
// Submit post | |||
$("#submit-post").on("click",function(){ | $("#submit-post").on("click",function(){ | ||
var | var text=$("#post-text").val().trim(); | ||
if(! | if(!text&&!postFile){toast("Please write something or add a photo");return;} | ||
var $ | var $btn=$(this).prop("disabled",true).text("Posting..."); | ||
function | |||
var | function createPost(imageUrl){ | ||
if( | var params={action:"socialfeed",sfaction:"createpost",content:text||"(photo)"}; | ||
api.postWithToken("csrf", | if(imageUrl)params.image_url=imageUrl; | ||
cm(" | api.postWithToken("csrf",params).then(function(){ | ||
cm("post-modal"); | |||
$("#post-text").val(""); | |||
postFile=null; | |||
$("#post-preview").removeClass("show").attr("src",""); | |||
$btn.prop("disabled",true).text("Post"); | |||
toast("Post created!"); | |||
loadFeed(); | |||
}).catch(function(e){ | |||
toast("Error creating post: "+e); | |||
$btn.prop("disabled",false).text("Post"); | |||
}); | |||
} | |||
if(postFile){ | |||
upload(postFile,"Post").then(createPost).catch(function(e){ | |||
toast("Error uploading image: "+e); | |||
$btn.prop("disabled",false).text("Post"); | |||
}); | }); | ||
}else{ | |||
createPost(); | |||
} | } | ||
}); | }); | ||
// Open edit profile modal | |||
$("#edit-profile-btn,#edit-pic-btn,#edit-cover-btn,#add-bio-btn,#edit-details-btn").on("click",function(){ | |||
om("profile-modal"); | |||
}); | |||
// Profile photo upload | |||
var picFile=null,coverFile=null; | var picFile=null,coverFile=null; | ||
$("#pic-upload").on("click",function(){$(this).find("input").click();}); | $("#pic-upload").on("click",function(){$(this).find("input").click();}); | ||
$("#pic-upload input").on("change",function(){if(this.files[0]){picFile=this.files[0];var | $("#pic-upload input").on("change",function(){ | ||
if(this.files&&this.files[0]){ | |||
picFile=this.files[0]; | |||
var reader=new FileReader(); | |||
reader.onload=function(e){$("#pic-preview").attr("src",e.target.result).addClass("show");}; | |||
reader.readAsDataURL(this.files[0]); | |||
} | |||
}); | |||
// Cover photo upload | |||
$("#cover-upload").on("click",function(){$(this).find("input").click();}); | $("#cover-upload").on("click",function(){$(this).find("input").click();}); | ||
$("#cover-upload input").on("change",function(){if(this.files[0]){coverFile=this.files[0];var | $("#cover-upload input").on("change",function(){ | ||
if(this.files&&this.files[0]){ | |||
coverFile=this.files[0]; | |||
var reader=new FileReader(); | |||
reader.onload=function(e){$("#cover-preview").attr("src",e.target.result).addClass("show");}; | |||
reader.readAsDataURL(this.files[0]); | |||
} | |||
}); | |||
// Save profile | |||
$("#save-profile").on("click",function(){ | $("#save-profile").on("click",function(){ | ||
var $ | var $btn=$(this).prop("disabled",true).text("Saving..."); | ||
var newBio=$("#bio-input").val().trim(); | |||
var newLocation=$("#location-input").val().trim(); | |||
var uploads=[]; | |||
var | if(picFile)uploads.push(upload(picFile,"ProfilePic").then(function(url){ud.photo=url;localStorage.setItem("sf_photo_"+user,url);})); | ||
if(picFile) | if(coverFile)uploads.push(upload(coverFile,"Cover").then(function(url){ud.cover=url;localStorage.setItem("sf_cover_"+user,url);})); | ||
if(coverFile) | |||
$.when.apply($, | $.when.apply($,uploads).then(function(){ | ||
ud.bio=newBio; | |||
if(ud.cover)$("#cover-area").html('<img src="'+ud.cover+'"><button class=" | ud.location=newLocation; | ||
$("#intro- | localStorage.setItem("sf_bio_"+user,newBio); | ||
$("#add-bio").text( | localStorage.setItem("sf_location_"+user,newLocation); | ||
cm("profile-modal");$ | |||
// Update UI | |||
$("#profile-pic").html(av(ud.photo,user,64)); | |||
$(".sf-composer-av").html(av(ud.photo,user,16)); | |||
if(ud.cover){ | |||
$("#cover-area").html('<img src="'+esc(ud.cover)+'"><button class="sf-cover-btn" id="edit-cover-btn">'+icons.camera+' Edit cover photo</button>'); | |||
} | |||
$("#intro-bio").html(newBio?'<div style="text-align:center;padding:12px 16px;color:#050505">'+esc(newBio)+'</div>':''); | |||
$("#add-bio-btn").text(newBio?'Edit bio':'Add bio'); | |||
$("#location-display").text(newLocation||'Add hometown'); | |||
cm("profile-modal"); | |||
$btn.prop("disabled",false).text("Save changes"); | |||
picFile=null; | |||
coverFile=null; | |||
toast("Profile updated!"); | |||
}).fail(function(e){ | |||
toast("Error saving profile: "+e); | |||
$btn.prop("disabled",false).text("Save changes"); | |||
}); | }); | ||
}); | }); | ||
$app.on("click",". | // Like post | ||
$app.on("click",". | $app.on("click",".sf-post-act[data-like]",function(){ | ||
$app.on("click",". | var $btn=$(this); | ||
$app.on("keypress",". | var postId=$btn.data("like"); | ||
api.postWithToken("csrf",{action:"socialfeed",sfaction:"react",post_id:postId,reaction_type:"like"}).then(function(){ | |||
loadFeed(); | |||
}).catch(function(e){ | |||
toast("Error: "+e); | |||
}); | |||
}); | |||
// Delete post | |||
$app.on("click",".sf-post-del[data-del]",function(){ | |||
if(confirm("Delete this post?")){ | |||
var postId=$(this).data("del"); | |||
api.postWithToken("csrf",{action:"socialfeed",sfaction:"deletepost",post_id:postId}).then(function(){ | |||
toast("Post deleted"); | |||
loadFeed(); | |||
}).catch(function(e){ | |||
toast("Error: "+e); | |||
}); | |||
} | |||
}); | |||
// Toggle comments | |||
$app.on("click",".sf-view-comments",function(){ | |||
var id=$(this).data("id"); | |||
var $comments=$("#comments-"+id); | |||
$comments.toggleClass("show"); | |||
if($comments.hasClass("show"))loadComments(id); | |||
}); | |||
// Submit comment | |||
$app.on("keypress",".sf-cmt-input",function(e){ | |||
if(e.which===13){ | |||
var $input=$(this); | |||
var text=$input.val().trim(); | |||
var postId=$input.data("id"); | |||
if(text){ | |||
$input.val("").prop("disabled",true); | |||
api.postWithToken("csrf",{action:"socialfeed",sfaction:"comment",post_id:postId,content:text}).then(function(){ | |||
$input.prop("disabled",false); | |||
loadComments(postId); | |||
loadFeed(); | |||
}).catch(function(e){ | |||
$input.prop("disabled",false); | |||
toast("Error: "+e); | |||
}); | |||
} | |||
} | |||
}); | |||
// Share post | |||
$app.on("click",".sf-post-act.share-btn",function(){ | |||
var url=window.location.href; | |||
if(navigator.clipboard){ | |||
navigator.clipboard.writeText(url).then(function(){toast("Link copied!");}); | |||
}else{ | |||
toast("Share: "+url); | |||
} | |||
}); | |||
function loadFeed(){ | function loadFeed(){ | ||
api.get({action:"socialfeed",sfaction:"getposts",limit:20}).then(function(r){ | api.get({action:"socialfeed",sfaction:"getposts",limit:20}).then(function(r){ | ||
var posts=(r.socialfeed&&r.socialfeed.posts)||[]; | var posts=(r.socialfeed&&r.socialfeed.posts)||[]; | ||
if(!posts.length){$("#feed").html('<div class=" | $("#post-count").text(posts.length); | ||
var | if(!posts.length){ | ||
$("#feed").html('<div class="sf-card sf-empty">'+icons.images+'<div class="sf-empty-title">No posts yet</div><div class="sf-empty-text">When you share photos, they will appear here.</div></div>'); | |||
return; | |||
} | |||
var html=posts.map(function(p){ | |||
var pic=localStorage.getItem("sf_photo_"+p.username)||""; | var pic=localStorage.getItem("sf_photo_"+p.username)||""; | ||
var likes=0;for(var k in p.reaction_counts)likes+=p.reaction_counts[k]; | var likes=0; | ||
var media=p.image_url?'<div class=" | for(var k in p.reaction_counts)likes+=p.reaction_counts[k]; | ||
var | var isLiked=p.user_reaction==="like"; | ||
var | var media=p.image_url?'<div class="sf-post-img"><img src="'+esc(p.image_url)+'"></div>':""; | ||
var delBtn=p.username===user?'<button class="sf-post-del" data-del="'+p.id+'" title="Delete">×</button>':""; | |||
var out='<div class="sf-card sf-post">'; | |||
out+='<div class="sf-post-head"><div class="sf-post-av">'+av(pic,p.username,16)+'</div><div class="sf-post-meta"><div class="sf-post-name">'+esc(p.username.replace(/_/g," "))+'</div><div class="sf-post-time">'+ago(p.created)+'</div></div>'+delBtn+'</div>'; | |||
if(p.content&&p.content!=="(photo)")out+='<div class="sf-post-text">'+esc(p.content)+'</div>'; | |||
out+=media; | |||
out+='<div class="sf-post-stats"><span>'+likes+' like'+(likes!==1?'s':'')+'</span><span class="sf-view-comments" data-id="'+p.id+'">'+p.comments+' comment'+(p.comments!==1?'s':'')+'</span></div>'; | |||
out+='<div class="sf-post-bar">'; | |||
out+='<button class="sf-post-act'+(isLiked?' liked':'')+'" data-like="'+p.id+'">'+icons.like+' Like</button>'; | |||
out+='<button class="sf-post-act sf-view-comments" data-id="'+p.id+'">'+icons.comment+' Comment</button>'; | |||
out+='<button class="sf-post-act share-btn">'+icons.share+' Share</button>'; | |||
out+='</div>'; | |||
out+='<div class="sf-comments" id="comments-'+p.id+'"></div>'; | |||
out+='<div class="sf-cmt-form"><div class="sf-cmt-av">'+av(ud.photo,user,12)+'</div><input class="sf-cmt-input" data-id="'+p.id+'" placeholder="Write a comment..."></div>'; | |||
out+='</div>'; | |||
return out; | |||
}).join(""); | }).join(""); | ||
$("#feed").html( | |||
$("#feed").html(html); | |||
}).catch(function(e){ | |||
$("#feed").html('<div class="sf-card sf-empty"><div class="sf-empty-title">Error loading posts</div><div class="sf-empty-text">'+esc(String(e))+'</div></div>'); | |||
}); | }); | ||
} | } | ||
function loadComments( | function loadComments(postId){ | ||
api.get({action:"socialfeed",sfaction:"getcomments",post_id: | api.get({action:"socialfeed",sfaction:"getcomments",post_id:postId}).then(function(r){ | ||
var | var comments=(r.socialfeed&&r.socialfeed.comments)||[]; | ||
var | var html=comments.length?comments.map(function(c){ | ||
var pic=localStorage.getItem("sf_photo_"+c.username)||""; | var pic=localStorage.getItem("sf_photo_"+c.username)||""; | ||
return '<div class=" | return '<div class="sf-cmt"><div class="sf-cmt-av">'+av(pic,c.username,12)+'</div><div class="sf-cmt-bubble"><div class="sf-cmt-name">'+esc(c.username.replace(/_/g," "))+'</div><div class="sf-cmt-text">'+esc(c.content)+'</div></div></div>'; | ||
}).join("") | }).join(""):'<div style="padding:8px;color:#65676b;text-align:center">No comments yet. Be the first!</div>'; | ||
$("#comments-"+postId).html(html); | |||
}); | }); | ||
} | } | ||
// Initial load | |||
loadFeed(); | loadFeed(); | ||
}); | }); | ||
} | } | ||
Latest revision as of 15:30, 4 February 2026
/* SocialFeed v11 - Fixed buttons and full functionality */
if (mw.config.get("wgPageName").indexOf("User:Docmoates/Social") !== -1) {
mw.loader.using(["mediawiki.api", "mediawiki.util"]).then(function() {
var $=jQuery,api=new mw.Api(),apiUrl=mw.util.wikiScript("api"),user=mw.config.get("wgUserName")||"Guest";
var ud={photo:localStorage.getItem("sf_photo_"+user)||"",cover:localStorage.getItem("sf_cover_"+user)||"",bio:localStorage.getItem("sf_bio_"+user)||"",location:localStorage.getItem("sf_location_"+user)||""};
function gi(n){return(n||"?").charAt(0).toUpperCase();}
function esc(t){var d=document.createElement("div");d.textContent=t;return d.innerHTML;}
function ago(ds){var s=Math.floor((Date.now()-new Date(ds))/1000);if(s<60)return"Just now";var m=Math.floor(s/60);if(m<60)return m+"m";var h=Math.floor(m/60);if(h<24)return h+"h";return Math.floor(h/24)+"d";}
function av(p,n,s){return p?'<img src="'+esc(p)+'">':'<div class="sf-av-ph" style="font-size:'+s+'px">'+gi(n)+'</div>';}
function om(id){$("#"+id).addClass("open");}
function cm(id){$("#"+id).removeClass("open");}
function upload(file,pre){
return api.getToken("csrf").then(function(tk){
var fd=new FormData();
fd.append("action","upload");
fd.append("filename",pre+"_"+user+"_"+Date.now()+"."+file.name.split(".").pop());
fd.append("file",file);
fd.append("token",tk);
fd.append("format","json");
fd.append("ignorewarnings","1");
return $.ajax({url:apiUrl,type:"POST",data:fd,processData:false,contentType:false});
}).then(function(r){
if(r.upload&&r.upload.imageinfo)return r.upload.imageinfo.url;
throw new Error("Upload failed");
});
}
setTimeout(function(){$('button,a').filter(function(){return $(this).text().indexOf('Design')!==-1;}).hide();},200);
var css='';
css+='[class*="wikibox"]{display:none!important}';
css+='#social-app{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:#f0f2f5;min-height:100vh;margin:-20px;padding:0}';
css+='#social-app *{box-sizing:border-box}';
css+='.sf-cover-wrap{max-width:940px;margin:0 auto;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,0.1)}';
css+='.sf-cover{height:350px;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);border-radius:0 0 8px 8px;position:relative;overflow:hidden}';
css+='.sf-cover img{width:100%;height:100%;object-fit:cover}';
css+='.sf-cover-btn{position:absolute;bottom:16px;right:16px;background:rgba(0,0,0,0.6);color:#fff;border:none;padding:8px 12px;border-radius:6px;font-weight:600;font-size:14px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}';
css+='.sf-cover-btn:hover{background:rgba(0,0,0,0.8)}';
css+='.sf-cover-btn svg{width:16px;height:16px;fill:currentColor}';
css+='.sf-header{display:flex;align-items:flex-end;padding:0 32px 16px;margin-top:-32px;position:relative}';
css+='.sf-pic-wrap{position:relative;margin-right:24px}';
css+='.sf-pic{width:168px;height:168px;border-radius:50%;border:4px solid #fff;overflow:hidden;background:#fff;box-shadow:0 2px 8px rgba(0,0,0,0.15);cursor:pointer}';
css+='.sf-pic img{width:100%;height:100%;object-fit:cover}';
css+='.sf-av-ph{width:100%;height:100%;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700}';
css+='.sf-cam{position:absolute;bottom:12px;right:12px;width:36px;height:36px;border-radius:50%;background:#e4e6eb;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px rgba(0,0,0,0.1)}';
css+='.sf-cam:hover{background:#d8dadf}';
css+='.sf-cam svg{width:20px;height:20px;fill:#050505}';
css+='.sf-info{flex:1;padding-bottom:8px}';
css+='.sf-name{font-size:32px;font-weight:700;color:#050505;margin:0 0 4px 0}';
css+='.sf-stats{font-size:15px;color:#65676b}';
css+='.sf-actions{display:flex;gap:8px;padding-bottom:8px;margin-left:auto}';
css+='.sf-btn{padding:8px 16px;border-radius:6px;font-weight:600;font-size:15px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;border:none}';
css+='.sf-btn-primary{background:#1877f2;color:#fff}';
css+='.sf-btn-primary:hover{background:#166fe5}';
css+='.sf-btn-secondary{background:#e4e6eb;color:#050505}';
css+='.sf-btn-secondary:hover{background:#d8dadf}';
css+='.sf-nav{border-top:1px solid #ddd}';
css+='.sf-tabs{display:flex;max-width:940px;margin:0 auto;padding:4px 32px 0}';
css+='.sf-tab{padding:16px 16px;font-size:15px;font-weight:600;color:#65676b;cursor:pointer;border-bottom:3px solid transparent;margin-right:4px;text-decoration:none}';
css+='.sf-tab:hover{background:#f0f2f5;border-radius:8px 8px 0 0}';
css+='.sf-tab.active{color:#1877f2;border-bottom-color:#1877f2}';
css+='.sf-main{max-width:908px;margin:16px auto;display:flex;gap:16px;padding:0 16px}';
css+='.sf-sidebar{width:360px;flex-shrink:0}';
css+='.sf-feed{flex:1;min-width:0}';
css+='.sf-card{background:#fff;border-radius:8px;box-shadow:0 1px 2px rgba(0,0,0,0.1);margin-bottom:16px;overflow:hidden}';
css+='.sf-card-title{padding:16px;font-size:20px;font-weight:700;color:#050505}';
css+='.sf-intro-item{display:flex;align-items:center;gap:12px;padding:12px 16px;font-size:15px;color:#050505}';
css+='.sf-intro-item svg{width:20px;height:20px;fill:#65676b;flex-shrink:0}';
css+='.sf-intro-btn{display:block;width:calc(100% - 32px);margin:8px 16px 16px;padding:8px 12px;background:#e4e6eb;border:none;border-radius:6px;font-weight:600;font-size:15px;color:#050505;cursor:pointer;text-align:center}';
css+='.sf-intro-btn:hover{background:#d8dadf}';
css+='.sf-composer{padding:12px 16px}';
css+='.sf-composer-row{display:flex;gap:8px;align-items:center}';
css+='.sf-composer-av{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}';
css+='.sf-composer-av img{width:100%;height:100%;object-fit:cover}';
css+='.sf-composer-input{flex:1;background:#f0f2f5;border:none;border-radius:20px;padding:8px 16px;font-size:17px;color:#65676b;cursor:pointer;text-align:left}';
css+='.sf-composer-input:hover{background:#e4e6eb}';
css+='.sf-composer-btns{display:flex;border-top:1px solid #e4e6eb;margin-top:12px;padding-top:8px}';
css+='.sf-composer-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:8px;padding:8px;border:none;background:none;border-radius:8px;font-size:14px;font-weight:600;color:#65676b;cursor:pointer}';
css+='.sf-composer-btn:hover{background:#f0f2f5}';
css+='.sf-composer-btn svg{width:24px;height:24px}';
css+='.sf-post{border-top:8px solid #f0f2f5}';
css+='.sf-post:first-child{border-top:none}';
css+='.sf-post-head{display:flex;gap:8px;padding:12px 16px}';
css+='.sf-post-av{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}';
css+='.sf-post-av img{width:100%;height:100%;object-fit:cover}';
css+='.sf-post-meta{flex:1}';
css+='.sf-post-name{font-weight:600;font-size:15px;color:#050505}';
css+='.sf-post-time{font-size:13px;color:#65676b}';
css+='.sf-post-del{width:32px;height:32px;border-radius:50%;background:none;border:none;cursor:pointer;font-size:20px;color:#65676b;display:flex;align-items:center;justify-content:center}';
css+='.sf-post-del:hover{background:#f0f2f5}';
css+='.sf-post-text{padding:0 16px 12px;font-size:15px;line-height:1.4;color:#050505;white-space:pre-wrap}';
css+='.sf-post-img{width:100%}';
css+='.sf-post-img img{width:100%;display:block}';
css+='.sf-post-stats{display:flex;justify-content:space-between;padding:10px 16px;font-size:15px;color:#65676b}';
css+='.sf-post-stats span{cursor:pointer}';
css+='.sf-post-stats span:hover{text-decoration:underline}';
css+='.sf-post-bar{display:flex;border-top:1px solid #e4e6eb;margin:0 16px;padding:4px 0}';
css+='.sf-post-act{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:8px;border:none;background:none;border-radius:4px;font-size:15px;font-weight:600;color:#65676b;cursor:pointer}';
css+='.sf-post-act:hover{background:#f0f2f5}';
css+='.sf-post-act.liked{color:#1877f2}';
css+='.sf-post-act svg{width:20px;height:20px;fill:currentColor}';
css+='.sf-comments{padding:8px 16px;display:none}';
css+='.sf-comments.show{display:block}';
css+='.sf-cmt{display:flex;gap:8px;margin-bottom:8px}';
css+='.sf-cmt-av{width:32px;height:32px;border-radius:50%;overflow:hidden;flex-shrink:0}';
css+='.sf-cmt-av img{width:100%;height:100%;object-fit:cover}';
css+='.sf-cmt-bubble{background:#f0f2f5;padding:8px 12px;border-radius:18px;max-width:calc(100% - 48px)}';
css+='.sf-cmt-name{font-weight:600;font-size:13px;color:#050505}';
css+='.sf-cmt-text{font-size:15px;color:#050505}';
css+='.sf-cmt-form{display:flex;gap:8px;padding:8px 16px;border-top:1px solid #e4e6eb}';
css+='.sf-cmt-input{flex:1;background:#f0f2f5;border:none;border-radius:20px;padding:8px 12px;font-size:15px;outline:none}';
css+='.sf-empty{padding:48px 24px;text-align:center}';
css+='.sf-empty svg{width:64px;height:64px;fill:#bcc0c4;margin-bottom:16px}';
css+='.sf-empty-title{font-size:20px;font-weight:700;color:#050505;margin-bottom:8px}';
css+='.sf-empty-text{font-size:15px;color:#65676b}';
css+='.sf-modal{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(255,255,255,0.9);z-index:9999;align-items:center;justify-content:center}';
css+='.sf-modal.open{display:flex}';
css+='.sf-modal-box{background:#fff;border-radius:8px;box-shadow:0 2px 20px rgba(0,0,0,0.2);width:500px;max-width:95vw;max-height:90vh;overflow:auto}';
css+='.sf-modal-head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #e4e6eb}';
css+='.sf-modal-title{font-size:20px;font-weight:700;margin:0}';
css+='.sf-modal-x{width:36px;height:36px;border-radius:50%;background:#e4e6eb;border:none;font-size:24px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1}';
css+='.sf-modal-x:hover{background:#d8dadf}';
css+='.sf-modal-body{padding:16px}';
css+='.sf-textarea{width:100%;min-height:100px;border:none;font-size:18px;resize:none;outline:none;font-family:inherit}';
css+='.sf-modal-foot{padding:16px;border-top:1px solid #e4e6eb}';
css+='.sf-modal-submit{width:100%;padding:10px;background:#1877f2;color:#fff;border:none;border-radius:6px;font-size:15px;font-weight:600;cursor:pointer}';
css+='.sf-modal-submit:disabled{background:#e4e6eb;color:#bcc0c4;cursor:not-allowed}';
css+='.sf-modal-submit:not(:disabled):hover{background:#166fe5}';
css+='.sf-upload{border:2px dashed #ddd;border-radius:8px;padding:24px;text-align:center;cursor:pointer;margin-top:12px;background:#f7f8fa}';
css+='.sf-upload:hover{background:#ebedf0;border-color:#1877f2}';
css+='.sf-upload input{display:none}';
css+='.sf-upload svg{width:40px;height:40px;fill:#65676b;margin-bottom:8px}';
css+='.sf-upload-text{font-weight:600;color:#1877f2}';
css+='.sf-upload-hint{font-size:12px;color:#65676b;margin-top:4px}';
css+='.sf-preview{width:100%;border-radius:8px;margin-top:12px;display:none}';
css+='.sf-preview.show{display:block}';
css+='.sf-field{margin-bottom:16px}';
css+='.sf-field label{display:block;font-weight:600;margin-bottom:8px;color:#050505}';
css+='.sf-field input,.sf-field textarea{width:100%;padding:10px 12px;border:1px solid #ddd;border-radius:6px;font-size:15px;font-family:inherit}';
css+='.sf-field input:focus,.sf-field textarea:focus{outline:none;border-color:#1877f2}';
css+='.sf-toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:#323232;color:#fff;padding:12px 24px;border-radius:8px;font-size:14px;z-index:10000;display:none}';
css+='.sf-toast.show{display:block}';
css+='@media(max-width:900px){.sf-sidebar{display:none}.sf-main{padding:0 8px}.sf-header{flex-direction:column;align-items:center;text-align:center;padding:0 16px 16px}.sf-pic{width:128px;height:128px}.sf-actions{margin:16px 0 0}}';
$("<style>").text(css).appendTo("head");
var icons={
camera:'<svg viewBox="0 0 24 24"><path d="M12 15.2a3.2 3.2 0 100-6.4 3.2 3.2 0 000 6.4z"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></svg>',
home:'<svg viewBox="0 0 24 24"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>',
pin:'<svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>',
video:'<svg viewBox="0 0 24 24" fill="#f3425f"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>',
photo:'<svg viewBox="0 0 24 24" fill="#45bd62"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>',
smile:'<svg viewBox="0 0 24 24" fill="#f7b928"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></svg>',
like:'<svg viewBox="0 0 24 24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>',
comment:'<svg viewBox="0 0 24 24"><path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18z"/></svg>',
share:'<svg viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/></svg>',
images:'<svg viewBox="0 0 24 24"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></svg>'
};
function toast(msg){
var $t=$("#sf-toast");
if(!$t.length)$t=$('<div id="sf-toast" class="sf-toast"></div>').appendTo("body");
$t.text(msg).addClass("show");
setTimeout(function(){$t.removeClass("show");},3000);
}
var h='<div class="sf-cover-wrap">';
h+='<div class="sf-cover" id="cover-area">'+(ud.cover?'<img src="'+esc(ud.cover)+'">':'')+'<button class="sf-cover-btn" id="edit-cover-btn">'+icons.camera+' Edit cover photo</button></div>';
h+='<div class="sf-header">';
h+='<div class="sf-pic-wrap"><div class="sf-pic" id="profile-pic">'+av(ud.photo,user,64)+'</div><button class="sf-cam" id="edit-pic-btn">'+icons.camera+'</button></div>';
h+='<div class="sf-info"><h1 class="sf-name">'+esc(user.replace(/_/g," "))+'</h1><div class="sf-stats"><span id="post-count">0</span> posts · <span id="follower-count">0</span> followers</div></div>';
h+='<div class="sf-actions"><button class="sf-btn sf-btn-primary" id="edit-profile-btn">Edit profile</button></div>';
h+='</div>';
h+='<div class="sf-nav"><div class="sf-tabs">';
h+='<a class="sf-tab active" href="#" data-tab="posts">Posts</a>';
h+='<a class="sf-tab" href="#" data-tab="about">About</a>';
h+='<a class="sf-tab" href="#" data-tab="photos">Photos</a>';
h+='</div></div>';
h+='</div>';
h+='<div class="sf-main">';
h+='<div class="sf-sidebar">';
h+='<div class="sf-card"><div class="sf-card-title">Intro</div>';
h+='<div id="intro-bio"></div>';
h+='<button class="sf-intro-btn" id="add-bio-btn">'+(ud.bio?'Edit bio':'Add bio')+'</button>';
h+='<div class="sf-intro-item">'+icons.home+'<span>Lives in <strong id="location-display">'+(ud.location||'Add hometown')+'</strong></span></div>';
h+='<div class="sf-intro-item">'+icons.pin+'<span>From <strong>Earth</strong></span></div>';
h+='<button class="sf-intro-btn" id="edit-details-btn">Edit details</button>';
h+='</div></div>';
h+='<div class="sf-feed">';
h+='<div class="sf-card">';
h+='<div class="sf-composer">';
h+='<div class="sf-composer-row"><div class="sf-composer-av">'+av(ud.photo,user,16)+'</div>';
h+='<button class="sf-composer-input" id="open-composer">What\'s on your mind, '+esc(user.split(/[\s_]/)[0])+'?</button></div>';
h+='<div class="sf-composer-btns">';
h+='<button class="sf-composer-btn" id="btn-live">'+icons.video+' Live video</button>';
h+='<button class="sf-composer-btn" id="btn-photo">'+icons.photo+' Photo/video</button>';
h+='<button class="sf-composer-btn" id="btn-feeling">'+icons.smile+' Feeling/activity</button>';
h+='</div></div></div>';
h+='<div id="feed"></div>';
h+='</div></div>';
// Create Post Modal
h+='<div class="sf-modal" id="post-modal"><div class="sf-modal-box">';
h+='<div class="sf-modal-head"><h2 class="sf-modal-title">Create post</h2><button class="sf-modal-x" data-close="post-modal">×</button></div>';
h+='<div class="sf-modal-body">';
h+='<div style="display:flex;gap:12px;margin-bottom:16px"><div class="sf-composer-av">'+av(ud.photo,user,16)+'</div><div><strong>'+esc(user.replace(/_/g," "))+'</strong><div style="font-size:12px;color:#65676b">Public</div></div></div>';
h+='<textarea class="sf-textarea" id="post-text" placeholder="What\'s on your mind, '+esc(user.split(/[\s_]/)[0])+'?"></textarea>';
h+='<img class="sf-preview" id="post-preview">';
h+='<div class="sf-upload" id="post-upload"><input type="file" accept="image/*">'+icons.images+'<div class="sf-upload-text">Add photos/videos</div><div class="sf-upload-hint">or drag and drop</div></div>';
h+='</div>';
h+='<div class="sf-modal-foot"><button class="sf-modal-submit" id="submit-post" disabled>Post</button></div>';
h+='</div></div>';
// Edit Profile Modal
h+='<div class="sf-modal" id="profile-modal"><div class="sf-modal-box">';
h+='<div class="sf-modal-head"><h2 class="sf-modal-title">Edit profile</h2><button class="sf-modal-x" data-close="profile-modal">×</button></div>';
h+='<div class="sf-modal-body">';
h+='<div class="sf-field"><label>Profile photo</label><div class="sf-upload" id="pic-upload"><input type="file" accept="image/*">'+icons.camera+'<div class="sf-upload-text">Upload new photo</div></div><img class="sf-preview" id="pic-preview"'+(ud.photo?' src="'+esc(ud.photo)+'" style="display:block"':'')+'></div>';
h+='<div class="sf-field"><label>Cover photo</label><div class="sf-upload" id="cover-upload"><input type="file" accept="image/*">'+icons.images+'<div class="sf-upload-text">Upload new cover</div></div><img class="sf-preview" id="cover-preview"'+(ud.cover?' src="'+esc(ud.cover)+'" style="display:block"':'')+'></div>';
h+='<div class="sf-field"><label>Bio</label><textarea id="bio-input" rows="3" placeholder="Tell people about yourself...">'+esc(ud.bio)+'</textarea></div>';
h+='<div class="sf-field"><label>Current city</label><input type="text" id="location-input" value="'+esc(ud.location)+'" placeholder="Where do you live?"></div>';
h+='</div>';
h+='<div class="sf-modal-foot"><button class="sf-modal-submit" id="save-profile">Save changes</button></div>';
h+='</div></div>';
$("#social-app").html(h);
// Update bio display
if(ud.bio)$("#intro-bio").html('<div style="text-align:center;padding:12px 16px;color:#050505">'+esc(ud.bio)+'</div>');
var $app=$("#social-app");
// Modal close handlers
$app.on("click","[data-close]",function(){cm($(this).data("close"));});
$app.on("click",".sf-modal",function(e){if($(e.target).hasClass("sf-modal"))cm(this.id);});
// Tab switching
$app.on("click",".sf-tab",function(e){
e.preventDefault();
$(".sf-tab").removeClass("active");
$(this).addClass("active");
});
// Open create post modal
$("#open-composer,#btn-photo").on("click",function(){om("post-modal");$("#post-text").focus();});
$("#btn-live,#btn-feeling").on("click",function(){toast("This feature is coming soon!");});
// Post file upload
var postFile=null;
$("#post-upload").on("click",function(){$(this).find("input").click();});
$("#post-upload input").on("change",function(){
if(this.files&&this.files[0]){
postFile=this.files[0];
var reader=new FileReader();
reader.onload=function(e){$("#post-preview").attr("src",e.target.result).addClass("show");updatePostBtn();};
reader.readAsDataURL(this.files[0]);
}
});
$("#post-text").on("input",updatePostBtn);
function updatePostBtn(){$("#submit-post").prop("disabled",!$("#post-text").val().trim()&&!postFile);}
// Submit post
$("#submit-post").on("click",function(){
var text=$("#post-text").val().trim();
if(!text&&!postFile){toast("Please write something or add a photo");return;}
var $btn=$(this).prop("disabled",true).text("Posting...");
function createPost(imageUrl){
var params={action:"socialfeed",sfaction:"createpost",content:text||"(photo)"};
if(imageUrl)params.image_url=imageUrl;
api.postWithToken("csrf",params).then(function(){
cm("post-modal");
$("#post-text").val("");
postFile=null;
$("#post-preview").removeClass("show").attr("src","");
$btn.prop("disabled",true).text("Post");
toast("Post created!");
loadFeed();
}).catch(function(e){
toast("Error creating post: "+e);
$btn.prop("disabled",false).text("Post");
});
}
if(postFile){
upload(postFile,"Post").then(createPost).catch(function(e){
toast("Error uploading image: "+e);
$btn.prop("disabled",false).text("Post");
});
}else{
createPost();
}
});
// Open edit profile modal
$("#edit-profile-btn,#edit-pic-btn,#edit-cover-btn,#add-bio-btn,#edit-details-btn").on("click",function(){
om("profile-modal");
});
// Profile photo upload
var picFile=null,coverFile=null;
$("#pic-upload").on("click",function(){$(this).find("input").click();});
$("#pic-upload input").on("change",function(){
if(this.files&&this.files[0]){
picFile=this.files[0];
var reader=new FileReader();
reader.onload=function(e){$("#pic-preview").attr("src",e.target.result).addClass("show");};
reader.readAsDataURL(this.files[0]);
}
});
// Cover photo upload
$("#cover-upload").on("click",function(){$(this).find("input").click();});
$("#cover-upload input").on("change",function(){
if(this.files&&this.files[0]){
coverFile=this.files[0];
var reader=new FileReader();
reader.onload=function(e){$("#cover-preview").attr("src",e.target.result).addClass("show");};
reader.readAsDataURL(this.files[0]);
}
});
// Save profile
$("#save-profile").on("click",function(){
var $btn=$(this).prop("disabled",true).text("Saving...");
var newBio=$("#bio-input").val().trim();
var newLocation=$("#location-input").val().trim();
var uploads=[];
if(picFile)uploads.push(upload(picFile,"ProfilePic").then(function(url){ud.photo=url;localStorage.setItem("sf_photo_"+user,url);}));
if(coverFile)uploads.push(upload(coverFile,"Cover").then(function(url){ud.cover=url;localStorage.setItem("sf_cover_"+user,url);}));
$.when.apply($,uploads).then(function(){
ud.bio=newBio;
ud.location=newLocation;
localStorage.setItem("sf_bio_"+user,newBio);
localStorage.setItem("sf_location_"+user,newLocation);
// Update UI
$("#profile-pic").html(av(ud.photo,user,64));
$(".sf-composer-av").html(av(ud.photo,user,16));
if(ud.cover){
$("#cover-area").html('<img src="'+esc(ud.cover)+'"><button class="sf-cover-btn" id="edit-cover-btn">'+icons.camera+' Edit cover photo</button>');
}
$("#intro-bio").html(newBio?'<div style="text-align:center;padding:12px 16px;color:#050505">'+esc(newBio)+'</div>':'');
$("#add-bio-btn").text(newBio?'Edit bio':'Add bio');
$("#location-display").text(newLocation||'Add hometown');
cm("profile-modal");
$btn.prop("disabled",false).text("Save changes");
picFile=null;
coverFile=null;
toast("Profile updated!");
}).fail(function(e){
toast("Error saving profile: "+e);
$btn.prop("disabled",false).text("Save changes");
});
});
// Like post
$app.on("click",".sf-post-act[data-like]",function(){
var $btn=$(this);
var postId=$btn.data("like");
api.postWithToken("csrf",{action:"socialfeed",sfaction:"react",post_id:postId,reaction_type:"like"}).then(function(){
loadFeed();
}).catch(function(e){
toast("Error: "+e);
});
});
// Delete post
$app.on("click",".sf-post-del[data-del]",function(){
if(confirm("Delete this post?")){
var postId=$(this).data("del");
api.postWithToken("csrf",{action:"socialfeed",sfaction:"deletepost",post_id:postId}).then(function(){
toast("Post deleted");
loadFeed();
}).catch(function(e){
toast("Error: "+e);
});
}
});
// Toggle comments
$app.on("click",".sf-view-comments",function(){
var id=$(this).data("id");
var $comments=$("#comments-"+id);
$comments.toggleClass("show");
if($comments.hasClass("show"))loadComments(id);
});
// Submit comment
$app.on("keypress",".sf-cmt-input",function(e){
if(e.which===13){
var $input=$(this);
var text=$input.val().trim();
var postId=$input.data("id");
if(text){
$input.val("").prop("disabled",true);
api.postWithToken("csrf",{action:"socialfeed",sfaction:"comment",post_id:postId,content:text}).then(function(){
$input.prop("disabled",false);
loadComments(postId);
loadFeed();
}).catch(function(e){
$input.prop("disabled",false);
toast("Error: "+e);
});
}
}
});
// Share post
$app.on("click",".sf-post-act.share-btn",function(){
var url=window.location.href;
if(navigator.clipboard){
navigator.clipboard.writeText(url).then(function(){toast("Link copied!");});
}else{
toast("Share: "+url);
}
});
function loadFeed(){
api.get({action:"socialfeed",sfaction:"getposts",limit:20}).then(function(r){
var posts=(r.socialfeed&&r.socialfeed.posts)||[];
$("#post-count").text(posts.length);
if(!posts.length){
$("#feed").html('<div class="sf-card sf-empty">'+icons.images+'<div class="sf-empty-title">No posts yet</div><div class="sf-empty-text">When you share photos, they will appear here.</div></div>');
return;
}
var html=posts.map(function(p){
var pic=localStorage.getItem("sf_photo_"+p.username)||"";
var likes=0;
for(var k in p.reaction_counts)likes+=p.reaction_counts[k];
var isLiked=p.user_reaction==="like";
var media=p.image_url?'<div class="sf-post-img"><img src="'+esc(p.image_url)+'"></div>':"";
var delBtn=p.username===user?'<button class="sf-post-del" data-del="'+p.id+'" title="Delete">×</button>':"";
var out='<div class="sf-card sf-post">';
out+='<div class="sf-post-head"><div class="sf-post-av">'+av(pic,p.username,16)+'</div><div class="sf-post-meta"><div class="sf-post-name">'+esc(p.username.replace(/_/g," "))+'</div><div class="sf-post-time">'+ago(p.created)+'</div></div>'+delBtn+'</div>';
if(p.content&&p.content!=="(photo)")out+='<div class="sf-post-text">'+esc(p.content)+'</div>';
out+=media;
out+='<div class="sf-post-stats"><span>'+likes+' like'+(likes!==1?'s':'')+'</span><span class="sf-view-comments" data-id="'+p.id+'">'+p.comments+' comment'+(p.comments!==1?'s':'')+'</span></div>';
out+='<div class="sf-post-bar">';
out+='<button class="sf-post-act'+(isLiked?' liked':'')+'" data-like="'+p.id+'">'+icons.like+' Like</button>';
out+='<button class="sf-post-act sf-view-comments" data-id="'+p.id+'">'+icons.comment+' Comment</button>';
out+='<button class="sf-post-act share-btn">'+icons.share+' Share</button>';
out+='</div>';
out+='<div class="sf-comments" id="comments-'+p.id+'"></div>';
out+='<div class="sf-cmt-form"><div class="sf-cmt-av">'+av(ud.photo,user,12)+'</div><input class="sf-cmt-input" data-id="'+p.id+'" placeholder="Write a comment..."></div>';
out+='</div>';
return out;
}).join("");
$("#feed").html(html);
}).catch(function(e){
$("#feed").html('<div class="sf-card sf-empty"><div class="sf-empty-title">Error loading posts</div><div class="sf-empty-text">'+esc(String(e))+'</div></div>');
});
}
function loadComments(postId){
api.get({action:"socialfeed",sfaction:"getcomments",post_id:postId}).then(function(r){
var comments=(r.socialfeed&&r.socialfeed.comments)||[];
var html=comments.length?comments.map(function(c){
var pic=localStorage.getItem("sf_photo_"+c.username)||"";
return '<div class="sf-cmt"><div class="sf-cmt-av">'+av(pic,c.username,12)+'</div><div class="sf-cmt-bubble"><div class="sf-cmt-name">'+esc(c.username.replace(/_/g," "))+'</div><div class="sf-cmt-text">'+esc(c.content)+'</div></div></div>';
}).join(""):'<div style="padding:8px;color:#65676b;text-align:center">No comments yet. Be the first!</div>';
$("#comments-"+postId).html(html);
});
}
// Initial load
loadFeed();
});
}