familysearch reference client

Post on 14-Jun-2015

159 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Talk given at the FamilySearch Developers Conference in September 2014

TRANSCRIPT

THE FAMILYSEARCHREFERENCE CLIENT

Open-source implementation of the Family Tree UI

Written as a Single-Page Application in Javascript

using the REST API

DALLAN QUASSLYNN MONSON

DOVY PAUKSTYS

TOUR

SELECT PERSON

PERSON

PERSON (SCROLL DOWN)

EDIT NAME

CHINESE

ADD EVENT

ADD SOURCE

SOURCE BOX

WHY WAS ITDEVELOPED?

1. Make it easy for partners to allow their customers to accessthe FamilySearch tree using an easily-extensible framework

2. Provide a set of re-usable components for use by partners3. A real-world example of accessing the FamilySearch Tree

using the FamilySearch Javascript SDK

DISCLAIMERSNot official - not an official FamilySearch projectNot supported - code is provided as-isNot maintained - everything currently works...

WHAT CAN I DO WITH IT?1. Have you ever thought you could improve upon the

FamilySearch UI?Fork this project and extend it

2. Do you want to allow people to edit names and are intimidatedby the complexity?

Use the name edit component from this project3. Would you like to understand better how to use the

FamilySearch REST API?Review the source for this project

HOW CAN I GETSTARTED?

Install pre-requisites

Install node.js (nodejs.org)npm install -g bower install bowernpm install -g grunt-cli install gruntInstall PhantomJS (phantomjs.org)

HOW CAN I GETSTARTED?

Get an App Key

Contact FamilySearch developer support if you don't alreadyhave one

Ask FamilySearch developer support to add as an OAuth redirect URLhttp://localhost:9000/#!/auth

HOW CAN I GETSTARTED?

Run the code

clone github.com/rootsdev/familysearch-reference-clientnpm install install build dependenciesbower install install client dependenciesgrunt watch launch a server and watch for changesVisit Sign in using your sandbox username and password

http://localhost:9000

ANGULARJS

ANGULARJS FRAMEWORKModelsViewsControllers Directives (components)FiltersServices dependency injectioneasy unit tests

index.htmlhead> <!-- include stylesheets, fonts, and javascripts --></head>body> <div class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="/">FamilySearch Reference Client</a> <span ng-show="busy" style="float: left; line-height: 50px"><i class="fa fa-spinner fa-spin" </div> <div class="collapse navbar-collapse header-collapse"> </div> </div> </div> <div id="themeContainer" class="familysearch_theme"> <div fs-growl=""></div> <div fs-re-authenticate=""></div> <div class="container" ui-view=""></div> </div></body>

