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:
Родитель
c7d3d77f8b
Коммит
45d41eda1f
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
17
package.json
17
package.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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
|
@ -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));
|
||||
|
||||
|
56
src/app.ts
56
src/app.ts
|
@ -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));
|
13
src/types.ts
13
src/types.ts
|
@ -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";
|
|
@ -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);
|
||||
}
|
|
@ -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",
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче