Update code for detached container and to match new website tutorial (#151)

* detached container

* Update package.json

* changed to js and fixed all the things

* fix default view in azure example

* Update src/app-azure.js

Co-authored-by: Skyler Jokiel <skjokiel@microsoft.com>

* fixed azure view

* change back to app

* Update src/view/vueView.js

Co-authored-by: Skyler Jokiel <skjokiel@microsoft.com>

* changes due to feedback

* remove tinylicious not because it automatically runs

Co-authored-by: Skyler Jokiel <skjokiel@microsoft.com>
This commit is contained in:
Micah Godbolt 2021-09-02 15:52:47 -07:00 коммит произвёл GitHub
Родитель c7d3d77f8b
Коммит 45d41eda1f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 2662 добавлений и 1490 удалений

3705
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -13,20 +13,21 @@
"start:server": "npx tinylicious"
},
"dependencies": {
"@fluid-experimental/fluid-framework": "^0.45.0",
"@fluid-experimental/frs-client": "^0.45.0",
"@babel/preset-react": "^7.14.5",
"@fluidframework/azure-client": "^0.47.0-35961",
"@fluidframework/tinylicious-client": "^0.47.0-35961",
"fluid-framework": "^0.47.0-35961",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"vue": "^3.0.5"
"vue": "^3.2.6"
},
"devDependencies": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^3.0.0",
"concurrently": "^5.3.0",
"html-webpack-plugin": "^4.3.0",
"ts-loader": "^8.0.17",
"typescript": "~4.1.3",
"webpack": "^4.46.0",
"webpack-cli": "^4.2.0",
"webpack-dev-server": "^3.11.0"
@ -35,7 +36,7 @@
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"singleQuote": false,
"printWidth": 100
}
}

63
src/app-azure.js Normal file
Просмотреть файл

@ -0,0 +1,63 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { SharedMap } from "fluid-framework";
import { AzureClient, InsecureTokenProvider } from "@fluidframework/azure-client";
import { jsRenderView as renderDiceRoller } from "./view";
export const diceValueKey = "dice-value-key";
// This configures the AzureClient to use a local in-memory service called Tinylicious.
// You can run Tinylicious locally using "npx tinylicious".
const localConfig = {
tenantId: "local",
tokenProvider: new InsecureTokenProvider("anyValue", { id: "userId" }),
// if you"re running Tinylicious on a non-default port, you"ll need change these URLs
orderer: "http://localhost:7070",
storage: "http://localhost:7070",
};
// This configures the AzureClient to use a remote Azure Fluid Service instance.
// const azureUser = {
// userId: "Test User",
// userName: "test-user"
// }
// const prodConfig: AzureConnectionConfig = {
// tenantId: "",
// tokenProvider: new AzureFunctionTokenProvider("", azureUser),
// orderer: "",
// storage: "",
// };
const client = new AzureClient(localConfig);
const containerConfig = {
initialObjects: { diceMap: SharedMap }
};
const root = document.getElementById("content");
const createNewDice = async () => {
const { container } = await client.createContainer(containerConfig);
container.initialObjects.diceMap.set(diceValueKey, 1);
const id = container.attach();
renderDiceRoller(container.initialObjects.diceMap, root);
return id;
}
const loadExistingDice = async (id) => {
const { container } = await client.getContainer(id, containerConfig);
renderDiceRoller(container.initialObjects.diceMap, root);
}
async function start() {
if (location.hash) {
await loadExistingDice(location.hash.substring(1))
} else {
const id = await createNewDice();
location.hash = id;
}
}
start().catch((error) => console.error(error));

42
src/app.js Normal file
Просмотреть файл

