-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
加入 Google Calendar 支持,并完成了一个包含前端逻辑的日程页面 #1167
Changes from all commits
096d336
d0027d5
0165d77
c5512f1
c05cb69
f49f08f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ menu: | |
#about: /about | ||
archives: /archives | ||
tags: /tags | ||
schedule: /schedule | ||
#commonweal: /404.html | ||
|
||
|
||
|
@@ -42,11 +43,12 @@ menu: | |
# Key is the name of menu item and value is the name of FontAwsome icon. Key is case-senstive. | ||
# When an question mask icon presenting up means that the item has no mapping icon. | ||
menu_icons: | ||
enable: true | ||
enable: false | ||
#KeyMapsToMenuItemKey: NameOfTheIconFromFontAwesome | ||
home: home | ||
about: user | ||
categories: th | ||
schedule: calendar | ||
tags: tags | ||
archives: archive | ||
commonweal: heartbeat | ||
|
@@ -333,6 +335,22 @@ busuanzi_count: | |
# Enable baidu push so that the blog will push the url to baidu automatically which is very helpful for SEO | ||
baidu_push: false | ||
|
||
# Google Calendar | ||
# Share your recent schedule to others via calendar page | ||
# | ||
# API Documentation: | ||
# https://developers.google.com/google-apps/calendar/v3/reference/events/list | ||
calendar: | ||
enable: false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enable 为 false 之后,底下那些字段都可以 不用注释 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, ok. 我忘了把它关掉了。 |
||
calendar_id: <required> | ||
api_key: <required> | ||
orderBy: startTime | ||
offsetMax: 24 | ||
offsetMin: 4 | ||
timeZone: | ||
showDeleted: false | ||
singleEvents: true | ||
maxResults: 250 | ||
|
||
|
||
#! --------------------------------------------------------------- | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
{% if theme.calendar.enable %} | ||
{% if page.type == 'schedule' %} | ||
|
||
<script> | ||
|
||
// Initialization | ||
var _n = function(arg) { if(arg) return arg; else return void 0;} | ||
|
||
var cal_data = void 0; | ||
|
||
var now = new Date(); | ||
var timeMax = new Date(); | ||
var timeMin = new Date(); | ||
|
||
// Read config form theme config file | ||
var calId = _n('{{ theme.calendar.calendar_id }}') ; | ||
var apiKey = _n('{{ theme.calendar.api_key }}') ; | ||
var orderBy = _n('{{ theme.calendar.ordarBy }}') || 'startTime'; | ||
var showLocation = _n('{{ theme.calendar.showLocation }}') || 'false' ; | ||
var offsetMax = _n( {{ theme.calendar.offsetMax }} ) || 72 ; | ||
var offsetMin = _n( {{ theme.calendar.offsetMin }} ) || 4 ; | ||
var timeZone = _n( {{ theme.calendar.timeZone }} ) || void 0 ; | ||
var showDeleted = _n( {{ theme.calendar.showDeleted }} ) || 'false' ; | ||
var singleEvents = _n( {{ theme.calendar.singleEvents }} ) || 'true' ; | ||
var maxResults = _n( {{ theme.calendar.maxResults }} ) || '250' ; | ||
|
||
timeMax.setHours(now.getHours() + offsetMax); | ||
timeMin.setHours(now.getHours() - offsetMin); | ||
|
||
// Build URL | ||
BASE_URL = 'https://www.googleapis.com/calendar/v3/calendars/'; | ||
FIELD_KEY = 'key'; | ||
FIELD_ORDERBY = 'orderBy'; | ||
FIELD_TIMEMAX = 'timeMax'; | ||
FIELD_TIMEMIN = 'timeMin'; | ||
FIELD_TIMEZONE = 'timeZone'; | ||
FIELD_SHOWDELETED = 'showDeleted'; | ||
FIELD_SINGLEEVENTS = 'singleEvents'; | ||
FIELD_MAXRESULTS = 'maxResults'; | ||
|
||
timeMaxISO = timeMax.toISOString(); | ||
timeMinISO = timeMin.toISOString(); | ||
|
||
request_url = BASE_URL + calId + '/events?' + | ||
FIELD_KEY + '=' + apiKey + '&' + | ||
FIELD_ORDERBY + '=' + orderBy + '&' + | ||
FIELD_TIMEMAX + '=' + timeMaxISO + '&' + | ||
FIELD_TIMEMIN + '=' + timeMinISO + '&' + | ||
FIELD_SHOWDELETED + '=' + showDeleted + '&' + | ||
FIELD_SINGLEEVENTS + '=' + singleEvents + '&' + | ||
FIELD_MAXRESULTS + '=' + maxResults; | ||
|
||
if (timeZone) { | ||
request_url = request_url + '&' + FIELD_TIMEZONE + '=' + timeZone; | ||
} | ||
|
||
fetchData(); | ||
var queryLoop = setInterval(fetchData, 60000); | ||
|
||
function fetchData() { | ||
$.ajax({ | ||
dataType: 'json', | ||
url: request_url, | ||
success: function(data) { | ||
|
||
$eventList = $('#schedule #event-list'); | ||
|
||
// clean the event list | ||
$eventList.html(""); | ||
var prevEnd = 0; // used to decide where to insert an <hr> | ||
|
||
data['items'].forEach((event) => { | ||
|
||
// parse data | ||
var start = new Date(event.start.dateTime); | ||
var end = new Date(event.end.dateTime); | ||
|
||
tense = judgeTense(now, start, end); // 0:now 1:future -1:past | ||
|
||
if (tense == 1 && prevEnd < now) $eventList.append('<hr>'); | ||
|
||
eventDOM = buildEventDOM(tense, event); | ||
$eventList.append(eventDOM); | ||
|
||
prevEnd = end; | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
function getRelativeTime(current, previous) { | ||
var msPerMinute = 60 * 1000; | ||
var msPerHour = msPerMinute * 60; | ||
var msPerDay = msPerHour * 24; | ||
var msPerMonth = msPerDay * 30; | ||
var msPerYear = msPerDay * 365; | ||
|
||
var elapsed = current - previous; | ||
var tense = elapsed > 0 ? "ago" : "later"; | ||
|
||
elapsed = Math.abs(elapsed); | ||
|
||
if ( elapsed < msPerHour ) { | ||
return Math.round(elapsed/msPerMinute) + ' minutes ' + tense; | ||
} | ||
else if ( elapsed < msPerDay ) { | ||
return Math.round(elapsed/msPerHour) + ' hours ' + tense; | ||
} | ||
else if ( elapsed < msPerMonth ) { | ||
return 'about ' + Math.round(elapsed/msPerDay) + ' days ' + tense; | ||
} | ||
else if ( elapsed < msPerYear ) { | ||
return 'about ' + Math.round(elapsed/msPerMonth) + ' months ' + tense; | ||
} | ||
else { | ||
return 'about' + Math.round(elapsed/msPerYear) + ' years' + tense; | ||
} | ||
} | ||
|
||
function judgeTense(now, eventStart, eventEnd) { | ||
if (eventEnd < now) { return -1; } | ||
else if (eventStart > now) { return 1; } | ||
else { return 0; } | ||
} | ||
|
||
function buildEventDOM(tense, event) { | ||
var tenseClass = ""; | ||
var start = new Date(event.start.dateTime); | ||
var end = new Date(event.end.dateTime); | ||
switch(tense) { | ||
case 0 : // now | ||
tenseClass = "event-now"; | ||
break; | ||
case 1 : // future | ||
tenseClass = "event-future"; | ||
break; | ||
case -1: // past | ||
tenseClass = "event-past"; | ||
break; | ||
default: | ||
throw "Time data error"; | ||
} | ||
durationFormat = { | ||
weekday: 'short', | ||
hour: '2-digit', | ||
minute:'2-digit' | ||
}; | ||
relativeTimeStr = (tense == 0) ? "NOW" : getRelativeTime(now, start); | ||
durationStr = start.toLocaleTimeString([], durationFormat) + " - " + | ||
end.toLocaleTimeString([], durationFormat); | ||
|
||
liOpen = `<li class="event ${tenseClass}">`; | ||
liClose = `</li>`; | ||
h2Open = `<h2 class="event-summary">`; | ||
h2Close = `</h2>`; | ||
|
||
locationDOM = ""; | ||
if (showLocation && event['location']) { | ||
locationDOM = `<span class="event-location event-details"> | ||
${event['location']} | ||
</span>`; | ||
} | ||
relativeTimeDOM = `<span class="event-relative-time"> | ||
${relativeTimeStr} | ||
</span>`; | ||
durationDOM = `<span class="event-duration event-details"> | ||
${durationStr} | ||
</span>`; | ||
|
||
eventContent = | ||
liOpen + | ||
h2Open + | ||
event['summary'] + | ||
relativeTimeDOM+ | ||
h2Close + | ||
locationDOM + | ||
durationDOM + | ||
liClose; | ||
return eventContent; | ||
} | ||
|
||
</script> | ||
|
||
{% endif %} | ||
{% endif %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{% extends '_layout.swig' %} | ||
{% import '_macro/sidebar.swig' as sidebar_template %} | ||
|
||
{% block title %} {{ __('title.schedule') }} | {{ config.title }} {% endblock %} | ||
|
||
{% block page_calendar %}page-post-detail{% endblock %} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. {% block page_class %}page-calendar{% endblock %} |
||
|
||
{% block content %} | ||
|
||
<section id="schedule"> | ||
<ul id="event-list"> | ||
</ul> | ||
</section> | ||
|
||
{% endblock %} | ||
|
||
{% block sidebar %} | ||
{{ sidebar_template.render(false) }} | ||
{% endblock %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ | |
|
||
@import "archive"; | ||
@import "categories"; | ||
@import "schedule"; | ||
@import "post-detail"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
@keyframes dot-flash { | ||
from {opacity: 1; transform:scale(1.1);} | ||
to {opacity: 0; transform:scale(1);} | ||
} | ||
|
||
#schedule { | ||
ul#event-list { | ||
padding-left: 30px | ||
hr { | ||
margin: 20px 0 45px 0!important | ||
background: #222 | ||
&:after { | ||
display: inline-block | ||
content: 'NOW' | ||
background: #222 | ||
color: #FFF | ||
font-weight:bold | ||
text-align: right | ||
padding: 0 5px | ||
} | ||
} | ||
li.event { | ||
margin: 20px 0px | ||
background: #F9F9F9 | ||
padding-left: 10px | ||
min-height: 40px | ||
h2.event-summary { | ||
margin: 0 | ||
padding-bottom: 3px | ||
&:before { | ||
display: inline-block | ||
font-family: FontAwesome | ||
font-size: 8px | ||
content: '\f111' | ||
vertical-align: middle | ||
margin-right: 25px | ||
color: #bbb | ||
} | ||
} | ||
span.event-relative-time { | ||
display: inline-block | ||
font-size: 12px | ||
font-weight: 400 | ||
padding-left: 12px | ||
color: #bbb | ||
} | ||
span.event-details { | ||
display: block | ||
color: #bbb | ||
margin-left: 56px | ||
padding-top: 3px | ||
padding-bottom: 6px | ||
text-indent: -24px | ||
line-height: 18px | ||
&:before { | ||
text-indent: 0 | ||
display: inline-block | ||
width: 14px | ||
font-family: FontAwesome | ||
text-align: center | ||
margin-right: 9px | ||
color: #bbb | ||
} | ||
&.event-location:before { | ||
content: '\f041' | ||
} | ||
&.event-duration:before { | ||
content: '\f017' | ||
} | ||
} | ||
} | ||
li.event-past { | ||
background: #FCFCFC | ||
& > * { | ||
opacity: .6 | ||
} | ||
h2.event-summary { | ||
color: #bbb | ||
&:before { | ||
color: #DFDFDF | ||
} | ||
} | ||
} | ||
li.event-now { | ||
background: #222 | ||
color: #FFF | ||
padding: 15px 0 15px 10px | ||
h2.event-summary { | ||
&:before { | ||
transform: scale(1.2) | ||
color: #FFF | ||
animation: dot-flash 1s alternate infinite ease-in-out; | ||
} | ||
} | ||
* { | ||
color: #FFF!important | ||
} | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Menu 这边默认是开启的,这个可以在 template 里面去判断是否 enable,然后显示隐藏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, 好的。我在本地又做了大量优化,但没这两天太忙,没时间拆分支重新 commit,我可能下周再推一个 commit。这个会在下次改过来的。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. Nice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我看了一下,这个 Menu 的 template 没有对其他的 menu-item 做判断,我觉得判断也有点冗余,我会默认注释掉这个菜单项,但不做逻辑判断了
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok