Gate record features and storage entry points with invitation guidance; enforce record access in the backend. Add homepage greetings, right-swipe profile dismissal, and move the smoking switch to settings. Simplify API base configuration and remove the unused legacy tab bar component.
392 lines
16 KiB
JavaScript
392 lines
16 KiB
JavaScript
const { guardRecordEntries } = require('../../utils/record-access');
|
|
const { tryLoginRenewal } = require('../../utils/subscribe');
|
|
const announcementSession = require('../../utils/announcement-session');
|
|
const { itemsOf, totalOf } = require('../../utils/list-pagination');
|
|
let lastLocationReport = 0;
|
|
let locationReportUser = null;
|
|
const { withDataCache } = require('../../utils/page-cache');
|
|
const { getLayout } = require('../../utils/layout');
|
|
// pages/index/index.js
|
|
const { api, getImageUrl } = require('../../utils/request');
|
|
const { diaryEntry } = require('../../utils/journal');
|
|
const { saveScrollPosition, resetScrollOnTabShow } = require('../../utils/scroll-pos');
|
|
|
|
Page(guardRecordEntries(withDataCache({
|
|
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
|
|
onResize: function() { this.setData(getLayout()); },
|
|
data: {
|
|
showAnnouncement:false, announcement:null,
|
|
greetingDate:'', greetingText:'你好', greetingNote:'把今天的小美好,也留在这里。',
|
|
userId: null,
|
|
coupleId: null,
|
|
|
|
partnerName: '和TA',
|
|
loveStartDate: '',
|
|
daysCount: '?',
|
|
daysLabel: '已注册',
|
|
datePrefix: '注册于',
|
|
partner1AvatarUrl: '',
|
|
partner2AvatarUrl: '',
|
|
partner1Initial: '',
|
|
partner2Initial: '',
|
|
partner1Birthday: '',
|
|
partner2Birthday: '',
|
|
partner1Name: '?',
|
|
partner2Name: '?',
|
|
partner1Location: '',
|
|
partner2Location: '',
|
|
coupleDistance: '',
|
|
|
|
stats: {
|
|
timeline: 0, album: 0, diary: 0,
|
|
songs: 0, checkin: 0, pet: 0
|
|
},
|
|
bgImage: '',
|
|
scrollTop: 0,
|
|
refreshing: false,
|
|
relationshipKnown: false, homeError: false, statsLoading: true, statsError: false, recentDiary: [], recentAlbums: [], upcomingMemory: null
|
|
},
|
|
|
|
onLoad: function() {
|
|
this.updateGreeting();
|
|
this.setData(getLayout());
|
|
this.setData({
|
|
userId: wx.getStorageSync('userId'),
|
|
coupleId: wx.getStorageSync('coupleId'),
|
|
scrollTop: 0
|
|
});
|
|
this.loadBg();
|
|
this.loadData();
|
|
},
|
|
|
|
onShow: function() {
|
|
this.updateGreeting();
|
|
this._announcementActive=true;
|
|
this.loadAnnouncement();
|
|
resetScrollOnTabShow(this, 'index');
|
|
const tabBar = this.getTabBar && this.getTabBar();
|
|
if (tabBar) tabBar.syncActive();
|
|
var prevCoupleId = this.data.coupleId;
|
|
var curCoupleId = wx.getStorageSync('coupleId');
|
|
this.setData({
|
|
userId: wx.getStorageSync('userId'),
|
|
coupleId: curCoupleId
|
|
});
|
|
// 检查是否已被解绑(之前有 coupleId 但被清除了)
|
|
if (prevCoupleId && !curCoupleId) {
|
|
wx.reLaunch({ url: '/pages/index/index' });
|
|
return;
|
|
}
|
|
this.loadBg();
|
|
this.loadData();
|
|
this.fetchIpLocation();
|
|
},
|
|
|
|
updateGreeting: function() {
|
|
const now = new Date(), hour = now.getHours();
|
|
const weekdays = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
|
|
let text, note;
|
|
if (hour < 5) { text='夜深了'; note='记下心里的小事,也别忘了早点休息。'; }
|
|
else if (hour < 11) { text='早上好'; note='新的一天,也有值得珍藏的小美好。'; }
|
|
else if (hour < 14) { text='中午好'; note='好好吃饭,慢慢收藏生活里的甜。'; }
|
|
else if (hour < 18) { text='下午好'; note='让忙碌暂停一会儿,记下此刻的心情。'; }
|
|
else { text='晚上好'; note='今天的故事,想留在哪一页?'; }
|
|
this.setData({greetingDate:(now.getMonth()+1)+'月'+now.getDate()+'日 '+weekdays[now.getDay()],greetingText:text,greetingNote:note});
|
|
},
|
|
|
|
onHide: function() { this._announcementActive=false; },
|
|
onUnload: function() { this._announcementActive=false; },
|
|
loadAnnouncement: function() {
|
|
if(this._announcementLoading)return this._announcementLoading;
|
|
this._announcementLoading=(async()=>{
|
|
try {
|
|
const notice=await api.announcement();
|
|
if(this._announcementActive===false)return;
|
|
const userId=wx.getStorageSync('userId');
|
|
const show=announcementSession.shouldShow(notice,userId);
|
|
this.setData({announcement:notice, showAnnouncement:show});
|
|
if(show)announcementSession.markShown(notice,userId);
|
|
} catch(e) {} // 公告读取失败不阻塞首页。
|
|
})().finally(()=>{this._announcementLoading=null;});
|
|
return this._announcementLoading;
|
|
},
|
|
closeAnnouncement: function() {
|
|
this.setData({showAnnouncement:false});
|
|
},
|
|
preventNoticeMove: function() {},
|
|
// 保存滚动位置
|
|
onScroll: function(e) {
|
|
this._scrollTop = e.detail.scrollTop;
|
|
saveScrollPosition('index', e.detail.scrollTop);
|
|
},
|
|
|
|
_loadingData: false,
|
|
_daysSince: function(dateStr) {
|
|
if (!dateStr) return '?';
|
|
var parts = dateStr.substring(0, 10).split('-').map(Number);
|
|
if (parts.length !== 3 || parts.some(isNaN) || parts[0] < 1970 ||
|
|
parts[1] < 1 || parts[1] > 12 || parts[2] < 1 || parts[2] > 31) return '?';
|
|
var start = new Date(parts[0], parts[1] - 1, parts[2]);
|
|
if (start.getFullYear() !== parts[0] || start.getMonth() !== parts[1] - 1 ||
|
|
start.getDate() !== parts[2]) return '?';
|
|
var now = new Date();
|
|
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
return Math.max(0, Math.round((today - start) / 86400000));
|
|
},
|
|
loadData: async function() {
|
|
var self = this;
|
|
// 防止并发重复加载
|
|
if (this._loadingData) return;
|
|
this._loadingData = true;
|
|
this.setData({ homeError: false });
|
|
try {
|
|
const info = await api.coupleInfo();
|
|
this.setData({userId: wx.getStorageSync('userId')});
|
|
this.setData({ relationshipKnown: true });
|
|
// 未绑定情侣时显示用户注册时间和默认信息
|
|
if (info.not_bound) {
|
|
wx.removeStorageSync('coupleId');
|
|
var p1 = info.partner1 || {};
|
|
let daysCount = '?';
|
|
let loveStartDate = '';
|
|
if (info.love_start_date) {
|
|
daysCount = this._daysSince(info.love_start_date);
|
|
var parts = info.love_start_date.split('-');
|
|
loveStartDate = parseInt(parts[0]) + '年' + parseInt(parts[1]) + '月' + parseInt(parts[2]) + '日';
|
|
}
|
|
this.setData({
|
|
coupleId: null,
|
|
partner1AvatarUrl: getImageUrl(p1.avatar),
|
|
partner1Name: p1.nickname || '未命名',
|
|
partner1Initial: (p1.nickname || '未命名')[0],
|
|
partner1Birthday: '',
|
|
partner1Location: p1.location || '',
|
|
partner2AvatarUrl: '',
|
|
partner2Name: '未绑定',
|
|
partner2Initial: '?',
|
|
partner2Birthday: '',
|
|
partner2Location: '',
|
|
daysCount: daysCount,
|
|
daysLabel: '已注册',
|
|
datePrefix: '注册于',
|
|
loveStartDate: loveStartDate,
|
|
coupleDistance: '',
|
|
stats: { timeline: 0, album: 0, diary: 0, songs: 0, checkin: 0, pet: 0 }
|
|
});
|
|
await this.loadStats();
|
|
return;
|
|
}
|
|
// 同步情侣ID:红包/邀请创建后分享人刷新即可识别
|
|
if (info.id) {
|
|
var prevCid = wx.getStorageSync('coupleId');
|
|
if (prevCid != info.id) {
|
|
wx.setStorageSync('coupleId', info.id);
|
|
}
|
|
}
|
|
const myUid = this.data.userId;
|
|
var p1 = info.partner1 || {};
|
|
var p2 = info.partner2 || {};
|
|
|
|
if (p2.id == myUid) {
|
|
var tmp = p1; p1 = p2; p2 = tmp;
|
|
}
|
|
|
|
let daysCount = '?';
|
|
let loveStartDate = '';
|
|
var togetherStart = info.love_start_date || (info.created_at || '').substring(0, 10);
|
|
if (togetherStart) {
|
|
daysCount = this._daysSince(togetherStart);
|
|
var parts = togetherStart.split('-');
|
|
loveStartDate = parseInt(parts[0]) + '年' + parseInt(parts[1]) + '月' + parseInt(parts[2]) + '日';
|
|
}
|
|
|
|
function getBirthdayText(birthday) {
|
|
if (!birthday) return '';
|
|
var t = new Date(); t.setHours(0,0,0,0);
|
|
var p = birthday.split('-');
|
|
if (p.length !== 3) return '';
|
|
var nb = new Date(t.getFullYear(), parseInt(p[1])-1, parseInt(p[2]));
|
|
if (nb < t) nb.setFullYear(nb.getFullYear()+1);
|
|
var c = Math.ceil((nb - t) / 86400000);
|
|
if (c === 0) return '今天!';
|
|
if (c === 1) return '1天';
|
|
if (!Number.isFinite(c)) return '';
|
|
return c + '天';
|
|
}
|
|
|
|
// 双方相距距离(后端基于双方经纬度用 Haversine 公式计算,单位:公里)
|
|
var coupleDistance = '';
|
|
if (info.distance !== null && info.distance !== undefined && info.distance !== '') {
|
|
var d = parseFloat(info.distance);
|
|
if (!isNaN(d)) {
|
|
coupleDistance = d < 1
|
|
? '相距 ' + Math.round(d * 1000) + ' 米'
|
|
: '相距 ' + d.toFixed(1) + ' 公里';
|
|
}
|
|
}
|
|
|
|
var dn = {
|
|
coupleId: info.id,
|
|
daysCount: daysCount,
|
|
daysLabel: '我们在一起',
|
|
datePrefix: '始于',
|
|
loveStartDate: loveStartDate,
|
|
partner1AvatarUrl: getImageUrl(p1.avatar),
|
|
partner2AvatarUrl: getImageUrl(p2.avatar),
|
|
partner1Name: p1.nickname || p1.username || '?',
|
|
partner2Name: p2.nickname || p2.username || '?',
|
|
partner1Initial: (p1.nickname || p1.username || '?')[0],
|
|
partner2Initial: (p2.nickname || p2.username || '?')[0],
|
|
partner1Birthday: getBirthdayText(p1.birthday),
|
|
partner2Birthday: getBirthdayText(p2.birthday),
|
|
partner1Location: (p1.location && !/^-?\d+(\.\d+)?,-?\d+(\.\d+)?$/.test(p1.location)) ? p1.location : '',
|
|
partner2Location: (p2.location && !/^-?\d+(\.\d+)?,-?\d+(\.\d+)?$/.test(p2.location)) ? p2.location : '',
|
|
coupleDistance: coupleDistance
|
|
};
|
|
|
|
this.setData(dn);
|
|
await this.loadStats();
|
|
|
|
} catch (e) {
|
|
this.setData({ homeError: true, relationshipKnown: !!this._hasCachedData, statsLoading: false, statsError: true });
|
|
console.error(e);
|
|
|
|
} finally {
|
|
this._loadingData = false;
|
|
}
|
|
},
|
|
|
|
loadStats: async function() {
|
|
if (!this.data.coupleId) {
|
|
this.setData({recentDiary:[],recentAlbums:[],upcomingMemory:null,statsLoading:false,statsError:false,stats:{timeline:0,album:0,diary:0,songs:0,checkin:0,pet:0}});
|
|
return;
|
|
}
|
|
this.setData({ statsLoading: !this._hasCachedData, statsError: false });
|
|
try {
|
|
const [timeline, album, diary, songs, checkin, pet] = await Promise.all([
|
|
api.timelineList(), api.albumList(), api.diaryList(),
|
|
api.songsList(), api.checkinList(),
|
|
this.data.coupleId ? api.petInfo() : Promise.resolve({ has_pet: false })
|
|
]);
|
|
var photoCount = album && album.photo_total != null ? Number(album.photo_total) : itemsOf(album).reduce((sum,item)=>sum+(Number(item.photo_count)||0),0);
|
|
var petLevel = 0;
|
|
if (pet && pet.has_pet && pet.pet) {
|
|
petLevel = parseInt(pet.pet.level || 0);
|
|
}
|
|
var recentDiary = ((diary && diary.items) || []).slice(0, 1).map(diaryEntry);
|
|
var today = new Date(); today.setHours(0,0,0,0);
|
|
var future = itemsOf(timeline).filter(function(item) { return item.event_date && new Date(item.event_date.replace(/-/g, '/')) >= today; }).sort(function(a,b) { return a.event_date.localeCompare(b.event_date); });
|
|
var next = timeline && !Array.isArray(timeline) && 'upcoming' in timeline ? timeline.upcoming : future[0];
|
|
this.setData({
|
|
recentDiary: recentDiary,
|
|
recentAlbums: itemsOf(album).filter(function(item) { return !!item.cover; }).slice(0,3).map(function(item) { return { id:item.id, title:item.title || '我们的相册', cover:getImageUrl(item.cover) }; }),
|
|
upcomingMemory: next ? { title: next.title || '下一个记忆日', date: next.event_date, dateMonth: next.event_date.slice(5,7), dateDay: next.event_date.slice(8,10), days: Math.ceil((new Date(next.event_date.replace(/-/g, '/')) - today) / 86400000) } : null,
|
|
stats: {
|
|
timeline: totalOf(timeline),
|
|
album: photoCount,
|
|
diary: (diary && diary.total) ? diary.total : 0,
|
|
songs: totalOf(songs),
|
|
checkin: (checkin && checkin.checkin_log_total !== undefined) ? checkin.checkin_log_total : ((checkin && checkin.items) ? checkin.items.length : (Array.isArray(checkin) ? checkin.length : 0)),
|
|
pet: petLevel
|
|
}
|
|
});
|
|
} catch (e) { console.error(e); this.setData({ statsError: true }); } finally { this.setData({ statsLoading: false }); }
|
|
},
|
|
|
|
// 获取本机经纬度并上报后端解析地址(后端按距离判断是否重新解析,超过5km才更新),并刷新首页双方地址显示
|
|
fetchIpLocation: function() {
|
|
const locationUser = wx.getStorageSync('userId');
|
|
if (locationReportUser === locationUser && Date.now() - lastLocationReport < 300000) return;
|
|
locationReportUser = locationUser;
|
|
lastLocationReport = Date.now();
|
|
var self = this;
|
|
var report = function(lat, lng) {
|
|
var data = {};
|
|
if (lat && lng) { data.lat = lat; data.lng = lng; }
|
|
api.updateIpLocation(data).then(function(resp) {
|
|
if (resp && resp.location) {
|
|
self.loadData();
|
|
}
|
|
}).catch(function() { lastLocationReport = 0; });
|
|
};
|
|
// 优先用模糊定位获取经纬度(getFuzzyLocation 与 getLocation 互斥,统一用模糊定位)
|
|
if (wx.getFuzzyLocation) {
|
|
wx.getFuzzyLocation({
|
|
type: 'gcj02',
|
|
success: function(res) {
|
|
report(res.latitude, res.longitude);
|
|
},
|
|
fail: function() {
|
|
report(0, 0);
|
|
}
|
|
});
|
|
} else {
|
|
report(0, 0);
|
|
}
|
|
},
|
|
|
|
// 背景图以接口返回为准
|
|
loadBg: function() {
|
|
var self = this;
|
|
return api.getBgConfig().then(function(cfg) {
|
|
self.setData({ bgImage: cfg && cfg.image ? getImageUrl(cfg.image) : '' });
|
|
}).catch(function(e){ console.warn('loadBg error:', e && (e.error || e._statusCode || e)); });
|
|
},
|
|
|
|
// 跳转前保存当前滚动位置
|
|
_savePosBeforeLeave: function() {
|
|
saveScrollPosition('index', this._scrollTop == null ? this.data.scrollTop : this._scrollTop);
|
|
},
|
|
|
|
// 页面跳转
|
|
goToDiary: function() { this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/diary/diary' }); },
|
|
goToProfile: function() { this._savePosBeforeLeave(); wx.switchTab({ url: '/pages/profile/profile' }); },
|
|
goToTimeline: function() { this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/timeline/timeline' }); },
|
|
goToAlbum: function() { this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/albums/albums' }); },
|
|
goToSongs: function() { this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/lovewords/lovewords' }); },
|
|
goToCheckin: function() { this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/punch/punch' }); },
|
|
goToPet: function() { this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/pet/pet' }); },
|
|
goToPeriod: function() { this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/period/period' }); },
|
|
bindPartner: function() { getApp().globalData.openProfileBind = true; wx.switchTab({ url: '/pages/profile/profile' }); },
|
|
openJournalEntry: function(e) { wx.navigateTo({ url: '/pages/diary_detail/diary_detail?id=' + e.currentTarget.dataset.id }); },
|
|
writeDiary: function() { wx.navigateTo({ url: '/pages/publish/publish?type=diary' }); },
|
|
openAlbumPreview: function(e) { wx.navigateTo({ url: '/pages/album/album?album_id=' + e.currentTarget.dataset.id }); },
|
|
openRecentDiary: function(e) { wx.navigateTo({ url: '/pages/diary_detail/diary_detail?id=' + e.currentTarget.dataset.id }); },
|
|
showMore: function() {
|
|
wx.switchTab({ url: '/pages/discover/discover' });
|
|
},
|
|
onPullDownRefresh: async function() {
|
|
await this.loadData();
|
|
this.loadBg();
|
|
wx.stopPullDownRefresh();
|
|
},
|
|
onRefresh: async function() {
|
|
this.setData({ refreshing: true });
|
|
try {
|
|
await this.loadData();
|
|
this.loadBg();
|
|
} catch (e) {
|
|
console.error('下拉刷新失败', e);
|
|
} finally {
|
|
this.setData({ refreshing: false });
|
|
}
|
|
},
|
|
|
|
// 分享到好友
|
|
onShareAppMessage: function() {
|
|
return {
|
|
title: '让所有的爱有迹可循',
|
|
path: '/pages/index/index',
|
|
imageUrl: '/images/share.png'
|
|
};
|
|
},
|
|
// 分享到朋友圈
|
|
onShareTimeline: function() {
|
|
return {
|
|
title: '让所有的爱有迹可循',
|
|
query: '',
|
|
imageUrl: '/images/share.png'
|
|
};
|
|
}
|
|
}, {"loadData": ["coupleInfo", "timelineList", "albumList", "diaryList", "songsList", "checkinList", "petInfo", "getBgConfig"]}), ['goToDiary', 'goToTimeline', 'goToAlbum', 'goToSongs', 'goToCheckin', 'goToPet', 'goToPeriod', 'openJournalEntry', 'writeDiary', 'openAlbumPreview', 'openRecentDiary', 'showMore']));
|