@ -0,0 +1,42 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { SharedMap } from "fluid-framework";
import { TinyliciousClient } from "@fluidframework/tinylicious-client";
import { jsRenderView as renderDiceRoller } from "./view";
export const diceValueKey = "dice-value-key";
const client = new TinyliciousClient();
const containerSchema = {
initialObjects: { diceMap: SharedMap }
};
const root = document.getElementById("content");
const createNewDice = async () => {
const { container } = await client.createContainer(containerSchema);
container.initialObjects.diceMap.set(diceValueKey, 1);
const id = container.attach();
renderDiceRoller(container.initialObjects.diceMap, root);
return id;
}
const loadExistingDice = async (id) => {
const { container } = await client.getContainer(id, containerSchema);
renderDiceRoller(container.initialObjects.diceMap, root);
}
async function start() {
if (location.hash) {
await loadExistingDice(location.hash.substring(1))
} else {
const id = await createNewDice();
location.hash = id;
}
}
start().catch((error) => console.error(error));

Просмотреть файл

@ -1,56 +0,0 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { ContainerSchema, ISharedMap, SharedMap } from "@fluid-experimental/fluid-framework";
import { FrsClient, FrsConnectionConfig, FrsContainerConfig, InsecureTokenProvider } from "@fluid-experimental/frs-client";
import { getContainerId } from "./utils";
import { vueRenderView as renderView } from "./view";
async function start() {
const { id, isNew } = getContainerId();
// This configures the FrsClient to use a local in-memory service called Tinylicious.
// You can run Tinylicious locally using 'npx tinylicious'.
const localConfig: FrsConnectionConfig = {
tenantId: "local",
tokenProvider: new InsecureTokenProvider("anyValue", { id: "userId" }),
// if you're running Tinylicious on a non-default port, you'll need change these URLs
orderer: "http://localhost:7070",
storage: "http://localhost:7070",
};
// This configures the FrsClient to use a remote Azure Fluid Service instance.
// const frsAzUser = {
// userId: "Test User",
// userName: "test-user"
// }
// const prodConfig: FrsConnectionConfig = {
// tenantId: "",
// tokenProvider: new FrsAzFunctionTokenProvider("", frsAzUser),
// orderer: "",
// storage: "",
// };
const client = new FrsClient(localConfig);
const containerConfig: FrsContainerConfig = { id };
const containerSchema: ContainerSchema = {
name: "hello-world-demo-container",
initialObjects: { dice: SharedMap }
};
const { fluidContainer } = isNew
? await client.createContainer(containerConfig, containerSchema)
: await client.getContainer(containerConfig, containerSchema);
renderView(
fluidContainer.initialObjects.dice as ISharedMap,
document.getElementById("content") as HTMLDivElement
);
}
start().catch((error) => console.error(error));

Просмотреть файл

@ -1,13 +0,0 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { ISharedMap } from "@fluidframework/map";
/**
* Render Dice into a given HTMLElement as a text character, with a button to roll it.
* @param data - The Data to be rendered
* @param div - The HTMLElement to render into
*/
export type IRenderView = (data: ISharedMap, div: HTMLDivElement) => void;

Просмотреть файл

@ -1,23 +0,0 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* Each Fluid document needs a unique ID. Typically applications will leverage a service to create
* and retrive these IDs, similar to a file picker or loading a saved game instance.
*
* For demo purposes the getContainerId function simplifies ID creation by generating and returning
* a hash of the current timestamp as the new document ID. If the URL already has a hash,
* we assume that a document with that ID has already been created and we return that
* hash value as the ID.
*/
export const getContainerId = (): { id: string; isNew: boolean } => {
let isNew = false;
if (location.hash.length === 0) {
isNew = true;
location.hash = Date.now().toString();
}
const id = location.hash.substring(1);
return { id, isNew };
};

Просмотреть файл

@ -1,6 +0,0 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
export * from './getContainerId';

Просмотреть файл

@ -3,6 +3,6 @@
* Licensed under the MIT License.
*/
export * from './jsView';
export * from './reactView';
export * from './vueView';
export * from "./jsView";
export * from "./reactView";
export * from "./vueView";

36
src/view/jsView.js Normal file
Просмотреть файл

