Hank

Hi there! I’m Hank, a software engineer who loves to build things.

I’ve been coding for over 10 years and have experience in a variety of programming languages, including Python, Golang, Ruby on Rails, JavaScript, TypeScript. I also have experience in blockchain development, having worked on several projects using Ethereum, Solidity, and Web3.js, also contributed to the famous wallet project Metamask.

I also have a passion for DevOps stuff, such as cloud computing, Kubernetes, and DevOps. I enjoy learning new technologies and sharing my knowledge with others.

Plus, I’m a certified Advanced Mendix Developer, which is a low-code platform for building web and mobile applications.

Want to say hi, or need me to build something for you? Feel free to drop me an email to hank#momane.com. I will try to reply as soon as possible.

Some of My Certifications

Posts by Hank

2019 nCoV

It's 23th Jan already.2019-nCoV virus is raging in China and some other countries, and also lunar new year tomorrow.Just like 17 years ago, SARS everywhere. Back then I was a senior middle school student. Sat in the classroom every day, the teachers tried sterilization by boiling vinegar in the classroom.Luckily it's spring soon, SARS gone quietly just like how it came. Hundreds of people died, lots of them were doctors and nurses. Feel so sad about them.Then in 2009, there was H1N1. I was at university. I only saw the news on TV and the internet. Weird thing is, in my memory, it was not as bad as in 2003. I was busying getting prepared for job, nothing different. But I just found out today on the internet, about 648 people died in China mainland, while it was 349 in 2003 SARS. Kind of shock news to me.And now, more than 600 people in China mainland got this virus, 17 of them dead. Heard from some news there would be more soon since Wuhan is a big city and many people who work there will go back to their own town to celebrate the lunar new year.In the meantime in the US, CDC says about 6600 died because of the flu.And in Australia, the fire has burnt for months and it's still burning.So many disasters this year. I just wish everything will be OK for everyone and no more disaster to every country and every human being.

Read More

RIP, Kobe Bryant

RIP![RIP, Kobe Bryant](https://s3.amazonaws.com/ogden_images/www.adirondackdailyenterprise.com/images/2020/01/26152959/BryantKobe05wiki-563x840.jpg 'RIP, Kobe Bryant')

Read More

My First LEGO

Got a LEGO gift from an old friend. Finished it in two weeks :D Love it a lot.

Read More

Disable Web Search Result in Windows Search 2019

**TL;DR**: download this [windows reg file](https://github.com/HangYang/disable-windows-web-search) and double click to import it. OR- copy the code from below - open you favourite text editor, or just use notepad - paste the code into it - save the file with `.reg` extension - double click the file``` Windows Registry Editor Version 5.00[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Search ] "ConnectedSearchUseWebOverMeteredConnections"=dword:00000000 "AllowCortana"=dword:00000000 "DisableWebSearch "=dword:00000001 "ConnectedSearchUseWeb"=dword:00000000[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Search] "CortanaConsent"=dword:00000000 "BingSearchEnabled"=dword:00000000 "AllowSearchToUseLocation"=dword:00000000 ```Windows is promoting it's `Cortana` and Bing search for a long time. One of the most annoying thing is that it's showing web search content when you search in start menu.So you press `Windows` button, type some keywords, all you want to do is just access your installed app or file quickly, but then you saw some shitty stuff in the result list.Yes, it's showing web search content and even some so called `search suggesttion`. it's based on your keywords and some trends on the web, since you are searching how to disable this, I know that you also think it's totally garbage.In the beginning, Windows gave us an option in the search menu to turn this off. But then Windows known that most of the users hated it, so it just removed this option...I have to use group policy, regeditor to disable it, and have to search for this every time when it updates.To do it step by step is a bit complicated and it's totally a waste of time. So I just create this reg file so that anyone who want to do this can do it by one click.Comment for these reg code in case someone doubt it:---`"ConnectedSearchUseWebOverMeteredConnections"=dword:00000000 ` // \_disable web search use mobile connection`"AllowCortana"=dword:00000000`// disable Cortana`"DisableWebSearch "=dword:00000001` // disable web search`"ConnectedSearchUseWeb"=dword:00000000` // disabled web searech connection`"CortanaConsent"=dword:00000000` // disable Cortana`"BingSearchEnabled"=dword:00000000` // disable bing search in start menu`"AllowSearchToUseLocation"=dword:00000000` // disable search feature access your location---Enjoy :)

Read More

MySQL: Sort Number in String Format

TL;DR: use `key_name` `*` `1`; like``` select id, name, age from users order by salary * 1 ```Well, it's better to store data in a database in a number format. But there is always an accident, sometimes you or your colleague just stored some numbers in the database with string format, of course for some reason.And one day you need to get data from that table and sort the result by that string-number column. Then you find something is not right.If you run a command like this:``` select "1000" > "2" ```you will find that the result is _0_. it means, **100 1**.There are several work around to make this works, like use [conv()](https://dev.mysql.com/doc/refman/8.0/en/mathematical-functions.html#function_conv). So write your SQL like this:``` select * from users order by conv(`salary`, 10, 10) ````conv(original_value, from_base, to_base)` is a MySQL function, to convert a value from one base to another base, as you can also convert a numeric base system 2 to a numeric base system 10.But there is another shorter and cooler way to do this. that's mul the value by 1. Like so:``` select * from users order by salary * 1 ```It works like a charm.Need to notice that if the value is too large, the result will be in _Scientific notation_ like `1e17`.

Read More

Change Ubuntu 18.04 update source to mirror site in China

## In China, Ubuntu might be slow to updateTo use ubuntu is not a easy thing, and if you are in China, it's even more difficult. Once you want to update your ubuntu. you'd find it's very slow. Because of some well known reason.So it's better to change the update source to some mirror site in China.Here are several famous mirror sites. What you need todo is pick one from below and edit `etc/apt/sources.list`, replace the content in this file.**Better backup you `etc/apt/sources.list` before you do this**### 163 (netease)_source:http://mirrors.163.com/_``` deb http://mirrors.163.com/ubuntu/ bionic main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ bionic-security main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ bionic-updates main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ bionic-proposed main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ bionic-backports main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ bionic main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ bionic-security main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ bionic-updates main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ bionic-proposed main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ bionic-backports main restricted universe multiverse ```### alibaba (aliyun)_source:https://opsx.alibaba.com/mirror_``` deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse ```And here are also two Chinese university mirror, use them only if you are in Chinese university.### tsinghua university``` deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-security main restricted universe multiverse deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-security main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-proposed main restricted universe multiverse deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-proposed main restricted universe multiverse ```### ustc (university of science and technology of China)``` deb https://mirrors.ustc.edu.cn/ubuntu/ bionic main restricted universe multiverse deb-src https://mirrors.ustc.edu.cn/ubuntu/ bionic main restricted universe multiverse deb https://mirrors.ustc.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse deb-src https://mirrors.ustc.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse deb https://mirrors.ustc.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse deb-src https://mirrors.ustc.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse deb https://mirrors.ustc.edu.cn/ubuntu/ bionic-security main restricted universe multiverse deb-src https://mirrors.ustc.edu.cn/ubuntu/ bionic-security main restricted universe multiverse deb https://mirrors.ustc.edu.cn/ubuntu/ bionic-proposed main restricted universe multiverse deb-src https://mirrors.ustc.edu.cn/ubuntu/ bionic-proposed main restricted universe multiverse ```Enjoy:)

Read More

安装了梅林固件的路由器开启SSH以及超频

部分路由器可以通过安装Merlin梅林固件来实现更多的自定义功能。安装更多第三方插件来让路由器变得更强大。在软件自定义的时候不妨试试将路由器进行硬件超频来使其拥有更强劲的性能。实现起来也非常简单,不过需要注意的是,超频可能会造成不可恢复的硬件损害,俗称变砖。超频前要三思,也不要妄图过分超频来让路由器硬件性能达到其不可能达到的等级。要超频需要先登入路由器命令行。推荐使用SSH。Windows下使用puttty等你熟悉的SSH工具,macOS则直接用终端即可。首先梅林默认是不开启SSH的。需要进入路由器管理界面——系统管理——SSH Daemon来开启。 设置如下:其中选项都非常清楚明白,按照个人所需设置即可。SSH key 可以让你登录的时候不用输入密码。也就是你登录路由器管理页面设置的密码。然后在putty或者终端里输入如下命令来登入路由器命令行。命令里的`用户名`是你在浏览器中登录路由器管理页面的用户名。``` ssh 用户名@192.168.50.1 ```一般梅林的默认路由器ip有两个,`192.168.2.1`或者`192.168.50.1`。根据你自己路由器的ip来更改以上命令登入。如果你没设置SSH key的话,接下来需要输入密码了。密码正确就进入路由器命令行了, 你会看到类似以下文字:``` ASUSWRT-Merlin [路由器型号] [梅林版本] Tue Sep 25 12:04:55 UTC 2018 [用户名]@[路由器型号]:/tmp/home/root# ```其中没有中文字的,这里只是说明这里字符代表的意思,你应该能看到你自己路由器的型号、版本、用户名等信息。接下来运行以下命令:``` nvram set clkfreq=1000,800 ```这里的1000是设置CPU频率,800是设置内存频率(800相当于1600)。不要改太大。一般建议CPU频率设置数值为默认频率加200就比较安全。性能也能得到较好提升。否则变砖甚至烧机得不偿失。然后运行以下命令来提交更改。``` nvram commit ```最后运行`reboot`来重启路由器。如果没什么问题,重启完成后路由器就超频成功了。有时候会遇到路由器亮两个红灯无法正常启动的情况,不要慌,这不是变砖,只是启动失败。刷了梅林之后会经常遇到启动失败的问题。这时候只要给路由器断电,等几秒,再次按电源键启动即可。如果还是启动不了就再重启一次。多试几次一般都能成功启动。实在不行只能重置。拿笔戳重置按钮30秒,然后继续按着重置按钮不动,重置电源启动30秒,再关闭电源30秒,最后再启动。传说中的`30-30-30`大法,重置成功就代表没变砖。

Read More

How to fix You have to set a contact email in Chrome Web Store

I have multiple accounts in Chrome Web Store to manage multiple Chrome extensions.Today I found that one of my extension [one click extensions manager](https://chrome.google.com/webstore/detail/one-click-extensions-mana/pbgjpgbpljobkekbhnnmlikbbfhbhmem) did not get updated as schedule. which is very abnormal because it's scheduled by Github action, and the log shows that the latest version had been submitted to the store several days ago.In the dashboard it's telling me to add a contract email in my account so that I could publish it. And I already added it long time ago.After several meaningless refreshes and clicking on the save button, I realized it might because of my other accounts. So I switched to other accounts and found there is one account without contact email. And the item got published once I fixed this issue.So weird because it never asks me to add contact email before. And the store hint is so unclear, it needs you to add contact email on every account, not just the current one.Honestly, the new Chrome web store dashboard is not as easy to use as the old one. Only the UI is fresher than before.Also, got an email from Microsoft Edge team, they ask me to submit my extension to Edge store, as Edge is using Chromium now. So if you have published Chrome extension before, you can try to submit it to Edge store too. The process would take less than 30 min to do it.And, the Edge store dashboard is hard to use too, especially if your extension surppots multiple language, you will need to set information like logo, screenshot, description for every language. insane, right?.

Read More

Change Ubuntu 20.04 update source to mirror site in China

Ubuntu 20.04 has been released for a while, its code is `Focal Fossa`. Again, if you are in China, better change the update source to mirror sites.This post is just a copy from [Change Ubuntu 18.04 update source to mirror site in China](https://momane.com/change-ubuntu-18-04-source-to-china-mirror), and I just replaced the source url from thoes ones of 18.04 to 20.04Here are several famous mirror sites. What you need todo is pick one from below and edit `etc/apt/sources.list`, replace the content in this file.There are some links beend commented out.- starts with`deb-src` is for source code, without them the speed would be even faster - the one with `focal-proposed` are just proposed updates, only enable them if you know the risk.**Better backup you `etc/apt/sources.list` before you do this**#### 163 (netease)_source:http://mirrors.163.com/_``` deb http://mirrors.163.com/ubuntu/ focal main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ focal-security main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ focal-updates main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ focal-backports main restricted universe multiverse # deb http://mirrors.163.com/ubuntu/ focal-proposed main restricted universe multiverse # deb-src http://mirrors.163.com/ubuntu/ focal main restricted universe multiverse # deb-src http://mirrors.163.com/ubuntu/ focal-security main restricted universe multiverse # deb-src http://mirrors.163.com/ubuntu/ focal-updates main restricted universe multiverse # deb-src http://mirrors.163.com/ubuntu/ focal-proposed main restricted universe multiverse # deb-src http://mirrors.163.com/ubuntu/ focal-backports main restricted universe multiverse ```#### alibaba (aliyun)_source:https://opsx.alibaba.com/mirror_``` deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse # deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse # deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse # deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse # deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse # deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse # deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse ```And here are also two Chinese university mirror, use them only if you are in Chinese university.#### tsinghua university``` deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse # deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse ```#### ustc (university of science and technology of China)``` deb https://mirrors.ustc.edu.cn/ubuntu/ focal main restricted universe multiverse deb https://mirrors.ustc.edu.cn/ubuntu/ focal-updates main restricted universe multiverse deb https://mirrors.ustc.edu.cn/ubuntu/ focal-backports main restricted universe multiverse deb https://mirrors.ustc.edu.cn/ubuntu/ focal-security main restricted universe multiverse # deb https://mirrors.ustc.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse # deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal main restricted universe multiverse # deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal-updates main restricted universe multiverse # deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal-backports main restricted universe multiverse # deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal-security main restricted universe multiverse # deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse ```Enjoy:)

Read More

用 Taro 和 Vue 写了一个微信小程序

最近尝试写微信小程序,毕竟是现在的趋势。然后发现因为入场太晚,各种轮子已经造得遍地飞了。比如这里有一个框架,出自京东,叫做Taro,它的目的是写一份代码,就可以在诸如微信、支付宝、字节跳动(它家的什么app支持小程序?)等端作为小程序运行。于是拿来试了以下。我本来只熟悉Vue的,这个Taro原生支持React,代码风格也都是React的样式。虽说现在3.0以上支持Vue了,可是要做的时候还是有一些转换成本。然后框架本身也还存在一些bug,不过一次编写,多端运行还是挺有意思的。项目地址:https://github.com/hankxdev/taro-v2ex以下内容来自README:### Taro + Taro-UI + Vue + V2EX API 多端小程序如题,这是一个使用了这几个技术的练习项目。我是第一次使用Taro和写微信小程序。Taro官方有个[渐进式教程](https://taro-docs.jd.com/taro/docs/guide/),使用的也是V2EX的API。#### 技术栈:- [Taro](https://github.com/NervJS/taro) - [Taro-UI](https://github.com/NervJS/taro-ui) - [Taro-ui-vue](http://taro-ui-vue.fontend.com/) - [V2EX API](https://github.com/igaozp/V2EX-API) - Vue - Vuex - SCSS - Typescript#### 本地运行和测试- `yarn` - `yarn dev:weapp` - 因为调用了v2ex 的API,用自己的微信小程序ID的话,需要去后台设置一下域名白名单,否则 API 调用会失败。#### 比教程多了一些的是:- 代码完整。官方教程的代码比较零散,甚至有些跑不通。 - 用户详情页面和节点详情页面 - 全部使用真正的V2EX API 而不是使用自定义数据 - 自定义 Tabbar - 使用了Taro-UI来做界面#### 项目存在的问题:以下是我知道的主要问题。大佬们如果发现新问题或者知道这些问题的答案,欢迎提 issues 或者 PR, 望不吝赐教。- 几个页面在切换的时候并不会刷新。这里怀疑是Taro的bug,文档中写的 [onShow](https://nervjs.github.io/taro/docs/vue/#onshow-1) 方法并不能在页面显示的时候被成功调用。 - 不怎么好看。因为主要用来熟悉Taro以及微信小程序和多端小程序开发,因此并未在界面上花功夫,几乎没有怎么写样式。使用的全都是Taro-UI 默认的组件。 - 有点卡。一方面v2ex并未提供分页的API。有些 endpoints 诸如获得节点列表、帖子列表等,都是一大堆上千条数据一下子全返回。网络慢的时候导致卡顿更加明显。 - TypeScript 使用并不规范。我很少写 TypeScript。也一直懒得学,因此基本上都是用JavaScript 的写法在写 TypeScript。同样,写好或者学习 TypeScript 也并不是我写本项目的目的。略过。 - CustomTabBar 的实现并不规范。现在的做法是写一个 CustomTabBar, 然后放置进每个页面中。正确的做法应该是。1. 在 `app.config.ts` 中的`tabList` 里,设置 `custom: true` 2. 在 `src` 目录下新建 `custome-tab-bar` 文件夹 3. 在 `custome-tab-bar` 下面添加 `index.js`,`index.wxml`, `index.wxss` 来自定义 tabbar 的样式和行为。 需要注意的是,这里不能直接写 `index.vue`,编译不出来的。![hot](https://github.com/hankxdev/taro-v2ex/raw/master/readme/hot.png)

Read More

How to trigger click event in Gmail

If you want to hack Gmail UI, to build web extension or any other similar tool that works on top of Gmail, you always want or need to trigger some DOM events in Gmail UI, like you want to click on some buttons/icons, to send out email, to make the compose window pop out, etc.You can't do it by simply use `click` function, either by Vanilla JS or jQuery, either `click` or `trigger` function can not achieve your goal.You need to do this by using [`Document.createEvent()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createEvent).Take `click` for example, you want to click an `icon`:```javascript const mouseDown = document.createEvent('MouseEvents') mouseDown.initEvent('mousedown', true, false) icon.dispatchEvent(mouseDown)const mouseUp = document.createEvent('MouseEvents') mouseUp.initEvent('mouseup', true, false) icon.dispatchEvent(mouseUp) ```You may want to make it as a function so that you can use it click any element.

Read More

Boost your Frontend development speed in China

Waiting for something is wasting time, especially when development, you always need to wait for compiling, wait for install dependencies...And if you are in China, this waiting time will be much longer because of some well known reason.Here are some changes that will save you some time in China if you are doing frontend development.## Ruby and Railschange your gem source to China mirror, source: https://ruby-china.org/```bash gem source -a https://gems.ruby-china.com ```## npmchange your npm source to China mirror, source , source: https://developer.aliyun.com/mirror/NPM```bash npm config set registry https://registry.npm.taobao.org ```or you can also install a `cnpm`, use `cnpm` rather than `npm`, to avoid change the source url permanently.```bash $ npm install -g cnpm --registry=https://registry.npm.taobao.org ```## yarnSame as npm.```bash yarn config set registry https://registry.npm.taobao.org --global ```there is no `cyarn` though## ubuntuchange sourcelist, check [this post](/change-ubuntu-18-04-source-to-china-mirror)## homebrewchange homebrew core source and repo to USTC (University of Science and Technology of China) [Mirror](https://lug.ustc.edu.cn/wiki/mirrors/help/brew.git).- change brew.git: ```bash cd "$(brew --repo)" git remote set-url origin https://mirrors.ustc.edu.cn/brew.git ```- change homebrew-core.git ```bash cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core" git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git ```- change homebrew bottles source for `bash` user ```bash echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.bash_profile source ~/.bash_profile ``` for `zsh` user ```bash echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.zshrc source ~/.zshrc ```

Read More

How to fix Git show all file as modified in WSL

## SolutionIf you open your code in Windows, and run git in WSL bash, you will have a big chance to meet an issue that `git status` shows all your file get modified. To fix the issue, run this in WSL bash:```bash git config --global core.autocrlf true ```Normally I use OSX, but today I have to go back to my Windows PC to try an extension on Edge.I'm using WSL (that's Windows Subsystem for Linux) to manage and build my projects.After I cloned my repo from Github, I tried to create a new branch for Edge version. But after I ran `git branch branchname` , it told me that there were changes need to commit first.So I ran `git status`, to check what's been changed. Then I got this:This was a simple app. so it's basically showing that every file got modified. But I hadn't do anything to any file.It's because I was using git in WSL, but I opened files in Windows system. So the line ending is `CRLF`, but git thought it's on Linux so the line should ends with `LF`.So the fix command will tell git to use `CRLF` and line ending.

Read More

Use git archive to zip your source code

To zip compiled project file is easy, normally I just use [archivejs](), write a small nodejs script, and then add a script in `package.json`.And then I need to zip the source code only. Exclude the dist folder, the huge `node_modules` folder and other folders.Basically just source code and exclude everything in .gitignore file.I tried to use `archivejs`'s `glob` api. Like so:```javascript archive.glob('**', { ignore: ['folder/**', 'file'] }) ```But I could not make it works, it either zip nothing or zip everything in my folder, I think it's because the path that I set is not correct. And I don't want to spend too much time on reading and exploring archivejs's api doc.So I came to use `zip` command in terminal. To use it is very simple, just use `-x` to exclude file, like this```bash zip -r output.zip directory -x \*\*/\*/node_modules/\* ```Which is great. But the command is too long and if there are many files you want to exclude, you will need to write down them every time.At the same time I found that with `git archive` I can do this easily. so just run```bash git archive --format=zip HEAD -o zipfile.zip ```it will pack all your source code into zip file, exclude everything in the .gitignore file. Super neat.

Read More

Shout out to Jetbrains IDE

I write this post because [Jetbrains](https://www.jetbrains.com/?from=one-click-extensions-manager) just gave me full access to all its tools as an open source project developer. So I just want to say thanks with this post.After I tried Webstorm for several days, I found it's much better than VS Code.What am I saying, Webstorm is a commercial software, while VS Code is open source and free. 😅Anyway, I found myself coding much faster with Webstorm and other [Jetbrains](https://www.jetbrains.com/?from=one-click-extensions-manager) tools. Like, I don't have to pick and configure all those extensions carefully to make so that it won't popup css selector when I'm coding JavaScript. Webstorm also has built-in spell check, it's like a life saver for non-native English speaker.Some people might think that VS code eats less RAM than Webstorm, also faster when launching. While my computer has 32 GB RAM and i7, so I never need to care about the RAM. Plus, if you install and enable too many extensions, you can also notice that VS Code takes a lot of RAM too.And I barely need to code some stuff within 30 sec, so the launching time is not an issue to me too. This is not mean I'm not gonna use VS Code, it's still great, especially it can edit any programming languages, with extensions.Its price is also reasonable, for individual, most of the tools are between $59 - $69 for the first year, cheaper for the next years.It also has many free plans, students, teachers, etc. If you are an active open source developer, you can even ask for a free license.Like me, since I have this small open source project [one click extensions manager](https://github.com/hankxdev/one-click-extensions-manager), it has more than 30k users.[Jetbrains](https://www.jetbrains.com/?from=one-click-extensions-manager) gives me full access to all its tools for a year, and can be re-subscribed every year.So if you also have open source project, try to apply for its license and try this great IDE :D

Read More

How to copy image and text to clipboard with JavaScript

I used to saw my colleague add a 30kb lib only to implement a copy function that when the user clicks on a text, copy it to the clipboard.And I also saw someone still trying to use flash or Clipboard lib to do the same thing.#### Here is how to copy text to your clipboard with JavaScript with just several lines of code. And it's cross-browserand device. No need for any 3rd party lib.```javascript function copyToClipboard(text) { const ta = document.createElement('textarea') ta.value = this.url document.body.appendChild(ta) //hide it ta.style.padding = 0 ta.style.border = 'none' ta.style.outline = 'none' ta.style.boxShadow = 'none' //select the text ta.focus() ta.select() document.execCommand('copy') document.body.removeChild(ta) } ```it's very simple, you just append an invisible textarea to the page, set value as the text you want to copy, and run `document.execCommand('copy')`. Then remove the textarea from the page.#### here is how to copy the image to your clipboard with JavaScriptNote that this one only works on Chrome and Edge right now (2020-10) https://caniuse.com/mdn-api_clipboarditemThis requires you to use `canvas` to port the image to blob first.```javascript function imageToBlob(imageURL) { const img = new Image() const c = document.createElement('canvas') const ctx = c.getContext('2d') img.crossOrigin = '' img.src = imageURL return new Promise((resolve) => { img.onload = function () { c.width = this.naturalWidth c.height = this.naturalHeight ctx.drawImage(this, 0, 0) c.toBlob( (blob) => { // here the image is a blob resolve(blob) }, 'image/png', 0.75 ) } }) } ```Then use the `Clipboard` API to do the magic```javascript async function copyImage(imageURL) { const blob = await imageToBlob(imageURL) const item = new ClipboardItem({ 'image/png': blob }) navigator.clipboard.write([item]) } ```Simple as that.

Read More

如何在电脑上调试手机真机网页以及微信浏览器网页

遇到个需求需要调试在微信中打开的网页。之前没有做过,搜了一圈那个所谓的在微信对话框中打开debugx5.qq.com的方法已经过时无用, 打开就显示你所使用对内核非X5内核。至于TBStool更是在2017年就已经停止更新,打开就闪退,浪费时间安装。看来因为开发人员对QQ搞出来的X5内核怨声载道,所以微信不得不妥协,弃用了X5内核作为内置浏览器内核了。至于spy-debugger等工具,我嫌麻烦也没装。找半天找不到有用的方法,好在同样的问题在手机上的Chrome里面打开也存在,于是就想先在Chrome里调试,万一能一起修复了呢。没想到就发现了Chrome调试工具居然也可以调试微信浏览器里打开的页面。看起来微信内建浏览器也采用了Chromium内核了。以下步骤同时适用于在电脑上调试手机浏览器网页调试和微信内建浏览器网页, 目前只限Android系统。iPhone还没去看。- 打开手机的开发者模式,这个各个手机打开方式都差不多,通过多次点击版本号来开启。- 在开发者模式里面打开 USB 调试功能- 把你要调试的网址发送到微信对话框里,然后点击打开- 将手机通过USB数据线连接到电脑- 在电脑上打开Chrome浏览器- 新建一个标签页,输入网址 `chrome://inspect/#devices`。以前你需要在 Chrome 的开发者工具里面通过点击多个菜单来打开,Chrome现在将这个页面独立出来了,体验更佳。- 这时候你的手机应该会收到 `允许当前连接的设备访问手机数据` 的通知,点击允许即可- 在电脑端 Chrome 的 `chrome://inspect/#devices` 里面就应该能看到你手机 Chrome 里面打开的页面了- 同时如果你已经在微信内建浏览器里打开了网页,你应该也能看到一个名为 `com.tencent.mm` 的浏览器里面打开了一个页面。- 点击网址下面的 `inspect` 就可以打开调试页面,其拥有电脑端Chrome开发工具的几乎所有调试功能。不得不说发现这点还是挺惊喜的。

Read More

为什么不要买罗技K380无线键盘

罗技K380无线键盘,被很多平台吹为几乎最佳的移动设备伴侣键盘,深受很多iPad乃至MacBook用户的欢迎。其优点有:蓝牙连接,最多可以连接三台设备,切换便捷。轻薄小巧,手感和笔记本键盘非常相似。而多色键帽更满足了不同审美用户的需求。因为有移动办公的需求,而Macbook的键盘手感实在太烂,所以前段时间我也买了一个,用了不多几次发现这是一个非常烂的键盘。手感差其实没有什么可以挑剔的,要做到轻薄小巧,必然牺牲键程和手感。但是其做工和用料却非常差,放在包里背了几次,就掉了一个键。不像其他键盘,键帽掉了以后按上去就可以了。K380的这个键帽我怎么装都装不上去。看了一下还在保修期内,就送回京东售后维修。然而京东最终给的答复是,键帽掉落属于外观损伤,不属于质量问题,拒绝保修。直接返回来了。返回后我一看,不但掉落的键没有装上去,旁边还多掉了一个键。仔细看了一下,之前掉落的键里面的卡件已经被按平,所以可以猜想是京东的售后人员先是尝试将按键按照传统方式按回去,暴力按了几下以后发现装不上, 然后又想拆旁边的键看一下结构怎么装。结果拆了以后没看出所以然来,新拆的这个键也装不回去了。就把键盘按键这种核心部件说成外观拒绝保修了。话说最近在另外一个网站看到有人的耳机罩在保修期内自动裂开,内部部件外露,送到京东也是以属于外观问题不予保修拒绝售后。不知道京东以相似的理由拒绝了多少保修。吐槽完京东,再仔细研究一下这个键盘,其剪刀脚结构是纯塑料,而且是非常脆弱的那种,稍微用点力就很容易变形乃至断掉,这个键就没用了。而键帽下面的连接件也是非常小而软的金属挂钩,很容易变形,当然也可以给掰回来,但是如果掰次数多了,就会断掉。也就是说,**如果你的K380键帽掉了,千万不要像其他键盘那样,尝试用力将键帽按回去,不但装不回去,还有可能永久损伤键帽甚至键帽下面的连接件**,连接件一旦坏了,键盘就废了,除非你能接受键盘少一个键。如果损伤键帽,淘宝上有几家卖的,估计就是送修或者报废的k380拆了卖零件的。一个键帽是10块钱起,下面的剪刀脚也是10块,不包邮。卖一排键帽就能买一个新的K380。所以如果你的K380键帽坏了,不要去买键帽,上淘宝或者其他二手平台卖键帽,然后拿换来的钱买一把新的键盘。当然买新键盘千万别再买k380了。

Read More

How to fix: Enzyme expects an adapter to be configured

Working on updating a legacy React project. It's still using React 8 and Node 7.9, I'd like to upgrate it to the latest React and Node.The upgrading process was boring and straight forward, I just upgrade the dependencies and fix those broken changes in some dependencies, luckily most of them do noe need any change.#### ProblemBut when I run jest test, there is an error popup in almost every test file:``` Enzyme Internal Error: Enzyme expects an adapter to be configured, but found none. To configure an adapter, you should call `Enzyme.configure({ adapter: new Adapter() })` before using any of Enzyme's top level APIs, where `Adapter` is the adapter corresponding to the library currently being tested. For example: import Adapter from 'enzyme-adapter-react-16'; ```Searched a bit, it looks like after Enzyme 3.0 and React15, you have to configure the `Adapter` to run the tests.#### SolutionTo fix it is very simple, just put these lines on the top of your test file:``` import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-15';configure({ adapter: new Adapter() }); ```While `enzyme-adapter-react-15` is also needed, you will need to use correct version per your react version.But, what if your project is very large, it has like more than 30 test files, is it mean that you will need to put these line on top of every of your test file?Of course no, here is what you should do.- create a js file, name it as `jestConfig.js` or any name you think is best. put it in any path in your project. - either create a `jest.config.js` in your project root or a block in `package.json` file - put/append these content into it:``` "jest": { // ... other jest configrations "setupFilesAfterEnv": ["/path/to/your/jestConfig.js"], } ```Notice that some tutorial might suggest you to do this with old Enzyme version``` "jest": { // ... other jest configrations "setupTestFrameworkScriptFile": "/path/to/your/jestConfig.js" } ```In newer version the key has been changed from `setupTestFrameworkScriptFile` to `setupFilesAfterEnv`, and you can use multiple config file since it now uses array as value.#### what about React 17While the most recent official `ezyme-adapter-react` is for 16, the 17 is not surpported yet, so if you are using React 17, here is another unofficial package you can use [@wojtekmaj/enzyme-adapter-react-17](https://www.npmjs.com/package/@wojtekmaj/enzyme-adapter-react-17)It has more than 6k downloads every week now, seems reliable.

Read More

how to skip git hooks when committing

Well, it's not very recommended skipping git hooks. These hooks are often for formatting code, running tests, etc.But life happens, sometimes you want to commit something quickly or even an empty commit to the server so that you can trigger some CI/CD, but you have to wait for 5 min for those tests running.So if you want to skip git hooks while commting, simply run command like this:```bash git commit --no-verify -m "YOUR COMMIT MESSAGE" ```or for short```bash git commit -n -m "YOUR COMMIT MESSAGE" ```BTW, the command to push an empty commit is```bash git commit --allow-empty -m "YOUR COMMIT MESSAGE" ```Just remember only use these commands when you are really urgent and confident about your change.

Read More

Do not store file outside of WSL, it is slow

For some reason, I have switch back to Windows OS after years of working on MacOS.Most of the time I'm working on frontend projects, so to get better DX, I use Windows Subsystem for Linux (WSL for short). And I'm using version 2 of it.But I'm not a fancy Vim programmer, I heavily relay on IDEs like VS Code and WebStorm. So I still have to install these IDEs on the Windows.I'm able to install zsh, oh-my-zsh, fuck, p10k along with other tools in the WSL, gives me a similar experience as on Mac.At the first I put the project on Windows file system, because in this way I can easily access the file from IDE. And then in the WSL2 terminal, I go to the Windows path like this:```shell cd /mnt/DISK/PROJECT_FOLDER ```I can run all these frontend commands like `yarn`, `nvm`. Seems smooth.Soon I find a big issue. The hot module replacement not working, the building process is very slow. I have to restart the project dev command after making change to the code. Clearly this is not an acceptable solution.After searching a bit on the internet, I move the project back to WSL file system, then every thing works perfectly. The building is faster, the HMR is working.To access the file in WSL from IDEs, VS Code has [extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) to open file from WSL easily, just install it, and in the terminal, just switch to the project folder and type `code .`. Or in the IDE, click `File-Open Folder`, it will show your linux home folder by default. For WebStorm, it will also show the WSL folder in the open file dialog.Finally, I can use my i7+32G Windows, don't need to listen to the crazy fan noise on the MBP :D

Read More

A wrong solution on Leetcode

With all these years as an engineer, I never tried Leetcode.Maybe it's because I never prepared for interview before.Well, now I have to, because the company I just joined in 6 month ago, is gonna shutdown it's office here.Technically not shutdown, it just lays off every programmer because it thinks we are more expensive than those in another country.So people all say you have to try Leetcode to practice your programming skill.As a frontend engineer, why should I try it? Most of the time, I'm building UI, calling API, fighting with webpack configuration, cursing Google Lighthouse always gives my webpage low score, arguing with designer why is same font size seems so big on production than on Figma...After I tried to solve some problems on it, I found it is pretty fun, except it made me think myself not a qualified programmer.For the problem [palindrome permutation](https://leetcode.com/problems/palindrome-permutation/). I found it's top 1 solution is not correct.The code is like this:```javascript var canPermutePalindrome = function (s) { let arr = [...new Set(s.split(''))] let m = arr.length - Math.ceil(s.length / 2) if (arr.length == 1 || m == -1 || m == 0) return true return false } ```The code is very short and clean, pretty impressive at the first glance, and most of the cases it works, Leetcode also thinks it's a correct one. But if you try with a string `aaaabbbbcc`, it returns `false`, while it should be `true`. `ababccbaba` clearly is a palindrome. Clearly the test cases could not cover all the scenes Here is my solution, a simple and basic one:```javascript var canPermutePalindrome = function (s) { let set = new Set() for (const str of s) { set.has(str) ? set.delete(str) : set.add(str) } return set.size

Read More

Use Husky to manage git hooks

[Git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) is a powerful tool to make your life easier.It fires off custom scripts when certain important actions occur. one common scene like: you want to lint your code every time when you commit changes, so that your code has a good style and following your team's code specification. Or you want to run test before pushing your code/> Like many other Version Control Systems, Git has a way to fire off custom scripts when certain important actions occur. There are two groups of these hooks: client-side and server-side. Client-side hooks are triggered by operations such as committing and merging, while server-side hooks run on network operations such as receiving pushed commits. You can use these hooks for all sorts of reasons.With git hooks, you can make these actions automatically.While [**husky**](https://www.npmjs.com/package/husky) is a tool to make Git hooks easier to use. To use it is very easyFirst you install it as a development dependency with npm:```bash npm install husky --save-dev ```OR **yarn**:```bash yarn add husky -D ```Then in your `package.json` , add:```json { "husky": { "hooks": { "pre-commit": "yarn lint", "pre-push": "yarn test", "...": "..." } } } ```In the example, we added 2 hooks, `pre-commit` and `pre-push`, they are meant what they named.- **pre-commit** means, every time you run `git commit`, it will run `yarn lint` first, and commit the code if `yarn lint` passes without error. - **pre-push** run command `yarn test` when you run `git push` - `yarn lint` and `yarn test` are commands that you define in your `scripts` in package.json. normally those commands that you runs manually, like `yarn start` or `yarn dev`, etc. you can add any command you want and run the, in the hooks.Pretty straight forward and easy :)

Read More

how to use jQuery with TypeScript

It's 2020, why do you still need to use jQuery?Yeah, jQuery has 53.8k stars on Github. And still, there are more than 97% of websites are using it.Even if you don't want to use it in your fancy project. It's still a good choice to use it when you are building a demo, prototype, or some small side project. I mean, when you are configuring your fancy project with wepack, react/vue/angular, babel, I already have done a small page which can send request to the API and show the result on the page.Anyway, if you want to use jQuery with TypeScript. Here are the steps that you need to do.#### add jquery```bash yarn add jquery ```### add jquery typesthis will suppress VS Code complains that it can not find jquery types```bash yarn add @types/jquery ```### config tsconfig.json to suppress another warning message> can only be default-imported using the 'allowSyntheticDefaultImports' flagts(1259) > index.d.ts(35, 1): This module is declared with using 'export =', and can only be used with a default import when using the 'allowSyntheticDefaultImports' flag.goto your `tsconfig.json` file, add one line in your the `compilerOptions` block. if you don't have this tsconfig file in your project's root folder, then create one.```json "allowSyntheticDefaultImports": true, ```Then you can use jquery in your ts file like this:```javascript import $ from 'jquery'$.ajax().then().catch().finnally() ```

Read More

Firefox offline installer official download page

Why do I need offline installer of Firefox?Because its online installer is too slow. (Yes the installer that you download from its index page is online installer, basically it's just a downloader, that when you run it, it just downloads the offline installer and install it for you. If you don't feel it's slow, then you don't need to read this post.) And maybe the online installer even uses P2P so when it will also upload to others while installing.So in this [Firefox offline installer download page](https://www.mozilla.org/en-US/firefox/all/#product-desktop-release), you can pick any version for any OS to download the Firefox, like Firefox Developer Edition, Firefox Beta, for Mac, for Windows, fore Linux and even for Android mobile.![img.png](/blog/firefox_offline_installer.png)And, here is also [Chrome release channels](http://www.chromium.org/getting-involved/dev-channel), where you can download several chrome versions, like Chrome Dev version, Beta, and Canary, for different OS.Like me, I like running different profile in different versions of Chrome and Firefox, because their icon are different ,easier than check the profile name in the top right corner.

Read More

Enable HTTPS on local, even with a customizable domain

Normally, when you're debugging your web app on local, you can access it with either `http://localhost:PORT` or `http://127.0.0.1:POST`.The `POST` might be `8080` or `3000` or what's ever.But sometimes you might need to access your local web app with `https`, like `https://localhost:8080`. So you search online with `local https`, tons of posts/tutorials teach you how to generate self-signed SSL certificate and assign to your localhost.As an app developer, you might feel it's too complicated and don't want to spend half an hour on it.So here I found the easiest way to achieve this with just one small tool and 1 line command, without changing anything in your actual app.This tool is (**local-ssl-proxy**)[https://www.npmjs.com/package/local-ssl-proxy] , it is an open sourced npm package. so you can just install it by running```shell npm install -g local-ssl-proxy ```Then you can just access your local web app with this command(let's assume the app is running on 8080 port):``` local-ssl-proxy -s 8081 -t 8080 ```In this way, your web app's url will be `https://localhost:8081`. And you can still access your app from the original http url and port.The **-s** is the port that the app will be proxies to, the **8080** is the actual port your app running on.### what's more?If you don't like to use the localhost, or you have to assign as domain to your local app. you can simply make it with 2 more steps.- add a line in your computer's **hosts** file. - if you are on Windows, the hosts file's path is `c:\windows\system32\drivers\etc\hosts` - on Mac and Linux the path simply is `/etc/hosts` - you need Admin/root privilege to modify it The line would be ```shell 127.0.0.1 YOUR_DOMAIN ``` The `YOUR_DOMAIN` can be anything, you don't even register the domain, because it only works on your computer. But don't use some famous one like youtube.com or google.com, some browser would block it. Let's say we use the `hank.momane` in below example.- Then run the proxy like this:```shell local-ssl-proxy -s 443 -t 8080 -n hank.momane ```So by using the SSL port **443** and an extra parameter **-n**, now you can access your app via `https://hank.momane`.

Read More

Fixing the Close Action for a Modal popup in Mendix

Our tester engineer recently reported an issue where in a Mendix app, it takes more than 3 seconds to close a popup after clicking the close button.We looked closely at the log and found that it always does some rollback and fetching before actually closing the modal.We found that the default Close action for the modal is Cancel, which means cancel changes that are not committed in Mendix. This explains why it triggers rollback and retrieve before closing, even though no changes have been made.To fix this, you can change the Close action to _Close Page_.However, there is no Close page in the list. There are some options that are bound to other buttons in the modal.So you have to add a button in the modal, bind close page action to the button, and change the Close action of the model to the button.Sometimes you don’t need this extra button shown in the modal; you can just give it a hidden style, like adding `display:none` style in its appearance tab.---EOF---

Read More

Mendix: How to connect to built in hsql database from dbeaver

By default, Mendix project is using HSQL as the database.If you want to check the db tables and data, you can simply do this:- start the project from Mendixo Studio Pro - in the console tab, you will find a sub tab `Advanced` - click it will open a menu, there is a item `start built-in database viewer`A database viewer will open, it has simple and basic UI, which you can check all the tables or you project. and do query, delete actions easily.And you can also run SQL in it.Well, this is nice. but it's a bit too simple.Sometimes, especially for those who has more development experience and has own favorite data viewer like MySql workbench, or DBeaver, how to use them to view the Mendix project's database.It's also simple.Let's take DBeaver as example.- open the built-in db viewer in Mendix Studio Pro.- Click `File` - `Connect`, this will open a small window.- In this window, it shows the connect info of current project's dababase, let's call this window `SP Window` for referrence. - Now open DBeaver- Click the new connection icon, in the popup window, type `HSQL` to search, and pick `HSQL Server`- Click `next`. in next window, select `URL`, copy the `jdbc...` value in the SP Window as the `JDBC URL` in DBeaver- For the `Username` and `Password` just use the same one in the SP Window- Click `Finish`You are done, now you can play with the Mendix project database with DBeaver---EOF---

Read More

Mendix: How to debug javaaction with IntelliJ and VS Code

Sometimes, you may want to debug the Java actions in Mendix project. In the Mendix Documentation, it only shows how to debug Java actions with Eclipse. But for those who are using IntelliJ or VS Code, how to do that?In this post, I will show you how to debug Java actions in Mendix project with IntelliJ and VS Code.## Debug Java action with IntelliJ1. Open the Mendix project in Mendix Studio Pro 2. Goto you App settings, double click your app configuration. 3. In the `Server` tab, input `-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005` in the `Extra JVM parameters` field. This will enable the remote debug mode for the Mendix project. ![Mendix Project Remote Debug config](/blog/mendx-app-config.png) 4. Open the Mendix Project in IntelliJ 5. Open the debug configuration, click the `+` icon to add a new configuration, select `Remote JVM Debug` in the popup window. 6. Make sure the parameters in `host` and `port` are the same as the `address` in the Mendix project settings. and also `Command line arguments for remote JVM` should be same as the `Extra JVM parameters` in the Mendix project settings. ![IntelliJ Remote Debug config](/blog/intellij-debug-config.png) 7. Click `Apply` and `OK` to save the configuration. 8. In the Mendix Studio Pro, click the `Run` button to start the project. 9. Now you can set breakpoints in the Java action code, and click the debug button to start the debug session.## Debug Java action with VS Code 1. Keep the Mendix project settings as the same as the IntelliJ settings. 2. Open the Mendix project in VS Code 3. Install the `Java Extension Pack` in the VS Code 4. Open the debug panel in VS Code, click the `Add Configuration` button, select `Java: Attach to Remote Program` in the popup window. 5. Make sure the `host` and `port` are the same as the `address` in the Mendix project settings. and also `Command line arguments for remote JVM` should be same as the `Extra JVM parameters` in the Mendix project settings. ```json { "type": "java", "name": "Mendix Remote Debug", "request": "attach", "hostName": "localhost", "port": 5005 } ``` 6. Click `Apply` and `OK` to save the configuration. 7. In the Mendix Studio Pro, click the `Run` button to start the project. 8. Now you can set breakpoints in the Java action code, and click the debug button to start the debug session.

Read More
A Simple bash script to modify macOS DNS by detecting SSID

A Simple bash script to modify macOS DNS by detecting SSID

I believe most of us have different DNS settings for different networks. For example, in my case, I use Google's public DNS `8.8.8.8` and Cloudflare's `1.1.1.1` at home, but at work, I have to clearthe DNS reacord and let my Mac use the default DNS provided by the company.It's a bit annoying to change the DNS settings manually every time I switch networks. So I wrote a simple bash script to automatically change the DNS settings based on the SSID of the network I'm connected to.Here is the script:```bash #!/bin/bash # clear or set dns function set_or_clear_dns() { local current_ssid=$(networksetup -getairportnetwork en0 | cut -d ':' -f 2 | xargs) if [[ "$current_ssid" == "YOUR_COMPANY_WIFI_SSID" ]]; then # Clear DNS to Automatic (Empty) sudo networksetup -setdnsservers Wi-Fi empty else # Set DNS to 1.1.1.1 and 8.8.8.8 sudo networksetup -setdnsservers Wi-Fi 1.1.1.1 8.8.8.8 fi } set_or_clear_dns ```You can replace `YOUR_COMPANY_WIFI_SSID` with your company's Wi-Fi SSID. Ofcause, you can add more SSID conditions and DNS settings as needed. And in the code I just simply clear the DNS settings to automatic when connected to the company's Wi-Fi, you can also set it to other DNS servers, by replacing `empty` with the DNS servers you want to use.

Read More
How to shuffle an array

How to shuffle an array

In a small talk, one of my friends asked me, without searching or using AI, can you figure out how to shuffle an array in JavaScript? Surprisingly, I couldn't answer him immediately. I was thinking about the `sort` method, but I couldn't remember the exact implementation, and I even thought about using timestamp as seed to generate a random number.After the talk, I figured out the answer in a very short time. Here is the code:```javascriptconst shuffle = (arr) => arr.sort(() => Math.random() - 0.5); ``` Fine, this code is written by AI and its so simple and obvious. And here is the code that I want to write:```javascript const shuffle = (arr) => { const len = arr.length; for (let i = 0; i { for (let i = arr.length - 1; i > 0; i--) { const randomIdx = Math.floor(Math.random() * (i + 1)); [arr[i], arr[randomIdx]] = [arr[randomIdx], arr[i]]; } return arr; } ```why it is more effective? Because - compare with using sort function, it only needs to iterate through the array once, while the sort method may iterate multiple times - compare with my implementation, mine always generates a random number which from 0 to the length of the array, while Fisher-Yates shuffle algorithm only generates a random number which from 0 to the current index of the array. - compare with my implementation, mine always swaps the element with itself, while Fisher-Yates shuffle algorithm only swaps the element with the element that has not been swapped.-EOF-

Read More
The differences between React.memo, useCallback and useMemo

The differences between React.memo, useCallback and useMemo

In React, `React.memo`, `useCallback` and `useMemo` are three hooks that can help you optimize your React application. They are similar in some ways, but they are used for different purposes. In this article, I will explain the differences between them.## React.memo`React.memo` is a higher-order component that can be used to prevent unnecessary re-renders of a functional component. It is similar to `PureComponent` in class components. When you wrap a functional component with `React.memo`, React will only re-render the component if the props have changed.Here is an example:```javascript import React from 'react';const MyComponent = React.memo(({ name }) => { return {name}; }); ``` In the example above, `MyComponent` will only re-render if the `name` prop has changed.## useCallback`useCallback` is a hook that returns a memoized version of a callback function. It is useful when you need to pass a callback function to a child component, and you want to prevent the child component from re-rendering unnecessarily.Here is an example:```javascript import React, { useCallback } from 'react';const MyComponent = ({ onClick }) => { return Click me; };const ParentComponent = () => { const handleClick = useCallback(() => { console.log('Button clicked'); }, []); return ; }; ```In the example above, `MyComponent` will only re-render if the `onClick` prop has changed.## useMemo`useMemo` is a hook that returns a memoized value. It is useful when you need to calculate a value that is expensive to compute, and you want to prevent the value from being recalculated unnecessarily.Here is an example:```javascript import React, { useMemo } from 'react';const MyComponent = ({ a, b }) => { const result = useMemo(() => { return a + b; }, [a, b]); return {result}; }; ```In the example above, `result` will only be recalculated if the `a` or `b` props have changed.## Summary- `React.memo` is used to prevent unnecessary re-renders of a functional component. - `useCallback` is used to return a memoized version of a callback function. - `useMemo` is used to return a memoized value. - `useCallback` is actually a special case of `useMemo`, where the memoized value is a function.

Read More

Mendix: How to call Microflow in Java Action and use its return value

After you dive into Mendix for a longer time, you would find yourself writing java code now and then. Don't worry, good news is that you don't need to write a lot of java code, Mendix is Low Code anyway.In Mendix, java code is been called Java Action.And you will also find you have to call Microflow in your java code. And in the latest version, approx Mendix 8.1, calling a Microflow can be in this way:```java com.mendix.core.Core.microflowCall("YourModule.SUB_ShowMessage") .inTransaction(true) .withParam("Name", "Hank") .withParam("Message", "Just another developer") .execute(this.getContext()); ```This will call your Microflow `SUB_ShowMessage` in your `YourModule` module, passing **Name** and **Message** to it.Sometimes if your Microflow has too many parameters, clearly chaining too many **withParam** is not a proper way. You can use **HashMap**```java java.util.Map params = new java.util.HashMap<>(); params.put("Name", "Hank"); params.put("Message", "Just another developer"); params.put("note", "This seems also a lot of code") com.mendix.core.Core.microflowCall("YourModule.SUB_ShowMessage") .inTransaction(true) .withParams(params) .execute(this.getContext()); ```You can see there is no return value in the Microflow or you don't want/need to use the return value of it.But what about you want to use it?If your Microflow returns a simple value, like _String_, _Long/Integer_, _Boolean_, then it's easy, just assign it to your Java variable:```java Boolean mfValue = com.mendix.core.Core.microflowCall("YourModule.SUB_ShowMessageWithReturnValue") .inTransaction(true) .withParam("Name", "Hank") .withParam("Message", "Just another developer") .execute(this.getContext());// then you can use the mfValue ```Well, this seems easy, what if the Microflow returns an object?When a Microflow returns an Object, it will lose its Entity definition and returns an interface **IMendixObject\*. You can not use it without init it with the corresponding Entity. Let's say, it returns a **System.User\* entity, then you will do this to use it:```java // returns IMendixObject IMendixObject mendixObj = com.mendix.core.Core.microflowCall("YourModule.SUB_ShowMessageWithReturnValue") .inTransaction(true) .withParam("Name", "Hank") .withParam("Message", "Just another developer") .execute(this.getContext());// init with Entity's init function final User user = User.initialize(this.getContext(), mendixObj); ```---EOF---

Read More
Mendix: how to set entity association in Java Action

Mendix: how to set entity association in Java Action

In Mendix, you can set an entity association in a microflow by simply set the association attribute of the entity. But how to do this in a Java Action?Let's say you have two entities, `EntityA` and `EntityB`, and EntityA has a one to many association with EntityB. The association attribute in EntityB is `EntityB_EntityA`. Well its quite straight forward.Here is the code snippet:```java import com.mendix.core.Core; import com.mendix.systemwideinterfaces.core.IContext; import com.mendix.systemwideinterfaces.core.IMendixObject; import com.mendix.systemwideinterfaces.core.IMendixObjectMember; import module.name.proxies.EntityA; import module.name.proxies.EntityB;//... your other action codeEntityB entityB = new EntityB(context); EntityA entityA = new EntityA(context); entityB.setEntityB_EntityA(entityA); // when everything is done, you might also want to commit it entityB.commit() ```

Read More

JavaScript :How to detect if an element is visible

There are many ways to detect if an element is visible on page. like```javascript element.style.display !== none && element.visibility !== hidden ```And what if you want to detect if an elemtn is in current viewport?Easy, right? You just need to use `getBoundingClientRect`, check if it's top and bottom are all in viewport:```javascript const position = element.getBoundingClientRect()if (position.top >= 0 && position.bottom I'm not visible```If you run `document.querySelector('child').style.display === 'block'`, you will get a **true**.How to fix this?Well we can use `offsetParent`. The HTMLElement.offsetParent read-only property returns a reference to the element which is the closest (nearest in the containment hierarchy) positioned ancestor element.so if `elment.offsetParent === null`, then we can tell that the `element` is not visible.

Read More
I created a new app called IronTrack

I created a new app called IronTrack

IronTrack is my new mobile app for anyone who wants a simple, motivating way to plan gym sessions and track every workout. I built it to remove the friction between "I should train" and "I did train" by making plans easy to create and logging fast and reliable.## Why I built IronTrackI wanted one place to plan workouts, stick to them, and see progress without a dozen taps. IronTrack focuses on the essentials: smart plans, manual control, streaks, and precise tracking for every set.## Key features### AI plan generatorTell IronTrack your height, weight, age, and goal, and it builds a plan for you. This is great if you want a quick, reasonable starting point without spending hours researching routines.### Manual plan builderPrefer full control? Build a plan by hand. Add exercises, customize sets and reps, and tweak things anytime.### StreaksConsistency matters. IronTrack shows your streaks so you can keep the momentum and see your habits build over time.### Detailed workout trackingLog every workout with time, reps, weight, and exercise name. IronTrack keeps it clean and fast, so you can stay focused on training.## Screenshots Dashboard IronTrack Plans Tracking workout Exercise details Finish screen History screen AI plan builder Chinese version Workout history Exercise library## How to get started1. Create a plan with AI or build one manually. 2. Start your workout and log time, reps, and weight as you go. 3. Keep your streak alive and watch your history grow.## ShoutoutsBig thanks to Cloudflare, GitHub, and Neon for their generous free tiers. They made it much easier to ship IronTrack and keep costs low while I built and tested the app.If you want a clean, focused gym tracker that still feels smart, IronTrack is for you. I would love feedback and feature requests as I keep improving it.

Read More