1678279200
Nextcloud — это ваше собственное облако с открытым исходным кодом. Вот как сделать переключатель.
Если вы опасаетесь передавать свои данные в облачные службы, контролируемые корпорацией, но вам нравится удобство удаленного хранения и легкий доступ через Интернет, вы не одиноки. Облако популярно из-за того, что оно может делать. Но облако не обязательно должно быть закрытым . К счастью, проект Nextcloud с открытым исходным кодом предоставляет набор приложений для личного и частного облака.
Легко устанавливать и импортировать данные, включая контакты, календари и фотографии. Настоящая хитрость заключается в получении ваших данных от облачных провайдеров, таких как iCloud. В этой статье я демонстрирую шаги, которые необходимо предпринять, чтобы перенести свою цифровую жизнь на Nextcloud.
Как и в случае с устройствами Android , сначала необходимо перенести существующие данные из Apple iCloud в Nextcloud. Затем вы можете настроить две новые учетные записи для своих устройств Apple, чтобы полностью автоматически синхронизировать адресные книги и встречи. Apple поддерживает CalDAV для календарей и CardDAV для контактов, поэтому вам даже не нужно устанавливать дополнительное приложение.
Чтобы экспортировать свою адресную книгу, вы можете либо открыть приложение «Контакты» на своем iPhone/iPad, либо войти в iCloud в веб-браузере:
Выберите все записи адресной книги, которые вы хотите перенести в Nextcloud, и выберите «Файл» > «Экспорт» > «Экспорт vCard» , чтобы сохранить файл . vcfфайл на локальном диске.
Импортируйте файл . vcfфайл в Nextcloud. Для этого выберите приложение «Контакты» , нажмите «Настройки» в левом нижнем углу и нажмите кнопку «Импорт контактов» . В следующем диалоговом окне нажмите Выбрать локальный файл и откройте ранее сохраненную визитную карточку.
Чтобы настроить учетную запись CardDAV на iPhone или iPad, выберите «Настройки» > «Контакты» > «Учетные записи» > «Добавить учетную запись» :
Выберите «Другое» , а затем «Добавить учетную запись CardDAV» . В поле Сервер введите URL-адрес Nextcloud (например, https://nextcloudpi.local ). Ниже этого места для имени пользователя и пароля учетной записи Nextcloud. Откройте дополнительные настройки для новой учетной записи.
Убедитесь, что опция Использовать SSL включена. URL-адрес учетной записи обычно указывается правильно. Он содержит, среди прочего, имя хоста вашего Nextcloud и ваше имя пользователя.
Чтобы создать новую учетную запись в macOS для синхронизации адресных книг, откройте приложение «Контакты» и выберите «Добавить учетную запись» в меню «Контакты» . Установите флажок «Другая учетная запись контактов» и нажмите «Продолжить» . Вы можете принять запись CardDAV . В раскрывающемся меню «Тип учетной записи» выберите «Ввод вручную» .
(Хайке Юрзик, CC BY-SA 4.0)
Введите имя пользователя, пароль и адрес сервера Nextcloud. Текущая версия macOS требует указать порт 443 (для SSL) в адресе сервера. Например, если адрес вашего Nextcloud — https://nextcloudpi.local , а имя пользователя — hej, то введите в поле следующее:
https://nextcloudpi.local:443/remote.php/dav/principals/users/hej
Экспорт ваших календарей работает аналогичным образом. Через приложение «Календарь» вы можете сделать это с помощью iCloud в браузере, на своем смартфоне/планшете или на рабочем столе macOS.
Сначала сделайте календарь общедоступным . Это не означает, что каждый может получить доступ к вашему календарю. Он используется только для создания ссылки для подписки на календарь. Скопируйте URL-адрес в буфер обмена. Импортировать календарь напрямую в Nextcloud пока нельзя, потому что для этого нужна не ссылка, а файл .ics(iCalendar). Вот как сгенерировать такой файл по ссылке:
Скопируйте ссылку в буфер обмена
Вставьте ссылку в адресную строку веб-браузера
Измените начало URL и замените webcalнаhttp
Нажмите Enter и сохраните .icsфайл на свой диск.
(Хайке Юрзик, CC BY-SA 4.0)
Теперь вы можете импортировать .icsфайл. Для этого откройте приложение «Календарь» в Nextcloud, нажмите «Настройки календаря» в левом нижнем углу, а затем «Импортировать календарь» . Выберите .icsфайл, который вы сохранили в файловом менеджере.
Повторите этот процесс для всех календарей iCloud. После этого пришло время заменить старый сервис синхронизации iCloud.
Чтобы синхронизировать новые события с Nextcloud, настройте новую учетную запись на своих клиентских устройствах (смартфон, планшет, компьютер):
iPhone/iPad: «Настройки » / «Календарь » / «Учетные записи » / «Добавить учетную запись» , выберите «Другое» , а затем выберите «Добавить учетную запись CalDAV» . В поле «Сервер» введите локальный URL-адрес Nextcloud, то есть https://nextcloudpi.local. Вы можете увидеть место для имени пользователя и пароля учетной записи Nextcloud.
macOS: откройте приложение «Календарь» и выберите «Добавить учетную запись» в меню «Календарь» . Установите флажок «Другая учетная запись CalDAV» и нажмите «Продолжить» . В раскрывающемся меню «Тип учетной записи» выберите «Ввод вручную» . Введите имя пользователя и пароль Nextcloud, а также адрес сервера Nextcloud. Не забудьте в адресе сервера указать порт 443 (для SSL); в противном случае настройка учетной записи завершится ошибкой.
Совет: Если вы хотите синхронизировать другие файлы, такие как документы, фотографии, видео и т. д., в дополнение к вашим контактам и календарям, вы можете установить приложение Nextcloud, предлагаемое в App Store.
Оригинальный источник статьи: https://opensource.com/
1678275185
Nextcloud 是您自己的开源云。这是进行切换的方法。
如果您对将数据提交给公司控制的云服务持谨慎态度,但喜欢远程存储的便利性和基于 Web 的轻松访问,那么您并不孤单。云因其功能而广受欢迎。但云不必关闭。幸运的是,开源 Nextcloud 项目提供了个人和私有云应用程序套件。
安装和导入数据很容易——包括联系人、日历和照片。真正的诀窍是从 iCloud 等云提供商那里获取数据。在本文中,我演示了将您的数字生活迁移到 Nextcloud 需要采取的步骤。
对于Android 设备,首先您必须将现有数据从 Apple 的 iCloud 传输到 Nextcloud。然后,您可以为您的 Apple 设备设置两个新帐户,以完全自动同步地址簿和约会。Apple 支持用于日历的 CalDAV 和用于联系人的 CardDAV,因此您甚至不需要安装额外的应用程序。
要导出您的地址簿,您可以在您的 iPhone/iPad 上打开联系人应用程序或在您的网络浏览器中登录 iCloud:
选择要传输到 Nextcloud 的所有地址簿条目,然后选择文件 > 导出 > 导出 vCard以保存一个 . vcf本地磁盘上的文件。
导入 . vcf文件到 Nextcloud。为此,请选择“联系人”应用程序,单击 左下角的“设置” ,然后选择“导入联系人”按钮。在接下来的对话框中,点击 选择本地文件,打开之前保存的vCard。
要在 iPhone 或 iPad 上设置 CardDAV 帐户,请转至设置>通讯录>帐户>添加帐户:
选择其他,然后选择添加 CardDAV 帐户。在服务器字段中,输入 Nextcloud 的 URL(例如 https://nextcloudpi.local)。下面是 Nextcloud 帐户的用户名和密码的空间。打开新帐户的高级设置。
确保启用使用 SSL 选项。帐户 URL 通常设置正确。其中包含您的 Nextcloud 的主机名和您的用户名。
要在 macOS 上创建用于同步地址簿的新帐户,请打开“联系人”应用程序并从“联系人”菜单中选择“添加帐户”。激活其他联系人帐户复选框,然后单击继续。您可以接受CardDAV条目。在账户类型下拉菜单中,选择手动输入。
(Heike Jurzik,CC BY-SA 4.0)
输入您的 Nextcloud 用户名、密码和服务器地址。当前的 macOS 版本要求您在服务器地址中指定端口 443(用于 SSL)。例如,如果您的 Nextcloud 的地址是https://nextcloudpi.local并且用户名是 hej,则在字段中输入以下内容:
https://nextcloudpi.local:443/remote.php/dav/principals/users/hej
导出日历的工作方式类似。通过日历应用程序,您可以在浏览器、智能手机/平板电脑或 macOS 桌面上使用 iCloud 执行此操作。
首先,将日历设置为public。这并不意味着每个人都可以访问您的日历。它仅用于生成日历订阅的链接。将 URL 复制到剪贴板。目前还无法将日历直接导入 Nextcloud,因为您不需要链接,而是文件.ics(iCalendar)。以下是如何从链接生成这样的文件:
将链接复制到剪贴板
将链接粘贴到网络浏览器的地址栏中
更改 URL 的开头并替换webcal为http
按Enter并将.ics文件保存到磁盘上
(Heike Jurzik,CC BY-SA 4.0)
您现在可以导入该.ics文件。为此,请在 Nextcloud 中打开日历应用程序,单击左下角的日历设置,然后单击导入日历。选择.ics您保存在文件管理器中的文件。
对所有 iCloud 日历重复此过程。之后,就该更换旧的 iCloud 同步服务了。
要与 Nextcloud 同步新事件,请在您的客户端设备(智能手机、平板电脑、台式机)上设置一个新帐户:
iPhone/iPad:设置/日历/账户/添加账户,选择其他然后选择添加 CalDAV 账户。在服务器字段中,输入您本地的 Nextcloud URL,即https://nextcloudpi.local. 您可以看到 Nextcloud 帐户的用户名和密码的空格。
macOS:打开日历应用程序并从日历菜单中选择添加帐户。激活复选框Other CalDAV Account并单击Continue。从账户类型下拉菜单中,选择手动输入。输入您的 Nextcloud 用户名和密码以及 Nextcloud 服务器地址。不要忘记在服务器地址中指定端口 443(用于 SSL);否则帐户设置将失败。
提示:如果您想同步其他文件,如文档、照片、视频等,除了您的联系人和日历,您可以安装 App Store 中提供的 Nextcloud 应用程序。
文章原文出处:https: //opensource.com/
1678271160
Nextcloud is your very own open source cloud. Here's how to make the switch.
If you're wary of committing your data to cloud services controlled by a corporation but love the convenience of remote storage and easy web-based access, you're not alone. The cloud is popular because of what it can do. But the cloud doesn't have to be closed. Luckily, the open source Nextcloud project provides a personal and private cloud application suite.
It's easy to install and import data—including contacts, calendars, and photos. The real trick is getting your data from cloud providers like iCloud. In this article, I demonstrate the steps you need to take to migrate your digital life to Nextcloud.
As with Android devices, first you must transfer existing data from Apple's iCloud to Nextcloud. Then you can set up two new accounts for your Apple devices to fully automatically synchronize address books and appointments. Apple supports CalDAV for calendars and CardDAV for contacts, so you don't even need to install an extra app.
To export your address book, you can either open the Contacts app on your iPhone/iPad or log into iCloud in your web browser:
Select all address book entries you want to transfer to Nextcloud and choose File > Export > Export vCard to save a .vcf
file on your local disk.
Import the .vcf
file into Nextcloud. To do this, select the Contacts app, click Settings at the bottom left and select the Import contacts button. In the following dialogue window, click Select local file, and open the previously saved vCard.
To set up a CardDAV account on your iPhone or iPad, go to Settings > Contacts > Accounts > Add Account:
Select Other and then Add CardDAV account. In the Server field, enter the URL of Nextcloud (for example, https://nextcloudpi.local). Below this is space for the username and password of the Nextcloud account. Open the Advanced Settings for the new account.
Ensure the Use SSL option is enabled. The account URL is usually set correctly. It contains, amongst other things, the host name of your Nextcloud and your user name.
To create a new account on macOS for synchronizing address books, open the Contacts app and select Add Account from the Contacts menu. Activate the checkbox Other Contacts Account and click on Continue. You can accept the CardDAV entry. In the Account Type drop-down menu, select Manual entry.
(Heike Jurzik, CC BY-SA 4.0)
Enter your Nextcloud user name, password, and server address. The current macOS version requires you to specify port 443 (for SSL) in the server address. For example, if the address of your Nextcloud is https://nextcloudpi.local and the username is hej, then enter the following in the field:
https://nextcloudpi.local:443/remote.php/dav/principals/users/hej
Exporting your calendars works similarly. Through the Calendar app, you can do this with iCloud in the browser, on your smartphone/tablet, or the macOS desktop.
First, set the calendar to public. This doesn't mean that everyone can access your calendar. It's only used to generate a link for the calendar subscription. Copy the URL to the clipboard. It's not yet possible to import the calendar directly into Nextcloud because you don't need a link for this, but an .ics
file (iCalendar). Here is how to generate such a file from the link:
Copy the link to the clipboard
Paste the link into the address bar of a web browser
Change the beginning of the URL and replace webcal
with http
Press Enter and save the .ics
file on your disk
(Heike Jurzik, CC BY-SA 4.0)
You can now import the .ics
file. To do this, open the Calendar app in Nextcloud, click Calendar settings at the bottom left and then Import calendar. Select the .ics
file you saved in the file manager.
Repeat this process for all iCloud calendars. After that, it's time to replace the old iCloud synchronization service.
To synchronize new events with Nextcloud, set up a new account on your client devices (smartphone, tablet, desktop):
iPhone/iPad: Settings / Calendar / Accounts / Add Account, select Other and then choose Add CalDAV Account. In the Server field, enter your local Nextcloud URL, which is https://nextcloudpi.local
. You can see a space for the username and password of the Nextcloud account.
macOS: Open the Calendar app and select Add Account from the Calendar menu. Activate the checkbox Other CalDAV Account and click Continue. From the Account Type drop-down menu, select Manual entry. Enter your Nextcloud username and password as well as the Nextcloud server address. Don't forget to specify the port 443 (for SSL) in the server address; otherwise the account setup will fail.
Tip: If you want to synchronize other files like documents, photos, videos, and so on, in addition to your contacts and calendars, you can install the Nextcloud app offered in the App Store.
Original article source at: https://opensource.com/
1678111261
yuzu is the world's most popular, open-source, Nintendo Switch emulator — started by the creators of Citra.
It is written in C++ with portability in mind, and we actively maintain builds for Windows and Linux.
The emulator is capable of running most commercial games at full speed, provided you meet the necessary hardware requirements.
For a full list of games yuzu support, please visit our Compatibility page
Check out our website for the latest news on exciting features, monthly progress reports, and more!
Most of the development happens on GitHub. It's also where our central repository is hosted. For development discussion, please join us on Discord.
If you want to contribute, please take a look at the Contributor's Guide and Developer Information. You can also contact any of the developers on Discord in order to know about the current state of the emulator.
If you want to contribute to the user interface translation project, please check out the yuzu project on transifex. We centralize translation work there, and periodically upstream translations.
You can download the latest releases automatically via the installer on our downloads page.
If you enjoy the project and want to support us financially, check out our Patreon!
Any donations received will go towards things like:
If you wish to support us a different way, please join our Discord and talk to bunnei. You may also contact: donations@yuzu-emu.org.
Author: Yuzu-emu
Source Code: https://github.com/yuzu-emu/yuzu
License: GPL-3.0 license
1670447880
When you’re building a game, you’re probably going to have more than one screen. For example, you might have an initial menu screen, a main gameplay screen, and a game over screen. These screens can be thought of as scenes in a game, the same way you can think of scenes in film.
Every scene that you have in your game represents a clean slate, or a fresh starting point for that particular subsection of content.
In this tutorial, we’re going to see how to create multiple scenes, switch between them, and pass data between them, using Phaser 3.x and simple JavaScript.
To get an idea of what we plan to accomplish, take a look at the following animated image:
We’re not doing much in the above image, but that doesn’t make it any less important. We have two scenes in the above example, one with a plane, and one with some game over text. Upon some kind of trigger, the scenes are switched, and in this example the first scene with the plane, passes a message to the second scene. That message is rendered on the screen.
Let’s work towards reproducing the above example.
Depending on how you’ve been creating Phaser games in the past, designing your game for multiple scenes could look very different in terms of code formatting. Rather than dumping everything into an index.html file or similar, we need to have a class for every scene that we plan to use.
To see what I’m talking about, create a new directory on your computer with an index.html, scene-one.js, and scene-two.js file inside. Within the index.html file, add the following markup:
<!DOCTYPE html>
<html>
<head>
<script src="//cdn.jsdelivr.net/npm/phaser@3.24.1/dist/phaser.min.js"></script>
<script src="scene-one.js"></script>
<script src="scene-two.js"></script>
</head>
<body>
<div id="game"></div>
<script>
const phaserConfig = {
type: Phaser.AUTO,
parent: "game",
width: 1280,
height: 720,
backgroundColor: "#5DACD8",
scene: [ ]
};
const game = new Phaser.Game(phaserConfig);
</script>
</body>
</html>
Remember, we’re not going to dump all of our code in the <script>
tag for this example. Notice that we’re importing both the scene-one.js and scene-two.js files for use. As of right now, if we ran this project, it likely wouldn’t run because we don’t have any scene information defined.
We’re going to add the foundation for our scenes in the next step.
Both of our scenes are going to be simplistic in comparison to what can be accomplished. I don’t want to add too much complexity to take away from the point I’m trying to show, which is in scenes.
Open the scene-one.js file and add the following code:
var SceneOne = new Phaser.Class({
Extends: Phaser.Scene,
initialize: function() {
Phaser.Scene.call(this, { "key": "SceneOne" });
},
init: function() {},
preload: function() {
this.load.image("plane", "plane.png");
},
create: function() {
this.plane = this.add.image(640, 360, "plane");
},
update: function() {}
});
In the above Phaser class, we give it a name which will be important when switch scenes. The class has access to the same init
, preload
, create
, and update
events that you’d expect. For this particular example, we’re loading an image and adding it to the screen. The image is not particularly important, we’re just using it as something to recognize which scene we are in.
Now open the scene-two.js file:
var SceneTwo = new Phaser.Class({
Extends: Phaser.Scene,
initialize: function() {
Phaser.Scene.call(this, { "key": "SceneTwo" });
},
init: function() {},
preload: function() {},
create: function() {
var text = this.add.text(
640,
360,
"Hello World",
{
fontSize: 50,
color: "#000000",
fontStyle: "bold"
}
).setOrigin(0.5);
},
update: function() {}
});
Pay attention to the key
again, because that will be important when it comes to switching between scenes.
In the above scene example, we are rendering text to the screen, in this case the center of our Phaser screen. Again, nothing particularly exciting is happening in this scene, just like with the previous.
With the scene logic in place, we can add them to the overall Phaser configuration. Within the index.html file, change the phaserConfig
to look like the following:
const phaserConfig = {
type: Phaser.AUTO,
parent: "game",
width: 1280,
height: 720,
backgroundColor: "#5DACD8",
scene: [ SceneOne, SceneTwo ]
};
Notice that we’ve added both scenes to our scene
array. The order is important because the first scene in the array will be the default scene. You can always navigate between them, but it is important to know which one will display first.
If you run the game, you should see the scene with the image.
Now that we have multiple scenes each with their own lifecycle events, we probably want to switch between them and even pass data between them. Before we worry about the data side of things, let’s just focus on switching.
The idea is as follows:
SceneOne
scene.SceneTwo
scene.Ideally you’d probably want to switch scenes based on some game object interaction, but for demonstration purposes, a timer will be fine.
Within the create
function of the scene-one.js file, change it to look like the following:
create: function() {
this.plane = this.add.image(640, 360, "plane");
this.time.addEvent({
delay: 3000,
loop: false,
callback: () => {
this.scene.start("SceneTwo");
}
})
},
Notice the use of the timer. After three seconds, the next named scene will start. When the next scene starts, that scene goes through each of the lifecycle events necessary for configuring and rendering the scene.
If we did nothing else, our code would work. However, it might make sense to pass data from one scene to another. An example being with score information. Maybe the first scene is the gameplay scene and you’re accumulating a score. Then maybe the second scene is a game over scene that should display the score. Well, you’re going to need to transfer the score to the new scene, so this example could be useful.
Within the create
function of the scene-one.js file, change the scene.start
slightly to look like the following:
this.scene.start("SceneTwo", {
"message": "Game Over"
});
Notice that this time we’re passing an object. The key and values in this object can be whatever you want.
Since we’re passing values, we need a way to receive them in the next scene. Within the init
function of the scene-two.js file, change it to look like the following:
init: function(data) {
this.message = data.message;
},
Notice that this function is accepting a parameter. That parameter is the data from the previous scene. We need to set it to a locally scoped scene variable so that it can be accessed throughout the lifecycle events of our scene.
Now let’s modify the create
function in the same file:
create: function() {
var text = this.add.text(
640,
360,
this.message,
{
fontSize: 50,
color: "#000000",
fontStyle: "bold"
}
).setOrigin(0.5);
},
Notice that instead of rendering “Hello World”, we’re now rendering the message that was passed from the previous scene.
You just saw how to work with numerous scenes and pass data between them in a Phaser 3.x game. This is useful if you’re creating a larger scale game, or just need to be able to transition between different screens such as a menu screen, gameplay screen, and game over screen.
In this example, we just used a static image. If you’re interested in learning how to animate that image, check out my previous tutorial on the subject titled, Animate Spritesheets in a Phaser Game.
Original article source at: https://www.thepolyglotdeveloper.com/
1668844686
Learn how to write switch statement in React and JSX
The switch/case
statement is a conditional operator that’s commonly used as an alternative to if...else
statement.
For example, let’s say you have a blogging application. You want to display different layout based on the user’s role:
function App(props){
const { role } = props
if (role === 'author'){
return <AuthorLayout>What will you write today?</AuthorLayout>
}
if (role === 'admin'){
return <AdminLayout>Your latest reports </AdminLayout>
}
if (role === 'moderator'){
return <ModeratorLayout>Your ongoing events</ModeratorLayout>
}
return <GuestLayout>Your current feed</GuestLayout>
}
You can refactor the above component using switch/case
statement as follows:
function App(props){
const { role } = props
switch(role) {
case 'author':
return <AuthorLayout>What will you write today?</AuthorLayout>
case 'admin':
return <AdminLayout>Your latest reports </AdminLayout>
case 'moderator':
return <ModeratorLayout>Your ongoing events</ModeratorLayout>
default:
return <GuestLayout>Your current feed</GuestLayout>
}
}
Unlike in general JavaScript function where you need to use break
statement, React component already has return
statement that stops the switch
operation.
But if you use switch
without return
, then you still need the break
statement to prevent “falling through” the next case:
function App(props){
const { role } = props
let component
switch(role) {
case 'author':
component = <AuthorLayout>What will you write today?</AuthorLayout>
break
case 'admin':
component = <AdminLayout>Your latest reports </AdminLayout>
break
case 'moderator':
component = <ModeratorLayout>Your ongoing events</ModeratorLayout>
break
default:
component = <GuestLayout>Your current feed</GuestLayout>
}
return component
}
Both patterns are valid in React, so you can choose which one’s perfect for your case. When you have a range of conditions, it’s better to use if...else...then
:
if(role === 'author' && isSuspended === false && published === true){
// run the code
}
When you have only one value to evaluate, it’s generally recommended to use switch
.
Original article source at: https://sebhastian.com/
1666519560
Installation
Just add the RAMPaperSwitch
folder to your project.
or use CocoaPods with Podfile:
pod 'RAMPaperSwitch'
or Carthage users can simply add to their Cartfile
:
github "Ramotion/paper-switch"
Usage
RAMPaperSwitch is a drop-in replacement of UISwitch. You just need to set the onTintColor
property of the switch, and it will automatically paint over its superview with the selected color. You have ability to set duration of animation instead of default value.
Create a new UISwitch in your storyboard or nib.
Set the class of the UISwitch to RAMPaperSwitch in your Storyboard or nib.
Set onTintColor
for the switch
Set duration
property programmatically if You want to change animation duration.
Add animation for other views near the switch if need.
Animate views
You can animate other views near the switch. For example, you can change color to views or labels that are inside the same superview. Duration of animation can be gotten from the RAMPaperSwitch's property duration
. You can animate CoreAnimation properties like this:
self.paperSwitch.animationDidStartClosure = {(onAnimation: Bool) in
UIView.transitionWithView(self.label, duration: self.paperSwitch.duration, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
self.label.textColor = onAnimation ? UIColor.whiteColor() : UIColor.blueColor()
}, completion:nil)
}
Try this UI component and more like this in our iOS app. Contact us if interested.
Author: Ramotion
Source Code: https://github.com/Ramotion/paper-switch
License: MIT license
1662647879
Switcher button. Flutter switch button with minimal design and material animation and highly customizable.It can be use as switch button or toggle buttons.
Table of Contents
[TOC]
###Preview
###Example
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:swipe_button_widget/swipe_button_widget.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Home(),
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
const SizedBox(height: 8),
SwipeButtonWidget(
acceptPoitTransition: 0.7,
margin: const EdgeInsets.all(0),
padding: const EdgeInsets.all(0),
boxShadow: const [
BoxShadow(
blurRadius: 4,
color: Color.fromRGBO(197, 197, 197, 0.25),
spreadRadius: 1.5,
),
],
borderRadius: BorderRadius.circular(8),
colorBeforeSwipe: Colors.white,
colorAfterSwiped: Colors.white,
height: 60,
childBeforeSwipe: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.red[900],
),
width: 100,
height: double.infinity,
child: const Center(
child: Text(
'>>>',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28,
color: Colors.white),
),
),
),
childAfterSwiped: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.red[900],
),
width: 100,
height: double.infinity,
child: const Center(
child: Text(
'>>>',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28,
color: Colors.white),
),
),
),
leftChildren: const [
Align(
alignment: Alignment(0.9, 0),
child: Text(
'Swip for confirmation',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
color: Colors.red),
),
)
],
rightChildren: const [
Align(
alignment: Alignment(-0.6, 0),
child: Text(
'Swip for arrived',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
color: Colors.red),
),
)
],
onHorizontalDragUpdate: (e) {},
onHorizontalDragRight: (e) {
return areYouSureDialog(context);
},
onHorizontalDragleft: (e) async {
return false;
}),
const SizedBox(height: 8),
SwipeButtonWidget(
padding: const EdgeInsets.all(15),
borderRadius: BorderRadius.circular(8),
childBeforeSwipe: Text(
'>>> Slide To Logout',
style: TextStyle(
fontSize: 18,
color: Color.fromRGBO(5, 132, 55, 1),
),
),
childAfterSwiped: Text(
'<<< Slide To Login',
style: TextStyle(
fontSize: 18,
color: Colors.white,
),
),
leftChildren: const [
Align(
alignment: Alignment.centerRight,
child: Text(
'Active',
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
],
rightChildren: const [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Offline',
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
],
onHorizontalDragUpdate: (e) {},
onHorizontalDragRight: (e) {
return areYouSureDialog(context);
},
onHorizontalDragleft: (e) {
return areYouSureDialog(context);
},
),
],
),
);
}
}
Future<bool> areYouSureDialog(BuildContext context) async {
bool isActive = false;
await showDialog(
context: context,
barrierDismissible: false,
builder: (context) => Dialog(
child: Container(
color: Colors.white,
height: 140,
width: 150,
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Are you sure?'),
SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
isActive = true;
Navigator.of(context).pop();
},
child: Text('Yes'),
),
SizedBox(width: 10),
TextButton(
onPressed: () {
isActive = false;
Navigator.of(context).pop();
},
child: Text('No'),
),
],
),
],
),
),
),
);
return isActive;
}
params | Type | default value |
---|---|---|
key | Key? | null |
childBeforeSwipe | Widget | const Icon(Icons.arrow_forward_ios) |
childAfterSwiped | Widget | const Icon(Icons.arrow_back_ios) |
height | double? | null |
width | double? | null |
margin | EdgeInsetsGeometry | const EdgeInsets.all(0) |
padding | EdgeInsetsGeometry | const EdgeInsets.all(0) |
colorBeforeSwipe | Color | Colors.green |
colorAfterSwiped | Color | Colors.red |
boxShadow | List | const [BoxShadow( color:Colors.black54, blurRadius: 6,offset: Offset(0, 4),),] |
borderRadius | BorderRadiusGeometry? | null |
duration | Duration | const Duration(milliseconds: 50) |
constraints | BoxConstraints? | null |
rightChildren | List | const [] |
leftChildren | List | const [] |
onHorizontalDragUpdate | void Function(DragUpdateDetails) | required |
onHorizontalDragRight | Future Function(DragEndDetails) | required |
onHorizontalDragleft | Future Function(DragEndDetails) | required |
acceptPoitTransition | double | 0.5 |
Run this command:
With Flutter:
$ flutter pub add swipe_button_widget
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
swipe_button_widget: ^0.0.2
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:swipe_button_widget/swipe_button_widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:swipe_button_widget/swipe_button_widget.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Home(),
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
const SizedBox(height: 8),
SwipeButtonWidget(
acceptPoitTransition: 0.7,
margin: const EdgeInsets.all(0),
padding: const EdgeInsets.all(0),
boxShadow: const [
BoxShadow(
blurRadius: 4,
color: Color.fromRGBO(197, 197, 197, 0.25),
spreadRadius: 1.5,
),
],
borderRadius: BorderRadius.circular(8),
colorBeforeSwipe: Colors.white,
colorAfterSwiped: Colors.white,
height: 60,
childBeforeSwipe: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.red[900],
),
width: 100,
height: double.infinity,
child: const Center(
child: Text(
'>>>',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28,
color: Colors.white),
),
),
),
childAfterSwiped: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.red[900],
),
width: 100,
height: double.infinity,
child: const Center(
child: Text(
'>>>',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28,
color: Colors.white),
),
),
),
leftChildren: const [
Align(
alignment: Alignment(0.9, 0),
child: Text(
'Swip for confirmation',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
color: Colors.red),
),
)
],
rightChildren: const [
Align(
alignment: Alignment(-0.6, 0),
child: Text(
'Swip for arrived',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
color: Colors.red),
),
)
],
onHorizontalDragUpdate: (e) {},
onHorizontalDragRight: (e) {
return areYouSureDialog(context);
},
onHorizontalDragleft: (e) async {
return false;
}),
const SizedBox(height: 8),
SwipeButtonWidget(
padding: const EdgeInsets.all(15),
borderRadius: BorderRadius.circular(8),
childBeforeSwipe: Text(
'>>> Slide To Logout',
style: TextStyle(
fontSize: 18,
color: Color.fromRGBO(5, 132, 55, 1),
),
),
childAfterSwiped: Text(
'<<< Slide To Login',
style: TextStyle(
fontSize: 18,
color: Colors.white,
),
),
leftChildren: const [
Align(
alignment: Alignment.centerRight,
child: Text(
'Active',
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
],
rightChildren: const [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Offline',
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
],
onHorizontalDragUpdate: (e) {},
onHorizontalDragRight: (e) {
return areYouSureDialog(context);
},
onHorizontalDragleft: (e) {
return areYouSureDialog(context);
},
),
],
),
);
}
}
Future<bool> areYouSureDialog(BuildContext context) async {
bool isActive = false;
await showDialog(
context: context,
barrierDismissible: false,
builder: (context) => Dialog(
child: Container(
color: Colors.white,
height: 140,
width: 150,
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Are you sure?'),
SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
isActive = true;
Navigator.of(context).pop();
},
child: Text('Yes'),
),
SizedBox(width: 10),
TextButton(
onPressed: () {
isActive = false;
Navigator.of(context).pop();
},
child: Text('No'),
),
],
),
],
),
),
),
);
return isActive;
}
Download Details:
Author: mahmoudsalah37
Source Code: https://github.com/mahmoudsalah37/swipe_button_widget
1660570612
flutter_advanced_switch
An advanced switch widget, that can be fully customized with size, text, color, radius of corners.
Switch Light | Switch Dark |
---|---|
![]() | ![]() |
In the pubspec.yaml
of your flutter project, add the following dependency:
dependencies:
...
flutter_advanced_switch: <latest_version>
Import in your project:
import 'package:flutter_advanced_switch/flutter_advanced_switch.dart';
// ...
// 1. Create a controller in the state of the StatefulWidget
final _controller = ValueNotifier<bool>(false);
// 2. In case, you want to call setState on switch changes.
// 2.1. Add event listener, for example in the initState() method.
// ...
bool _checked = false;
// ...
@override
void initState() {
super.initState();
_controller.addListener(() {
setState(() {
if (_controller00.value) {
_checked = true;
} else {
_checked = false;
}
});
});
}
// 3. Add AdvancedSwitch to the build method.
// ...
AdvancedSwitch(
controller: _controller,
),
// ...
Regular Switch
// ...
final _controller = ValueNotifier<bool>(false);
// ...
AdvancedSwitch(
controller: _controller,
)
// ...
Customized Switch
// ...
final _controller = ValueNotifier<bool>(false);
// ...
AdvancedSwitch(
controller: _controller,
activeColor: Colors.green,
inactiveColor: Colors.grey,
activeChild: Text('ON'),
inactiveChild: Text('OFF'),
activeImage: AssetImage('assets/images/on.png'),
inactiveImage: AssetImage('assets/images/off.png'),
borderRadius: BorderRadius.all(const Radius.circular(15)),
width: 50.0,
height: 30.0,
enabled: true,
disabledOpacity: 0.5,
),
// ...
Custom thumb
// ...
final _controller = ValueNotifier<bool>(false);
// ...
AdvancedSwitch(
controller: _controller,
thumb: ValueListenableBuilder(
valueListenable: _controller,
builder: (_, value, __) {
return Icon(value
? Icons.lightbulb
: Icons.lightbulb_outline);
},
),
),
// ...
Parameter | Description | Type | Default |
---|---|---|---|
controller | Determines if widget is enabled. | ValueNotifier | |
activeColor | Determines current state. | Color | Colors.green |
inactiveColor | Determines background color for the active state. | Color | Colors.grey |
activeChild | Determines background color for the inactive state. | Widget | |
inactiveChild | Determines label for the active state. | Widget | |
activeImage | Determines label for the inactive state. | ImageProvider | |
inactiveImage | Determines background image for the active state. | ImageProvider | |
borderRadius | Determines background image for the inactive state. | BorderRadius | Radius.circular(15) |
width | Determines border radius. | Double | 50.0 |
height | Determines width. | Double | 30.0 |
enabled | Determines height. | bool | true |
disabledOpacity | Determines opacity of disabled control. | double | 0.5 |
thumb | Custom thumb widget | Widget |
Demo
Run this command:
With Flutter:
$ flutter pub add flutter_advanced_switch
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
flutter_advanced_switch: ^3.0.1
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:flutter_advanced_switch/flutter_advanced_switch.dart';
import 'package:flutter/material.dart';
import 'package:flutter_advanced_switch/flutter_advanced_switch.dart';
// import 'package:flutter_icons/flutter_icons.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _controller00 = ValueNotifier<bool>(false);
final _controller01 = ValueNotifier<bool>(false);
final _controller02 = ValueNotifier<bool>(false);
final _controller03 = ValueNotifier<bool>(false);
final _controller04 = ValueNotifier<bool>(false);
final _controller05 = ValueNotifier<bool>(false);
final _controller06 = ValueNotifier<bool>(false);
final _controller07 = ValueNotifier<bool>(false);
final _controller08 = ValueNotifier<bool>(false);
final _controller09 = ValueNotifier<bool>(false);
final _controller10 = ValueNotifier<bool>(false);
final _controller11 = ValueNotifier<bool>(false);
final _controller12 = ValueNotifier<bool>(false);
final _controller13 = ValueNotifier<bool>(false);
final _controller14 = ValueNotifier<bool>(false);
final _controller15 = ValueNotifier<bool>(false);
bool _enabled = false;
bool _themeDark = false;
@override
void initState() {
super.initState();
_controller00.addListener(() {
setState(() {
if (_controller00.value) {
_themeDark = true;
} else {
_themeDark = false;
}
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: _themeDark ? ThemeData.dark() : ThemeData.light(),
home: Scaffold(
appBar: AppBar(
title: const Text('Advanced Switch Example'),
),
body: Container(
width: double.infinity,
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(
vertical: 40,
),
physics: ClampingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildLabel('Switch Theme'),
AdvancedSwitch(
controller: _controller00,
thumb: ValueListenableBuilder<bool>(
valueListenable: _controller00,
builder: (_, value, __) {
return Icon(
value ? Icons.lightbulb : Icons.lightbulb_outline);
},
),
),
UnconstrainedBox(
child: AdvancedSwitch(
controller: _controller01,
width: 110,
enabled: false,
inactiveColor: Colors.red,
activeColor: Colors.green,
activeChild: Text(
'File selected',
style: TextStyle(
color: Colors.black87,
),
),
inactiveChild: Text(
'No File Selected',
style: TextStyle(
color: Colors.black87,
),
),
),
),
_buildLabel('Default Switch'),
Row(
mainAxisSize: MainAxisSize.min,
children: [
AdvancedSwitch(
controller: _controller01,
),
SizedBox(width: 25),
AdvancedSwitch(
controller: _controller01,
thumb: ValueListenableBuilder<bool>(
valueListenable: _controller01,
builder: (_, value, __) {
return Icon(value
? Icons.cloud_upload
: Icons.cloud_download);
},
),
),
],
),
_buildLabel('Disabled Switch'),
Row(
mainAxisSize: MainAxisSize.min,
children: [
AdvancedSwitch(
enabled: _enabled,
controller: ValueNotifier(false),
),
SizedBox(width: 25),
AdvancedSwitch(
enabled: _enabled,
controller: ValueNotifier(true),
),
SizedBox(width: 25),
ElevatedButton(
onPressed: () => setState(() => _enabled = !_enabled),
child: Text('Enable/Disable'),
)
],
),
_buildLabel('Color/Icon/Image Switch'),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AdvancedSwitch(
activeColor: Colors.yellow,
inactiveColor: Colors.indigo,
activeChild: Text('Yellow'),
inactiveChild: Text('Indigo'),
width: 80,
controller: _controller03,
),
AdvancedSwitch(
activeChild: Icon(
Icons.terrain,
color: Colors.blue,
),
inactiveChild: Icon(Icons.cloud),
activeColor: Colors.yellowAccent,
inactiveColor: Colors.deepPurple,
width: 60,
controller: _controller15,
),
AdvancedSwitch(
controller: _controller13,
activeImage: AssetImage('assets/images/day_sky.png'),
inactiveImage: AssetImage('assets/images/night_sky.jpg'),
),
AdvancedSwitch(
controller: _controller14,
width: 80,
activeChild: Text('DAY'),
inactiveChild: Text('NIGHT'),
activeImage: AssetImage('assets/images/day_sky.png'),
inactiveImage: AssetImage('assets/images/night_sky.jpg'),
),
],
),
_buildLabel('ON/OFF Switch'),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AdvancedSwitch(
activeChild: Text('1'),
inactiveChild: Text('0'),
width: 70,
controller: _controller02,
),
AdvancedSwitch(
activeChild: Text('ON'),
inactiveChild: Text('OFF'),
borderRadius: BorderRadius.circular(5),
width: 76,
controller: _controller04,
),
AdvancedSwitch(
activeChild: Text('true'),
inactiveChild: Text('false'),
borderRadius: BorderRadius.zero,
width: 76,
controller: _controller05,
),
],
),
_buildLabel('XXS/XS Switch'),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AdvancedSwitch(
width: 16,
height: 8,
controller: _controller06,
),
AdvancedSwitch(
width: 32,
height: 16,
controller: _controller07,
),
],
),
_buildLabel('S/M/L Switch'),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AdvancedSwitch(
width: 48,
height: 24,
controller: _controller08,
),
AdvancedSwitch(
width: 56,
height: 28,
controller: _controller09,
),
AdvancedSwitch(
width: 72,
height: 36,
controller: _controller10,
borderRadius: BorderRadius.circular(18),
),
],
),
_buildLabel('XL/XXL Switch'),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AdvancedSwitch(
width: 96,
height: 48,
controller: _controller11,
borderRadius: BorderRadius.circular(24),
),
AdvancedSwitch(
width: 112,
height: 56,
controller: _controller12,
borderRadius: BorderRadius.circular(29),
),
],
),
],
),
),
),
),
);
}
Widget _buildLabel(String value) {
return Container(
margin: EdgeInsets.only(
top: 25,
bottom: 5,
),
child: Text(
'$value',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: Colors.grey,
),
),
);
}
@override
void dispose() {
_controller01.dispose();
_controller02.dispose();
_controller03.dispose();
_controller04.dispose();
_controller05.dispose();
_controller06.dispose();
_controller07.dispose();
_controller08.dispose();
_controller09.dispose();
_controller10.dispose();
_controller11.dispose();
_controller12.dispose();
_controller13.dispose();
_controller14.dispose();
_controller15.dispose();
super.dispose();
}
}
Download Details:
Author: alex-melnyk
Source Code: https://github.com/alex-melnyk/flutter_advanced_switch
1660426740
Switch Git branches by their pull request title
npm install --global switch-branch-cli
$ switch-branch --help
Switch Git branches by their pull request title
Usage
$ switch-branch
This CLI needs it to fetch your pull requests for the current Git repository. Your personal access token is stored locally and you will be asked for it only once (unless you revoke it or token expires).
Author: Vadimdemedes
Source Code: https://github.com/vadimdemedes/switch-branch-cli
1660260480
Assertions that can be turned on or off with a switch, with no runtime penalty when they're off.
To install, simply do
julia> ]
(v1.x) pkg> add ToggleableAsserts
at the julia prompt.
ToggleableAsserts
Suppose we have a function with an assertion we only want to be on while debugging:
using ToggleableAsserts
function foo(u, v)
@toggled_assert length(u) == length(v)
1
end
We can now make sure our assertions work:
julia> foo([1, 2], [1])
ERROR: AssertionError: length(u) == length(v)
Stacktrace:
[1] foo(::Array{Int64,1}, ::Array{Int64,1}) at ./REPL[1]:2
[2] top-level scope at REPL[2]:1
and also turn them off
julia> toggle(false)
[ Info: Toggleable asserts turned off.
julia> foo([1, 2], [1])
1
Once assertions are turned off, any function depending on them is recompiled with the assertions removed. For instance, the LLVM code for foo
now simply returns 1
without any bounds checking at runtime:
julia> @code_llvm foo([1,2], [1])
; @ REPL[1]:2 within `foo'
define i64 @julia_foo_16854(%jl_value_t addrspace(10)* nonnull align 16 dereferenceable(40), %jl_value_t addrspace(10)* nonnull align 16 dereferenceable(40)) {
top:
ret i64 1
}
Just like the standard @assert
macro, you can add custom error text to a @toggled_assert
:
julia> @toggled_assert iseven(3) "3 is an odd number!"
ERROR: AssertionError: 3 is an odd number!
Stacktrace:
[1] top-level scope at REPL[21]:1
If you try to set toggle
outside of the global scope, you may suffer world-age issues until you return to the global scope. e.g.
julia> function bar()
toggle(false)
foo([1, 2], [1])
toggle(true)
foo([1, 2], [1])
end
bar (generic function with 1 method)
julia> bar()
[ Info: Toggleable asserts turned off.
[ Info: Toggleable asserts turned on.
1
julia> foo([1, 2], [1])
ERROR: AssertionError: length(u) == length(v)
Stacktrace:
[1] foo(::Array{Int64,1}, ::Array{Int64,1}) at ./REPL[45]:2
[2] top-level scope at REPL[48]:1
Hence, it should be preferred to only use toggle
in the global scope.
This isn't my idea, I just packaged it up. The idea came from this Julia Discourse thread
Author: MasonProtter
Source Code: https://github.com/MasonProtter/ToggleableAsserts.jl
License: MIT license
1659763140
Framework
ID: Project ini Menggunakan beberapa bahasa framework sebagai berikut.
EN: This project uses several framework as follows.
Language Code
ID: Project ini Menggunakan beberapa bahasa code program sebagai berikut.
EN: This project uses several programming language languages as follows.
Introduction
ID: switchscript adalah library untuk memudahkan dalam devlopment di dart dengan tambahan syntax dan beberapa method yang telah di rubah agar lebih mudah di pakai.
EN: switchscript is a library to make it easier to develop on darts with additional syntax and some methods that have been changed to make it easier to use.
Use This Library
ID: untuk anda yang ingin mencoba menggunakan source code ini, silahkan ikuti step berikut ini :
EN: for those of you who want to try to using this source code, please follow these steps :
Documentations
Install Library
flutter pub add switchscript
Quickstart
import 'package:switchscript/switchscript.dart';
void main() async {
var option = {
"method": "get",
};
var fetch = await UrlFetchApp.fetch(
"https://jsonplaceholder.typicode.com/posts", option);
console.log(fetch.body);
console.log("hallo".toBoolean);
console.log({}.isType);
console.log("hallo gays".encode.Base64);
}
Run this command:
With Dart:
$ dart pub add switchscript
With Flutter:
$ flutter pub add switchscript
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
switchscript: ^0.0.3
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:switchscript/switchscript.dart';
example/lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
The project has a separate contribution file. Please adhere to the steps listed in the separate contributions file
You can reach me on Telegram
Donate
ID: Jika Anda Menyukai karya saya dan ingin memberikan dana untuk saya membeli beberapa snack silahkan donasi seberapapun itu akan saya terima dan terima kasih banyak ya.
EN: If you like my work and want to give me funds to buy some snacks, please donate any amount I will accept and thank you very much.
Original article source at: https://pub.dev/packages/switchscript
1659564480
Logidze provides tools for logging DB records changes when using PostgreSQL (>=9.6). Just like audited and paper_trail do (but faster).
Logidze allows you to create a DB-level log (using triggers) and gives you an API to browse this log. The log is stored with the record itself in JSONB column. No additional tables required.
🤔 How is Logidze pronounced?
Other requirements:
Add Logidze to your application's Gemfile:
gem "logidze", "~> 1.1"
Install required DB extensions and create trigger function:
bundle exec rails generate logidze:install
This creates a migration for adding trigger function and enabling the hstore extension.
Run migrations:
bundle exec rails db:migrate
NOTE: Logidze uses DB functions and triggers, hence you need to use SQL format for a schema dump:
# application.rb
config.active_record.schema_format = :sql
Logidze seamlessly integrates with fx gem to make it possible to continue using schema.rb
for the database schema dump.
Add fx
gem to your Gemfile and run the same Logidze generators: rails g logidze:install
or rails g logidze:model
.
If for some reason Logidze couldn't detect the presence of Fx in your bundle, you can enforce it by passing --fx
option to generators.
On the other hand, if you have fx
gem but don't want Logidze to use it—pass --no-fx
option.
Run the following migration to enable changes tracking for an Active Record model and adding a log_data::jsonb
column to the table:
bundle exec rails generate logidze:model Post
bundle exec rails db:migrate
This also adds has_logidze
line to your model, which adds methods for working with logs.
By default, Logidze tries to infer the path to the model file from the model name and may fail, for example, if you have unconventional project structure. In that case, you should specify the path explicitly:
bundle exec rails generate logidze:model Post --path "app/models/custom/post.rb"
To backfill table data (i.e., create initial snapshots) add backfill
option to the generator:
bundle exec rails generate logidze:model Post --backfill
Now your migration should contain and UPDATE ...
statement to populate the log_data
column with the current state.
Otherwise a full snapshot will be created the first time the record is updated.
You can create a snapshot manually by performing the following query:
UPDATE <my_table> as t
SET log_data = logidze_snapshot(to_jsonb(t))
Or by using the following methods:
Model.create_logidze_snapshot
# specify the timestamp column to use for the initial version (by default the current time is used)
Model.create_logidze_snapshot(timestamp: :created_at)
# filter columns
Model.create_logidze_snapshot(only: %w[name])
Model.create_logidze_snapshot(except: %w[password])
# or call a similar method (but with !) on a record
my_model = Model.find(params[:id])
my_model.create_logidze_snapshot!(timestamp: :created_at)
A snapshot is only created if log_data
is null.
You can provide the limit
option to generate
to limit the size of the log (by default it's unlimited):
bundle exec rails generate logidze:model Post --limit=10
You can log only particular columns changes. There are mutually exclusive except
and only
options for this:
# track all columns, except `created_at` and `active`
bundle exec rails generate logidze:model Post --except=created_at,active
# track only `title` and `body` columns
bundle exec rails generate logidze:model Post --only=title,body
By default, Logidze tries to get a timestamp for a version from record's updated_at
field whenever appropriate. If your model does not have that column, Logidze will gracefully fall back to statement_timestamp()
.
To change the column name or disable this feature completely, you can use the timestamp_column
option:
# will try to get the timestamp value from `time` column
bundle exec rails generate logidze:model Post --timestamp_column time
# will always set version timestamp to `statement_timestamp()`
bundle exec rails generate logidze:model Post --timestamp_column nil # "null" and "false" will also work
If you would like to re-do your rails generate
anew, as with other generators you can use rails destroy
to revert it, which will delete the migration file and undo the injection of has_logidze
into the model file:
bundle exec rails destroy logidze:model Post
IMPORTANT: If you use non-UTC time zone for Active Record (config.active_record.default_timezone
), you MUST always infer log timestamps from a timestamp column (e.g., when back-filling data); otherwise, you may end up with inconsistent logs (#199). In general, we recommend using UTC as the database time unless there is a very strong reason not to.
Your model now has log_data
column, which stores changes log.
To retrieve record version at a given time use #at
or #at!
methods:
post = Post.find(27)
# Show current version
post.log_version #=> 3
# Show log size (number of versions)
post.log_size #=> 3
# Get copy of a record at a given time
post.at(time: 2.days.ago)
# or revert the record itself to the previous state (without committing to DB)
post.at!(time: "2018-04-15 12:00:00")
# If no version found
post.at(time: "1945-05-09 09:00:00") #=> nil
You can also get revision by version number:
post.at(version: 2)
NOTE: If log_data
is nil, #at(time:)
returns self and #at(version:)
returns nil
. You can opt-in to return nil
for time-based #at
as well by setting Logidze.return_self_if_log_data_is_empty = false
.
It is also possible to get version for relations:
Post.where(active: true).at(time: 1.month.ago)
You can also get diff from specified time:
post.diff_from(time: 1.hour.ago)
#=> { "id" => 27, "changes" => { "title" => { "old" => "Logidze sucks!", "new" => "Logidze rulz!" } } }
# the same for relations
Post.where(created_at: Time.zone.today.all_day).diff_from(time: 1.hour.ago)
NOTE: If log_data
is nil, #diff_from
returns an empty Hash as "changes"
.
There are also #undo!
and #redo!
options (and more general #switch_to!
):
# Revert record to the previous state (and stores this state in DB)
post.undo!
# You can now user redo! to revert back
post.redo!
# More generally you can revert record to arbitrary version
post.switch_to!(2)
You can initiate reloading of log_data
from the DB:
post.reload_log_data # => returns the latest log data value
Typically, if you update record after #undo!
or #switch_to!
you lose all "future" versions and #redo!
is no longer possible. However, you can provide an append: true
option to #undo!
or #switch_to!
, which will create a new version with old data. Caveat: when switching to a newer version, append
will have no effect.
post = Post.create!(title: "first post") # v1
post.update!(title: "new title") # v2
post.undo!(append: true) # v3 (with same attributes as v1)
Note that redo!
will not work after undo!(append: true)
because the latter will create a new version instead of rolling back to an old one. Alternatively, you can configure Logidze always to default to append: true
.
Logidze.append_on_undo = true
You can store any meta information you want inside your version (it could be IP address, user agent, etc.). To add it you should wrap your code with a block:
Logidze.with_meta({ip: request.ip}) do
post.save!
end
NOTE: You should pass metadata as a Hash; passing keyword arguments doesn't work in Ruby 3.0+.
Meta expects a hash to be passed so you won't need to encode and decode JSON manually.
By default .with_meta
wraps the block into a DB transaction. That could lead to an unexpected behavior, especially, when using .with_meta
within an around_action. To avoid wrapping the block into a DB transaction use transactional: false
option.
Logidze.with_meta({ip: request.ip}, transactional: false) do
post.save!
end
A special application of meta information is storing the author of the change, which is called Responsible ID. There is more likely that you would like to store the current_user.id
that way.
To provide responsible_id
you should wrap your code in a block:
Logidze.with_responsible(user.id) do
post.save!
end
And then to retrieve responsible_id
:
post.log_data.responsible_id
Logidze does not require responsible_id
to be SomeModel
ID. It can be anything. Thus Logidze does not provide methods for retrieving the corresponding object. However, you can easily write it yourself:
class Post < ActiveRecord::Base
has_logidze
def whodunnit
id = log_data.responsible_id
User.find(id) if id.present?
end
end
And in your controller:
class ApplicationController < ActionController::Base
around_action :use_logidze_responsible, only: %i[create update]
def use_logidze_responsible(&block)
Logidze.with_responsible(current_user&.id, &block)
end
end
By default .with_responsible
wraps the block into a DB transaction. That could lead to an unexpected behavior, especially, when using .with_responsible
within an around_action. To avoid wrapping the block into a DB transaction use transactional: false
option.
Logidze.with_responsible(user.id, transactional: false) do
post.save!
end
If you want to make update without logging (e.g., mass update), you can turn it off the following way:
Logidze.without_logging { Post.update_all(seen: true) }
# or
Post.without_logging { Post.update_all(seen: true) }
Reset the history for a record (or records):
# for a single record
record.reset_log_data
# for relation
User.where(active: true).reset_log_data
You can instruct Logidze to create a full snapshot instead of a diff for a particular log entry.
It could be useful in combination with .without_logging
: first, you perform multiple updates without logging, then you want to create a log entry with the current state. To do that, you should use the Logidze.with_full_snapshot
method:
record = Model.find(params[:id])
Logidze.without_logging do
# perform multiple write operations with record
end
Logidze.with_full_snapshot do
record.touch
end
Logidze also supports associations versioning. This feature is disabled by default (due to the number of edge cases). You can learn more in the wiki.
By default, Active Record selects all the table columns when no explicit select
statement specified.
That could slow down queries execution if you have field values which exceed the size of the data block (typically 8KB). PostgreSQL turns on its TOAST mechanism), which requires reading from multiple physical locations for fetching the row's data.
If you do not use compaction (generate logidze:model ... --limit N
) for log_data
, you're likely to face this problem.
Logidze provides a way to avoid loading log_data
by default (and load it on demand):
class User < ActiveRecord::Base
# Add `ignore_log_data` option to macros
has_logidze ignore_log_data: true
end
If you want Logidze to behave this way by default, configure the global option:
# config/initializers/logidze.rb
Logidze.ignore_log_data_by_default = true
# or
# config/application.rb
config.logidze.ignore_log_data_by_default = true
However, you can override it by explicitly passing ignore_log_data: false
to the ignore_log_data
. You can also enforce loading log_data
in-place by using the .with_log_data
scope, e.g. User.all.with_log_data
loads all the users with log_data
included.
The chart below shows the difference in PG query time before and after turning ignore_log_data
on. (Special thanks to @aderyabin for sharing it.)
If you try to call #log_data
on the model loaded in such way, you'll get nil
. If you want to fetch log data (e.g., during the console debugging)–use user.reload_log_data
, which forces loading this column from the DB.
Unlike, for example, PaperTrail, Logidze is designed to only track changes. If the record has been deleted, everything is lost.
If you want to keep changes history after records deletion as well, consider using specialized tools for soft-delete, such as, Discard or Paranoia.
See also the discussion: #61.
By default, Logidze raises an exception which causes the entire transaction to fail. To change this behavior, it's now possible to override logidze_capture_exception(error_data jsonb)
function.
For example, you may want to raise a warning instead of an exception and complete the transaction without updating log_data.
Related issues: #193
We try to make an upgrade process as simple as possible. For now, the only required action is to create and run a migration:
bundle exec rails generate logidze:install --update
This updates core logdize_logger
DB function. No need to update tables or triggers.
NOTE: When using fx
, you can omit the --update
flag. The migration containing only the updated functions would be created.
If you want to update Logidze settings for the model, run migration with --update
flag:
bundle exec rails generate logidze:model Post --update --only=title,body,rating
You can also use the --name
option to specify the migration name to avoid duplicate migration names:
$ bundle exec rails generate logidze:model Post --update --only=title,body,rating --name add_only_filter_to_posts_log_data
create db/migrate/20202309142344_add_only_filter_to_posts_log_data.rb
Logidze can check for a pending upgrade. Use Logidze.on_pending_upgrade = :warn
to be notified by warning, or Logidze.on_pending_upgrade = :raise
if you want Logidze to raise an error.
Most SQL function definitions have changed without backward compatibility. Perform the following steps to upgrade:
Re-install Logidze: bundle exec rails generate logidze:install --update
.
Re-install Logidze triggers for all models: bundle exec rails generate logidze:model <model> --update
.
NOTE: If you had previously specified whitelist/blacklist attributes, you will need to include the --only
/--except
option as appropriate. You can easily copy these column lists from the previous logidze migration for the model.
Remove the include Logidze::Migration
line from the old migration files (if any)—this module has been removed.
Rewrite legacy logidze migrations to not use the #current_setting(name)
and #current_setting_missing_supported?
methods, or copy them from the latest 0.x release.
The deprecated time
positional argument has been removed from #at
and #diff_from
methods. Now you need to use keyword arguments, i.e., model.at(some_tome) -> model.at(time: some_time)
.
The log_data
column has the following format:
{
"v": 2, // current record version,
"h": // list of changes
[
{
"v": 1, // change number
"ts": 1460805759352, // change timestamp in milliseconds
"c": {
"attr": "new value", // updated fields with new values
"attr2": "new value"
},
"r": 42, // Resposibility ID (if provided), not in use since 0.7.0
"m": {
"_r": 42 // Resposibility ID (if provided), in use since 0.7.0
// any other meta information provided, please see Track meta information section for the details
}
}
]
}
If you specify the limit in the trigger definition, then log size will not exceed the specified size. When a new change occurs, and there is no more room for it, the two oldest changes will be merged.
log_data
is nil when using Rails fixturesRails fixtures are populated with triggers disabled. Thus, log_data
is null initially for all records. You can use #create_logidze_snapshot
manually to build initial snapshots.
First, read Apartment docs on installing PostgreSQL extensions. You need to use the described approach to install Hstore (and drop the migration provided by Logidze during installation).
Secondly, set config.use_sql = true
in the Apartment configuration.
Finally, when using fx
along with schema.rb
, you might face a problem with duplicate trigger definitions (for different schemas). Here is a patch to fix this: dump_triggers.rake.
Related issues: #50.
PG::UntranslatableCharacter: ERROR
That could happen when your row data contain null bytes. You should sanitize the data before writing to the database. From the PostgreSQL docs: jsonb type also rejects \u0000 (because that cannot be represented in PostgreSQL's text type)
.
Related issues: #155.
pg_restore
fails to restore a dumpFirst, when restoring data dumps you should consider using --disable-triggers
option (unless you have a strong reason to invoke the triggers).
When restoring data dumps for a particular PostgreSQL schema (e.g., when using Apartment), you may encounter the issue with non-existent Logidze functions. That happens because pg_dump
adds SELECT pg_catalog.set_config('search_path', '', false);
, and, thus, breaks our existing triggers/functions, because they live either in "public" or in a tenant's namespace (see this thread).
PG::NumericValueOutOfRange: ERROR: value overflows numeric format
Due to the usage of hstore_to_jsonb_loose
under the hood, there could be a situation when you have a string representing a number in the scientific notation (e.g., "557236406134e62000323100"). Postgres would try to convert it to a number (a pretty big one, for sure) and fail with the exception.
Related issues: #69.
This project requires a PostgreSQL instance running with the following setup:
# For testing
createdb -h postgres -U postgres logidze_test
# For benchmarks
createdb -h postgres -U postgres logidze_bench
createdb -h postgres -U postgres logidze_perf_bench
psql -d logidze_bench -c 'CREATE EXTENSION IF NOT EXISTS hstore;'
This project is compatible with Reusable Docker environment setup.
Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/logidze.
The gem is available as open source under the terms of the MIT License.
Author: palkan
Source code: https://github.com/palkan/logidze
License: MIT license
1658568180
A callbag operator that creates and switches to the new source whenever original source emits
npm install callbag-switch-map
Pullable Source
const pipe = require('callbag-pipe');
const switchMap = require('callbag-switch-map');
const fromIter = require('callbag-from-iterable');
const forEach = require('callbag-for-each');
console.log('Pullable source');
pipe(
fromIter('hi'),
switchMap(char => fromIter([10, 20, 30]), (char,num) => char + num),
forEach(x => console.log(x))
);
// Pullable source
// h10
// i10
// i20
// i30
Listenable Source
const pipe = require('callbag-pipe');
const switchMap = require('callbag-switch-map');
const interval = require('callbag-interval');
const forEach = require('callbag-for-each');
const fromPromise = require('callbag-from-promise');
const fakeAjax = value => new Promise((resolve, reject) => {
let period = value % 2 ? 400 : 1200; // Resolve odd numbers quickly
setTimeout(resolve, period, (value*value));
});
console.log('Listenable source');
pipe(
interval(500),
switchMap(i => fromPromise(fakeAjax(i))),
forEach(x => console.log(x))
);
// Listenable source
// 1
// 9
// 25
// 49
// 81
// 121
// ....
Author: Avinashcodes
Source Code: https://github.com/avinashcodes/callbag-switch-map
License: MIT license
1652225340
An exploit toolkit for the Nintendo Switch™
Installation
npm install
Usage
sudo node start.js
sudo node start.js --webapplet
instead.It should no longer be necessary to run usefulscripts/SetupNew.js
, since PegaSwitch will now do it automatically.
Documentation
API documentation for SploitCore is automatically generated using jsdoc comments.
You can find the latest version of documentation hosted here
To view locally: npm run docs:serve
then visit http://localhost:4001
To generate to docs
folder: npm run docs:generate
Troubleshooting
You can override the IP address that pegaswitch responds with by passing an --ip
argument to the node start.js
command.
eg.
sudo node start.js --ip 1.2.3.4
Pegaswitch should function on Windows, albeit with the curses ui disabled.
If --logfile is not specified, pegaswitch.log is used. You may open it with the text editor of your choice.
ex:
C:\pegaswitch\> node start.js --logfile log.txt
If you encounter problems using pegaswitch on Windows, we suggest installing through WSL.
Author: Reswitched
Source Code: https://github.com/reswitched/pegaswitch
License: ISC license