@ -0,0 +1,36 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { diceValueKey } from "../app";
export const jsRenderView = (diceMap, elem) => {
const wrapperDiv = document.createElement("div");
wrapperDiv.style.textAlign = "center";
elem.append(wrapperDiv);
const dice = document.createElement("div");
dice.style.fontSize = "200px";
const rollButton = document.createElement("button");
rollButton.style.fontSize = "50px";
rollButton.textContent = "Roll";
// Set the value at our dataKey with a random number between 1 and 6.
rollButton.onclick = () => diceMap.set(diceValueKey, Math.floor(Math.random() * 6) + 1);
wrapperDiv.append(dice, rollButton);
// Get the current value of the shared data to update the view whenever it changes.
const updateDice = () => {
const diceValue = diceMap.get(diceValueKey);
// Unicode 0x2680-0x2685 are the sides of a dice (⚀⚁⚂⚃⚄⚅)
dice.textContent = String.fromCodePoint(0x267f + diceValue);
dice.style.color = `hsl(${diceValue * 60}, 70%, 50%)`;
};
updateDice();
// Use the changed event to trigger the rerender whenever the value changes.
diceMap.on("valueChanged", updateDice);
}

Просмотреть файл

@ -1,38 +0,0 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { IRenderView } from '../types';
export const jsRenderView: IRenderView = (data, div) => {
const dataKey = 'dataKey';
const wrapperDiv = document.createElement('div');
wrapperDiv.style.textAlign = 'center';
div.append(wrapperDiv);
const diceCharDiv = document.createElement('div');
diceCharDiv.style.fontSize = '200px';
const rollButton = document.createElement('button');
rollButton.style.fontSize = '50px';
rollButton.textContent = 'Roll';
// Set the value at our dataKey with a random number between 1 and 6.
rollButton.addEventListener('click', () =>
data.set(dataKey, Math.floor(Math.random() * 6) + 1)
);
wrapperDiv.append(diceCharDiv, rollButton);
// Get the current value of the shared data to update the view whenever it changes.
const updateDiceChar = () => {
const diceValue = data.get(dataKey) || 1;
// Unicode 0x2680-0x2685 are the sides of a dice (⚀⚁⚂⚃⚄⚅)
diceCharDiv.textContent = String.fromCodePoint(0x267f + (diceValue as number));
diceCharDiv.style.color = `hsl(${diceValue * 60}, 70%, 50%)`;
};
updateDiceChar();
// Use the changed event to trigger the rerender whenever the value changes.
data.on('valueChanged', updateDiceChar);
}

39
src/view/reactView.jsx Normal file
Просмотреть файл

@ -0,0 +1,39 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import React from "react";
import ReactDOM from "react-dom";
import { diceValueKey } from "../app";
export const reactRenderView = (dice, elem) => {
ReactDOM.render(<ReactView dice={dice} />, elem);
}
const ReactView = (props) => {
const { dice } = props;
const [diceValue, setDiceValue] = React.useState(1);
const rollDice = () => dice.set(diceValueKey, Math.floor(Math.random() * 6)+1);
React.useEffect(() => {
const syncLocalAndFluidState = () => setDiceValue(dice.get(diceValueKey));
syncLocalAndFluidState();
dice.on("valueChanged", syncLocalAndFluidState);
return () => {
dice.off("valueChanged", syncLocalAndFluidState);
};
});
return (
<div style={{ textAlign: "center" }}>
<div style={{ fontSize: 200, color: `hsl(${diceValue * 60}, 70%, 50%)` }}>
{String.fromCodePoint(0x267F + diceValue)}
</div>
<button style={{ fontSize: 50 }} onClick={rollDice}>
Roll
</button>
</div>
);
};

Просмотреть файл

