feat: 实现关注系统 + 通知侧边栏分类 TAB + 点赞聚合通知
- 新增关注系统:UserFollow 模型、FollowService(toggle/status/列表隐私控制) - User 新增 FollowersCount/FollowingCount/FollowListPublic/NotifyPrefs 字段 - Space 页面增加四态关注按钮(关注/已关注/回关/已互粉)+ 粉丝/关注数链接 - 新增 /space/:uid/followers 和 /space/:uid/following 列表页 - 新增 NotifyFollow/NotifyLikeAggregated 通知类型 - ReactionService 点赞时写入 daily_like_summary,访问时生成聚合通知 - FollowService 关注时触发 NotifyFollow 通知 - 消息中心侧边栏升级为分类 TAB(全部/系统通知/@艾特/点赞/关注) - NotificationService 新增 ListByCategory 按分类分页查询
This commit is contained in:
@ -30,6 +30,15 @@
|
||||
<span class="space-divider">·</span>
|
||||
<span class="space-joined">加入于 {{.SpaceUser.CreatedAt.Format "2006-01-02"}}</span>
|
||||
</div>
|
||||
<div class="follow-links">
|
||||
<a href="/space/{{.SpaceUser.ID}}/following"><strong>{{.SpaceUser.FollowingCount}}</strong> 关注</a>
|
||||
<a href="/space/{{.SpaceUser.ID}}/followers"><strong>{{.SpaceUser.FollowersCount}}</strong> 粉丝</a>
|
||||
</div>
|
||||
{{if not .IsOwnSpace}}
|
||||
<div class="follow-btn-wrapper">
|
||||
<button id="followBtn" class="follow-btn" data-uid="{{.SpaceUser.ID}}">关注</button>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -89,5 +98,75 @@
|
||||
{{template "layout/footer.html" .}}
|
||||
|
||||
<script src="/static/js/common.js?v={{assetV "/static/js/common.js"}}"></script>
|
||||
{{if not .IsOwnSpace}}
|
||||
<script>
|
||||
(function() {
|
||||
var followBtn = document.getElementById('followBtn');
|
||||
if (!followBtn) return;
|
||||
var targetUid = followBtn.getAttribute('data-uid');
|
||||
var csrfMeta = document.querySelector('meta[name="csrf-token"]');
|
||||
var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
|
||||
|
||||
// 四个按钮状态文案
|
||||
var labels = {
|
||||
none: '关注',
|
||||
following: '已关注',
|
||||
followBack: '回关',
|
||||
mutual: '已互粉'
|
||||
};
|
||||
|
||||
var classes = {
|
||||
none: '',
|
||||
following: 'following',
|
||||
followBack: '',
|
||||
mutual: 'mutual'
|
||||
};
|
||||
|
||||
function buttonState(status) {
|
||||
if (status.i_follow && status.they_follow) return 'mutual';
|
||||
if (status.i_follow) return 'following';
|
||||
if (status.they_follow) return 'followBack';
|
||||
return 'none';
|
||||
}
|
||||
|
||||
function updateBtn(status) {
|
||||
var state = buttonState(status);
|
||||
followBtn.textContent = labels[state];
|
||||
followBtn.className = 'follow-btn ' + (classes[state] || '');
|
||||
}
|
||||
|
||||
// 获取初始状态
|
||||
fetch('/api/users/' + targetUid + '/follow-status')
|
||||
.then(function(res) { return res.json(); })
|
||||
.then(function(data) {
|
||||
if (data && data.success && data.data) {
|
||||
updateBtn(data.data);
|
||||
}
|
||||
});
|
||||
|
||||
// 点击切换
|
||||
followBtn.addEventListener('click', function() {
|
||||
followBtn.classList.add('waiting');
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/api/users/' + targetUid + '/follow', true);
|
||||
xhr.setRequestHeader('X-CSRF-Token', csrfToken);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4) {
|
||||
followBtn.classList.remove('waiting');
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
var resp = JSON.parse(xhr.responseText);
|
||||
if (resp.data && resp.data.status) {
|
||||
updateBtn(resp.data.status);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user