MediaWiki:Gadget-SocialFeed.js: Difference between revisions

From XMethod Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
/**
/* SocialFeed v11 - Fixed buttons and full functionality */
* SocialFeed Gadget - Instagram-style social media page
if (mw.config.get("wgPageName").indexOf("User:Docmoates/Social") !== -1) {
* Only runs on User:Docmoates/Social page
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";
(function() {
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)||""};
'use strict';
if (mw.config.get('wgPageName') !== 'User:Docmoates/Social') return;


mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function() {
function gi(n){return(n||"?").charAt(0).toUpperCase();}
var $=jQuery,api=new mw.Api(),apiUrl=mw.util.wikiScript('api'),user=mw.config.get('wgUserName')||'Guest';
function esc(t){var d=document.createElement("div");d.textContent=t;return d.innerHTML;}
var ud={photo:localStorage.getItem('sf_photo_'+user)||'',cover:localStorage.getItem('sf_cover_'+user)||'',bio:localStorage.getItem('sf_bio_'+user)||'',name:localStorage.getItem('sf_name_'+user)||user};
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";}
var pp={image:'',video:'',location:''},uf={};
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");


function gi(n){return(n||'?').split(/[\s_]+/).map(function(w){return(w[0]||'').toUpperCase();}).join('').substring(0,2)||'?';}
var icons={
function esc(t){var d=document.createElement('div');d.textContent=t;return d.innerHTML;}
  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>',
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';}
  home:'<svg viewBox="0 0 24 24"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>',
function av(pic,name,sz){if(pic)return'<img src="'+esc(pic)+'">';var c=sz==='s'?'sa-comment-avatar-placeholder':sz==='p'?'sa-post-avatar-placeholder':sz==='c'?'sa-composer-avatar-placeholder':'sa-profile-pic-placeholder';return'<div class="'+c+'">'+gi(name)+'</div>';}
  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>',
function om(id){$('#'+id).addClass('open');}
  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>',
function cm(id){$('#'+id).removeClass('open');}
  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 upload(file,pre,prog){
function toast(msg){
return api.getToken('csrf').then(function(tk){
  var $t=$("#sf-toast");
var ext=file.name.split('.').pop()||'jpg',fn=pre+'_'+user+'_'+Date.now()+'.'+ext,fd=new FormData();
  if(!$t.length)$t=$('<div id="sf-toast" class="sf-toast"></div>').appendTo("body");
fd.append('action','upload');fd.append('filename',fn);fd.append('file',file);fd.append('token',tk);fd.append('format','json');fd.append('ignorewarnings','1');
  $t.text(msg).addClass("show");
return $.ajax({url:apiUrl,type:'POST',data:fd,processData:false,contentType:false,xhr:function(){var x=$.ajaxSettings.xhr();if(x.upload&&prog)x.upload.addEventListener('progress',function(e){if(e.lengthComputable)prog(Math.round(e.loaded/e.total*100));});return x;}});
  setTimeout(function(){$t.removeClass("show");},3000);
}).then(function(r){if(r.upload&&r.upload.imageinfo)return r.upload.imageinfo.url;throw new Error(r.error?r.error.info:'Upload failed');});
}
}


function build(){
var h='<div class="sf-cover-wrap">';
var h='<div class="sa-profile-header"><div class="sa-cover" id="cover-area">'+(ud.cover?'<img src="'+esc(ud.cover)+'">':'')+'<button class="sa-cover-edit" id="edit-cover-btn">📷 Edit Cover</button></div><div class="sa-profile-info"><div class="sa-profile-pic" id="profile-pic">'+av(ud.photo,user,'')+'</div><div class="sa-profile-details"><div class="sa-profile-top"><h1 class="sa-username" id="display-name">'+esc(ud.name)+'</h1><button class="sa-edit-btn" id="edit-profile-btn">Edit Profile</button></div><div class="sa-stats"><div class="sa-stat"><div class="sa-stat-num" id="post-count">0</div><div class="sa-stat-label">posts</div></div><div class="sa-stat"><div class="sa-stat-num">0</div><div class="sa-stat-label">followers</div></div><div class="sa-stat"><div class="sa-stat-num">0</div><div class="sa-stat-label">following</div></div></div><div class="sa-bio" id="bio-display">'+(ud.bio?'<div class="sa-bio-name">'+esc(ud.name)+'</div>'+esc(ud.bio).replace(/\n/g,'<br>'):'Click Edit Profile to add a bio')+'</div></div></div></div>';
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="sa-main"><div class="sa-stories" id="stories-container"></div><div class="sa-composer"><div class="sa-composer-top"><div class="sa-composer-avatar" id="composer-avatar">'+av(ud.photo,user,'c')+'</div><textarea class="sa-composer-input" id="post-input" placeholder="What\'s on your mind?"></textarea></div><div class="sa-composer-preview" id="post-preview"><div class="sa-composer-preview-inner"><button class="sa-composer-preview-remove" id="remove-media">×</button></div></div><div class="sa-composer-location" id="post-location">📍 <span></span><button id="remove-location">×</button></div><div class="sa-composer-actions"><div class="sa-composer-buttons" style="position:relative"><button class="sa-icon-btn" id="btn-add-photo">🖼️</button><button class="sa-icon-btn" id="btn-add-video">🎬</button><button class="sa-icon-btn" id="btn-add-location">📍</button><button class="sa-icon-btn" id="btn-add-emoji">😊</button><div class="sa-emoji-picker" id="emoji-picker"></div></div><button class="sa-post-btn" id="btn-submit-post" disabled>Post</button></div></div><div class="sa-feed" id="feed-container"></div></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">&times;</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>';


// Modals
// Edit Profile Modal
h+='<div class="sa-modal" id="modal-edit-profile"><div class="sa-modal-content"><div class="sa-modal-header"><span class="sa-modal-title">Edit Profile</span><button class="sa-modal-close" data-close="modal-edit-profile">×</button></div><div class="sa-modal-body"><label style="font-weight:600;display:block;margin-bottom:8px">Display Name</label><input class="sa-input" id="input-display-name" value="'+esc(ud.name)+'"><label style="font-weight:600;display:block;margin-bottom:8px">Bio</label><textarea class="sa-textarea" id="input-bio" placeholder="Tell people about yourself...">'+esc(ud.bio)+'</textarea><div class="sa-btn-row"><button class="sa-btn sa-btn-secondary" data-close="modal-edit-profile">Cancel</button><button class="sa-btn sa-btn-primary" id="save-profile">Save</button></div></div></div></div>';
h+='<div class="sf-modal" id="profile-modal"><div class="sf-modal-box">';
h+='<div class="sa-modal" id="modal-profile-pic"><div class="sa-modal-content"><div class="sa-modal-header"><span class="sa-modal-title">Profile Picture</span><button class="sa-modal-close" data-close="modal-profile-pic">×</button></div><div class="sa-tabs"><div class="sa-tab active" data-panel="panel-pic-upload">Upload</div><div class="sa-tab" data-panel="panel-pic-url">URL</div></div><div class="sa-tab-panel active" id="panel-pic-upload"><div class="sa-upload-zone" id="zone-profile-pic"><input type="file" accept="image/*"><div class="sa-upload-icon">📷</div><div class="sa-upload-text">Drag photo here or <span>browse</span></div></div><div class="sa-progress" id="progress-profile-pic"><div class="sa-progress-bar"></div></div><img class="sa-preview-img" id="preview-profile-pic"></div><div class="sa-tab-panel" id="panel-pic-url"><input class="sa-input" id="input-pic-url" placeholder="Paste image URL..."><img class="sa-preview-img" id="preview-pic-url"></div><div class="sa-btn-row" style="padding:0 20px 20px"><button class="sa-btn sa-btn-secondary" data-close="modal-profile-pic">Cancel</button><button class="sa-btn sa-btn-primary" id="save-profile-pic">Save</button></div></div></div>';
h+='<div class="sf-modal-head"><h2 class="sf-modal-title">Edit profile</h2><button class="sf-modal-x" data-close="profile-modal">&times;</button></div>';
h+='<div class="sa-modal" id="modal-cover"><div class="sa-modal-content"><div class="sa-modal-header"><span class="sa-modal-title">Cover Photo</span><button class="sa-modal-close" data-close="modal-cover">×</button></div><div class="sa-tabs"><div class="sa-tab active" data-panel="panel-cover-upload">Upload</div><div class="sa-tab" data-panel="panel-cover-url">URL</div></div><div class="sa-tab-panel active" id="panel-cover-upload"><div class="sa-upload-zone" id="zone-cover"><input type="file" accept="image/*"><div class="sa-upload-icon">🖼️</div><div class="sa-upload-text">Drag photo here or <span>browse</span></div></div><div class="sa-progress" id="progress-cover"><div class="sa-progress-bar"></div></div><img class="sa-preview-img" id="preview-cover"></div><div class="sa-tab-panel" id="panel-cover-url"><input class="sa-input" id="input-cover-url" placeholder="Paste image URL..."><img class="sa-preview-img" id="preview-cover-url"></div><div class="sa-btn-row" style="padding:0 20px 20px"><button class="sa-btn sa-btn-secondary" data-close="modal-cover">Cancel</button><button class="sa-btn sa-btn-primary" id="save-cover">Save</button></div></div></div>';
h+='<div class="sf-modal-body">';
h+='<div class="sa-modal" id="modal-photo"><div class="sa-modal-content"><div class="sa-modal-header"><span class="sa-modal-title">Add Photo</span><button class="sa-modal-close" data-close="modal-photo">×</button></div><div class="sa-tabs"><div class="sa-tab active" data-panel="panel-photo-upload">Upload</div><div class="sa-tab" data-panel="panel-photo-url">URL</div></div><div class="sa-tab-panel active" id="panel-photo-upload"><div class="sa-upload-zone" id="zone-photo"><input type="file" accept="image/*"><div class="sa-upload-icon">📤</div><div class="sa-upload-text">Drag photo here or <span>browse</span></div></div><div class="sa-progress" id="progress-photo"><div class="sa-progress-bar"></div></div><img class="sa-preview-img" id="preview-photo"></div><div class="sa-tab-panel" id="panel-photo-url"><input class="sa-input" id="input-photo-url" placeholder="Paste image URL..."><img class="sa-preview-img" id="preview-photo-url"></div><div class="sa-btn-row" style="padding:0 20px 20px"><button class="sa-btn sa-btn-secondary" data-close="modal-photo">Cancel</button><button class="sa-btn sa-btn-primary" id="add-photo">Add Photo</button></div></div></div>';
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="sa-modal" id="modal-video"><div class="sa-modal-content"><div class="sa-modal-header"><span class="sa-modal-title">Add Video</span><button class="sa-modal-close" data-close="modal-video">×</button></div><div class="sa-tabs"><div class="sa-tab active" data-panel="panel-video-upload">Upload</div><div class="sa-tab" data-panel="panel-video-url">URL</div></div><div class="sa-tab-panel active" id="panel-video-upload"><div class="sa-upload-zone" id="zone-video"><input type="file" accept="video/*"><div class="sa-upload-icon">🎬</div><div class="sa-upload-text">Drag video here or <span>browse</span></div></div><div class="sa-progress" id="progress-video"><div class="sa-progress-bar"></div></div><video class="sa-preview-img" id="preview-video" controls style="display:none"></video></div><div class="sa-tab-panel" id="panel-video-url"><input class="sa-input" id="input-video-url" placeholder="YouTube, Vimeo, or direct video URL..."><p style="color:#8e8e8e;font-size:12px">Supports YouTube, Vimeo, and direct video links</p></div><div class="sa-btn-row" style="padding:0 20px 20px"><button class="sa-btn sa-btn-secondary" data-close="modal-video">Cancel</button><button class="sa-btn sa-btn-primary" id="add-video">Add Video</button></div></div></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="sa-modal" id="modal-location"><div class="sa-modal-content"><div class="sa-modal-header"><span class="sa-modal-title">Add Location</span><button class="sa-modal-close" data-close="modal-location">×</button></div><div class="sa-modal-body"><button class="sa-btn sa-btn-primary" id="detect-location" style="width:100%;margin-bottom:16px">📍 Use Current Location</button><div style="text-align:center;color:#8e8e8e;margin-bottom:16px">— or —</div><input class="sa-input" id="input-location" placeholder="Enter location manually..."><div class="sa-btn-row"><button class="sa-btn sa-btn-secondary" data-close="modal-location">Cancel</button><button class="sa-btn sa-btn-primary" id="add-location">Add</button></div></div></div></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="sa-modal" id="modal-story"><div class="sa-modal-content"><div class="sa-modal-header"><span class="sa-modal-title">Add to Story</span><button class="sa-modal-close" data-close="modal-story">×</button></div><div class="sa-tabs"><div class="sa-tab active" data-panel="panel-story-upload">Upload</div><div class="sa-tab" data-panel="panel-story-url">URL</div></div><div class="sa-tab-panel active" id="panel-story-upload"><div class="sa-upload-zone" id="zone-story"><input type="file" accept="image/*"><div class="sa-upload-icon">📷</div><div class="sa-upload-text">Drag photo here or <span>browse</span></div></div><div class="sa-progress" id="progress-story"><div class="sa-progress-bar"></div></div><img class="sa-preview-img" id="preview-story"></div><div class="sa-tab-panel" id="panel-story-url"><input class="sa-input" id="input-story-url" placeholder="Paste image URL..."><img class="sa-preview-img" id="preview-story-url"></div><div class="sa-btn-row" style="padding:0 20px 20px"><button class="sa-btn sa-btn-secondary" data-close="modal-story">Cancel</button><button class="sa-btn sa-btn-primary" id="add-story">Share to Story</button></div></div></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 class="sa-story-viewer" id="story-viewer"><div class="sa-story-viewer-content"><div class="sa-story-viewer-progress"><div class="sa-story-viewer-progress-bar" id="sv-prog"></div></div><button class="sa-story-viewer-close" id="close-story-viewer">×</button><div class="sa-story-viewer-header"><div class="sa-story-viewer-avatar" id="story-viewer-avatar"></div><div class="sa-story-viewer-name" id="story-viewer-name"></div></div><img id="story-viewer-image" src=""></div></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);
$("#social-app").html(h);
}


function events(){
// Update bio display
var $a=$('#social-app');
if(ud.bio)$("#intro-bio").html('<div style="text-align:center;padding:12px 16px;color:#050505">'+esc(ud.bio)+'</div>');
$a.on('click','[data-close]',function(){cm($(this).data('close'));});
$a.on('click','.sa-modal',function(e){if($(e.target).hasClass('sa-modal'))cm(this.id);});
$a.on('click','.sa-tab',function(){var $t=$(this),$p=$('#'+$t.data('panel'));$t.addClass('active').siblings().removeClass('active');$p.addClass('active').siblings('.sa-tab-panel').removeClass('active');});


function suz(zid,pid,prid,fk){
var $app=$("#social-app");
var $z=$('#'+zid),$i=$z.find('input'),$pv=$('#'+pid),$pr=$('#'+prid);
$z.on('click',function(){$i.click();});
$z.on('dragover',function(e){e.preventDefault();$(this).addClass('dragover');});
$z.on('dragleave drop',function(){$(this).removeClass('dragover');});
$z.on('drop',function(e){e.preventDefault();var f=e.originalEvent.dataTransfer.files;if(f.length)hf(f[0]);});
$i.on('change',function(){if(this.files.length)hf(this.files[0]);});
function hf(f){uf[fk]=f;if(f.type.startsWith('image/')){var r=new FileReader();r.onload=function(e){$pv.attr('src',e.target.result).addClass('show');};r.readAsDataURL(f);}else if(f.type.startsWith('video/')){$pv.attr('src',URL.createObjectURL(f)).addClass('show').show();}}
return{reset:function(){uf[fk]=null;$pv.removeClass('show').hide().attr('src','');$pr.removeClass('show').find('.sa-progress-bar').css('width',0);$i.val('');},setP:function(p){$pr.addClass('show').find('.sa-progress-bar').css('width',p+'%');}};
}


var upPic=suz('zone-profile-pic','preview-profile-pic','progress-profile-pic','profilePic');
// Modal close handlers
var upCov=suz('zone-cover','preview-cover','progress-cover','cover');
$app.on("click","[data-close]",function(){cm($(this).data("close"));});
var upPho=suz('zone-photo','preview-photo','progress-photo','photo');
$app.on("click",".sf-modal",function(e){if($(e.target).hasClass("sf-modal"))cm(this.id);});
var upVid=suz('zone-video','preview-video','progress-video','video');
var upSto=suz('zone-story','preview-story','progress-story','story');


$('#input-pic-url').on('input',function(){var v=$(this).val();$('#preview-pic-url').attr('src',v).toggleClass('show',!!v);});
// Tab switching
$('#input-cover-url').on('input',function(){var v=$(this).val();$('#preview-cover-url').attr('src',v).toggleClass('show',!!v);});
$app.on("click",".sf-tab",function(e){
$('#input-photo-url').on('input',function(){var v=$(this).val();$('#preview-photo-url').attr('src',v).toggleClass('show',!!v);});
  e.preventDefault();
$('#input-story-url').on('input',function(){var v=$(this).val();$('#preview-story-url').attr('src',v).toggleClass('show',!!v);});
  $(".sf-tab").removeClass("active");
  $(this).addClass("active");
});


$('#edit-profile-btn').on('click',function(){om('modal-edit-profile');});
// Open create post modal
$('#save-profile').on('click',function(){ud.name=$('#input-display-name').val().trim()||user;ud.bio=$('#input-bio').val().trim();localStorage.setItem('sf_name_'+user,ud.name);localStorage.setItem('sf_bio_'+user,ud.bio);$('#display-name').text(ud.name);$('#bio-display').html(ud.bio?'<div class="sa-bio-name">'+esc(ud.name)+'</div>'+esc(ud.bio).replace(/\n/g,'<br>'):'Click Edit Profile to add a bio');cm('modal-edit-profile');});
$("#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!");});


$('#profile-pic').on('click',function(){upPic.reset();$('#input-pic-url').val('');$('#preview-pic-url').removeClass('show');om('modal-profile-pic');});
// Post file upload
$('#save-profile-pic').on('click',function(){var ap=$('#modal-profile-pic .sa-tab.active').data('panel');if(ap==='panel-pic-url'){var u=$('#input-pic-url').val().trim();if(u){savePic(u);}else{alert('Please enter a URL');}}else if(uf.profilePic){upload(uf.profilePic,'ProfilePic',upPic.setP).then(savePic).fail(function(e){alert('Upload error');});}else{alert('Please select an image');}});
var postFile=null;
function savePic(u){ud.photo=u;localStorage.setItem('sf_photo_'+user,u);$('#profile-pic').html(av(u,user,''));$('#composer-avatar').html(av(u,user,'c'));cm('modal-profile-pic');upPic.reset();}
$("#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);}


$a.on('click','#edit-cover-btn',function(){upCov.reset();$('#input-cover-url').val('');$('#preview-cover-url').removeClass('show');om('modal-cover');});
// Submit post
$('#save-cover').on('click',function(){var ap=$('#modal-cover .sa-tab.active').data('panel');if(ap==='panel-cover-url'){var u=$('#input-cover-url').val().trim();if(u){saveCov(u);}else{alert('Please enter a URL');}}else if(uf.cover){upload(uf.cover,'Cover',upCov.setP).then(saveCov).fail(function(){alert('Upload error');});}else{alert('Please select an image');}});
$("#submit-post").on("click",function(){
function saveCov(u){ud.cover=u;localStorage.setItem('sf_cover_'+user,u);$('#cover-area').html('<img src="'+u+'"><button class="sa-cover-edit" id="edit-cover-btn">📷 Edit Cover</button>');cm('modal-cover');upCov.reset();}
  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...");


$('#btn-add-photo').on('click',function(){upPho.reset();$('#input-photo-url').val('');$('#preview-photo-url').removeClass('show');om('modal-photo');});
  function createPost(imageUrl){
$('#add-photo').on('click',function(){var ap=$('#modal-photo .sa-tab.active').data('panel');if(ap==='panel-photo-url'){var u=$('#input-photo-url').val().trim();if(u){setPM('image',u);cm('modal-photo');}else{alert('Please enter a URL');}}else if(uf.photo){upload(uf.photo,'Post',upPho.setP).then(function(u){setPM('image',u);cm('modal-photo');upPho.reset();}).fail(function(){alert('Upload error');});}else{alert('Please select an image');}});
    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");
    });
  }


$('#btn-add-video').on('click',function(){upVid.reset();$('#input-video-url').val('');om('modal-video');});
  if(postFile){
$('#add-video').on('click',function(){var ap=$('#modal-video .sa-tab.active').data('panel');if(ap==='panel-video-url'){var u=$('#input-video-url').val().trim();if(u){setPM('video',u);cm('modal-video');}else{alert('Please enter a URL');}}else if(uf.video){upload(uf.video,'Video',upVid.setP).then(function(u){setPM('video',u);cm('modal-video');upVid.reset();}).fail(function(){alert('Upload error');});}else{alert('Please select a video');}});
    upload(postFile,"Post").then(createPost).catch(function(e){
      toast("Error uploading image: "+e);
      $btn.prop("disabled",false).text("Post");
    });
  }else{
    createPost();
  }
});