@ -1,44 +0,0 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import React from "react";
import ReactDOM from "react-dom";
import { ISharedMap } from "@fluidframework/map";
import { IRenderView } from '../types';
export const reactRenderView: IRenderView = (data, div) => {
ReactDOM.render(<ReactView data={data} />, div);
}
interface ReactViewProps {
data: ISharedMap
}
const ReactView = (props: ReactViewProps) => {
const { data } = props;
const [diceValue, setDiceValue] = React.useState(1);
const diceCharacter = String.fromCodePoint(0x267F + diceValue);
const rollDice = () => data.set("dice", Math.floor(Math.random() * 6) + 1);
React.useEffect(() => {
const syncLocalAndFluidState = () => setDiceValue(data.get("dice") || 1);
syncLocalAndFluidState();
data.on("valueChanged", syncLocalAndFluidState);
return () => {
data.off("valueChanged", syncLocalAndFluidState);
};
});
return (
<div style={{ textAlign: "center" }}>
<div style={{ fontSize: 200, color: `hsl(${diceValue * 60}, 70%, 50%)` }}>
{diceCharacter}
</div>
<button style={{ fontSize: 50 }} onClick={rollDice}>
Roll
</button>
</div>
);
};

Просмотреть файл

@ -3,14 +3,15 @@
* Licensed under the MIT License.
*/
import { createApp } from 'vue';
import { IRenderView } from "../types";
import { createApp } from "vue";
import { diceValueKey } from "../app";
/**
* Render Dice into a given HTMLElement as a text character, with a button to roll it.
* @param dataObject - The Data Object to be rendered
* @param div - The HTMLElement to render into
* @param dice - The SharedMap holding the collaborative data
* @param elem - The HTMLElement to render into
*/
export const vueRenderView: IRenderView = (data, div) => {
export const vueRenderView = (dice, elem) => {
const app = createApp({
template: `
<div style="text-align: center" >
@ -21,10 +22,10 @@ export const vueRenderView: IRenderView = (data, div) => {
Roll
</button>
</div>`,
data: () => ({ diceValue: 1 }),
data: () => ({ diceValue: dice.get(diceValueKey) }),
computed: {
diceCharacter() {
return String.fromCodePoint(0x267f + (this.diceValue as number));
return String.fromCodePoint(0x267f + (this.diceValue));
},
diceColor() {
return `hsl(${this.diceValue * 60}, 70%, 50%)`;
@ -32,19 +33,19 @@ export const vueRenderView: IRenderView = (data, div) => {
},
methods: {
rollDice() {
data.set('dice', Math.floor(Math.random() * 6) + 1);
dice.set(diceValueKey, Math.floor(Math.random() * 6)+1);
},
syncLocalAndFluidState() {
this.diceValue = data.get('dice');
this.diceValue = dice.get(diceValueKey);
},
},
mounted() {
data.on('valueChanged', this.syncLocalAndFluidState);
dice.on("valueChanged", this.syncLocalAndFluidState);
},
unmounted() {
data.off('valueChanged', this.syncLocalAndFluidState);
dice.off("valueChanged", this.syncLocalAndFluidState);
},
});
app.mount(div);
app.mount(elem);
}

Просмотреть файл

@ -1,15 +0,0 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"target": "es6",
"noUnusedLocals": true,
"module": "esnext",
"moduleResolution": "node",
"outDir": "built",
"sourceMap": true,
"inlineSources": true,
"strict": true,
"jsx": "react"
},
"include": ["src/**/*"]
}

Просмотреть файл

@ -19,21 +19,29 @@ module.exports = env => {
return {
devtool: "inline-source-map",
entry: {
app: "./src/app.ts",
app: "./src/app.js",
},
mode,
module: {
rules: [{
test: /\.tsx?$/,
loader: "ts-loader"
}]
},
output: {
filename: "[name].[contenthash].js",
},
module: {
rules: [
{
test: /\.jsx/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"]
}
}
}
]
},
plugins,
resolve: {
extensions: [".ts", ".tsx", ".js"],
extensions: [".jsx", ".js"],
alias: {
vue$: "vue/dist/vue.esm-bundler.js",
},