mirror of
https://github.com/status-im/fathom.git
synced 2025-03-01 03:20:27 +00:00
only re-fetch data when needed
This commit is contained in:
parent
876309ae59
commit
53c1702d0d
16
assets/src/js/components/Chart.js
vendored
16
assets/src/js/components/Chart.js
vendored
@ -100,14 +100,18 @@ class Chart extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if(this.props == newProps) {
|
||||
componentWillReceiveProps(newProps, newState) {
|
||||
if(!this.paramsChanged(this.props, newProps)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchData(newProps);
|
||||
this.fetchData(newProps)
|
||||
}
|
||||
|
||||
paramsChanged(o, n) {
|
||||
return o.siteId != n.siteId || o.before != n.before && o.after != n.after;
|
||||
}
|
||||
|
||||
@bind
|
||||
prepareChart() {
|
||||
let padding = { top: 12, right: 12, bottom: 24, left: 40 };
|
||||
@ -215,10 +219,10 @@ class Chart extends Component {
|
||||
fetchData(props) {
|
||||
this.setState({ loading: true })
|
||||
|
||||
Client.request(`/sites/${props.site.id}/stats/site/groupby/day?before=${props.before}&after=${props.after}`)
|
||||
Client.request(`/sites/${props.siteId}/stats/site/groupby/day?before=${props.before}&after=${props.after}`)
|
||||
.then((d) => {
|
||||
// request finished; check if args changed in the meantime
|
||||
if( props != this.props) {
|
||||
// request finished; check if params changed in the meantime
|
||||
if( this.paramsChanged(props, this.props)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,18 @@ class Realtime extends Component {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps, newState) {
|
||||
if(!this.paramsChanged(this.props, newProps)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchData()
|
||||
}
|
||||
|
||||
paramsChanged(o, n) {
|
||||
return o.siteId != n.siteId || o.before != n.before && o.after != n.after;
|
||||
}
|
||||
|
||||
@bind
|
||||
setDocumentTitle() {
|
||||
// update document title
|
||||
@ -33,16 +45,12 @@ class Realtime extends Component {
|
||||
|
||||
@bind
|
||||
fetchData() {
|
||||
Client.request(`/sites/${this.props.site.id}/stats/site/realtime`)
|
||||
let url = `/sites/${this.props.siteId}/stats/site/realtime`
|
||||
Client.request(url)
|
||||
.then((d) => {
|
||||
this.setState({ count: d })
|
||||
this.setDocumentTitle();
|
||||
})
|
||||
.catch((e) => {
|
||||
if(e.message == 401) {
|
||||
this.props.onError();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
render(props, state) {
|
||||
|
@ -17,24 +17,25 @@ class Sidebar extends Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps, newState) {
|
||||
if(newProps == this.props) {
|
||||
if(!this.paramsChanged(this.props, newProps)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchData(newProps);
|
||||
}
|
||||
|
||||
|
||||
|
||||
paramsChanged(o, n) {
|
||||
return o.siteId != n.siteId || o.before != n.before && o.after != n.after;
|
||||
}
|
||||
|
||||
@bind
|
||||
fetchData(props) {
|
||||
this.setState({ loading: true })
|
||||
|
||||
Client.request(`/sites/${props.site.id}/stats/site?before=${props.before}&after=${props.after}`)
|
||||
Client.request(`/sites/${props.siteId}/stats/site?before=${props.before}&after=${props.after}`)
|
||||
.then((data) => {
|
||||
// request finished; check if timestamp range is still the one user wants to see
|
||||
if( props != this.props ) {
|
||||
if(this.paramsChanged(props, this.props)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -43,8 +43,8 @@ class SiteSettings extends Component {
|
||||
onSubmit(evt) {
|
||||
evt.preventDefault();
|
||||
let site = this.props.site;
|
||||
|
||||
Client.request('sites', {
|
||||
let url = site.id > 0 ? `/sites/${site.id}` : `/sites`
|
||||
Client.request(url, {
|
||||
method: "POST",
|
||||
data: {
|
||||
id: site.id,
|
||||
@ -77,18 +77,20 @@ class SiteSettings extends Component {
|
||||
}
|
||||
|
||||
render(props, state) {
|
||||
let newSite = props.site.id == 0;
|
||||
|
||||
// TODO: Render different form for new sites vs. existing sites
|
||||
return (
|
||||
<div class="modal-wrap" style={"display: " + ( props.visible ? '' : 'none')} onClick={this.maybeCloseModal}>
|
||||
<div class="modal">
|
||||
<p>Update your site name or get your tracking code</p>
|
||||
<p>{newSite ? 'Add a new site to track with Fathom' : 'Update your site name or get your tracking code'}</p>
|
||||
<form onSubmit={this.onSubmit}>
|
||||
<fieldset>
|
||||
<label for="site-name">Site Name</label>
|
||||
<label for="site-name">Site name</label>
|
||||
<input type="text" name="site-name" id="site-name" placeholder="" onChange={this.updateSiteName} value={props.site.name} />
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<fieldset style={newSite ? 'display: none;' : ''}>
|
||||
<label>Add this code to your website <small class="right">(site ID = {props.site.trackingId})</small></label>
|
||||
<textarea ref={(el) => { this.textarea = el }} onClick={this.onTextareaClick} readonly="readonly">{`<!-- Fathom - simple website analytics - https://github.com/usefathom/fathom -->
|
||||
<script>
|
||||
@ -111,8 +113,8 @@ fathom('trackPageview');
|
||||
|
||||
<fieldset>
|
||||
<div class="half">
|
||||
<div class="submit"><button type="submit">Update site name</button></div>
|
||||
<div class="delete"><a href="javascript:void(0);" onClick={this.deleteSite}>Delete site</a></div>
|
||||
<div class="submit"><button type="submit">{newSite ? 'Create site' : 'Update site name'}</button></div>
|
||||
{newSite ? '' : (<div class="delete"><a href="javascript:void(0);" onClick={this.deleteSite}>Delete site</a></div>)}
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
@ -21,22 +21,26 @@ class Table extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if(this.props == newProps) {
|
||||
return;
|
||||
}
|
||||
componentWillReceiveProps(newProps, newState) {
|
||||
if(!this.paramsChanged(this.props, newProps)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchData(newProps);
|
||||
this.fetchData(newProps)
|
||||
}
|
||||
|
||||
paramsChanged(o, n) {
|
||||
return o.siteId != n.siteId || o.before != n.before && o.after != n.after;
|
||||
}
|
||||
|
||||
@bind
|
||||
fetchData(props) {
|
||||
this.setState({ loading: true });
|
||||
|
||||
Client.request(`/sites/${props.site.id}/${props.endpoint}?before=${props.before}&after=${props.after}&limit=${this.state.limit}`)
|
||||
Client.request(`/sites/${props.siteId}/${props.endpoint}?before=${props.before}&after=${props.after}&limit=${this.state.limit}`)
|
||||
.then((d) => {
|
||||
// request finished; check if timestamp range is still the one user wants to see
|
||||
if( this.props != props ) {
|
||||
if( this.paramsChanged(props, this.props) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -47,7 +51,7 @@ class Table extends Component {
|
||||
});
|
||||
|
||||
// fetch totals too
|
||||
Client.request(`/sites/${props.site.id}/${props.endpoint}/pageviews?before=${props.before}&after=${props.after}`)
|
||||
Client.request(`/sites/${props.siteId}/${props.endpoint}/pageviews?before=${props.before}&after=${props.after}`)
|
||||
.then((d) => {
|
||||
this.setState({
|
||||
total: d
|
||||
|
@ -111,7 +111,7 @@ class Dashboard extends Component {
|
||||
<li class="logo"><a href="/">Fathom</a></li>
|
||||
<SiteSwitcher sites={state.sites} selectedSite={state.site} onChange={this.changeSelectedSite} onAdd={this.openSiteSettings} showAdd={!state.isPublic}/>
|
||||
<Gearwheel onClick={this.openSiteSettings} visible={!state.isPublic} />
|
||||
<li class="visitors"><Realtime site={state.site} /></li>
|
||||
<li class="visitors"><Realtime siteId={state.site.id} /></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
@ -122,17 +122,17 @@ class Dashboard extends Component {
|
||||
</nav>
|
||||
|
||||
<div class="boxes">
|
||||
<Sidebar site={state.site} before={state.before} after={state.after} />
|
||||
<Sidebar siteId={state.site.id} before={state.before} after={state.after} />
|
||||
|
||||
<div class="boxes-col">
|
||||
<div class="box box-graph">
|
||||
<Chart site={state.site} before={state.before} after={state.after} />
|
||||
<Chart siteId={state.site.id} before={state.before} after={state.after} />
|
||||
</div>
|
||||
<div class="box box-pages">
|
||||
<Table endpoint="stats/pages" headers={["Top pages", "Views", "Uniques"]} site={state.site} before={state.before} after={state.after} />
|
||||
<Table endpoint="stats/pages" headers={["Top pages", "Views", "Uniques"]} siteId={state.site.id} before={state.before} after={state.after} />
|
||||
</div>
|
||||
<div class="box box-referrers">
|
||||
<Table endpoint="stats/referrers" headers={["Top referrers", "Views", "Uniques"]} site={state.site} before={state.before} after={state.after} showHostname="true" />
|
||||
<Table endpoint="stats/referrers" headers={["Top referrers", "Views", "Uniques"]} siteId={state.site.id} before={state.before} after={state.after} showHostname="true" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -56,6 +56,7 @@ func (agg *aggregator) Run() int {
|
||||
trackingIDMap[s.TrackingID] = s.ID
|
||||
}
|
||||
|
||||
// add each pageview to the various statistics we gather
|
||||
for _, p := range pageviews {
|
||||
|
||||
// discard pageview if site tracking ID is unknown
|
||||
@ -81,7 +82,6 @@ func (agg *aggregator) Run() int {
|
||||
|
||||
// referrer stats
|
||||
if p.Referrer != "" {
|
||||
|
||||
hostname, pathname, err := parseUrlParts(p.Referrer)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -2,14 +2,21 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/usefathom/fathom/pkg/models"
|
||||
)
|
||||
|
||||
// seed rand pkg on program init
|
||||
func init() {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
}
|
||||
|
||||
// GET /api/sites
|
||||
func (api *API) GetSitesHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
result, err := api.database.GetSites()
|
||||
@ -22,17 +29,31 @@ func (api *API) GetSitesHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
// POST /api/sites
|
||||
// POST /api/sites/{id}
|
||||
func (api *API) SaveSiteHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
s := &models.Site{}
|
||||
var s *models.Site
|
||||
vars := mux.Vars(r)
|
||||
sid, ok := vars["id"]
|
||||
if ok {
|
||||
id, err := strconv.ParseInt(sid, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s, err = api.database.GetSite(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
s = &models.Site{
|
||||
TrackingID: generateTrackingID(),
|
||||
}
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// generate tracking ID if this is a new site
|
||||
if s.ID == 0 && s.TrackingID == "" {
|
||||
s.TrackingID = generateTrackingID()
|
||||
}
|
||||
|
||||
log.Printf("Site tracking ID: %s\n", s.TrackingID)
|
||||
if err := api.database.SaveSite(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ type Datastore interface {
|
||||
|
||||
// sites
|
||||
GetSites() ([]*models.Site, error)
|
||||
GetSite(id int64) (*models.Site, error)
|
||||
SaveSite(s *models.Site) error
|
||||
DeleteSite(s *models.Site) error
|
||||
|
||||
|
@ -20,6 +20,13 @@ func (db *sqlstore) GetSites() ([]*models.Site, error) {
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (db *sqlstore) GetSite(id int64) (*models.Site, error) {
|
||||
s := &models.Site{}
|
||||
query := db.Rebind("SELECT * FROM sites WHERE id = ?")
|
||||
err := db.Get(s, query, id)
|
||||
return s, mapError(err)
|
||||
}
|
||||
|
||||
// SaveSite saves the website in the database (inserts or updates)
|
||||
func (db *sqlstore) SaveSite(s *models.Site) error {
|
||||
if s.ID > 0 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user