function setPM(t,u){pp.image='';pp.video='';pp[t]=u;var $pv=$('#post-preview'),$in=$pv.find('.sa-composer-preview-inner');if(t==='image'){$in.find('img,video').remove();$in.append('<img src="'+u+'">');}else{$in.find('img,video').remove();if(u.match(/youtube|youtu\.be/)){$in.append('<img src="https://img.youtube.com/vi/'+(u.match(/(?:v=|youtu\.be\/)([^&]+)/)||[])[1]+'/hqdefault.jpg">');}else{$in.append('<video src="'+u+'" style="max-height:200px"></video>');}}$pv.addClass('show');upd();}
// 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");
});


$('#remove-media').on('click',function(){pp.image='';pp.video='';$('#post-preview').removeClass('show').find('img,video').remove();upd();});
// 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]);
  }
});


$('#btn-add-location').on('click',function(){$('#input-location').val('');om('modal-location');});
// Cover photo upload
$('#detect-location').on('click',function(){var $b=$(this);$b.text('Detecting...').prop('disabled',true);if(navigator.geolocation){navigator.geolocation.getCurrentPosition(function(p){$.getJSON('https://nominatim.openstreetmap.org/reverse?format=json&lat='+p.coords.latitude+'&lon='+p.coords.longitude).done(function(d){var l=(d.display_name||'').split(',').slice(0,3).join(',')||p.coords.latitude+','+p.coords.longitude;$('#input-location').val(l);$b.text('📍 Use Current Location').prop('disabled',false);}).fail(function(){$('#input-location').val(p.coords.latitude+','+p.coords.longitude);$b.text('📍 Use Current Location').prop('disabled',false);});},function(){alert('Could not get location');$b.text('📍 Use Current Location').prop('disabled',false);});}else{alert('Geolocation not supported');$b.text('📍 Use Current Location').prop('disabled',false);}});
$("#cover-upload").on("click",function(){$(this).find("input").click();});
$('#add-location').on('click',function(){var l=$('#input-location').val().trim();if(l){pp.location=l;$('#post-location').addClass('show').find('span').text(l);cm('modal-location');}else{alert('Please enter a location');}});
$("#cover-upload input").on("change",function(){
$('#remove-location').on('click',function(){pp.location='';$('#post-location').removeClass('show');});
  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]);
  }
});