app.jsangular.module('fsReferenceClient', [ 'fsReferenceClientShared', 'templates-app', 'templates-common' .config(function($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise('/'); }) .config(function(fsApiProvider) { fsApiProvider .setClientId('WCQY-7J1Q-GKVV-7DNM-SQ5M-9Q5H-JX3H-CMJK') .setEnvironmentName('sandbox') .setRedirectUri('http://localhost:9000/#!/auth'); }) .config(function(fsLocationProvider) { var prefix = '/#'; fsLocationProvider.configure({ getPersonLocation: function(personId) { return { prefix: prefix, path: '/person/'+personId }; }, ... }); })

app.js (continued).controller('AppController', function ($scope) { $scope.environment = 'Sandbox'; $scope.$on('$stateChangeStart', function(event, toState) { if (toState.resolve) { $scope.busy = true; } }); $scope.$on('$stateChangeSuccess', function() { $scope.busy = false; }); $scope.$on('$stateChangeError', function() { $scope.busy = false; }); });

person.jsangular.module('fsReferenceClient') .config(function ($stateProvider) { $stateProvider.state('person', { url: '/person/:personId', controller: 'PersonController', templateUrl: 'person/person.tpl.html', data: { pageTitle: 'Person' }, resolve: { person: ['$stateParams','fsApi',function($stateParams, fsApi) { return fsApi.getPerson($stateParams.personId).then(function (response) { return response.getPerson(); }); }], sources: ['_','$q','$stateParams','fsApi',function(_, $q, $stateParams, fsApi) return fsApi.getPersonSourcesQuery($stateParams.personId).then(function(response) return _.map(response.getSourceRefs(), function(sourceRef) { return { ref: sourceRef, description: response.getSourceDescription(sourceRef.$sourceDescriptionId id: sourceRef.id }; }); }); }] } });

person.js (continued).controller('PersonController', function ($scope, $state, $rootScope, person, sources, fsApi, fsUtils, fsCurrentUserCache) var sections = ['vitalFacts', 'otherInfo', 'familyMembers', 'sources', 'discussions' $scope.states = {}; sections.forEach(function(section) { $scope.states[section] = {value: 'open'}; }); $scope.person = person; $scope.sources = sources; sources.forEach(function(source) { fsUtils.mixinStateFunctions($scope, source); });

var unbindRestored = $rootScope.$on('restored', function() { fsApi.getPerson($scope.person.id).then(function (response) { fsUtils.refresh($scope.person, response.getPerson()); }); }); $scope.$on('$destroy', unbindRestored);

$scope.$on('delete', function(event, person, changeMessage) { event.stopPropagation(); person._busy = true; person.$delete(changeMessage).then(function() { person._busy = false; fsCurrentUserCache.getUser().then(function(user) { $state.go('person', { personId: user.personId });

person.tpl.htmldiv fs-person-profile="" person="person"></div>div class="mainContent"> <div class="row"> <div class="col-md-9"> <div fs-vital-facts-section="" person="person" sources="sources" state="states <div fs-other-info-section="" person="person" state="states['otherInfo']"></div <div fs-family-members-section="" person="person" state="states['familyMembers <div fs-sources-section="" person="person" sources="sources" state="states['sources <div fs-discussions-section="" person="person" state="states['discussions']"></ <div fs-notes-section="" person="person" state="states['notes']"></div> </div> <div class="col-md-3 sidebar"> <div fs-changes-section="" person="person"></div> <div fs-tools-section="" person="person"></div> </div> </div></div>

fsPersonProfile.jsangular.module('fsReferenceClientShared') .directive('fsPersonProfile', function (fsLocation) { return { templateUrl: 'fsReferenceClientShared/fsPersonProfile/fsPersonProfile.tpl.html' scope: { person: '=' }, link: function(scope) { scope.treeHref = fsLocation.getTreeUrl(scope.person.id); } }; });

fsPersonProfile.tpl.htmldiv class="profileHeaderContainer"> <div class="tree-family jumbotron"> <div class="personWrapper"> <div class="nameWrapper"> <div class="row"> <div class="col-lg-2 col-md-2"> <i class="icon-rounded hidden-xs" ng-class="{'fs-icon-male': person._isMale(), 'fs-icon-female': !person._isMale()}"> </i> </div> <div class="col-lg-10 col-md-10"> <div class="pull-right"><span ng-show="person._busy"> <i class="fa fa-spinner fa-spin"></i></span></div> <h1>{{person.$getDisplayName()}}</h1> <span class="lifeSpan">{{person.$getDisplayBirthDate()}} - {{person.$getDisplayDeathDate()}}</span> <span class="pid">{{person.id}}</span> <div class="personActions"> <ul class="list-collapsed"> <li class="pull-left"><a ng-href="{{treeHref}}"> <i class="fs-icon-male fs-icon-pedigree"> </i>View Tree</a></li> </ul> </div> </div> </div> </div> </div>

EXTENDING

RESULT

RE-USE COMPONENTS

FORKFork

create a directory for your components; e.g.,src/common/dqComponents

https://github.com/rootsdev/familysearch-reference-client

dqPedigreeMini.jsangular.module('dqPedigreeMini', ['loDash', 'fsReferenceClientShared', 'dqPersonMini' .directive('dqPedigreeMini', function (_, fsApi) { return { templateUrl: 'dqComponents/dqPedigreeMini.tpl.html', scope: { personId: '@' }, controller: function($scope) { fsApi.getAncestry($scope.personId, {generations: 2}).then(function(response) $scope.persons = _.filter(response.getPersons(), function(person) { return person.$getAscendancyNumber() >= 1 && person.$getAscendancyNumber() <= 7; }); }); } }; });

dqPedigreeMini.tpl.html<div class="panel panel-info"> <div class="panel-heading"> <h4 class="panel-title">Mini Pedigree</h4> </div> <div class="panel-body dq-pedigree-mini-outer"> <div ng-repeat="person in persons"> <div dq-person-mini person="person" is-focus="{{person.$getAscendancyNumber() == 1}}" class="dq-pedigree-mini-person" ng-class="'dq-pedigree-mini-pos'+person.$getAscendancyNumber()"> </div> </div> </div></div>

dqPersonMini.jsangular.module('dqPersonMini', []) .directive('dqPersonMini', function () { return { templateUrl: 'dqComponents/dqPersonMini.tpl.html', scope: { person: '=', isFocus: '@' } }; });

dqPersonMini.tpl.htmldiv class="dq-person-mini"> <a href="" fs-person-popover="" person="person" popover-placement="left"> <span class="dq-person-mini-name" ng-style="{'font-weight': isFocus === 'true' ? 'bold' : 'normal', 'color': isFocus === 'true' ? '#333' : '#0051c4'}"> {{person.$getDisplayName()}} </span> </a></div>

INJECT INTO PERSON<div class="col-md-3 sidebar"> <div fs-changes-section="" person="person"></div> <div fs-tools-section="" person="person"></div> <div dq-pedigree-mini="" person-id="{{person.id}}"></div></div>

THE ENDSlides are at https://github.com/DallanQ/fs-reference-client-

2014-slides

top related