Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 254 additions & 0 deletions extensions/GamerCreeper12/ShareSheet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// Name: ShareSheet
// ID: gamercreepernoobssharesheetextension
Comment thread
CreeperNewGamer marked this conversation as resolved.
Outdated
// Description: Share data from your browser.
// By: GamerCreeper12 <https://scratch.mit.edu/users/GamerCreeper12/>
// License: MPL-2.0

(function (Scratch) {
"use strict";

if (!Scratch.extensions.unsandboxed) {
throw new Error("Share Sheet must run unsandboxed");
}

const Cast = Scratch.Cast;

class GamerCreeperNoobsShareSheetExtension {
Comment thread
CreeperNewGamer marked this conversation as resolved.
Outdated
constructor() {
this.dataType = "text";
this.title = "";
this.data = null;
this.shareData = {};
this.cancelled = false;
this.hasError = false;
this.errorMsg = "";
}
getInfo() {
return {
id: "gamercreepernoobssharesheetextension",
name: Scratch.translate("ShareSheet"),
color1: "#742DEB",
color3: "#a176e8",
blocks: [
{
opcode: "issupported",
blockType: Scratch.BlockType.BOOLEAN,
text: Scratch.translate("is share sheet supported?"),
},
{
opcode: "settitleandtextto",
blockType: Scratch.BlockType.COMMAND,
text: Scratch.translate("set title [TITLE]"),
arguments: {
TITLE: {
type: Scratch.ArgumentType.STRING,
defaultValue: Scratch.translate("Check out this file!"),
},
},
},
{
opcode: "setdatatotxt",
blockType: Scratch.BlockType.COMMAND,
text: Scratch.translate("set data to text [TXT]"),
arguments: {
TXT: {
type: Scratch.ArgumentType.STRING,
defaultValue: Scratch.translate("Hello, World!"),
},
},
},
{
opcode: "setdatatourl",
blockType: Scratch.BlockType.COMMAND,
text: Scratch.translate("set data to url [URL]"),
arguments: {
URL: {
type: Scratch.ArgumentType.STRING,
defaultValue: Scratch.translate("https://example.com"),
},
},
},
{
opcode: "setdatatoimageurl",
blockType: Scratch.BlockType.COMMAND,
text: Scratch.translate("set data to image url [URL]"),
arguments: {
URL: {
type: Scratch.ArgumentType.STRING,
defaultValue: Scratch.translate("https://example.com/myimage/"),
},
},
},
{
opcode: "setdatatocostume",
blockType: Scratch.BlockType.COMMAND,
text: Scratch.translate("set data to costume [COSTUME]"),
arguments: {
COSTUME: {
type: Scratch.ArgumentType.COSTUME,
},
},
},
{
opcode: "share",
blockType: Scratch.BlockType.COMMAND,
text: Scratch.translate("share data"),
},
{
opcode: "isdatatosharesupported",
blockType: Scratch.BlockType.BOOLEAN,
text: Scratch.translate("is last data to share supported?"),
},
{
opcode: "iscancelled",
blockType: Scratch.BlockType.BOOLEAN,
text: Scratch.translate("is sharing cancelled?"),
},
{
opcode: "gotaerrortwhilesharing",
blockType: Scratch.BlockType.BOOLEAN,
text: Scratch.translate("got a error while sharing?"),
},
{
opcode: "lasterrormsg",
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate("last error message"),
},
],
};
}
// helpers
isShareSheetSupported() {
return !!(navigator.share && navigator.canShare);
}

isShareSheetDataSupported(shareData) {
if (!this.isShareSheetSupported()) return false;
return !!navigator.canShare(shareData);
}

async shareDataFunc(shareData) {
if (!this.isShareSheetDataSupported(shareData)) return;
await navigator.share(shareData);
}

async shareDataForShareSheet() {
if (!this.isShareSheetSupported()) return;
this.shareData = {};
this.cancelled = false;
this.hasError = false;
try {
switch (this.dataType) {
case "text":
if (this.title) this.shareData["title"] = this.title;
if (this.data) this.shareData["text"] = this.data;
await this.shareDataFunc(this.shareData);
break;

case "url":
if (this.title) this.shareData["title"] = this.title;
if (this.data) this.shareData["url"] = this.data;
await this.shareDataFunc(this.shareData);
break;

case "image":
if (this.title) this.shareData["title"] = this.title;
if (this.data) this.shareData["files"] = [this.data];
await this.shareDataFunc(this.shareData);
break;

default:
return;
}
} catch (err) {
if (err.name === "AbortError") {
this.cancelled = true;
} else {
this.hasError = true;
this.errorMsg = err;
}
}
}

// blocks
issupported() {
return Cast.toBoolean(this.isShareSheetSupported());
}

settitleandtextto({ TITLE: title }) {
this.title = Cast.toString(title) || "Share";
}

setdatatotxt({ TXT: text }) {
this.dataType = "text";
this.data = Cast.toString(text);
}

setdatatourl({ URL: url }) {
this.dataType = "url";
this.data = Cast.toString(url);
}

async setdatatoimageurl({ URL: url }) {
this.hasError = false;
try {
const response = await Scratch.fetch(url);
const blob = await response.blob();

const imageFile = new File([blob], "image.png", {
type: blob.type,
});
Comment on lines +200 to +202

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes the image is a PNG?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it should detect the type of the asset


this.dataType = "image";
this.data = imageFile;
} catch (err) {
this.hasError = true;
this.errorMsg = err.message;
}
}

async setdatatocostume({ COSTUME: costumeName }, util) {
const target = util.target;
const costumeIndex = target.getCostumeIndexByName(costumeName);
const costume = target.sprite.costumes[costumeIndex];
const asset = costume.asset.encodeDataURI();

this.hasError = false;
try {
const response = await Scratch.fetch(asset);
const blob = await response.blob();

const imageFile = new File([blob], "image.png", {
type: blob.type,
});

this.dataType = "image";
this.data = imageFile;
} catch (err) {
this.hasError = true;
this.errorMsg = err.message;
}
}
Comment on lines +212 to +233

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was only able to get this block to work with bitmap costumes. Many costumes will be vector. Either specify "set data to bitmap costume" or, better, convert the costume to bitmap before setting the data.


async share() {
await this.shareDataForShareSheet();
}

isdatatosharesupported() {
return this.isShareSheetDataSupported(this.shareData);
}

iscancelled() {
return this.cancelled;
}

gotaerrortwhilesharing() {
return this.hasError;
}

lasterrormsg() {
return this.errorMsg;
}
}
Scratch.extensions.register(new GamerCreeperNoobsShareSheetExtension());
})(Scratch);
1 change: 1 addition & 0 deletions extensions/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"XmerOriginals/closecontrol",
"navigator",
"battery",
"GamerCreeper12/ShareSheet",
"PwLDev/vibration",
"TheShovel/CustomStyles",
"TheShovel/ColorPicker",
Expand Down
Loading