// Emoji picker
// Save profile
var emos={'😀':['😀','😃','😄','😁','😆','😅','🤣','😂','🙂','😉','😊','😇','🥰','😍','🤩','😘'],'👋':['👋','🤚','🖐️','✋','🖖','👌','✌️','🤞','🤟','🤘','🤙','👍','👎','✊','👊','👏'],'❤️':['❤️','🧡','💛','💚','💙','💜','🖤','🤍','🤎','💔','❣️','💕','💞','💓','💗','💖'],'🐶':['🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🐨','🐯','🦁','🐮','🐷','🐸','🐵','🐔'],'🍎':['🍎','🍐','🍊','🍋','🍌','🍉','🍇','🍓','🍈','🍒','🍑','🥭','🍍','🥥','🥝','🍅'],'⚽':['⚽','🏀','🏈','⚾','🎾','🏐','🏉','🎱','🏓','🏸','🏒','🥊','🎿','🏄','🏊','🚴']};
$("#save-profile").on("click",function(){
var $pk=$('#emoji-picker'),ecs=Object.keys(emos);
  var $btn=$(this).prop("disabled",true).text("Saving...");
$pk.html('<div class="sa-emoji-header">'+ecs.map(function(c,i){return'<button class="sa-emoji-cat'+(i===0?' active':'')+'" data-cat="'+c+'">'+c+'</button>';}).join('')+'</div><div class="sa-emoji-grid"></div>');
  var newBio=$("#bio-input").val().trim();
function se(c){$pk.find('.sa-emoji-grid').html(emos[c].map(function(e){return'<button class="sa-emoji-btn">'+e+'</button>';}).join(''));}
  var newLocation=$("#location-input").val().trim();
se(ecs[0]);
$pk.on('click','.sa-emoji-cat',function(){$(this).addClass('active').siblings().removeClass('active');se($(this).data('cat'));});
$pk.on('click','.sa-emoji-btn',function(){var em=$(this).text(),$ip=$('#post-input')[0],st=$ip.selectionStart,v=$ip.value;$ip.value=v.slice(0,st)+em+v.slice($ip.selectionEnd);$ip.selectionStart=$ip.selectionEnd=st+em.length;$ip.focus();upd();});
$('#btn-add-emoji').on('click',function(e){e.stopPropagation();$pk.toggleClass('open');});
$(document).on('click',function(e){if(!$(e.target).closest('#emoji-picker,#btn-add-emoji').length)$pk.removeClass('open');});


$('#post-input').on('input',upd);
  var uploads=[];
function upd(){var h=$('#post-input').val().trim()||pp.image||pp.video;$('#btn-submit-post').prop('disabled',!h);}
  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);}));


