Browse Source

feat: move everything around

fix/enable-imgix
Thomas Osmonson 4 years ago
parent
commit
0a0a4c6776
  1. 8
      lib/md-loader.js
  2. 2
      lib/remark-plugins.js
  3. 172
      next.config.js
  4. 11
      package.json
  5. BIN
      public/static/fonts/soehne-mono-web-buch.otf
  6. BIN
      public/static/fonts/soehne-web-buch.otf
  7. BIN
      public/static/fonts/soehne-web-halbfett_1.otf
  8. BIN
      public/static/fonts/soehne-web-kraftig_1.otf
  9. 102
      src/common/navigation.yaml
  10. 79
      src/common/routes/get-routes.js
  11. 123
      src/components/custom-blocks/page-reference.tsx
  12. 30
      src/components/layouts/markdown-wrapper.tsx
  13. 2
      src/components/mdx/components/heading.tsx
  14. 66
      src/components/mdx/md-contents.tsx
  15. 2
      src/components/mdx/mdx-components.tsx
  16. 9
      src/components/mdx/styles.tsx
  17. 10
      src/components/mdx/typography.ts
  18. 5
      src/components/pagination.tsx
  19. 306
      src/components/side-nav.tsx
  20. 2
      src/components/typography.tsx
  21. 1
      src/pages/_app.tsx
  22. 35
      src/pages/_document.tsx
  23. 586
      src/pages/android/tutorial.md
  24. 4
      src/pages/authentication/building-todo-app.md
  25. 5
      src/pages/authentication/connect.md
  26. 29
      src/pages/authentication/overview.md
  27. 7
      src/pages/authentication/profiles.md
  28. 76
      src/pages/build-an-app.md
  29. 5
      src/pages/common/android_ref.md
  30. 5
      src/pages/common/community_ref.md
  31. 5
      src/pages/common/ios_ref.md
  32. 5
      src/pages/common/javascript_ref.md
  33. 9
      src/pages/contact.md
  34. 20
      src/pages/core/faq_developer.md
  35. 26
      src/pages/core/faq_technical.md
  36. 0
      src/pages/data-indexing/collaborate.md
  37. 2
      src/pages/data-indexing/integrate.md
  38. 2
      src/pages/data-indexing/models.md
  39. 2
      src/pages/data-indexing/overview.md
  40. 2
      src/pages/data-indexing/server-extras.md
  41. 17
      src/pages/data-storage/authentication.md
  42. 1
      src/pages/data-storage/collection-type.md
  43. 1
      src/pages/data-storage/collections.md
  44. 0
      src/pages/data-storage/overview.md
  45. 116
      src/pages/data-storage/storage-guide.md
  46. 0
      src/pages/data-storage/storage-write-read.md
  47. 191
      src/pages/develop/connect/overview.md
  48. 4
      src/pages/develop/faq-data.json
  49. 111
      src/pages/develop/storage.md
  50. 0
      src/pages/ecosystem/address-confirmation.md
  51. 0
      src/pages/ecosystem/contributing.md
  52. 0
      src/pages/ecosystem/faq.md
  53. 4
      src/pages/ecosystem/overview.md
  54. 4
      src/pages/ecosystem/stacks-token-holders.md
  55. 3
      src/pages/ecosystem/stacks-token.md
  56. 5
      src/pages/home.tsx
  57. 8
      src/pages/index.md
  58. 700
      src/pages/ios/tutorial.md
  59. 6
      src/pages/mining.md
  60. 0
      src/pages/naming-services/architecture.md
  61. 0
      src/pages/naming-services/build-profile-search-index.md
  62. 0
      src/pages/naming-services/choose-name.md
  63. 0
      src/pages/naming-services/comparison.md
  64. 0
      src/pages/naming-services/create-namespace.md
  65. 0
      src/pages/naming-services/did.md
  66. 0
      src/pages/naming-services/forks.md
  67. 0
      src/pages/naming-services/manage-names.md
  68. 0
      src/pages/naming-services/namespaces.md
  69. 0
      src/pages/naming-services/overview.md
  70. 0
      src/pages/naming-services/register-name.md
  71. 0
      src/pages/naming-services/resolve-name.md
  72. 0
      src/pages/naming-services/subdomains-tutorial.md
  73. 0
      src/pages/naming-services/subdomains.md
  74. 6
      src/pages/org/whitepaper-blockchain.md
  75. 0
      src/pages/references/blockstack-cli-reference.md
  76. 0
      src/pages/references/clarity-reference.md
  77. 0
      src/pages/references/deploy-tips.md
  78. 0
      src/pages/references/faqs.md
  79. 0
      src/pages/references/glossary.md
  80. 0
      src/pages/references/stacks-blockchain-reference.md
  81. 0
      src/pages/references/stacks-rpc-api-reference.md
  82. 0
      src/pages/smart-contracts/cli-wallet-quickstart.md
  83. 11
      src/pages/smart-contracts/counter-tutorial.md
  84. 0
      src/pages/smart-contracts/hello-world-tutorial.md
  85. 0
      src/pages/smart-contracts/install-source.md
  86. 0
      src/pages/smart-contracts/overview.md
  87. 0
      src/pages/smart-contracts/principals.md
  88. 0
      src/pages/smart-contracts/running-testnet-node.md
  89. 0
      src/pages/smart-contracts/signing-transactions.md
  90. 0
      src/pages/smart-contracts/testing-contracts.md
  91. 0
      src/pages/stacks-blockchain/atlas/how-atlas-works.md
  92. 0
      src/pages/stacks-blockchain/atlas/overview.md
  93. 0
      src/pages/stacks-blockchain/atlas/usage.md
  94. 6
      src/pages/stacks-blockchain/best-practices.md
  95. 0
      src/pages/stacks-blockchain/install-api.md
  96. 0
      src/pages/stacks-blockchain/installing-memcached.md
  97. 5
      src/pages/stacks-blockchain/overview.md
  98. 0
      src/pages/stacks-blockchain/wire-format.md
  99. 0
      src/pages/stacks-wallet/install.md
  100. 0
      src/pages/stacks-wallet/overview.md

8
lib/md-loader.js

@ -1,6 +1,9 @@
const fm = require('gray-matter');
const remark = require('remark');
const strip = require('strip-markdown');
const readingTime = require('reading-time');
const getReadingTime = mdxContent => readingTime(mdxContent);
const getHeadings = mdxContent => {
const regex = /\n(#+)(.*)/gm;
@ -32,11 +35,10 @@ module.exports = async function (src) {
const callback = this.async();
const { content, data } = fm(src);
const headings = getHeadings(content);
const duration = getReadingTime(content).text;
const code =
`import { MDWrapper } from '@components/layouts/markdown-wrapper';
import { PageReference } from '@components/custom-blocks/page-reference';
const shortcodes = {['pagereference']: PageReference};
export const frontmatter = ${ JSON.stringify({...data, headings}) };
export const frontmatter = ${JSON.stringify({ duration, ...data, headings })};
const Layout = ({ children, ...props }) => (
<MDWrapper frontmatter={frontmatter} {...props}>{children}</MDWrapper>
)

2
lib/remark-plugins.js

@ -11,6 +11,7 @@ const slug = require('remark-slug');
const headingID = require('remark-heading-id');
const sectionize = require('remark-sectionize');
const customBlocks = require('remark-custom-blocks');
const externalLinks = require('remark-external-links');
const remarkPlugins = [
[memoize(include), { resolveFrom: path.join(__dirname, '../src/includes') }],
@ -22,6 +23,7 @@ const remarkPlugins = [
memoize(slug),
memoize(headingID),
memoize(sectionize),
memoize(externalLinks),
[
customBlocks,
{

172
next.config.js

@ -4,55 +4,135 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
const path = require('path');
const { remarkPlugins } = require('./lib/remark-plugins');
const { rehypePlugins } = require('./lib/rehype-plugins');
const withFonts = require('next-fonts');
module.exports = withBundleAnalyzer({
experimental: {
modern: true,
polyfillsOptimization: true,
jsconfigPaths: true,
},
pageExtensions: ['js', 'ts', 'tsx', 'md', 'mdx'],
webpack: (config, options) => {
config.module.rules.push({
test: /.mdx?$/, // load both .md and .mdx files
use: [
options.defaultLoaders.babel,
{
loader: '@mdx-js/loader',
options: {
remarkPlugins,
rehypePlugins,
async function redirects() {
return [
{ source: '/browser/todo-list.html', destination: '', permanent: true },
{ source: '/develop/connect/get-started.html', destination: '', permanent: true },
{ source: '/develop/connect/overview.html', destination: '', permanent: true },
{ source: '/develop/profiles.html', destination: '', permanent: true },
{ source: '/storage/overview.html', destination: '', permanent: true },
{ source: '/develop/storage.html', destination: '', permanent: true },
{ source: '/storage/authentication.html', destination: '', permanent: true },
{ source: '/storage/write-to-read.html', destination: '', permanent: true },
{ source: '/develop/radiks-intro.html', destination: '', permanent: true },
{ source: '/develop/radiks-setup.html', destination: '', permanent: true },
{ source: '/develop/radiks-models.html', destination: '', permanent: true },
{ source: '/develop/radiks-collaborate.html', destination: '', permanent: true },
{ source: '/develop/radiks-server-extras.html', destination: '', permanent: true },
{ source: '/core/smart/overview.html', destination: '', permanent: true },
{ source: '/core/smart/tutorial.html', destination: '', permanent: true },
{ source: '/core/smart/tutorial-counter.html', destination: '', permanent: true },
{ source: '/core/smart/tutorial-test.html', destination: '', permanent: true },
{ source: '/develop/connect/use-with-clarity.html', destination: '', permanent: true },
{ source: '/core/smart/principals.html', destination: '', permanent: true },
{ source: '/core/smart/testnet-node.html', destination: '', permanent: true },
{ source: '/core/smart/cli-wallet-quickstart.html', destination: '', permanent: true },
{ source: '/core/naming/introduction.html', destination: '', permanent: true },
{ source: '/core/naming/architecture.html', destination: '', permanent: true },
{ source: '/core/naming/namespaces.html', destination: '', permanent: true },
{ source: '/core/naming/comparison.html', destination: '', permanent: true },
{ source: '/core/naming/tutorial_subdomains.html', destination: '', permanent: true },
{ source: '/core/naming/search.html', destination: '', permanent: true },
{ source: '/core/faq_technical.html', destination: '', permanent: true },
{ source: '/core/naming/pickname.html', destination: '', permanent: true },
{ source: '/core/naming/creationhowto.html', destination: '', permanent: true },
{ source: '/core/naming/resolving.html', destination: '', permanent: true },
{ source: '/core/naming/register.html', destination: '', permanent: true },
{ source: '/core/naming/manage.html', destination: '', permanent: true },
{ source: '/core/naming/subdomains.html', destination: '', permanent: true },
{ source: '/core/naming/forks.html', destination: '', permanent: true },
{ source: '/develop/collections.html', destination: '', permanent: true },
{ source: '/develop/collection-type.html', destination: '', permanent: true },
{ source: '/storage/hub-operation.html', destination: '', permanent: true },
{ source: '/storage/amazon-s3-deploy.html', destination: '', permanent: true },
{ source: '/storage/digital-ocean-deploy.html', destination: '', permanent: true },
{ source: '/storage/hello-hub-choice.html', destination: '', permanent: true },
{ source: '/storage/gaia-admin.html', destination: '', permanent: true },
{ source: '/core/atlas/overview.html', destination: '', permanent: true },
{ source: '/core/atlas/howitworks.html', destination: '', permanent: true },
{ source: '/core/atlas/howtouse.html', destination: '', permanent: true },
{ source: '/org/overview.html', destination: '', permanent: true },
{ source: '/faqs/allFAQS.html', destination: '', permanent: true },
{ source: '/org/token.html', destination: '', permanent: true },
{ source: '/org/whitepaper-blockchain.html', destination: '', permanent: true },
{ source: '/org/wallet-intro.html', destination: '', permanent: true },
{ source: '/org/wallet-install.html', destination: '', permanent: true },
{ source: '/org/wallet-use.html', destination: '', permanent: true },
{ source: '/org/wallet-troubleshoot.html', destination: '', permanent: true },
{ source: '/org/tokenholders.html', destination: '', permanent: true },
{ source: '/core/cmdLineRef.html', destination: '', permanent: true },
{ source: '/core/smart/clarityRef.html', destination: '', permanent: true },
{ source: '/core/smart/rpc-api.html', destination: '', permanent: true },
{ source: '/common/javascript_ref.html', destination: '', permanent: true },
{ source: '/common/android_ref.html', destination: '', permanent: true },
{ source: '/common/ios_ref.html', destination: '', permanent: true },
{ source: '/common/core_ref.html', destination: '', permanent: true },
{ source: '/core/wire-format.html', destination: '', permanent: true },
{ source: '/storage/config-schema.html', destination: '', permanent: true },
{ source: '/org/secureref.html', destination: '', permanent: true },
{ source: '/develop/overview_auth.html', destination: '', permanent: true },
{ source: '/org/terms.html', destination: '', permanent: true },
];
}
module.exports = withFonts(
withBundleAnalyzer({
experimental: {
modern: true,
polyfillsOptimization: true,
jsconfigPaths: true,
},
pageExtensions: ['js', 'ts', 'tsx', 'md', 'mdx'],
webpack: (config, options) => {
config.module.rules.push({
test: /.mdx?$/, // load both .md and .mdx files
use: [
options.defaultLoaders.babel,
{
loader: '@mdx-js/loader',
options: {
remarkPlugins,
rehypePlugins,
},
},
},
path.join(__dirname, './lib/md-loader'),
],
});
path.join(__dirname, './lib/md-loader'),
],
});
config.module.rules.push({
test: /\.ya?ml$/,
type: 'json',
use: 'yaml-loader',
});
if (!options.dev) {
const splitChunks = config.optimization && config.optimization.splitChunks;
if (splitChunks) {
const cacheGroups = splitChunks.cacheGroups;
const test = /[\\/]node_modules[\\/](preact|preact-render-to-string|preact-context-provider)[\\/]/;
if (cacheGroups.framework) {
cacheGroups.preact = Object.assign({}, cacheGroups.framework, {
test,
});
cacheGroups.commons.name = 'framework';
} else {
cacheGroups.preact = {
name: 'commons',
chunks: 'all',
test,
};
if (!options.dev) {
const splitChunks = config.optimization && config.optimization.splitChunks;
if (splitChunks) {
const cacheGroups = splitChunks.cacheGroups;
const test = /[\\/]node_modules[\\/](preact|preact-render-to-string|preact-context-provider)[\\/]/;
if (cacheGroups.framework) {
cacheGroups.preact = Object.assign({}, cacheGroups.framework, {
test,
});
cacheGroups.commons.name = 'framework';
} else {
cacheGroups.preact = {
name: 'commons',
chunks: 'all',
test,
};
}
}
}
// Install webpack aliases:
const aliases = config.resolve.alias || (config.resolve.alias = {});
aliases.react = aliases['react-dom'] = 'preact/compat';
aliases['react-ssr-prepass'] = 'preact-ssr-prepass';
}
// Install webpack aliases:
const aliases = config.resolve.alias || (config.resolve.alias = {});
aliases.react = aliases['react-dom'] = 'preact/compat';
aliases['react-ssr-prepass'] = 'preact-ssr-prepass';
}
return config;
},
});
return config;
},
})
);

11
package.json

@ -22,7 +22,6 @@
"cache-manager-fs-hash": "^0.0.9",
"csvtojson": "^2.0.10",
"docsearch.js": "^2.6.3",
"eslint": "^7.4.0",
"fathom-client": "^3.0.0",
"front-matter": "^4.0.2",
"fs-extra": "^9.0.1",
@ -33,7 +32,8 @@
"lodash.debounce": "^4.0.8",
"mdi-react": "7.3.0",
"micro-memoize": "^4.0.9",
"next": "^9.5.1-canary.0",
"next": "^9.5.2-canary.1",
"next-fonts": "^1.4.0",
"next-google-fonts": "^1.1.0",
"next-mdx-remote": "^0.6.0",
"nprogress": "^0.2.0",
@ -44,6 +44,7 @@
"prettier": "^2.0.5",
"preval.macro": "^5.0.0",
"react-gesture-responder": "^2.1.0",
"reading-time": "^1.2.0",
"remark": "^12.0.1",
"remark-custom-blocks": "^2.5.0",
"remark-emoji": "2.1.0",
@ -68,7 +69,8 @@
"unist-util-is": "^4.0.2",
"unist-util-select": "^3.0.1",
"unist-util-visit": "^2.0.3",
"use-events": "^1.4.2"
"use-events": "^1.4.2",
"yaml-loader": "^0.6.0"
},
"devDependencies": {
"@babel/preset-react": "^7.10.4",
@ -101,7 +103,8 @@
},
"resolutions": {
"preact": "^10.4.4",
"@blockstack/ui": "2.12.2-beta.0"
"@blockstack/ui": "2.12.2-beta.0",
"eslint": "^7.4.0"
},
"prettier": "@blockstack/prettier-config"
}

BIN
public/static/fonts/soehne-mono-web-buch.otf

Binary file not shown.

BIN
public/static/fonts/soehne-web-buch.otf

Binary file not shown.

BIN
public/static/fonts/soehne-web-halbfett_1.otf

Binary file not shown.

BIN
public/static/fonts/soehne-web-kraftig_1.otf

Binary file not shown.

102
src/common/navigation.yaml

@ -0,0 +1,102 @@
sections:
- pages:
- path: /
- path: /build-an-app # is an overview page
- path: /smart-contracts
pages:
- path: /overview
- path: /testing-contracts
- path: /principals
- path: /running-testnet-node
sections:
- title: Tutorials
pages:
- path: /hello-world-tutorial
- path: /counter-tutorial
- path: /signing-transactions
- path: /mining # is an overview page
- title: Technology
pages:
- path: /authentication
pages:
- path: /overview
- path: /profiles
- path: /connect
sections:
- title: Tutorials
pages:
- path: /building-todo-app
- path: /data-storage
pages:
- path: /overview
- path: /storage-guide
- path: /authentication
- path: /storage-write-read
- path: /collections
- path: /collection-type
- path: /data-indexing
pages:
- path: /overview
- path: /integrate
- path: /models
- path: /collaborate
- path: /server-extras
- path: /stacks-blockchain
pages:
- path: /overview
- path: /best-practices
- path: /wire-format
- path: /atlas
pages:
- path: /overview
- path: /how-atlas-works
- path: /usage
sections:
- title: Tutorials
pages:
- path: /install-api
- path: /installing-memcached
- path: /naming-services
pages:
- path: /overview
- path: /architecture
- path: /comparison
- path: /did
- path: /forks
- path: /namespaces
- path: /subdomains
- path: /forks
sections:
- title: Tutorials
pages:
- path: /build-profile-search-index
- path: /choose-name
- path: /create-namespace
- path: /resolve-name
- path: /register-name
- path: /manage-names
- path: /subdomains-tutorial
- path: /storage-hubs # this seems out of date, kill?
pages:
- path: /overview
- path: /hello-hub-choice
- path: /gaia-admin
- path: /config-schema
- path: /digital-ocean-deploy
- path: /amazon-s3-deploy
- title: Ecosystem
pages:
- path: /overview #pbc et al
- path: /stacks-token
- path: /contributing
- title: References
pages:
- path: /clarity-reference
- path: /blockstack-cli-reference
- path: /stacks-blockchain-reference
- path: /stacks-rpc-api-reference
- path: /faqs
- path: /glossary
- path: /deploy-tips

79
src/common/routes/get-routes.js

@ -10,7 +10,46 @@
const fm = require('front-matter');
const fs = require('fs-extra');
const path = require('path');
const sections = require('./all-routes.json');
const slugify = string =>
string
.toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
const YAML = require('yaml');
const yamlFile = fs.readFileSync(path.resolve(__dirname, '../navigation.yaml'), 'utf8');
const navigation = YAML.parse(yamlFile);
const getFlatMap = navigation => {
return navigation.sections.flatMap(section =>
section.pages.flatMap(page => {
if (page.pages) {
let sectionPages = [];
if (page.sections) {
sectionPages = page.sections.flatMap(_section =>
_section.pages.flatMap(sectionPage => `${page.path}${sectionPage.path}`)
);
}
const pages = page.pages.flatMap(_page => {
if (_page.pages) {
return _page.pages.flatMap(p => `${page.path}${_page.path}${p.path}`);
} else {
return `${page.path}${_page.path}`;
}
});
return [...pages, ...sectionPages];
} else {
return `${section.title ? '/' + slugify(section.title) : ''}${page.path}`;
}
})
);
};
const allRoutes = getFlatMap(navigation);
const getHeadings = mdContent => {
const regex = /(#+)(.*)/gm;
@ -20,25 +59,23 @@ const getHeadings = mdContent => {
: undefined;
};
const routes = sections.map(section => {
const _routes = section.routes.map(route => {
try {
const fileContent = fs.readFileSync(path.join('./src/pages/', route.path + '.md'), 'utf8');
const data = fm(fileContent);
const headings = getHeadings(data.body);
return {
...route,
...data.attributes,
headings,
};
} catch (e) {
console.log(e);
}
});
return {
...section,
routes: _routes,
};
const routes = allRoutes.map(route => {
try {
const fileContent = fs.readFileSync(
path.join('./src/pages', (route === '/' ? 'index' : route) + '.md'),
'utf8'
);
const data = fm(fileContent);
const headings = getHeadings(data.body);
return {
path: route,
...data.attributes,
headings,
};
} catch (e) {
console.error('ROUTES ERROR');
console.warn(e);
}
});
module.exports = routes || [];
module.exports = routes;

123
src/components/custom-blocks/page-reference.tsx

@ -1,42 +1,113 @@
import React from 'react';
import { Box, color, Grid, space } from '@blockstack/ui';
import { onlyText } from '@common/utils';
import { useAppState } from '@common/hooks/use-app-state';
import { Text } from '@components/typography';
import { Box, Flex, BoxProps, color, Grid, space, transition } from '@blockstack/ui';
import { border, onlyText } from '@common/utils';
import { useTouchable } from '@common/hooks/use-touchable';
import { Caption, Text } from '@components/typography';
import Link from 'next/link';
import routes from '@common/routes';
const Image = (props: BoxProps) => (
<Box
transition="all 0.75s cubic-bezier(0.23, 1, 0.32, 1)"
flexShrink={0}
bg="#9985FF"
{...props}
/>
);
const Title = ({ children, ...props }: BoxProps) => (
<Text fontWeight="500" display="block" {...props}>
{children}
</Text>
);
const Description = ({ children, ...props }) => (
<Caption maxWidth="50ch" {...props}>
{children}
</Caption>
);
const FloatingLink = ({ href, ...props }: any) => (
<Link href={href} passHref>
<Box as="a" position="absolute" size="100%" left={0} top={0} />
</Link>
);
const InlineCard = ({ page }) => {
const { hover, active, bind } = useTouchable({
behavior: 'link',
});
return (
<Flex
border={border()}
borderColor={hover ? '#9985FF' : color('border')}
p={space('base-loose')}
borderRadius="12px"
align="center"
transition={transition}
boxShadow={hover ? 'mid' : 'none'}
position="relative"
{...bind}
>
<Image borderRadius={hover ? '100%' : '12px'} mr={space('base')} size="64px" />
<Flex flexDirection="column">
<Flex align="baseline">
<Title color={hover ? color('accent') : color('text-title')} mb={space('extra-tight')}>
{page.title || page.headings[0]}
</Title>
{page.tags?.length
? page.tags.map(tag => (
<Flex
ml={space('tight')}
borderRadius="18px"
px={space('base-tight')}
height="20px"
align="center"
justify="center"
fontSize="12px"
bg={color('border')}
textTransform="capitalize"
color={color('invert')}
transition={transition}
>
{tag}
</Flex>
))
: null}
</Flex>
<Description>{page.description}</Description>
</Flex>
<FloatingLink href={`${page.path}`} />
</Flex>
);
};
export const PageReference = ({ children }) => {
const { routes } = useAppState();
const content = onlyText(children).trim();
const [variant, _paths] = content.includes('\n') ? content.split('\n') : ['default', content];
const paths = _paths.includes(', ') ? _paths.split(', ') : [_paths];
const flatRoutes = routes.flatMap(section =>
section.routes.map(route => ({
section: section.title,
...route,
}))
);
if (!routes) return null;
const pages = paths
.map(path => flatRoutes.find(route => route.path === path))
.filter(page => page);
const pages = paths.map(path => routes?.find(route => route.path === path)).filter(page => page);
return (
<Grid
mt={space('extra-loose')}
gridColumnGap={space('loose')}
gridTemplateColumns="repeat(3, 1fr)"
gridTemplateColumns={`repeat(${pages.length === 1 ? 1 : 3}, 1fr)`}
>
{pages.map(page => (
<Box position="relative">
<Box mb={space('loose')} borderRadius="12px" height="144px" width="100%" bg="#9985FF" />
<Box pb={space('tight')}>
<Text fontWeight="600">{page.title || page.headings[0]}</Text>
{pages.map(page =>
variant === 'inline' ? (
<InlineCard page={page} />
) : (
<Box position="relative">
<Image height="144px" borderRadius="12px" mb={space('loose')} />
<Flex alignItems="flex-start" justifyContent="flex-start" flexDirection="column">
<Title mb={space('tight')}>{page.title || page.headings[0]}</Title>
<Description>{page.description}</Description>
</Flex>
<FloatingLink href={`${page.path}`} />
</Box>
<Box>{page.description}</Box>
<Link href={`/${page.path}`} passHref>
<Box as="a" position="absolute" size="100%" left={0} top={0} />
</Link>
</Box>
))}
)
)}
</Grid>
);
};

30
src/components/layouts/markdown-wrapper.tsx

@ -1,16 +1,44 @@
import React from 'react';
import { Box, Stack, space } from '@blockstack/ui';
import { MDContents } from '@components/mdx/md-contents';
import { H1 } from '@components/mdx';
import Head from 'next/head';
import { Caption, Text } from '@components/typography';
const getTitle = ({ title, headings }) => title || (headings?.length && headings[0].content);
const PageTop = props => {
return (
<Box px={space('extra-loose')} mb="64px">
<H1 mb="0 !important">{getTitle(props)}</H1>
{props.description ? (
<Box mt="24px !important">
<Text>{props.description}</Text>{' '}
</Box>
) : null}
<Stack isInline spacing={space('base')} mt={space('base')}>
{props.experience ? <Caption textTransform="capitalize">{props.experience}</Caption> : null}
{props.duration ? <Caption>{props.duration}</Caption> : null}
</Stack>
</Box>
);
};
export const MDWrapper = ({ frontmatter, dynamicHeadings = [], ...props }) => {
const { headings, description } = frontmatter;
return (
<>
<Head>
<title>{getTitle(frontmatter)} | Blockstack</title>
<meta name="description" content={description} />
</Head>
<MDContents headings={[...headings, ...dynamicHeadings]}>{props.children}</MDContents>
<MDContents
pageTop={() => <PageTop {...frontmatter} />}
headings={[...headings, ...dynamicHeadings]}
>
{props.children}
</MDContents>
</>
);
};

2
src/components/mdx/components/heading.tsx

@ -57,7 +57,7 @@ const AnchorOffset = ({ id }: BoxProps) =>
display="block"
position="absolute"
style={{ userSelect: 'none', pointerEvents: 'none' }}
top={`-${HEADER_HEIGHT + 42}px`}
top={`-64px`}
id={id}
/>
) : null;

66
src/components/mdx/md-contents.tsx

@ -12,35 +12,39 @@ import { border } from '@common/utils';
import { useRouter } from 'next/router';
const Search = dynamic(() => import('@components/search'));
export const MDContents: React.FC<any> = React.memo(({ headings, children }) => {
const router = useRouter();
const isHome = router.pathname === '/';
return (
<>
<ContentWrapper
width={['100%', '100%', '100%', `calc(100% - ${isHome ? 0 : TOC_WIDTH}px)`]}
mx="unset"
pt="unset"
css={css(styleOverwrites as any)}
>
{children}
</ContentWrapper>
{!isHome ? (
<Box>
<Box position="sticky" top={0} pt="64px" pl={space('extra-loose')}>
<Search mb={space('base')} />
{headings?.length > 1 ? (
<TableOfContents
pl={space('base')}
borderLeft={border()}
display={['none', 'none', 'none', 'block']}
headings={headings}
limit={2}
/>
) : null}
export const MDContents: React.FC<any> = React.memo(
({ pageTop: PageTop = null, headings, children }) => {
const router = useRouter();
const isHome = router.pathname === '/';
return (
<>
<ContentWrapper
width={['100%', '100%', '100%', `calc(100% - ${isHome ? 0 : TOC_WIDTH}px)`]}
mx="unset"
pt="unset"
css={css(styleOverwrites as any)}
>
{PageTop && <PageTop />}
{children}
</ContentWrapper>
{!isHome ? (
<Box>
<Box position="sticky" top={0} pt="64px" pl={space('extra-loose')}>
<Search mb={space('base')} />
{headings?.length > 1 ? (
<TableOfContents
pl={space('base')}
borderLeft={border()}
display={['none', 'none', 'none', 'block']}
headings={headings}
limit={2}
/>
) : null}
</Box>
</Box>
</Box>
) : null}
</>
);
});
) : null}
</>
);
}
);

2
src/components/mdx/mdx-components.tsx

@ -27,7 +27,7 @@ import { Code } from '@components/mdx/components';
import { PageReference } from '@components/custom-blocks/page-reference';
export const MDXComponents = {
h1: H1,
h1: () => null,
h2: H2,
h3: H3,
h4: H4,

9
src/components/mdx/styles.tsx

@ -5,6 +5,9 @@ import { getHeadingStyles } from '@components/mdx/typography';
import { border } from '@common/utils';
export const MdxOverrides = createGlobalStyle`
html, body {
font-family: 'Soehne', Inter, sans-serif;
}
@counter-style list {
pad: "0";
}
@ -95,6 +98,9 @@ export const MdxOverrides = createGlobalStyle`
color: ${color('accent')};
}
pre, code{
font-family: "Soehne Mono", "Fira Code", monospace;
}
pre{
display: inline-block;
}
@ -108,6 +114,9 @@ p, ul, ol, table {
export const styleOverwrites = {
section: {
'&:first-child > h2:first-child': {
mt: 0,
},
'& > *:not(pre):not(ul):not(ol):not(img):not([data-reach-accordion]):not(section)': {
px: space('extra-loose'),
},

10
src/components/mdx/typography.ts

@ -2,10 +2,11 @@ export const baseTypeStyles = {
letterSpacing: '-0.01em',
dispay: 'flex',
fontFeatureSettings: `'ss01' on`,
fontFamily: `'Soehne', Inter, sans-serif`,
};
const h1 = {
fontWeight: 'bolder',
fontWeight: 600,
fontSize: ['38px', '38px', '44px'],
lineHeight: '52px',
padding: '0.05px 0',
@ -23,7 +24,7 @@ const h1 = {
},
};
const h2 = {
fontWeight: 600,
fontWeight: 500,
fontSize: '27.5px',
lineHeight: '34px',
padding: '0.05px 0',
@ -61,6 +62,7 @@ const h3 = {
};
const h4 = {
fontWeight: 400,
fontSize: '19.25px',
lineHeight: '28px',
padding: '0.05px 0',
@ -78,15 +80,15 @@ const h4 = {
},
};
const h5 = {
fontWeight: 400,
fontSize: '16px',
lineHeight: '28px',
fontWeight: 'bold',
};
const h6 = {
fontWeight: 400,
fontSize: '14px',
lineHeight: '28px',
fontWeight: 'bold',
};
const headings = {

5
src/components/pagination.tsx

@ -15,6 +15,11 @@ const usePaginateRoutes = () => {
const getSection = _section => _section.routes.find(getRoute);
const findSectionByTitle = item => item.title === section.title;
return {
next: undefined,
prev: undefined,
};
const section = routes.find(getSection);
if (!section)

306
src/components/side-nav.tsx

@ -6,6 +6,10 @@ import { useRouter } from 'next/router';
import routes from '@common/routes';
import { useMobileMenuState } from '@common/hooks/use-mobile-menu';
import { SIDEBAR_WIDTH } from '@common/constants';
// @ts-ignore
import nav from '@common/navigation.yaml';
import ArrowLeftIcon from 'mdi-react/ArrowLeftIcon';
import { slugify } from '@common/utils';
const Wrapper: React.FC<BoxProps & { containerProps?: BoxProps }> = ({
width = `${SIDEBAR_WIDTH}px`,
@ -32,117 +36,239 @@ const Wrapper: React.FC<BoxProps & { containerProps?: BoxProps }> = ({
);
};
const LinkItem: React.FC<LinkProps & { isActive?: boolean }> = React.forwardRef(
({ isActive, ...rest }, ref) => (
<Text
ref={ref}
_hover={
!isActive
? {
color: 'var(--colors-accent)',
cursor: 'pointer',
textDecoration: 'underline',
}
: null
}
color={isActive ? color('accent') : color('text-caption')}
fontSize="14px"
lineHeight="20px"
as="a"
display="block"
py={space(['extra-tight', 'extra-tight', 'extra-tight'])}
{...rest}
/>
)
const capitalize = s => {
if (typeof s !== 'string') return '';
return s.charAt(0).toUpperCase() + s.slice(1);
};
const convertToTitle = (path: string) =>
!path ? null : path === '/' ? 'Home' : capitalize(path.replace('/', '').replace(/-/g, ' '));
const PageItem = ({ isActive, ...props }: any) => (
<Flex
_hover={{
cursor: 'pointer',
color: color('text-title'),
}}
color={isActive ? color('accent') : color('text-caption')}
mb={space('tight')}
fontSize="14px"
{...props}
/>
);
const Links: React.FC<BoxProps & { routes?: any }> = ({ routes, prefix = '', ...rest }) => {
const router = useRouter();
const { handleClose } = useMobileMenuState();
const { pathname } = router;
const SectionTitle: React.FC<BoxProps> = props => (
<Box
color={color('text-title')}
fontSize="16px"
mb={space('tight')}
fontWeight="400"
{...props}
/>
);
const ChildPages = ({ items, handleClick }) =>
items.pages.map((page, index) => {
const path = page.pages
? `${page.path}${page.pages[0].path}`
: items.path
? '/' + slugify(items.path) + page.path
: page.path;
const routePath = routes.find(route => route.path.endsWith(path));
return routes.map((route, linkKey) => {
const isActive = pathname === `/${route.path}`;
return (
<Box width="100%" py="1px" key={linkKey} onClick={handleClose} {...rest}>
<Link href={`/${route.path}`} passHref>
<LinkItem isActive={isActive} width="100%" href={`/${route.path}`}>
{route.title ||
(route.headings && route.headings.length && route.headings[0]) ||
route.path}
</LinkItem>
<Box mb={space('extra-tight')}>
<Link href={routePath.path} passHref>
<PageItem onClick={page.pages ? () => handleClick(page) : undefined} as="a">
{convertToTitle(page.path)}
</PageItem>
</Link>
</Box>
);
});
};
const SectionTitle: React.FC<BoxProps & { textStyles?: BoxProps }> = ({
children,
textStyles,
...rest
}) => (
<Box pb={space('extra-tight')} {...rest}>
<Caption fontSize="14px" fontWeight="500" color={color('text-title')} {...textStyles}>
{children}
</Caption>
</Box>
const ChildSection = ({ sections }) =>
sections.map(section => {
return (
<Box mt={space('base-loose')}>
<SectionTitle>{section.title}</SectionTitle>
<ChildPages items={section} />
</Box>
);
});
const BackItem = props => (
<PageItem align="center" color={color('text-caption')} {...props}>
<Box mr={space('extra-tight')}>
<ArrowLeftIcon size="16px" />
</Box>
Back
</PageItem>
);
const Section = ({ section, visible, isLast, isFirst, setVisible, ...rest }: any) => {
const isVisible = section.title === visible?.title;
const NewNav = () => {
const [selected, setSelected] = React.useState<any | undefined>({
type: 'default',
items: nav.sections,
selected: undefined,
});
return (
<Box width="100%" pt={isFirst ? 'unset' : space('base')} {...rest}>
{section.title ? (
<Flex
width="100%"
align="center"
onClick={() => setVisible(section)}
_hover={{
cursor: 'pointer',
}}
>
<SectionTitle>{section.title}</SectionTitle>
<Box ml="extra-tight" color={color('text-caption')}>
<ChevronIcon size="20px" direction={visible ? 'up' : 'down'} />
</Box>
</Flex>
) : null}
{isVisible && <Links routes={section.routes} />}
</Box>
);
const handleClick = (page: any) => {
if (page.pages) {
setSelected({
type: 'page',
items: page,
});
} else {
setSelected({
type: 'default',
items: nav.sections,
selected: page.path,
});
}
};
const handleBack = () =>
setSelected({
type: 'default',
items: nav.sections,
});
if (selected.type === 'page') {
return (
<Box>
<BackItem onClick={handleBack} mb={space('base')} />
<SectionTitle>{convertToTitle(selected.items.path)}</SectionTitle>
<Box>
{selected.items ? <ChildPages handleClick={handleClick} items={selected.items} /> : null}
{selected.items?.sections ? (
<ChildSection
sections={selected.items?.sections?.map(section => ({
...section,
path: selected.items.path,
}))}
/>
) : null}
</Box>
</Box>
);
}
if (selected.type === 'default') {
return selected.items.map((section, i) => {
const itemProps =
i === 0
? {
color: color('text-title'),
mb: space('tight'),
fontSize: '16px',
_hover: {
color: color('accent'),
cursor: 'pointer',
},
}
: {};
return (
<Box mb={space('base')}>
{section.title ? (
<Flex width="100%" align="center">
<SectionTitle>{section.title}</SectionTitle>
<Box
transform="translateY(-3px)"
color={color('text-caption')}
size="16px"
ml={space('extra-tight')}
>
<ChevronIcon direction="down" />
</Box>
</Flex>
) : null}
{section.pages.map(page => {
const path = page.pages
? `${page.path}${page.pages[0].path}`
: section?.title
? '/' + slugify(section?.title) + page.path
: page.path;
return (
<Box mb={space('extra-tight')}>
<Link href={path}>
<PageItem
as="a"
href={path}
{...itemProps}
isActive={selected.selected === page.path}
onClick={() => handleClick(page)}
>
{convertToTitle(page.path)}
</PageItem>
</Link>
</Box>
);
})}
</Box>
);
});
}
};
export const SideNav: React.FC<BoxProps & { containerProps?: BoxProps }> = ({
containerProps,
...rest
}) => {
const router = useRouter();
const { pathname } = router;
const active = routes.find(section =>
section.routes.find(route => pathname === `/${route.path}`)
);
const [visible, setVisible] = React.useState(active);
const handleSectionClick = (section: any) => {
if (section?.title === active?.title) {
setVisible(false);
} else {
setVisible(section);
}
// const router = useRouter();
// const { pathname } = router;
// const active = routes.find(section =>
// section.routes.find(route => pathname === `/${route.path}`)
// );
// const [visible, setVisible] = React.useState(active);
// const handleSectionClick = (section: any) => {
// if (section?.title === active?.title) {
// setVisible(false);
// } else {
// setVisible(section);
// }
// };
const getFlatMap = navigation => {
return navigation.sections.flatMap(section =>
section.pages.flatMap(page => {
if (page.pages) {
let sectionPages = [];
if (page.sections) {
sectionPages = page.sections.flatMap(_section =>
_section.pages.flatMap(sectionPage => `${page.path}${sectionPage.path}`)
);
}
const pages = page.pages.flatMap(_page => {
if (_page.pages) {
return _page.pages.flatMap(p => `${page.path}${_page.path}${p.path}`);
} else {
return `${page.path}${_page.path}`;
}
});
return [...pages, ...sectionPages];
} else {
return `${section?.title ? '/' + slugify(section.title) : ''}${page.path}`;
}
})
);
};
return (
<Wrapper containerProps={containerProps} {...rest}>
{routes.map((section, sectionKey, arr) => (
<Section
visible={visible}
key={sectionKey}
section={section}
isLast={sectionKey === arr.length - 1}
isFirst={sectionKey === 0}
setVisible={handleSectionClick}
/>
))}
<NewNav />
{/*{routes.map((section, sectionKey, arr) => (*/}
{/* <Section*/}
{/* visible={visible}*/}
{/* key={sectionKey}*/}
{/* section={section}*/}
{/* isLast={sectionKey === arr.length - 1}*/}
{/* isFirst={sectionKey === 0}*/}
{/* setVisible={handleSectionClick}*/}
{/* />*/}
{/*))}*/}
</Wrapper>
);
};

2
src/components/typography.tsx

@ -28,7 +28,7 @@ export const SectionTitle: React.FC<BoxProps> = props => (
export const Pre = React.forwardRef((props: BoxProps, ref) => (
<Text
fontFamily={`"Fira Code", monospace`}
fontFamily={`"Soehne Mono", "Fira Code", monospace`}
bg={color('bg-alt')}
borderRadius="3px"
px="extra-tight"

1
src/pages/_app.tsx

@ -95,7 +95,6 @@ const MyApp = ({ Component, pageProps, colorMode, ...rest }: any) => {
const { isHome } = pageProps;
return (
<>
<GoogleFonts href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Inter:wght@400;500;600;700&display=swap" />
<ThemeProvider>
<CSSReset />
<AppWrapper isHome={isHome} {...rest}>

35
src/pages/_document.tsx

@ -33,6 +33,41 @@ export default class MyDocument extends Document<any> {
<Html lang="en">
<Head />
<body>
<style
type="text/css"
dangerouslySetInnerHTML={{
__html: `
@font-face {
font-family: 'Soehne Mono';
src: url('/static/fonts/soehne-mono-web-buch.otf') format('opentype');
font-weight: 400;
font-display: auto;
font-style: normal;
}
@font-face {
font-family: 'Soehne';
src: url('/static/fonts/soehne-web-buch.otf') format('opentype');
font-weight: 400;
font-display: auto;
font-style: normal;
}
@font-face {
font-family: 'Soehne';
src: url('/static/fonts/soehne-web-kraftig_1.otf') format('opentype');
font-weight: 500;
font-display: auto;
font-style: normal;
}
@font-face {
font-family: 'Soehne';
src: url('/static/fonts/soehne-web-halbfett_1.otf') format('opentype');
font-weight: 600;
font-display: auto;
font-style: normal;
}
`,
}}
/>
<script
dangerouslySetInnerHTML={{
__html: `(function() {

586
src/pages/android/tutorial.md

@ -1,586 +0,0 @@
---
description: Learn about the Android SDK
---
# Android DApps
This tutorial is written for readers who are new to either or both Blockstack
and Android to create a decentralized application. It contains the following
content:
This tutorial was extensively tested using Android Studio 3.6 on a Dell XPS 13
running Ubuntu 19. If your environment is different, you may encounter
slight or even major discrepancies when performing the procedures in this
tutorial. Please [join the Blockstack discord server](https://chat.blockstack.org)
and post questions or comments to the `#support` channel.
Finally, this tutorial is written for all levels from the beginner to the most
experienced. For best results, beginners should follow the guide as written. It
is expected that the fast or furiously brilliant will skip ahead and improvise
on this material at will. Fair journey one and all.
If you prefer, you can skip working through the tutorial all together. Instead,
you can [download the final project code](images/helloandroid.zip) and import it
into Android Studio to review it.
## Understand the sample application flow
When complete, the sample application is a simple `hello-world` display. It is
intended for user on an Android phone.
![](images/final-app.png)
Only users with an existing blockstack id can run your
final sample application. When complete, users interact with the sample
application by doing the following:
![](images/app-flow.png)
## Set up your environment
This sample application has two code bases, a Blockstack `hello-blockstack`
web application and a `hello-android` Android application. Before you start
developing the sample, there are a few elements you need in your environment.
### Install Android Studio
If you are an experienced Android developer and already have an Android
development environment on your workstation, you can use that and skip this
step. However, you will need to adjust the remaining instructions for your
environment.
Follow the installation instructions to download and [Android Studio
3.6](https://developer.android.com/studio/install) for your operating system.
Depending on your network connection, this can take between 15-30 minutes.
![](images/studio-download.png)
### Do you have Node.js?
Node.js v10 or higher is recommended the minimum supported version is Node.js v8. Before you begin,
verify you have the correct version of Node.js and its tools installed.
```bash
$ node -v
v12.10.0
$ which npm npx
/usr/local/bin/npm
/usr/local/bin/npx
```
If you don't have these installed, take a moment to install or upgrade as needed.
## Build the Blockstack hello-world web app
In this section, you build a Blockstack `hello-world` web application that acts as the companion site to
your Android application.
### Generate and launch your hello-blockstack application
@include "scaffolding.md"
In this section, you build an initial React.js application called
`hello-blockstack`.
1. Create a `hello-blockstack` directory.
```bash
mkdir hello-blockstack
```
2. Change into your new directory.
```bash
cd hello-blockstack
```
3. Use the Blockstack application generator to create your initial `hello-blockstack` application.
```bash
$ npx generator-blockstack --plain
create package.json
create .gitignore
create webpack.config.js
create netlify.toml
create firebase.json
...
npm WARN ajv-errors@1.0.1 requires a peer of ajv@>=5.0.0 but none is installed. You must install peer dependencies yourself.
added 840 packages from 582 contributors and audited 843 packages in 18.84s
found 0 vulnerabilities
```
Depending on your environment you may have some warnings with the installation. Optionally, you can fix these before continuing to the next section.
4. Depending on your environment, respond to the prompts to populate the initial app.
You might see a prompt similar to the following:
```bash
[fsevents] Success:
"/Users/theuser/repos/hello-blockstack/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node"
is installed via remote npm notice created a lockfile as package-lock.json.
You should commit this file. added 1060 packages in 26.901s
```
5. After the initial setup you can now run the initial application.
```bash
$ npm run start
> hello-blockstack-webpack@0.0.0 start /home/user/hello-blockstack
> webpack-dev-server
ℹ 「wds」: Project is running at http://localhost:9000/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /home/user/hello-blockstack/dist
ℹ 「wdm」: Hash: f5d88efe9c9194f66ddd
Version: webpack 4.43.0
Time: 2733ms
Built at: 05/19/2020 10:44:50 AM
Asset Size Chunks Chunk Names
main.js 5.39 MiB main [emitted] main
....
[./src/index.js] 1.8 KiB {main} [built]
+ 610 hidden modules
ℹ 「wdm」: Compiled successfully.
```
The system opens a browser displaying your running application.
![](images/blockstack-signin.png)
At this point, the browser is running a Blockstack server on your local host.
This is for testing your applications only.
6. Choose **Get started**
The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one.
![](images/create-restore.png)
7. Follow the prompts appropriate to your situation.
At this point you have only a single application
on your test server. So, you should see this single application, with your
own `blockstack.id`, once you are signed in:
![](images/running-app.png)
## Create the hello-android project
In this section, you'll create an Android application in Android Studio. You'll
run the application in the emulator to test it.
### Create a simple project
In this section, you create an inital project. You'll validate the application's
iniatial state by creating an emulator to run it in. Open Android Studio and do the following:
1. Open Android Studio and choose **Start a new Andriod Studio project**.
If studio is already started, choose **File > New > New Project**.
2. Choose a project template: select **Empty Activity** and press **Next**
3. Enter these fields in the **Create Android Project** page.
| Name | Hello Android |
| ---------------- | --------------------------------------------------- |
| Package name | `blockstack.id.USERNAME.hello` |
| Project location | `/home/USERNAME/AndroidStudioProjects/helloandroid` |
| Language | Select (Kotlin) |
| Minimum SDK | Select (API 21: Android 5.0 (Lollipop)) |
![](images/configure-activity.png)
4. Press **Finish**.
Android studio builds your initial project. This can take a bit the first time you do it.
### Run the app in an emulator
In this section, you run the appliation and create an emulator when prompted.
1. Once the project is imported into studio, click the `app` module in the **Project** window.
2. Then, select **Run > Run** (or click the green arrow in the toolbar).
Studio prompts you to **Select Deployment Target**.
3. Choose **Create New Virtual Device** and press **OK**.
Studio prompts you to **Select Hardware**.
4. Choose a Phone device configuration like "Pixel".
![](images/select-hdw.png)
Studio prompts you for a system image.
5. Choose **Q** which is API level 29 and press **Next**.
![](images/q-api.png)
Studio asks you to verify your new emulator configuration.
6. Press **Finish**.
The emulation takes a moment to build. Then, studio launches the emulation and opens your application.
![](images/hello-andriod-1.png)
### Configure your application with the Blockstack SDK
Now that you have created your initial project and verified it running in an emulator, you are ready to begin configuring the application for use with Blockstack.
1. In studio, open the `AndroidManifest.xml` file.
2. Add an `<intent-filter>` with the custom handler for Blockstack. Replace the host ("flamboyant-darwin-d11c17.netlify.app") with your own url once you have hosted your web app publicly.
```xml
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="flamboyant-darwin-d11c17.netlify.app" />
</intent-filter>
```
3. Open the Project's `build.gradle` file ("Project: Hello Android").
4. Add the Jitpack repository `maven { url 'https://jitpack.io' }` to the `repositories` section. for all projects
When you finish, that section looks like this:
```js
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
```
5. Open the Module `build.gradle` file ("Module: app").
6. Fix a problem with duplicate meta data in the Android SDK: add `packagingOptions`
```js
android {
...
packagingOptions {
exclude 'META-INF/*'
}
}
```
7. Below this, add the Blockstack Android SDK dependency to your project's `dependencies` list:
When you are done you should see:
```js
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.github.blockstack:blockstack-android:7940940'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
}
```
**NOTE**: Ignore the warning on the `junit` dependencies.
8. Sync your project.
![](images/sync-project.png)
Be sure to check the sync completed successfully.
![](images/sync-success.png)
9. Run your app in the emulator.
You've made a lot of changes, make sure the emulator is still running
correctly.
### Add a simple interface
1. Open the `app/res/layout/activity_main.xml` file.
The `activity_main.xml` file defines the graphical elements. Some elements are required before you can functionality to your `MainActivity.kt` code.
2. Replace the entire content of the file with the following code:
The new interface includes a `BlockstackSignInButton` which is provided by
the SDK. This SDK includes a themed "Get Started" button
(`BlockstackSignInButton`). You use this button here with the
`org.blockstack.android.sdk.ui.BlockstackSignInButton` class.
```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
tools:context=".MainActivity">
<org.blockstack.android.sdk.ui.BlockstackSignInButton
android:id="@+id/signInButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/userDataTextView"/>
<TextView
android:id="@+id/userDataTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/signInButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
```
This codes adds a button and some text to your application.
3. Choose **Run > Apply changes**.
4. Choose **Run > Run app** in the emulator.
The emulator now contains a new interface with a button:
![](images/new-interface.png)
### Add session & authentication code
1. Open the `MainActivity.kt` file.
2. Add some additional imports to the top, replace the `android.os.Bundle` import.
When you are done, your imports should appear as follows:
```kotlin
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.blockstack.android.sdk.BlockstackSession
import org.blockstack.android.sdk.BlockstackSignIn
import org.blockstack.android.sdk.SessionStore
import org.blockstack.android.sdk.getBlockstackSharedPreferences
import org.blockstack.android.sdk.model.UserData
import org.blockstack.android.sdk.model.toBlockstackConfig
import org.blockstack.android.sdk.ui.SignInProvider
import org.blockstack.android.sdk.ui.showBlockstackConnect
```
3. Add one variable for the Blockstack sign-in flow and one for the Blockstack session that deals with all other function. This needs to be added before `onCreate`.
```kotlin
class MainActivity : AppCompatActivity() {
private lateinit var blockstackSession: BlockstackSession
private lateinit var blockstackSignIn: BlockstackSignIn
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
```
4. Replace the existing the `onCreate` function with the following, use your own domain if you are already hosting your web app somewhere:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val appConfig =
"https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig()
val sessionStore = SessionStore(getBlockstackSharedPreferences())
blockstackSession = BlockstackSession(sessionStore, appConfig)
blockstackSignIn = BlockstackSignIn(sessionStore, appConfig)
BlockstackSignIn.shouldLaunchInCustomTabs = false
signInButton.setOnClickListener {
showBlockstackConnect()
}
if (intent?.action == Intent.ACTION_VIEW) {
// handle the redirect from sign in
userDataTextView.text = "Signing in now ..."
lifecycleScope.launch(Dispatchers.IO) {
handleAuthResponse(intent)
}
}
}
```
This new `onCreate` does several things:
- Supply authentication information for connecting to your Blockstack app: `appDomain` (for `scopes`, `redirectURI`, `manifestURI` the default values are used)
- Add a listener for the button click.
- Handles the redirect intent from the sign-in flow.
Notice that the application in this example is a URI you have not set up.
Registering and application name takes time, so in time's interest you'll
use an existing app (https://flamboyant-darwin-d11c17.netlify.app) that is identical to the `hello-world` you created
earlier. For a production version, you'll need to replace `appDomain`,
`redirectURI`, `manifestURI` and `scopes` with values appropriate for your
app.
5. Add a private function to reflect when a user successfully signs in.
```kotlin
private fun onSignIn(userData: UserData) {
userDataTextView.text = "Signed in as ${userData.decentralizedID}"
signInButton.isEnabled = false
}
```
6. Create a handler for the authentication response.
```kotlin
private fun handleAuthResponse(intent: Intent) {
val authResponse = intent.data?.getQueryParameter("authResponse")
if (authResponse != null) {
val userData = blockstackSession.handlePendingSignIn(authResponse)
if (userData.hasValue) {
// The user is now signed in!
runOnUiThread {
onSignIn(userData.value!!)
}
}
}
}
```
7. Add `SignInProvider` interface for better user onboarding. Let the activity implement the interface and implement the member function `getBlockstackSignIn`
```kotlin
class MainActivity : AppCompatActivity(), SignInProvider {
...
override fun provideBlockstackSignIn(): BlockstackSignIn {
return blockstackSignIn
}
}
```
8. Verify your final `MainActivity.kt` code looks like this:
```kotlin
package blockstack.id.user.hello
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.\*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.blockstack.android.sdk.BlockstackSession
import org.blockstack.android.sdk.BlockstackSignIn
import org.blockstack.android.sdk.SessionStore
import org.blockstack.android.sdk.getBlockstackSharedPreferences
import org.blockstack.android.sdk.model.UserData
import org.blockstack.android.sdk.model.toBlockstackConfig
import org.blockstack.android.sdk.ui.SignInProvider
import org.blockstack.android.sdk.ui.showBlockstackConnect
class MainActivity : AppCompatActivity(), SignInProvider {
private lateinit var blockstackSession: BlockstackSession
private lateinit var blockstackSignIn: BlockstackSignIn
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val appConfig =
"https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig()
val sessionStore = SessionStore(getBlockstackSharedPreferences())
blockstackSession = BlockstackSession(sessionStore, appConfig)
blockstackSignIn = BlockstackSignIn(sessionStore, appConfig)
BlockstackSignIn.shouldLaunchInCustomTabs = false
signInButton.setOnClickListener {
showBlockstackConnect()
}
if (intent?.action == Intent.ACTION_VIEW) {
// handle the redirect from sign in
userDataTextView.text = "Signing in now ..."
lifecycleScope.launch(Dispatchers.IO) {
handleAuthResponse(intent)
}
}
}
private fun onSignIn(userData: UserData) {
userDataTextView.text = "Signed in as ${userData.decentralizedID}"
signInButton.isEnabled = false
}
private suspend fun handleAuthResponse(intent: Intent) {
val authResponse = intent.data?.getQueryParameter("authResponse")
if (authResponse != null) {
val userData = blockstackSession.handlePendingSignIn(authResponse)
if (userData.hasValue) {
// The user is now signed in!
runOnUiThread {
onSignIn(userData.value!!)
}
}
}
}
override fun provideBlockstackSignIn(): BlockstackSignIn {
return blockstackSignIn
}
}
```
### Run the final app in the emulator
1. Choose **Run > Apply changes**.
2. Choose **Run > Run app** in the emulator.
3. When you see the application open, choose **Get Started**.
4. A small information is presented about the sign-in flow
![](images/connect-ui.png)
5. Select **Get Started**
The system might prompt you how to select a browser.
6. Work through the Blockstack prompts to login.
7. Blockstack redirects you to a web site. Open it with your Android app: Select **Hello Android** and **Always**
![](images/connect-response.png)
8. The screen after the sign-in flow shows the owner address of the username that was entered during sign-in.
![](images/final-app.png)
## Where to go next
Congratulations, you've completed your Android app using the Blockstack Android SDK.
Learn more about Blockstack by [trying another tutorial](https://blockstack.org/tutorials).

4
src/pages/browser/todo-list.md → src/pages/authentication/building-todo-app.md

@ -1,12 +1,14 @@
---
title: Building a Todo app
description: Learn how to integrate authentication and data storage.
tags:
- tutorial
redirect_from:
- /develop/zero_to_dapp_1.html
- /browser/hello-blockstack.html
---
# Integrating Blockstack
# Building a Todo app
In this tutorial, you will learn about Blockstack authentication and storage by installing,
running and reviewing the code for a "Todos" web app built with Blockstack and [React](https://reactjs.org/).

5
src/pages/develop/connect/get-started.md → src/pages/authentication/connect.md

@ -1,3 +1,8 @@
---
description: 'Guide to Blockstack Connect'
experience: beginners
---
# Guide to Blockstack Connect
## Installation

29
src/pages/develop/overview_auth.md → src/pages/authentication/overview.md

@ -1,9 +1,12 @@
---
title: Authentication
description: Blockstack Auth provides single sign on and authentication without third parties or remote servers.
---
# Authentication protocol
Blockstack Auth provides single sign on and authentication without third parties or remote servers. On this page, you'll get an overview of authentication from a developer perspective. The following topics are covered:
Blockstack Auth provides single sign on and authentication without third parties or remote servers.
On this page, you'll get an overview of authentication from a developer perspective.
## Authentication flow
@ -20,12 +23,13 @@ Apps may request any of the following scopes:
| `store_write` | Read and write data to the user's Gaia hub in an app-specific storage bucket. |
| `publish_data` | Publish data so that other users of the app can discover and interact with the user. | |
The permissions scope should be specified through the <a href="https://blockstack.github.io/blockstack.js/classes/appconfig.html" target="\_blank">AppConfig</a> object. If no `scopes` array is provided to the `redirectToSignIn` or `makeAuthRequest`
functions, the default is to request `['store_write']`.
The permissions scope should be specified through the [`AppConfig`](https://blockstack.github.io/blockstack.js/classes/appconfig.html)
object. If no `scopes` array is provided to the `redirectToSignIn` or `makeAuthRequest` functions, the default is to request `['store_write']`.
## Manifest file
Decentralized apps have a manifest file. This file is based on the [W3C web app manifest specification](https://w3c.github.io/manifest/). The following is an example manifest file.
Decentralized apps have a manifest file. This file is based on the [W3C web app manifest specification](https://w3c.github.io/manifest/).
The following is an example manifest file.
```json
{
@ -42,23 +46,24 @@ Decentralized apps have a manifest file. This file is based on the [W3C web app
}
```
The Blockstack App retrieves the manifest file from the app during the
authentication process and displays the information in it such as the
app `name` and to the user during sign in. The location of the app manifest file is specific
in the authentication request token and **must** be on the same origin as the app
requesting authentication.
The Blockstack App retrieves the manifest file from the app during the authentication process and displays the
information in it such as the app `name` and to the user during sign in. The location of the app manifest file is specific
in the authentication request token and **must** be on the same origin as the app requesting authentication.
The manifest file **must** have [Cross-origin resource sharing (CORS) headers](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) that allow the manifest file to be fetched from any arbitrary source. This usually means returning a header like this:
The manifest file **must** have [Cross-origin resource sharing (CORS) headers](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
that allow the manifest file to be fetched from any arbitrary source. This usually means returning a header like this:
```
Access-Control-Allow-Origin: *
```
How you implement CORS depends in part on which platform/service you use to serve your application. For example, Netlify and Firebase have two different ways of configuring CORS. Consult your vendor documentation for more information.
How you implement CORS depends in part on which platform/service you use to serve your application. For example, Netlify
and Firebase have two different ways of configuring CORS. Consult your vendor documentation for more information.
## Key pairs
Blockstack Auth makes extensive use of public key cryptography. Blockstack uses ECDSA with the `secp256k1` curve. The following sections describe the three public-private key pairs used in the authentication process:
Blockstack Auth makes extensive use of public key cryptography. Blockstack uses ECDSA with the `secp256k1` curve. The
following sections describe the three public-private key pairs used in the authentication process:
- how they're generated
- where they're used

7
src/pages/develop/profiles.md → src/pages/authentication/profiles.md

@ -1,13 +1,16 @@
---
title: Guide to Blockstack Profiles
---
# Guide to Blockstack Profiles
You can use the blockstack.js library to create and register an Blockstack username on the Stacks blockchain. This section describes the `Profile` object and contains the following topics:
You can use `blockstack.js` to create and register an Blockstack username on the Stacks blockchain.
This section describes the `Profile` object and contains the following topics:
## About profiles
Profile data is stored using Gaia on the user's selected storage provider. An example of a `profile.json` file URL using Blockstack provided storage:
Profile data is stored using Gaia on the user's selected storage provider. An example of a `profile.json` file URL using
Blockstack provided storage:
```
https://gaia.blockstack.org/hub/1EeZtGNdFrVB2AgLFsZbyBCF7UTZcEWhHk/profile.json

76
src/pages/build-an-app.md

@ -0,0 +1,76 @@
---
title: Building decentralized apps
description: Overview and guides for getting started building decentralized applications.
---
# Building decentralized apps
Prefer to jump right in? Get started with this tutorial where you’ll create a decentralized to-do list app.
[[page-reference | inline]]
| /authentication/building-todo-app
## What are decentralized apps?
Decentralized apps are apps that don’t depend on a centralized platform, server or database. Instead, they use a
decentralized network, built on the Stacks blockchain, for authentication, data storage, and backend logic. Just like
Bitcoin, a decentralized network of applications is accessible to anyone and not controlled by any central authority.
To learn more about the Blockstack network and decentralization, read the Blockstack overview.
### User-owned data
Data is stored with the user and encrypted with a key that only they own. Developers aren’t responsible for, or have to
host, their users’ data. This protects users against security breaches and keeps their data private.
### Smart contracts
Decentralized apps can use smart contracts to make their backend logic public, open, and permissionless. Once published
on the blockchain, no one really owns or controls a smart contract. They will execute when their terms are met,
regardless of who interacts with it.
### Compatible and extendable
Decentralized apps are compatible by nature because they use the same data and shared state. You can build on top of
other apps without requiring permission or fear of being shut out.
## Getting started
To build your decentralized app, you’ll use authentication, data storage, data indexing (optional), and smart contracts
(optional). Get started with the documentation and tutorials below.
### Authentication and data storage
Like a regular app, yours will require user authentication and data storage — but decentralized. Get started with the
documentation below or try the tutorial.
[[page-reference | inline]]
| /authentication/overview
[[page-reference | inline]]
| /data-storage/overview
[[page-reference | inline]]
| /authentication/building-todo-app
### Data indexing
If you need to store and index data shared by multiple users, such as messages or a shared document, read the Radiks
documentation.
[[page-reference | inline]]
| /data-indexing/overview
### Smart contracts
You can use smart contracts to decentralize your app’s backend logic, making it open and permissionless. Smart contracts
on Blockstack are written in the Clarity language. View the smart contracts documentation or get started with a tutorial.
[[page-reference | inline]]
| /smart-contracts/overview
[[page-reference | inline]]
| /smart-contracts/hello-world-tutorial
[[page-reference | inline]]
| /smart-contracts/counter-tutorial

5
src/pages/common/android_ref.md

@ -1,5 +0,0 @@
---
layout: externalurl
redirect_url: https://blockstack.github.io/blockstack-android/
title: 'Blockstack Android Reference'
---

5
src/pages/common/community_ref.md

@ -1,5 +0,0 @@
---
layout: externalurl
redirect_url: https://community.blockstack.org/
title: 'Blockstack Community'
---

5
src/pages/common/ios_ref.md

@ -1,5 +0,0 @@
---
layout: externalurl
redirect_url: https://blockstack.github.io/blockstack-ios/
title: 'Blockstack iOS Reference'
---

5
src/pages/common/javascript_ref.md

@ -1,5 +0,0 @@
---
layout: externalurl
redirect_url: https://blockstack.github.io/blockstack.js/
title: 'Blockstack Javascript Reference'
---

9
src/pages/contact.md

@ -1,9 +0,0 @@
---
title: Got Any Questions?
permalink: /contact/
---
##### To get in touch with us...
Please feel free to post any [questions on our public forum](https://forum.blockstack.org/).
You can also request [access to our Slack instance](https://docs.google.com/a/blockstack.com/forms/d/e/1FAIpQLSed5Mnu0G5ZMJdWs6cTO_8sTJfUVfe1sYL6WFDcD51_XuQkZw/viewform).

20
src/pages/core/faq_developer.md

@ -1,20 +0,0 @@
---
description: Blockstack DApp technical FAQs
---
export { convertFaqAnswersToMDX as getStaticProps } from '@common/data/faq'
import { FAQs } from '@components/faq'
# DApp Developer FAQs
This document lists frequently-asked questions developers about Blockstack application development. If you are new to Blockstack, you should read the [general questions](/faqs/allFAQs) first.
For more technical FAQs about Stacks nodes, the Stacks blockchain, and other architectural elements, see the [entire set of technical FAQs](/core/faq_technical).
If you have a technical question that gets frequently asked on the
[forum](https://forum.blockstack.org) or [Slack](https://blockstack.slack.com),
feel free to send a pull-request with the question and answer.
<FAQs category="appdevs" data={props.mdx} />
<FAQs category="opensource" data={props.mdx} />

26
src/pages/core/faq_technical.md

@ -1,26 +0,0 @@
export { convertFaqAnswersToMDX as getStaticProps } from '@common/data/faq'
import { FAQs } from '@components/faq'
# Technical FAQ
This document lists frequently-asked questions by developers interested in working with Blockstack application and core components. If you are new to Blockstack, you should read the [general questions](/faqs/allFAQs) first.
If you have a technical question that gets frequently asked on the
[forum](https://forum.blockstack.org) or [Slack](https://blockstack.slack.com),
feel free to send a pull-request with the question and answer.
## DApp developers
<FAQs category="appdevs" data={props.mdx} />
## Core developers
<FAQs category="coredevs" data={props.mdx} />
## Open source developers
<FAQs category="opensource" data={props.mdx} />
## Miscellaneous questions
<FAQs category="miscquest" data={props.mdx} />

0
src/pages/develop/radiks-collaborate.md → src/pages/data-indexing/collaborate.md

2
src/pages/develop/radiks-setup.md → src/pages/data-indexing/integrate.md

@ -3,7 +3,7 @@ title: Integrate Radiks
description: Learn how to setup Radiks with your application.
---
# Set-up Radiks for your DApp
# Integrate Radiks
Using Radiks with your application requires a Radiks server and a client application constructed to use the server.
In this article, you learn how to install, setup, and run a pre-packaged Radiks server that connects to MongoDB.

2
src/pages/develop/radiks-models.md → src/pages/data-indexing/models.md

@ -1,4 +1,6 @@
---
title: Create and use models
description: Model and query application data with Radiks.
---
# Create and use models

2
src/pages/develop/radiks-intro.md → src/pages/data-indexing/overview.md

@ -1,4 +1,6 @@
---
title: Overview
description: Using Radiks you can build multi-player apps that index, store, and query user data.
---
# Radiks the data indexer

2
src/pages/develop/radiks-server-extras.md → src/pages/data-indexing/server-extras.md

@ -1,4 +1,6 @@
---
title: Radiks server tips and tricks
description: Some tips and tricks for working with a Radiks server.
---
# Radiks server tips and tricks

17
src/pages/storage/authentication.md → src/pages/data-storage/authentication.md

@ -4,7 +4,10 @@ description: 'Storing user data with Blockstack'
# Authentication and Gaia
Blockstack authentication is a bearer token-based authentication system. From an app user's perspective, login similar to third-party authentication techniques that they're familiar with. For an app developer, the flow is unlike the typical client-server flow of centralized sign-in services such as OAuth. With Blockstack the authentication flow happens entirely client-side.
Blockstack authentication is a bearer token-based authentication system. From an app user's perspective,
login similar to third-party authentication techniques that they're familiar with. For an app developer,
the flow is unlike the typical client-server flow of centralized sign-in services such as OAuth. With Blockstack
the authentication flow happens entirely client-side.
In this section, you get an overview of the authentication system and learn how Gaia fits into it.
@ -14,14 +17,18 @@ A decentralized application (DApp) and the Blockstack authenticator communicate
the authentication flow by passing back and forth two tokens. The requesting
application sends the Blockstack authenticator an `authRequest` token. Once a user
approves a sign-in, the Blockstack authenticator responds to the application with an
`authResponse` token. These tokens are <a href="https://jwt.io/" target="\_blank">JSON Web Tokens</a>, and they are passed via
URL query strings.
`authResponse` token. These tokens are <a href="https://jwt.io/" target="\_blank">JSON Web Tokens</a>, and they are
passed via URL query strings.
When a user chooses to "Sign in with Blockstack" on your DApp, the `redirectToSignIn()` method sends the user to the Blockstack authenticator. The browser responds with an authentication token and an _app private key_.
When a user chooses to "Sign in with Blockstack" on your DApp, the `redirectToSignIn()` method sends the user to the
Blockstack authenticator. The browser responds with an authentication token and an _app private key_.
![](/storage/images/app-sign-in.png)
The app private key is application-specific. It is generated from the user's identity address private key using the `appDomain` as input. This key is deterministic, meaning that for a given Blockstack ID and domain name, the same private key is generated each time. The app private key is securely shared with the app on each authentication and encrypted by the Blockstack authenticator. The key serves three functions, it:
The app private key is application-specific. It is generated from the user's identity address private key using the
`appDomain` as input. This key is deterministic, meaning that for a given Blockstack ID and domain name, the same
private key is generated each time. The app private key is securely shared with the app on each authentication and
encrypted by the Blockstack authenticator. The key serves three functions, it:
- is used to create the credentials that give an app access to the Gaia hub storage bucket for that specific app
- is used in the end-to-end encryption of files stored for the app on the user's Gaia hub

1
src/pages/develop/collection-type.md → src/pages/data-storage/collection-type.md

@ -1,4 +1,5 @@
---
title: Create a Collection type
---
# How to create a Collection type

1
src/pages/develop/collections.md → src/pages/data-storage/collections.md

@ -1,4 +1,5 @@
---
title: Collections (Preview)
---
# Work with Collections (Preview)

0
src/pages/storage/overview.md → src/pages/data-storage/overview.md

116
src/pages/data-storage/storage-guide.md

@ -0,0 +1,116 @@
---
title: Guide to Blockstack Storage
---
# Guide to Blockstack Storage
The Blockstack Platform stores application data in the Gaia Storage System. Transactional metadata is stored on the
Blockstack blockchain and user application data is stored in Gaia storage. Storing data off of the blockchain ensures
that Blockstack applications can provide users with high performance and high availability for data reads and writes
without introducing central trust parties.
-> Blockstack Gaia Storage APIs and on-disk format will change in upcoming pre-releases breaking backward compatibility. File encryption is currently opt-in on a file by file basis. Certain storage features such as collections are not implemented in the current version. These features will be rolled out in future updates.
## How data is stored
Gaia storage is a key-value store.
## Creating a file
Use the [UserSession.putFile](https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile) method:
```tsx
const userSession = new UserSession();
const options: PutFileOptions = {
encrypt: false,
};
userSession.putFile('/hello.txt', 'hello world!', options).then(() => {
// /hello.txt exists now, and has the contents "hello world!".
});
```
## Creating an encrypted file
Use the [UserSession.putFile](https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile) method and
pass `encrypt: true` within the options object. See the [`PutFileOptions` type definition here](https://blockstack.github.io/blockstack.js/interfaces/putfileoptions.html#encrypt)
```tsx
const userSession = new UserSession();
const options: PutFileOptions = {
encrypt: true,
};
userSession.putFile('/message.txt', 'Secret hello!', options).then(() => {
// message.txt exists now, and has the contents "hello world!".
});
```
## Reading a file
Use the [UserSession.getFile](https://blockstack.github.io/blockstack.js/classes/usersession.html#getfile) method:
```tsx
const userSession = new UserSession();
const options: PutFileOptions = {
decrypt: false,
};
userSession.getFile('/hello.txt', options).then(fileContents => {
// get the contents of the file /hello.txt
assert(fileContents === 'hello world!');
});
```
## Reading an encrypted file
Use the [UserSession.getFile](https://blockstack.github.io/blockstack.js/classes/usersession.html#getfile) method and pass
`decrypt: true` within the options object. See the [`GetFileOptions` type definition here](https://blockstack.github.io/blockstack.js/interfaces/getfileoptions.html#decrypt)
```tsx
const userSession = new UserSession();
const options: GetFileOptions = {
decrypt: true,
};
userSession.getFile('/message.txt', options).then(fileContents => {
// get & decrypt the contents of the file /message.txt
assert(fileContents === 'Secret hello!');
});
```
## Reading another user's unencrypted file
In order for files to be publicly readable, the app must request
the [`publish_data` scope](https://blockstack.github.io/blockstack.js/enums/authscope.html#publish_data) during authentication.
```jsx
const options = {
user: 'ryan.id', // the Blockstack ID of the user for which to lookup the file
app: 'https://BlockstackApp.com', // origin of the app this file is stored for
};
const userSession = new UserSession();
userSession.putFile('/hello.txt', 'hello world!', options).then(fileContents => {
// get the contents of the file /message.txt
assert(fileContents === 'hello world!');
});
```
## Delete a file
Use the [`UserSession.deleteFile`](https://blockstack.github.io/blockstack.js/classes/usersession.html#deletefile) from the application's data store.
```jsx
const userSession = new UserSession();
userSession.deleteFile('/hello.txt').then(() => {
// /hello.txt is now removed.
});
```
## Related Information
To learn more about the guarantees provided by Gaia, see [Storage write and read](/storage/write-to-read)

0
src/pages/storage/write-to-read.md → src/pages/data-storage/storage-write-read.md

191
src/pages/develop/connect/overview.md

@ -1,191 +0,0 @@
---
description: 'JavaScript library for integration authentication and smart contracts'
redirect_from:
- /develop/connect/get-started.html
---
# Guide to Blockstack Connect
## Introduction
Blockstack Connect is a JavaScript library for integrating Blockstack authentication and smart contracts into your app.
The library empowers you to:
- Register new users with a pre-built onboarding flow that quickly educates them as to the privacy benefits of using your app with Blockstack and provisions a "Secret Key" that secures their identity and data against the Stacks blockchain.
- Authenticate users when they return to your app using that same Secret Key.
- Prompt users to sign transactions with smart contracts as written in Clarity and published to the Stacks blockchain.
## How does this compare to `blockstack.js`?
Although [`blockstack.js`](https://github.com/blockstack/blockstack.js) can also be used to authenticate users, it implements the deprecated [Blockstack Browser](https://browser.blockstack.org/) and lacks any pre-built onboarding UI that educates users as to how your app is more secure for having implemented Blockstack. As such, we advise that you use `blockstack.js` for all other functionality apart from authentication, such as saving and retrieving user data with Gaia.
## Start building with Blockstack Connect
Head over to the [Tutorial for App Integration](/browser/todo-list) to learn how to build apps with Blockstack Connect.
## Installation
With yarn:
```bash
yarn add @blockstack/connect
```
With npm:
```bash
npm install --save @blockstack/connect
```
## Usage
### AuthOptions
Every major method you'll use with `connect` requires you to pass some options, like the name and icon of your app, and what to do when authentication is finished. In practice, this means you need to define these options, and pass them to the various API methods.
The exact interface you'll use [is defined as](https://github.com/blockstack/connect/blob/master/src/auth.ts#L12:L24):
```tsx
export interface AuthOptions {
redirectTo: string;
finished: (payload: FinishedData) => void;
sendToSignIn?: boolean;
userSession?: UserSession;
appDetails: {
name: string;
icon: string;
};
}
```
| parameter | type | default | optional | description |
| ------------ | ----------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| redirectTo | string | | false | The path in your app where users go after sign in. |
| appDetails | object | | false | an object which includes `appName: string` and `appIcon: string`. This will speed up the process of loading your app's information during onboarding. |
| finished | function | | false | A callback that can be invoked after authentication. This prevents having to do a whole page refresh in a new tab. One argument is passed to this callback, which is an object with `userSession` included. If included, then the `redirectTo` path is ignored, and the user will be logged in automatically. |
| sendToSignIn | boolean | false | true | Whether the user should go straight to the 'sign in' flow (true) or be presented with the 'sign up' flow (false) instead. |
| userSession | UserSession | | false | pass a `UserSession` instance to use for authentication. If it's not passed, `@blockstack/connect` will create one for you. |
### In React Apps
If you're using `connect` in a React app, then the best option is to include `connect`'s React Provider and hooks in your React app.
First, setup the `Connect` provider at the "top-level" of your app - probably next to wherever you would put a Redux provider, for example.
```tsx
import { Connect } from '@blockstack/connect';
const authOptions = {
redirectTo: '/',
finished: ({ userSession }) => {
console.log(userSession.loadUserData());
},
appDetails: {
name: 'My Cool App',
icon: 'https://example.com/icon.png',
},
};
const App: React.FC<AppProps> = props => {
return <Connect authOptions={authOptions}>{/** the rest of your app's components */}</Connect>;
};
```
Later, when you want to begin the onboarding process, use the `useConnect` hook to get `connect`'s `doOpenAuth` method.
```tsx
import { useConnect } from '@blockstack/connect';
const SignInButton = () => {
const { doOpenAuth } = useConnect();
return <Button onClick={doOpenAuth}>Sign In</Button>;
};
```
#### Sign In
To send the user straight to sign in, call `doOpenAuth(true)`.
### In ES6 (non-React) apps
If you aren't using React, or just want a simpler API, then you can use the `showBlockstackConnect` method.
```tsx
import { showBlockstackConnect } from '@blockstack/connect';
const authOptions = {
/** See docs above for options */
};
showBlockstackConnect(authOptions);
```
#### Sign In
To send the user straight to sign in, include `sendToSignIn: true` in your `authOptions`.
#### Note about dependency size:
If you're building a non-React app, note that importing `@blockstack/connect` will add React dependencies to your JavaScript bundle. We recommend using something like [Webpack resolve aliases](https://webpack.js.org/configuration/resolve/) to replace `react` with `preact` in production, which reduces your bundle size. Check out [our own webpack.config.js file](https://github.com/blockstack/ux/blob/fix/connect-modal-accessibility/packages/connect/webpack.config.js#L87:L95) to see how we use this for production builds.
If you're using the hosted version of `@blockstack/connect` (described below), then you already have a production-optimized bundle.
### Using a hosted version of `@blockstack/connect`
If you aren't using ES6 imports, you can still use `connect`! We package the library so that it can be automatically used with [unpkg](https://unpkg.com/).
First, include the script in your HTML:
```html
<script src="https://unpkg.com/@blockstack/connect">
```
Then, you can use API methods under the `blockstackConnect` global variable:
```jsx
const authOptions = {
/** See docs above for options */
};
blockstackConnect.showBlockstackConnect(authOptions);
```
## Handling redirect fallbacks
Connect is built to use popups with the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API, which provides a much better and seamless user experience. However, there are times when this flow can fail. For example, the popup may be blocked, or the `window.postMessage` API might not work properly (which often happens on mobile browsers).
To make sure your app handles this gracefully, you'll need to handle the case where authentication is performed through regular HTTP redirects. With redirects, your users will be sent back to your app at a URL like:
`${authOptions.redirectTo}?authResponse=....`
To finalize authentication with this flow, you'll need to utilize the `UserSession` methods `isSignInPending()` and `handlePendingSignIn()`. For more information, check out the [blockstack.js API reference](https://blockstack.github.io/blockstack.js/).
```jsx
const userSession = new UserSession(appConfig);
// ... call this code on page load
if (userSession.isSignInPending()) {
const userData = await userSession.handlePendingSignIn();
// your user is now logged in.
}
```
## Design Guidance
Blockstack is valuable to users, but it can also be a barrier to those unfamiliar with Blockstack. The following guidelines serve to remedy that and help you onboard as many new users as you can.
### Delay Blockstack onboarding as long as possible
People will often leave apps when things are asked of them before they experience the app. Give them a chance to try your app before you ask them to sign up with Blockstack. For example, a note taking app could let a new user write a couple of notes before prompting them to save their progress.
### Provide an easy way in for new users
Many new users to your app will not be familiar with Blockstack yet and will be hesitant to click a Blockstack-branded button. Provide a generic button for users that are new to your app and Blockstack. Blockstack Connect will introduce new users to Blockstack and recognize existing users.
[![Design Guidance Example](/develop/images/connect-call-to-action-branding.png)](/develop/images/connect-call-to-action-branding.png)
### Provide a quick way for existing users to sign in
You can point users to a specific part of the Blockstack App. For instance, a “Sign in” button on your website can redirect users to the sign in flow of the Blockstack App. If you do this, make sure you also have an option that is explicitly for new users and that points to the sign up flow.
To implement this functionality, check out our section on sending users to sign in immediately.

4
src/pages/develop/faq-data.json

@ -1,4 +0,0 @@
---
layout: null
---
{{ site.data.appFAQ | jsonify }}

111
src/pages/develop/storage.md

@ -1,111 +0,0 @@
---
---
# Guide to Blockstack Storage
The Blockstack Platform stores application data in the Gaia Storage System. Transactional metadata is stored on the Blockstack blockchain and user application data is stored in Gaia storage. Storing data off of the blockchain ensures that Blockstack applications can provide users with high performance and high availability for data reads and writes without introducing central trust parties.
> Blockstack Gaia Storage APIs and on-disk format will change in upcoming pre-releases breaking backward compatibility. File encryption is currently opt-in on a file by file basis.
> Certain storage features such as collections are not implemented in the current version. These features will be rolled out in future updates.
## How data is stored
Gaia storage is a key-value store.
## Creating a file
You use the <a href="https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile" target="_blank">UserSession.putFile</a>
```jsx
const userSession = new UserSession();
const options = {
encrypt: false,
};
userSession.putFile('/hello.txt', 'hello world!', options).then(() => {
// /hello.txt exists now, and has the contents "hello world!".
});
```
## Creating an encrypted file
You use the <a href="https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile" target="_blank"></a>
```jsx
const userSession = new UserSession();
const options = {
encrypt: true,
};
userSession.putFile('/message.txt', 'Secret hello!', options).then(() => {
// message.txt exists now, and has the contents "hello world!".
});
```
## Reading a file
You use the <a href="https://blockstack.github.io/blockstack.js/classes/usersession.html#getfile" target="_blank"></a>
```jsx
const userSession = new UserSession();
const options = {
decrypt: false,
};
userSession.getFile('/hello.txt', options).then(fileContents => {
// get the contents of the file /hello.txt
assert(fileContents === 'hello world!');
});
```
## Reading an encrypted file
You use the <a href="" target="_blank"></a>
```jsx
const userSession = new UserSession();
const options = {
decrypt: true,
};
userSession.getFile('/message.txt', options).then(fileContents => {
// get & decrypt the contents of the file /message.txt
assert(fileContents === 'Secret hello!');
});
```
## Reading another user's unencrypted file
In order for files to be publicly readable, the app must request
the `publish_data` scope during authentication.
```jsx
const options = {
user: 'ryan.id', // the Blockstack ID of the user for which to lookup the file
app: 'http://BlockstackApp.com', // origin of the app this file is stored for
};
const userSession = new UserSession();
userSession.putFile('/hello.txt', 'hello world!', options).then(fileContents => {
// get the contents of the file /message.txt
assert(fileContents === 'hello world!');
});
```
## Delete a file
You use the <a href="https://blockstack.github.io/blockstack.js/classes/usersession.html#deletefile" target="_blank">UserSession.deleteFile</a> from the application's data store.
```jsx
const userSession = new UserSession();
userSession.deleteFile('/hello.txt').then(() => {
// /hello.txt is now removed.
});
```
## Related Information
To learn more about the guarantees provided by Gaia, see [Storage write and read](/storage/write-to-read)

0
src/pages/org/address_check.md → src/pages/ecosystem/address-confirmation.md

0
src/pages/contributing.md → src/pages/ecosystem/contributing.md

0
src/pages/org/faq.md → src/pages/ecosystem/faq.md

4
src/pages/org/overview.md → src/pages/ecosystem/overview.md

@ -1,5 +1,5 @@
---
description: 'Blockstack Network documentation'
description: Learn about Blockstack and decentralization
---
# Overview of Blockstack
@ -43,7 +43,7 @@ Stacks token. This year's hard fork is an especially exciting milestone for the
- A genesis block that distributes Stacks tokens to existing purchasers.
A full technical description of the upgrade is available on <a href="https://forum.blockstack.org/t/blockstack-annual-hard-fork-2018/6518" target="\blank" >the Blockstack forum</a>.
A full technical description of the upgrade is available on [the Blockstack forum](https://forum.blockstack.org/t/blockstack-annual-hard-fork-2018/6518).
In future upgrades and hard forks, the blockchain will expand to introduce a new
scalable consensus algorithm to increase the number of transactions it can

4
src/pages/org/tokenholders.md → src/pages/ecosystem/stacks-token-holders.md

@ -18,7 +18,7 @@ address directly to Blockstack, you can either use the **Restore from Seed
Phrase** feature on the Stacks Wallet or contact us at <hello@stackstoken.com> for
help.
Use the following form to check your Stacks allocation:
<!-- Use the following form to check your Stacks allocation: -->
<!-- TODO: make work with react -->
<!-- <div class="uk-background-secondary uk-padding uk-panel"> -->
@ -44,7 +44,7 @@ You should see a report detailing the tokens allocated to your address and when
![](images/unlocking-address.png)
If you have questions or concerns about your report, please contact <hello@StacksToken.com>.
If you have questions or concerns about your report, please contact [hello@StacksToken.com](mailto:hello@StacksToken.com).
## Understanding the timeline for unlocking your tokens

3
src/pages/org/token.md → src/pages/ecosystem/stacks-token.md

@ -1,5 +1,6 @@
---
description: 'Blockstack token holder documentation'
title: Stacks token
description: Learn about the native token of Blockstack
---
# Learn more about the Stacks token

5
src/pages/home.tsx

@ -1,6 +1,5 @@
import React from 'react';
import Head from 'next/head';
import { HomeLayout } from '@components/layouts/home';
import { Hero } from '@components/home/sections/hero';
import { AtomAltIcon } from '@components/icons/atom-alt';
import { CodeIcon } from '@components/icons/code';
@ -55,9 +54,7 @@ const Homepage = () => {
<Head>
<title>Documentation | Blockstack</title>
</Head>
<HomeLayout>
<Hero cards={cards} />
</HomeLayout>
<Hero cards={cards} />
</>
);
};

8
src/pages/index.md

@ -8,18 +8,18 @@ All you need to build decentralized apps and smart contracts
## Get started
[[page-reference | title]]
| browser/todo-list, core/smart/overview, core/smart/testnet-node
[[page-reference]]
| /build-an-app, /smart-contracts/overview, /mining
## Tutorials
[[page-reference]]
| browser/todo-list, core/smart/tutorial-counter
| /authentication/building-todo-app, /smart-contracts/hello-world-tutorial, /smart-contracts/counter-tutorial
## Explore
[[page-reference]]
| org/overview, org/token
| /ecosystem/overview, /ecosystem/stacks-token
## Sample projects

700
src/pages/ios/tutorial.md

@ -1,700 +0,0 @@
---
description: How to use Blockstack on iOS Mobile
---
# iOS DApps
This tutorial teaches you how to create a decentralized application using
Blockstack's iOS SDK using the following content:
This tutorial was extensively tested using Xcode Version 11.2 (11B52) on a MacBook Pro
running Mojave 10.14. If your environment is different, you may encounter
slight or even major discrepancies when performing the procedures in this
tutorial. Please [join the Blockstack community Slack](https://slofile.com/slack/blockstack) and post questions or comments to
the `#support` channel.
Finally, this tutorial is written for all levels from the beginner to the most
experienced. For best results, beginners should follow the guide as written. It
is expected that the fast or furiously brilliant will skip ahead and improvise
on this material at will. God speed one and all.
If you want to download a complete application rather than working through a tutorial, see this _alternative_ sample, the <a href="https://github.com/blockstack/photoblock-demo" target="_blank">photoblock-demo</a>.
## Understand the sample application flow
When complete, the sample application is a simple `hello-world` intended for use
on an iOS phone.
![](images/final-app.png)
Only users with an existing `blockstack.id` can run your
final sample application. When complete, users interact with the sample
application by doing the following:
![](images/app-flow.png)
## Set up your environment
This sample application requires two code bases, a BlockStack `hello-blockstack` web
application and a `hello-ios` iOS application. You use the iOS application to run the
web application on an iOS device.
Before you start developing the sample, there are a few elements you need in
your environment.
### Install XCode
If you are an experienced iOS developer and already have an iOS
development environment on your workstation, you can use that and skip this
step. However, you may need to adjust the remaining instructions for your
environment.
Follow the installation instructions to [download and install XCode](https://developer.apple.com/xcode/) for your operating system.
Depending on your network connection, this can take between 15-30 minutes.
### Do you have Node.js?
Node.js v10 or higher is recommended the minimum supported version is Node.js v8. Before you begin, verify you have the correct version of Node.js and its tools installed.
```bash
$ node -v
v12.10.0
$ which npm npx
/usr/local/bin/npm
/usr/local/bin/npx
```
If you don't have these installed, take a moment to install or upgrade as needed.
### Install the CocoaPods 1.6.0 dependency manager
The sample application only runs on devices with iOS 11.0 or higher. You install
the Blockstack iOS SDK through the CocoaPods. Cocoapods is a dependency manager
for Swift and Objective-C Cocoa projects. It’s a simple, user friendly way to
use libraries from the community in your project.
You must use CocoaPods version `1.6.0` or newer to avoid an
incompatibility between Cocoapods and XCode. Before starting the tutorial, confirm
you have installed CocoaPods.
```bash
$ pod --version
1.8.1
```
If you don't have the CocoaPods beta version, install it:
```bash
sudo gem install cocoapods -v 1.8.1
```
## Build the Blockstack hello-world
In this section, you build a Blockstack `hello-world` application. Then, you
modify the `hello-world` to interact with the iOS app via a redirect.
### Generate and launch your hello-blockstack application
@include "scaffolding.md"
In this section, you build an initial React.js application called
`hello-blockstack`.
1. Create a `hello-blockstack` directory.
```bash
mkdir hello-blockstack
```
2. Change into your new directory.
```bash
cd hello-blockstack
```
3. Create your initial `hello-world-tutorial` application.
```bash
$ npx generator-blockstack --react
npx: installed 338 in 13.792s
create package.json
create .gitignore
create webpack.config.js
create netlify.toml
create firebase.json
...
I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.
> fsevents@1.2.9 install /private/tmp/testymc/node_modules/fsevents
> node install
added 775 packages from 455 contributors and audited 9435 packages in 20.934s
found 0 vulnerabilities
```
Depending on your environment you may have some warnings with the installation. Optionally, you can fix these before continuing to the next section.
4. Run the initial application.
```bash
npm run start
> hello-blockstack@0.0.0 start /Users/meepers/repos/hello-blockstack
> webpack-dev-server
Project is running at http://localhost:8080/
webpack output is served from /
404s will fallback to /index.html
Hash: 4d2312ba236a4b95dc3a
Version: webpack 2.7.0
Time: 2969ms
Asset Size Chunks Chunk Names
....
Child html-webpack-plugin for "index.html":
chunk {0} index.html 541 kB [entry] [rendered]
[0] ./~/lodash/lodash.js 540 kB {0} [built]
[1] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 533 bytes {0} [built]
[2] (webpack)/buildin/global.js 509 bytes {0} [built]
[3] (webpack)/buildin/module.js 517 bytes {0} [built]
webpack: Compiled successfully.
```
At this point, the browser is running a Blockstack server on your local host.
5. Navigate to `http://localhost:8080` with your browser to display the
application.
![](/browser/images/initial-app.png)
This local instances is for testing your applications only.
6. Choose **Sign in with Blockstack**
The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one.
![](/browser/images/login-choice.png)
7. Follow the prompts appropriate to your situation.
If you are restoring an existing ID, you may see a prompt about your user
being nameless, ignore it. At this point you have only a single application
on your test server. So, you should see this single application, with your
own `blockstack.id` display name, once you are signed in:
![](/browser/images/hello-authd.png)
### Add a redirect end point to your application
When a user opens the webapp from the Blockstack Browser on an iOS phone,
you want the web app to redirect the user to your iOS application. The work
you do here will allow it.
1. From the terminal command line, change directory to your web
application directory (`hello-blockstack`).
1. If it doesn't ext, create the `public` directory.
```bash
$ mkdir public
```
1. Use the `touch` command to add a redirect endpoint to your application.
This endpoint on the web version of your app will redirect iOS users back
to your mobile app.
```bash
$ touch public/redirect.html
```
1. Open `redirect.html` and add code to the endpoint.
```
<!DOCTYPE html>
<html>
<head>
<title>Hello, Blockstack!</title>
<script>
function getParameterByName(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
var authResponse = getParameterByName('authResponse')
window.location="myblockstackapp://?authResponse=" + authResponse
</script>
<body>
</body>
</html>
```
Blockstack apps are identified by their domain names. The endpoint will
receive a get request with the query parameter `authResponse=XXXX` and
should redirect the browser to `myblockstackapp:XXXX`.
`myblockstackapp:` is custom protocol handler. The handler should be unique
to your application. Your app's web-based authentication uses this handler
to redirect the user back to your iOS app. Later, you'll add a reference
to this handler in your iOS application.
1. Close and save the `redirect.html` file.
1. Ensure your Blockstack app compiles successfully.
The `npm` process should detect and compile your change.
## Build the hello-blockstack-ios
Now, you build an iOS application that can work with your Blockstack web
application on a mobile device.
### Create an XCode Project
This tutorial uses XCode 11.1, you can use another version but be aware that some
menu items and therefore these procedures may be differœent on your version.
1. Launch the XCode interface.
2. Choose **Create a new XCode project**.
3. Select **iOS**.
4. Select **Single View App**.
![](images/single-view-app.png)
5. On the **Choose options for your new project** dialog, set the following:
| Product Name | `hello-blockstack-ios` |
| ----------------- | ---------------------- |
| Organization Name | USERNAME |
| User Interface | Storyboard |
![](images/choose-new-options.png)
6. Press **Next**.
The system prompts you for a location to store your code.
7. Save your project in your `hello-blockstack-ios` directory.
8. Close XCode.
### Add and edit a Podfile
To use CocoaPods you need to define the XCode target to link them to.
So, for exampleM if you are writing an iOS app, it would be the name of your app.
Create a target section by writing target `$TARGET_NAME do` and an `end` a few
lines after.
1. Open a terminal window on your workstation.
2. Change directory into your new project directory where your `hello-blockstack-ios.xcodeproj` file was created.
```swift
$ cd hello-blockstack-ios
```
3. Create a Podfile.
```bash
$ pod init
```
The command creates a `Podfile` in the directory.
4. Open the `Podfile` for editing.
5. Add a line stating the Blockstack dependency.
```
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'hello-blockstack-ios' do
s
!
s
'
o
s
g
d
end
```
6. Save and close the `Podfile`.
### Install Blockstack SDK and open the pod project
1. Close your new XCode project if you haven't already.
2. In terminal, make sure it is open to the root of your `hello-blockstack-ios` project.
3. Initialize the project with Cocoapods via the `pod install` command.
```bash
$ pod install
Analyzing dependencies
Downloading dependencies
Installing Blockstack (1.0.1)
Installing CryptoSwift (0.15.0)
Installing PromisesObjC (1.2.8)
Installing PromisesSwift (1.2.8)
Installing STRegex (2.1.0)
Generating Pods project
Integrating client project
[!] Please close any current XCode sessions and use `hello-blockstack-ios.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 2 total pods installed.
[!] Automatically assigning platform `ios` with version `11.3` on target `hello-blockstack-ios` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
```
This command creates a number of files
4. Review the files that the `pod` installation created:
```bash
$ ls
Podfile hello-blockstack-ios hello-blockstack-iosTests
Podfile.lock hello-blockstack-ios.xcodeproj
Pods hello-blockstack-ios.xcworkspace
```
5. Start XCode and choose **Open another project**.
6. Choose the `.xcworkspace` file created in your project folder.
![](images/open-xcworkspace.png)
When you open the workspace, the system will begin indexing the project. Then, after indexing, you **may** see a warning indicator at the top in the
project title. If you see the warning, continue to step 7. Otherwise, go to the next section.
7. Click the signal to reveal the warning.
8. Click **Next**.
![](images/indicator.png)
9. Select all the targets and press **Next** and **Continue** when prompted.
Make sure you have no errors; warnings are acceptable to continue.
### Choose a custom protocol handler
You'll need to choose a custom protocol handler that is unique to your app. This
is so that your app's web-based authentication `redirect.html` endpoint can redirect
the user back to your iOS app. In this example, you use `myblockstackapp://`.
1. Open the `.xcworkspace` file in XCode if it isn't open already.
2. Select the top node of your project.
3. Select the **Info** tab in XCode.
4. Scroll to **URL Types** and press **+** (plus) sign.
5. Enter an **Identifier** and **URL Schemes** value.
| **Identifier** | `MyBlockstackApp` |
| **URL Schemes** | `myblockstackapp` |
6. Set the **Role** to **Editor**.
When you are done the **URL Types** appears as follows:
![](images/url-type.png)
### Add a splash screen
All iOS applications require a splash page.
1. Select `Assets.xcassets`
2. Move your cursor into the area below Appicon.
3. Right click and choose **New Image Set**
![](images/image-set-0.png)
4. Download the Blockstack icon.
![](images/blockstack-icon.png)
5. Drag the downloaded file into the **3X** position in your new Images folder.
![](images/image-set-1.png)
6. Select the `LaunchScreen.storyboard`.
7. Choose **Open As > Source Code**.
![](images/open-as.png)
8. Replace the content of the `<scenes>` element with the following:
```
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Image" translatesAutoresizingMaskIntoConstraints="NO" id="SpU-hA-y2f">
<rect key="frame" x="155.5" y="273" width="64" height="64"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hello Blockstack iOS" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wfj-A6-BZM">
<rect key="frame" x="108" y="432" width="158" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Wfj-A6-BZM" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="AZy-qf-xHq"/>
<constraint firstItem="Wfj-A6-BZM" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="412" id="SwP-qV-1RP"/>
<constraint firstItem="SpU-hA-y2f" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="XdI-Db-fDo"/>
<constraint firstItem="SpU-hA-y2f" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="253" id="xc5-po-W1E"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="52" y="374.66266866566718"/>
</scene>
</scenes>
```
9. Immediately after scenes but before the close of the `</document>` tag add the following `<resources>`.
```xml
>
<image name="Image" width="64" height="64"/>
>
>
```
10. Choose **Run > Run app** in the emulator.
The emulator now contains a new splash screen.
![](images/splash.png)
### Update the Main.storyboard
Rather than have you build up your own UI in the interface builder, this section has you copy and paste a layout into the XML file source code for the **Main.storyboard** file.
1. Select the `Main.storyboard` file.
2. Choose **Open As > Source Code**
The `hello-blockstack-ios/Base.lproj/Main.storyboard` file
defines the graphical elements, and their functionality will be defined in
a respective `.swift` file.
3. Within the `<viewController>` element, replace the existing `<view>` sub-element with the following:
```xml
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="hello-blockstack-ios" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9eE-ZS-LU9">
<rect key="frame" x="0.0" y="101" width="375" height="50"/>
<color key="backgroundColor" red="0.44735813140000003" green="0.1280144453" blue="0.57268613580000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="U5v-13-4Ux"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Lfp-KX-BDb">
<rect key="frame" x="100" y="382" width="175" height="40"/>
<color key="backgroundColor" red="0.1215686275" green="0.12941176469999999" blue="0.14117647059999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="8fN-Ro-Krn"/>
</constraints>
<color key="tintColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<state key="normal" title="Sign into Blocksack">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="signIn:" destination="BYZ-38-t0r" eventType="touchUpInside" id="nV7-rt-euZ"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="9eE-ZS-LU9" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="2ZP-tU-h9Y"/>
<constraint firstItem="9eE-ZS-LU9" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="81" id="DBh-q0-pAV"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="Lfp-KX-BDb" secondAttribute="trailing" constant="100" id="MHO-ew-4Bd"/>
<constraint firstItem="Lfp-KX-BDb" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="100" id="Rsm-LP-ya7"/>
<constraint firstItem="Lfp-KX-BDb" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="362" id="chE-B7-ya6"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="9eE-ZS-LU9" secondAttribute="trailing" id="j0x-8j-04u"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
`
```
4. Select the Main.storyboard and choose **Open as > Interface Builder - Storyboard**.
![](images/main-storyboard.png)
You should see the following:
![](images/new-storyboard.png)
### Add the UI variables to the ViewController file.
In this section, you edit the `ViewController.swift` file using the storyboard as a starting point.
1. Select the **Main.storyboard** and choose **Open As > Interface Builder - storyboard**.
2. With the interface builder open, choose **Editor > Assistant** to edit the `ViewController.swift` file.
![](images/view-editors.png)
3. In the storyboard, select the **Sign into Blockstack** button.
4. Control-drag from the button to the code display in the editor on the right, stopping the drag at the line below controller's `viewDidLoad()` statement.
![](images/add-action.gif)
5. Set the **Connection** to `Outlet`.
6. Name the new section `signInButton`.
![](images/signinbutton.png)
7. Press **Connect**.
8. Repeat with purple **hello-blockstack-ios** label, name it `nameLabel.
9. Add an import statement for the `Blockstack` and another for the `SafariServices` module.
When you are done, your `ViewController.swift` file contains the following:
```swift
import UIKit
import Blockstack
import SafariServices
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet var signInButton: UIButton!
}
```
And XCode has added two outlines to the `Main.storyboard` source.
```xml
<connections>
<outlet property="nameLabel" destination="9eE-ZS-LU9" id="Ahv-Te-ZZo"/>
<outlet property="signInButton" destination="Lfp-KX-BDb" id="yef-Jj-uPt"/>
</connections>
```
Your connectors will have their own `destination` and `id` values.
### Edit the ViewController.swift file
Now, you are ready to connect your application with your Blockstack Web
Application. Normally, after building your Web application you would have
registred it with Blockstack and the app would be available on the Web. This
example skips this registration step and uses an example application we've
already created for you:
`https://heuristic-brown-7a88f8.netlify.app/redirect.html`
This web application already has a redirect in place for you. You'll reference
this application in your mobile add for now. In XCode, do the following;
1. Open the `ViewController.swift` file.
2. Add an import both for `Blockstack`.
```
t
k
`
```
3. Add a private `updateUI()` function.
This function takes care of loading the user data from Blockstack.
```swift
private func updateUI() {
DispatchQueue.main.async {
if Blockstack.shared.isUserSignedIn() {
// Read user profile data
let retrievedUserData = Blockstack.shared.loadUserData()
print(retrievedUserData?.profile?.name as Any)
let name = retrievedUserData?.profile?.name ?? "Nameless Person"
self.nameLabel?.text = "Hello, \(name)"
self.nameLabel?.isHidden = false
self.signInButton?.setTitle("Sign Out", for: .normal)
print("UI update SIGNED_IN")
} else {
self.nameLabel?.text = "hello-blockstack-ios"
self.signInButton?.setTitle("Sign into Blockstack", for: .normal)
print("UI update SIGNED_OUT")
}
}
}
```
The function uses the `Blockstack.shared.isUserSignedIn()` method to determine if
the user is already logged into Blockstack or not. It then uses the
`Blockstack.shared.loadUserData()` method to load the user data and update
the application display with the username.
4. Replace the content of the `viewDidLoad()` function so that it calls this private function.
```swift
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.updateUI()
}
`
```
5. Create a `signIn()` function that handles both sign in and out.
The function uses the `Blockstack.shared.signIn()` and
`Blockstack.shared.signUserOut()` methods to sign the user into the application.
```swift
@IBAction func signIn(_ sender: UIButton) {
if Blockstack.shared.isUserSignedIn() {
print("Currently signed in so signing out.")
Blockstack.shared.signUserOut()
self.updateUI()
} else {
print("Currently signed out so signing in.")
// Address of deployed example web app
Blockstack.shared.signIn(redirectURI: URL(string: "https://heuristic-brown-7a88f8.netlify.app/redirect.html")!,
appDomain: URL(string: "https://heuristic-brown-7a88f8.netlify.app")!) { authResult in
switch authResult {
case .success(let userData):
print("Sign in SUCCESS", userData.profile?.name as Any)
self.updateUI()
case .cancelled:
print("Sign in CANCELLED")
case .failed(let error):
print("Sign in FAILED, error: ", error ?? "n/a")
}
}
}
}
```
## Troubleshooting your build in XCode
XCode builds can retain old data. To ensure your builds are clean, try the following:
1. Reset the simulator by choosing **Hardware -> Erase all content and settings** from the menu.
2. In XCode, clean the project by choosing **Product > Clean** from the menu or press `Command + Shift + K` on your keyboard.
3. Clean the build folder by pressing `Command + Option + Shift + K` on your keyboard.
4. Run the code on the simulator again.

6
src/pages/mining.md

@ -0,0 +1,6 @@
---
title: Mine Stacks Token
description: Run a node, earn STX, and learn how Proof of Transfer (PoX) works
---
# Mine Stacks Token

0
src/pages/core/naming/architecture.md → src/pages/naming-services/architecture.md

0
src/pages/core/naming/search.md → src/pages/naming-services/build-profile-search-index.md

0
src/pages/core/naming/pickname.md → src/pages/naming-services/choose-name.md

0
src/pages/core/naming/comparison.md → src/pages/naming-services/comparison.md

0
src/pages/core/naming/creationhowto.md → src/pages/naming-services/create-namespace.md

0
src/pages/core/naming/did.md → src/pages/naming-services/did.md

0
src/pages/core/naming/forks.md → src/pages/naming-services/forks.md

0
src/pages/core/naming/manage.md → src/pages/naming-services/manage-names.md

0
src/pages/core/naming/namespaces.md → src/pages/naming-services/namespaces.md

0
src/pages/core/naming/introduction.md → src/pages/naming-services/overview.md

0
src/pages/core/naming/register.md → src/pages/naming-services/register-name.md

0
src/pages/core/naming/resolving.md → src/pages/naming-services/resolve-name.md

0
src/pages/core/naming/tutorial_subdomains.md → src/pages/naming-services/subdomains-tutorial.md

0
src/pages/core/naming/subdomains.md → src/pages/naming-services/subdomains.md

6
src/pages/org/whitepaper-blockchain.md

@ -1,6 +0,0 @@
---
title: Blockstack Technical Whitepaper
layout: externalurl
redirect_url: https://blockstack.org/whitepaper.pdf
---

0
src/pages/core/cmdLineRef.md → src/pages/references/blockstack-cli-reference.md

0
src/pages/core/smart/clarityRef.md → src/pages/references/clarity-reference.md

0
src/pages/develop/deploy-tips.md → src/pages/references/deploy-tips.md

0
src/pages/faqs/allFAQS.md → src/pages/references/faqs.md

0
src/pages/org/terms.md → src/pages/references/glossary.md

0
src/pages/common/core_ref.md → src/pages/references/stacks-blockchain-reference.md

0
src/pages/core/smart/rpc-api.md → src/pages/references/stacks-rpc-api-reference.md

0
src/pages/core/smart/cli-wallet-quickstart.md → src/pages/smart-contracts/cli-wallet-quickstart.md

11
src/pages/core/smart/tutorial-counter.md → src/pages/smart-contracts/counter-tutorial.md

@ -1,12 +1,11 @@
---
description: Learn how to write a simple smart contract in the Clarity language
description: Learn how to write a simple smart contract in the Clarity language.
experience: intermediate
duration: 30 minutes
---
# Counter smart contract
| Experience | | **Intermediate** |
| Duration | | **30 minutes** |
In this tutorial, you learn how to implement a smart contract that stores and manipulates an integer value on the Stacks 2.0 blockchain. By the end of this tutorial, you will ...
- Have experienced test-driven development with Clarity
@ -173,7 +172,3 @@ With the completion of this tutorial, you:
- Experienced test-driven development with Clarity
- Understood more Clarity language design principles
- Developed a working Clarity counter smart contract
## Where to go next
- <a href="clarityRef">Clarity language reference</a>

0
src/pages/core/smart/tutorial.md → src/pages/smart-contracts/hello-world-tutorial.md

0
src/pages/core/smart/install-source.md → src/pages/smart-contracts/install-source.md

0
src/pages/core/smart/overview.md → src/pages/smart-contracts/overview.md

0
src/pages/core/smart/principals.md → src/pages/smart-contracts/principals.md

0
src/pages/core/smart/testnet-node.md → src/pages/smart-contracts/running-testnet-node.md

0
src/pages/develop/connect/use-with-clarity.md → src/pages/smart-contracts/signing-transactions.md

0
src/pages/core/smart/tutorial-test.md → src/pages/smart-contracts/testing-contracts.md

0
src/pages/core/atlas/howitworks.md → src/pages/stacks-blockchain/atlas/how-atlas-works.md

0
src/pages/core/atlas/overview.md → src/pages/stacks-blockchain/atlas/overview.md

0
src/pages/core/atlas/howtouse.md → src/pages/stacks-blockchain/atlas/usage.md

6
src/pages/core/best-practices.md → src/pages/stacks-blockchain/best-practices.md

@ -1,3 +1,9 @@
---
title: Best practices
---
# Best practices
## Hardware and OS requirements
- A 64-bit CPU running at at least 1 GHz is _highly_ recommended (but not strictly required)

0
src/pages/core/install-api.md → src/pages/stacks-blockchain/install-api.md

0
src/pages/core/memcached.md → src/pages/stacks-blockchain/installing-memcached.md

5
src/pages/stacks-blockchain/overview.md

@ -0,0 +1,5 @@
---
title: Overview
---
# Overview

0
src/pages/core/wire-format.md → src/pages/stacks-blockchain/wire-format.md

0
src/pages/org/wallet-install.md → src/pages/stacks-wallet/install.md

0
src/pages/org/wallet-intro.md → src/pages/stacks-wallet/overview.md

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save