$('#btn-submit-post').on('click',function(){var t=$('#post-input').val().trim();if(!t&&!pp.image&&!pp.video){alert('Please add some content');return;}var $b=$(this);$b.prop('disabled',true).text('Posting...');var p={action:'socialfeed',sfaction:'createpost',content:t||'(media)'};if(pp.image)p.image_url=pp.image;if(pp.video)p.video_url=pp.video;if(pp.location)p.location=pp.location;api.postWithToken('csrf',p).then(function(){$('#post-input').val('');pp={image:'',video:'',location:''};$('#post-preview').removeClass('show').find('img,video').remove();$('#post-location').removeClass('show');$b.prop('disabled',true).text('Post');loadP();}).fail(function(c,d){alert('Error posting: '+(d&&d.error?d.error.info:c));$b.prop('disabled',false).text('Post');});});
  $.when.apply($,uploads).then(function(){
    ud.bio=newBio;
    ud.location=newLocation;
    localStorage.setItem("sf_bio_"+user,newBio);
    localStorage.setItem("sf_location_"+user,newLocation);


// Stories
    // Update UI
$a.on('click','#add-story-btn',function(){upSto.reset();$('#input-story-url').val('');$('#preview-story-url').removeClass('show');om('modal-story');});
    $("#profile-pic").html(av(ud.photo,user,64));
$('#add-story').on('click',function(){var ap=$('#modal-story .sa-tab.active').data('panel');function ps(u){api.postWithToken('csrf',{action:'socialfeed',sfaction:'createstory',image_url:u}).then(function(){cm('modal-story');upSto.reset();loadS();}).fail(function(c,d){alert('Error: '+(d&&d.error?d.error.info:c));});}if(ap==='panel-story-url'){var u=$('#input-story-url').val().trim();if(u){ps(u);}else{alert('Please enter a URL');}}else if(uf.story){upload(uf.story,'Story',upSto.setP).then(ps).fail(function(){alert('Upload error');});}else{alert('Please select an image');}});
    $(".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');


var sto;
    cm("profile-modal");
$a.on('click','.sa-story[data-story-url]',function(){var u=$(this).data('story-url'),n=$(this).data('story-user'),p=localStorage.getItem('sf_photo_'+n)||'';$('#story-viewer-image').attr('src',u);$('#story-viewer-name').text(n);$('#story-viewer-avatar').html(av(p,n,'s'));$('#story-viewer').addClass('open');var $pb=$('#sv-prog');$pb.css('width','0');setTimeout(function(){$pb.css({width:'100%',transition:'width 5s linear'});},10);clearTimeout(sto);sto=setTimeout(function(){$('#story-viewer').removeClass('open');$pb.css({width:'0',transition:'none'});},5000);});
    $btn.prop("disabled",false).text("Save changes");
$('#close-story-viewer,#story-viewer').on('click',function(e){if(e.target.id==='close-story-viewer'||e.target.id==='story-viewer'){$('#story-viewer').removeClass('open');$('#sv-prog').css({width:'0',transition:'none'});clearTimeout(sto);}});
    picFile=null;
    coverFile=null;
    toast("Profile updated!");
  }).fail(function(e){
    toast("Error saving profile: "+e);
    $btn.prop("disabled",false).text("Save changes");
  });
});


// Feed interactions
// Like post
$a.on('click','.sa-post-action[data-like]',function(){var id=$(this).data('like');api.postWithToken('csrf',{action:'socialfeed',sfaction:'react',post_id:id,reaction_type:'like'}).then(loadP);});
$app.on("click",".sf-post-act[data-like]",function(){
$a.on('click','.sa-post-menu[data-delete]',function(){if(confirm('Delete this post?')){api.postWithToken('csrf',{action:'socialfeed',sfaction:'deletepost',post_id:$(this).data('delete')}).then(loadP);}});
  var $btn=$(this);
$a.on('click','.sa-post-comments-count',function(){var id=$(this).data('post'),$s=$('#comments-'+id);if($s.hasClass('show')){$s.removeClass('show');}else{$s.addClass('show');loadC(id);}});
  var postId=$btn.data("like");
$a.on('keypress','.sa-post-comment-input',function(e){if(e.which===13){var $i=$(this),t=$i.val().trim(),id=$i.data('post');if(t){$i.val('').prop('disabled',true);api.postWithToken('csrf',{action:'socialfeed',sfaction:'comment',post_id:id,content:t}).then(function(){$i.prop('disabled',false);loadC(id);loadP();}).fail(function(){$i.prop('disabled',false);});}}});
  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);
});


function loadP(){
// Submit comment
api.get({action:'socialfeed',sfaction:'getposts',limit:20}).then(function(r){
$app.on("keypress",".sf-cmt-input",function(e){
var ps=(r.socialfeed&&r.socialfeed.posts)||[];
  if(e.which===13){
$('#post-count').text(ps.length);
    var $input=$(this);
if(!ps.length){$('#feed-container').html('<div class="sa-empty"><div class="sa-empty-icon">📷</div><div class="sa-empty-title">Share Photos</div><div class="sa-empty-text">When you share photos, they will appear here.</div></div>');return;}
    var text=$input.val().trim();
var h=ps.map(function(p){
    var postId=$input.data("id");
var up=localStorage.getItem('sf_photo_'+p.username)||'',tr=0;
    if(text){
for(var k in p.reaction_counts)tr+=p.reaction_counts[k];
      $input.val("").prop("disabled",true);
var m='';
      api.postWithToken("csrf",{action:"socialfeed",sfaction:"comment",post_id:postId,content:text}).then(function(){
if(p.image_url)m='<div class="sa-post-media"><img src="'+esc(p.image_url)+'"></div>';
        $input.prop("disabled",false);
if(p.video_url){if(p.video_url.match(/youtube|youtu\.be/)){var vi=(p.video_url.match(/(?:v=|youtu\.be\/)([^&]+)/)||[])[1];if(vi)m='<div class="sa-post-media"><iframe src="https://www.youtube.com/embed/'+vi+'" allowfullscreen></iframe></div>';}else if(p.video_url.match(/vimeo/)){var vm=(p.video_url.match(/vimeo\.com\/(\d+)/)||[])[1];if(vm)m='<div class="sa-post-media"><iframe src="https://player.vimeo.com/video/'+vm+'" allowfullscreen></iframe></div>';}else{m='<div class="sa-post-media"><video src="'+esc(p.video_url)+'" controls></video></div>';}}
        loadComments(postId);
var lo=p.location?'<div class="sa-post-location">📍 '+esc(p.location)+'</div>':'';
        loadFeed();
var del=p.username===user?'<button class="sa-post-menu" data-delete="'+p.id+'">🗑️</button>':'';
      }).catch(function(e){
var lc=p.user_reaction==='like'?' liked':'';
        $input.prop("disabled",false);
return'<div class="sa-post"><div class="sa-post-header"><div class="sa-post-avatar">'+av(up,p.username,'p')+'</div><div class="sa-post-user"><div class="sa-post-username">'+esc(p.username)+'</div><div class="sa-post-meta">'+ago(p.created)+'</div></div>'+del+'</div>'+(p.content&&p.content!=='(media)'?'<div class="sa-post-content">'+esc(p.content)+'</div>':'')+lo+m+'<div class="sa-post-actions"><button class="sa-post-action'+lc+'" data-like="'+p.id+'">'+(p.user_reaction==='like'?'❤️':'🤍')+'</button><button class="sa-post-action">💬</button><button class="sa-post-action">📤</button><button class="sa-post-action" style="margin-left:auto">🔖</button></div>'+(tr?'<div class="sa-post-likes">'+tr+' like'+(tr>1?'s':'')+'</div>':'')+(p.comments?'<div class="sa-post-comments-count" data-post="'+p.id+'">View all '+p.comments+' comment'+(p.comments>1?'s':'')+'</div>':'')+'<div class="sa-comments-section" id="comments-'+p.id+'"></div><div class="sa-post-time">'+ago(p.created)+'</div><div class="sa-post-comment-form"><input class="sa-post-comment-input" data-post="'+p.id+'" placeholder="Add a comment..."><button class="sa-post-comment-submit">Post</button></div></div>';
        toast("Error: "+e);
}).join('');
      });
$('#feed-container').html(h);
    }
}).fail(function(){$('#feed-container').html('<div class="sa-empty"><div class="sa-empty-icon">😕</div><div class="sa-empty-title">Error Loading</div><div class="sa-empty-text">Could not load posts.</div></div>');});
  }
}
});


function loadC(id){
// Share post
api.get({action:'socialfeed',sfaction:'getcomments',post_id:id}).then(function(r){
$app.on("click",".sf-post-act.share-btn",function(){
var cs=(r.socialfeed&&r.socialfeed.comments)||[];
  var url=window.location.href;
var h=cs.map(function(c){var up=localStorage.getItem('sf_photo_'+c.username)||'';return'<div class="sa-comment"><div class="sa-comment-avatar">'+av(up,c.username,'s')+'</div><div class="sa-comment-body"><div class="sa-comment-text"><strong>'+esc(c.username)+'</strong> '+esc(c.content)+'</div><div class="sa-comment-time">'+ago(c.created)+'</div></div></div>';}).join('');
  if(navigator.clipboard){
$('#comments-'+id).html(h||'<div style="color:#8e8e8e;font-size:14px">No comments yet</div>');
    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">&times;</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 loadS(){
function loadComments(postId){
api.get({action:'socialfeed',sfaction:'getstories'}).then(function(r){
  api.get({action:"socialfeed",sfaction:"getcomments",post_id:postId}).then(function(r){
var ss=(r.socialfeed&&r.socialfeed.stories)||[];
    var comments=(r.socialfeed&&r.socialfeed.comments)||[];
var h='<div class="sa-story"><div class="sa-story-ring add"><div class="sa-story-inner" id="add-story-btn">+</div></div><div class="sa-story-name">Your story</div></div>';
    var html=comments.length?comments.map(function(c){
h+=ss.map(function(s){var up=localStorage.getItem('sf_photo_'+s.username)||'';return'<div class="sa-story" data-story-url="'+esc(s.image_url)+'" data-story-user="'+esc(s.username)+'"><div class="sa-story-ring"><div class="sa-story-inner">'+av(up,s.username,'s')+'</div></div><div class="sa-story-name">'+esc(s.username)+'</div></div>';}).join('');
      var pic=localStorage.getItem("sf_photo_"+c.username)||"";
$('#stories-container').html(h);
      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>';
}).fail(function(){});
    }).join(""):'<div style="padding:8px;color:#65676b;text-align:center">No comments yet. Be the first!</div>';
    $("#comments-"+postId).html(html);
  });
}
}


build();
// Initial load
events();
loadFeed();
loadP();
 
loadS();
});
});
})();
}

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">&times;</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">&times;</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">&times;</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();

});
}